world/view/projection transform bug in c++ [solved]

Started by
6 comments, last by Kapoef 14 years, 2 months ago
Hi I have a weird bug. I recently started using DirectX9 in visual studio C++. Thus far I have managed to load and display meshes from *.x files. The problem is: when I load in more than 1 model and wish to translate/rotate only one of the models all the models loaded are transformed. Basically what I want to do is load a room (which would be one model), and then use a different model to move around the room. I am using: D3DXMatrixRotationX, D3DXMatrixRotationY, D3DXMatrixRotationZ, and D3DXMatrixTranslation to transform a giving model. I think the problem is because the LPDIRECT3DDEVICE9 is used to load both the models and the same LPDIRECT3DDEVICE9 is used to transform the models. I don't know how to tell LPDIRECT3DDEVICE9 (or whatever I need to tell) to transform the models independently. I'm not using DXUT cuz I wanna know how to load and move object manually. Here is some code thus far, it might be to much code but I wanna define/show what I am doing and how I am doing it. I omitted some of the code cuz it gets to long. ////////////////////////////////////class header file//////////////////////// class CStaticMesh { private: LPDIRECT3DDEVICE9 pd3dDevice; //device pointer //MESH data LPD3DXMESH mesh; // Our mesh object D3DMATERIAL9 *meshMaterials; LPDIRECT3DTEXTURE9 *meshTextures; DWORD numMaterials; float MeshPosRot[6]; //end MESH DATA public: CStaticMesh(LPDIRECT3DDEVICE9 DevicePTR); ~CStaticMesh(); HRESULT loadMeshFile(char *dir); HRESULT drawMeshID(); HRESULT moveMeshID(FLOAT nx=0.0f,FLOAT ny=0.0f,FLOAT nz=0.0f); HRESULT rotateMeshID(FLOAT nx=0.0f,FLOAT ny=0.0f,FLOAT nz=0.0f); }; ///////////////////////////class implementation file///////////////////// CStaticMesh::CStaticMesh(LPDIRECT3DDEVICE9 DevicePTR) { ...... pd3dDevice=DevicePTR; ...... } HRESULT CStaticMesh::loadMeshFile(char *dir) { ......... if(FAILED(D3DXLoadMeshFromX(dir, D3DXMESH_SYSTEMMEM, pd3dDevice, NULL, &materialBuffer,NULL, &numMaterials, &mesh ))) return false; ........ } HRESULT CStaticMesh::drawMeshID() { for (DWORD i=0; i<numMaterials; i++) { // Set the material and texture for this subset pd3dDevice->SetMaterial(&meshMaterials); pd3dDevice->SetTexture(0,meshTextures); // Draw the mesh subset mesh->DrawSubset( i ); } return true; } HRESULT CStaticMesh::moveMeshID(FLOAT nx,FLOAT ny,FLOAT nz) { D3DXMATRIX objMat,moveMat; pd3dDevice->GetTransform(D3DTS_WORLD, &objMat); D3DXMatrixIdentity(&moveMat); MeshPosRot[0]=nx; MeshPosRot[1]=ny; MeshPosRot[2]=nz; D3DXMatrixTranslation(&moveMat,MeshPosRot[0],MeshPosRot[1],MeshPosRot[2]); D3DXMatrixMultiply(&objMat, &objMat, &moveMat); pd3dDevice->SetTransform(D3DTS_WORLD, &objMat); return true; } HRESULT CStaticMesh::rotateMeshID(FLOAT nx,FLOAT ny,FLOAT nz) { D3DXMATRIX matRotate,finalMat,objMat; pd3dDevice->GetTransform(D3DTS_WORLD, &objMat); MeshPosRot[3]=nx;//update new rotation MeshPosRot[4]=ny; MeshPosRot[5]=nz; // Set the rotation D3DXMatrixRotationX(&matRotate, MeshPosRot[3]); D3DXMatrixMultiply(&finalMat, &objMat, &matRotate); D3DXMatrixRotationY(&matRotate, MeshPosRot[4]); D3DXMatrixMultiply(&finalMat, &finalMat, &matRotate); D3DXMatrixRotationZ(&matRotate, MeshPosRot[5]); D3DXMatrixMultiply(&finalMat, &finalMat, &matRotate); pd3dDevice->SetTransform(D3DTS_WORLD, &finalMat); return true; } //////////////////////////main file////////////////////////// CStaticMesh *cmesh; CStaticMesh *cmeshRoom; VOID render() { pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER ,D3DCOLOR_XRGB( 0,0,0 ), 1.0f, 0 );//clear to black if( SUCCEEDED( pd3dDevice->BeginScene() ) ) { cameraStuff(); cmesh->moveMeshID(moveX,moveY,moveZ); cmesh->rotateMeshID(rotateX,rotateY,rotateZ); cmesh->drawMeshID(); cmeshRoom->drawMeshID(); pd3dDevice->EndScene(); pd3dDevice->Present(NULL,NULL,NULL,NULL); } } INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow ) { ....... cmesh=new CStaticMesh(pd3dDevice); cmeshRoom=new CStaticMesh(pd3dDevice); if(!cmesh->loadMeshFile("DwarfWithEffectInstance.x")) return false; if(!cmeshRoom->loadMeshFile("room.x")) return false; ........ render(); ........ delete cmesh; delete cmeshRoom; return (int) msg.wParam; } ///////////////////////////END//////////////////////////////// If you look at the render function: I am only moving the cmesh model and not the cmeshRoom model. But when I compile and run both models are transformed.... I hope you can help me. Thanks [Edited by - Kapoef on March 18, 2010 10:36:12 AM]
Advertisement
It sounds like you need to restore the world matrix after you're done drawing each object.
D3DXMATRIX oldWorld;dev->GetTransform(D3DTS_WORLD,&oldWorld);dev->SetTransform(D3DTS_WORLD,&thisObjectsMat); // set the transform here!... // do the renderingdev->SetTransfrom(D3DTS_WORLD,&oldWorld);

It's probably clearer (to others and yourself) if you set the world transform just before you render, not in another function.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

I tried your(Buckeye) approach but to no avail. When I implemented:

D3DXMATRIX oldWorld;
dev->GetTransform(D3DTS_WORLD,&oldWorld);
dev->SetTransform(D3DTS_WORLD,&thisObjectsMat); // set the transform here!
... // do the rendering
dev->SetTransfrom(D3DTS_WORLD,&oldWorld);

no transformation was done at all.

Is there a way to get access to the matrix for a specific model only. As I understand it the device in use only has one D3DTS_WORLD. But all objects loaded in are loaded using the one device thus making all the world transform relative to each other, or not?

What the code does now is:

Backup old world -> transform the model a bit -> display -> restore the old world AND the old model.

Thus the entire old world is restored and the next transformation will be voided, since the old world is continuously restored.

I guess I am missing something... but idk what thats the big question....
You can't use the device to store the transform state of a model instance. As you've noticed you can only have one world transform set onto the device at a time.

Typically you'll have "instances" of a particular model, where an instance is one occurrence of a particular model in your scene. So if you wanted to draw your room model 5 times in the scene with different positions, you would have 5 instances but only the one model.

The simplest version of instance can just store a world matrix, and a pointer to the model. Then when you want to move or rotate an instance you alter the matrix that belongs to that instance, and when you want to draw the instance you set its world matrix onto the device and render the model.

Oh and if you want to post code here, you can use the "code" or "source" tags. See the faq.
Quote:As I understand it the device in use only has one D3DTS_WORLD. But all objects loaded in are loaded using the one device thus making all the world transform relative to each other, or not?

Loading a file does not effect the world transform currently in use by the device. They're totally unrelated, provided you mean you use D3DXLoadMeshFromX or some similar function to load objects.

When you call mesh->DrawSubset(), the rendering is done with whatever viewport, view matrix and projection matrix the device currently has set.

Did you remove the SetTransform calls from your move/rotateMesh functions? If you didn't, the next mesh will use the device-world matrix from whatever you last set it to.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

ok as i understand it now:

basic code flow follows:

1. INIT all
1.1. Load 2 *.x model (model A and model B).
1.2. Set D3DTS_WORLD to (0,0,0).

2. LOOP
2.1. Begin frame (using pd3dDevice->BeginScene() ).
2.2. Set new D3DTS_WORLD for model A.
2.3. Display model A. (using DrawSubset() ).
2.4. Set new D3DTS_WORLD for model B (in this case D3DTS_WORLD to (0,0,0) ).
2.5. Display model B. (using DrawSubset() ).
2.6. End frame(using pd3dDevice->EndScene() ).
2.7. display Buffer (pd3dDevice->Present(NULL,NULL,NULL,NULL) ).

k well i tried the above approach and it still does not work... The entire frame is stationary model A does not move at all.

Do any of you have a simple complete example that i could look at? this is probably a trivial problem but i fail to correct my error.

By the way thanks for the advice so far ;)
D3DXMATRIX aMat, bMat;D3DXMatrixTranslation(&aMat,1,1,1); // at x=1, y=1, z=1D3DXMatrixTranslation(&bMat,0,0,0); // at the originfloat aAngle = 0;float bAngle = 0;LOOP:  dev->BeginScene();  D3DXMATRIX tmp, world;  aAngle += 0.1f;  bAngle -= 0.2f;  D3DXMatrixRotationX(&tmp,aAngle);  world = tmp * aMat;  dev->SetTransform(D3DTS_WORLD,&world);  objA->DrawSubset(..);  D3DXMatrixRotationY(&tmp,bAngle);  world = tmp * bMat;  dev->SetTransform(D3DTS_WORLD,&world);  objB->DrawSubset(..);  dev->EndScene();  dev->Present(...);// end LOOP

That's all off the top of my head, but it should result in objA at (1,1,1) rotating about it's local x-axis. objB, at the origin, should be rotating twice as fast as objA about the y-axis at the origin.

This all assumes you have a projection and view set up to be able to see those two points, etc., etc.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Buckeye you are my hero!!!!!!!!! IT IS ALIVE!!!

I understand where my error was now. I multiplied the matrices incorrectly. I guess I'll have to review my 3D math algebra again...

thank you thank you thank you thank you thank you thank you

This topic is closed to new replies.

Advertisement