Sign in to follow this  

DirectXTutorial Airplane 2.x propeller spin problems - please help

This topic is 2855 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I've been following the DirectXTutorials and have got to the point of loading textured meshes which works fine. In that tutorial we have to load the "airplane 2.x" mesh included with the directx sdk. At the end of the tutorial one of the exercises he suggests to do is to make the propeller of the plane spin. I am having real trouble with this one, all the other exercises I've been able to do just fine but I've been working on this propeller now for 4 days and I can't get it to work right. What happens is that it does rotate but it looks like the axis of rotation is off centre and hence the whole propeller appears to move around in more of a large circle rather than on a single point through the centre. I've searched this forum, and found this thread (which is now closed hence the new thread): http://www.gamedev.net/community/forums/topic.asp?topic_id=554618 however the one suggestion of moving it to the origin and back again doesn't work, even if moving it to the origin worked (which it doesn't), how on earth do you get the coordinates to move it back to where it's supposed to be again??? I've done other things with the scene which seems to be working OK, I've got a spaceship mesh diving in over the plane as if to bomb it etc, I've got the whole plane spinning around the Y-axis, which all works great, I just am really stuck on this propeller - could someone please help me?? Is there a way to move the propellers origin to the center of it perhaps?? Thanks a lot!

Share this post


Link to post
Share on other sites
Quote:
the axis of rotation is off centre ... moving it to the origin and back again doesn't work

The rotation around an axis other than the center of the propellor is because, as you read in the thread you referenced, the propeller needs to be moved to the origin, rotated, then moved back to its position relative to the rest of the plane.

That translation-rotation-translation needs to be done before you apply any other transforms (matrix multiplications).

First-

You need to determine the coordinates of the propeller at it's center of rotation. You can probably use the average value of all the vertex positions in the propeller mesh. You also need to determine which axis to rotate about. Even if you just guess which axis, you can easily change that later if it's not right.

You'll also need a variable to store the angle of rotation, and a matrix (D3DXMATRIX) to store the propeller's orientation transform.

Second-
Assume you'veccalculated the average position vector D3DXVECTOR3 Vavg.

Create a translation matrix, a rotation matrix, a second translation matrix and a combined matrix as follows:

D3DXMATRIX trans1, rot, trans2, propOrient;
D3DXMatrixTranslation(&trans1,-Vavg.x, -Vavg.y, -Vavg.z); // moves to origin
D3DXMatrixTranslation(&trans2, Vavg.x, Vavg.y, Vavg.z); // moves back to pos
D3DXMatrixRotationX(&rot, rotAngle); // may have to be rot about Y or Z
propOrient = trans1 * rot * trans2; // trans to origin, rot, trans back

Each frame, change the rotAngle a little (you've probably already figured how to do that), calculate the propOrient matrix.

Third-
When you render the propeller mesh, the world matrix for the prop = propOrient * meshWorld, where meshWorld is whatever matrix you're using to render the airplane.

The order of all those matrix multiplications is important!

Share this post


Link to post
Share on other sites
Hi thanks for the help, that's got me pretty close actually - unless I'm doing it completely wrong, the averages for the mesh aren't finding the center unfortunately, but I just spent quite a while going through every vertex point in the mesh and using that as the point for translation - after 1009 keypresses of testing each index I finally found one that was close enough!! It seems to have some wobble on it, but it's way better than it was...

I used a messagebox to print out the x,y,z of the vertex which seems pretty close, and the average coordinates, and they're way off each other, so not quite sure what's wrong there, I simply cloned the mesh so I could get it into a CustomFVF then added all the x's and the y's and the z's together then divided by the number of vertexes in the mesh.... also I can't believe the propeller has way over 1000 vertexes in it!!

Share this post


Link to post
Share on other sites
Quote:
added all the x's and the y's and the z's together then divided by the number of vertexes in the mesh

It that's just for the propeller mesh (not the entire airplane!), that's adequate. However, considering the propeller should be symmetric, that should be all but exact.

Sounds like you're applying some sort of transformation before you use the translation matrices, or not accessing the vertex positions in the vertex buffer correctly. Make sure you're using propOrient * meshWorld for the world transform for the propeller.

This may be a little easier. I don't know how your getting the vertex buffer but:

D3DXVECTOR3 Vavg(0,0,0), vertPos;
for(WORD v=0; v<numVertices; v++)
{
// calculate the offset into the vertex buffer
WORD offset = v*numBytesPerVertex; // you're doing this, right?
// I'm guessing you're not accessing the vertex pos correctly
vertPos = ... however you access the position vector for each vertex
Vavg += vertPos;
}
Vavg /= numVertices;

Share this post


Link to post
Share on other sites
Ok, you were right I was doing it for the whole mesh, it's still not working but I'm even closer now - I've had to optimize the mesh when it's loaded so I can get the attribute table, then every frame if it's time to render the propeller subset I am currently running the following code:


D3DXATTRIBUTERANGE* pAttribTable;
DWORD Size,Start, fvfflag = D3DFVF_XYZ;
LPD3DXMESH ClonedMesh;
D3DXVECTOR3* CustomVerts;

meshPlane->GetAttributeTable(NULL,&Size);
pAttribTable = new D3DXATTRIBUTERANGE[Size];
meshPlane->GetAttributeTable(pAttribTable,&Size);
Start = pAttribTable[5].VertexStart; // 5 is the subset number for the propeller

meshPlane->CloneMeshFVF(D3DXMESH_MANAGED,fvfflag,d3ddev,&ClonedMesh);

CustomVerts = new D3DXVECTOR3[ClonedMesh->GetNumVertices()];
ZeroMemory(CustomVerts, sizeof(CustomVerts));

if(SUCCEEDED(ClonedMesh->LockVertexBuffer(D3DLOCK_DISCARD,(LPVOID*)CustomVerts)))
{
D3DXVECTOR3 Vavg(0,0,0), vertPos;
for(WORD v=0; v<pAttribTable[5].VertexCount; v++)
{
vertPos = CustomVerts[v+Start];
Vavg += vertPos;
}

Vavg /= (float)pAttribTable[5].VertexCount;

D3DXMATRIX rotate,translate,translate2;
D3DXMatrixTranslation(&translate,-Vavg.x,-Vavg.y,-Vavg.z);
D3DXMatrixTranslation(&translate2,Vavg.x,Vavg.y,Vavg.z);
D3DXMatrixRotationZ(&rotate,propeller);
d3ddev->SetTransform(D3DTS_WORLD, &(translate*rotate*translate2*matRotateY));
ClonedMesh->UnlockVertexBuffer();
}
delete []CustomVerts;
delete []pAttribTable;


So as you can see I am not doing this :WORD offset = v*numBytesPerVertex; // you're doing this, right?

But I don't think that's needed as I'm using a cloned mesh as I have no idea what the fvf is of the original so I'm using the clone with a different fvf of just the customvertex so I can get the x,y,z properly... Unfortunately, at the moment it looks like it's getting garbage out of the cloned meshes vertices, not sure why!

Can't quite believe it's so complex just to obtain the vertices of a subset, and even then it's not working yet!

[Edited by - icehot on February 23, 2010 6:04:55 AM]

Share this post


Link to post
Share on other sites
That's an interesting approach, icehot, and very imaginative, actually.

First and foremost: You should check return codes from all the D3DX calls!Otherwise, you're writing code that says "I'm only going to check for a couple errors here and there and just hope I'm a perfect programmer."

Second: You can get the FVF from a mesh with (oddly enough [wink]) mesh->GetFVF().

Third: I've never done things the way you're doing it, but I don't know what the effect is to use information from the original mesh to access information in the cloned mesh.

Fourth: Don't new the CustomVertex pointer! Locking the vertex buffer will set CustomVertex to point to the data. You can access the information in the vertex buffer with CustomVertex[<index>]. And, for sure, don't delete it!

Fifth: Store the mesh's translation transforms in a couple of D3DXMATRIX's for later use rather than set the device world transform.
I.e,
Instead of "d3ddev->SetTransform(D3DTS_WORLD,..)", do something like:

meshTranslateMat = translate; // a D3DXMATRIX you can access later
meshUntranslateMat = translate2; // a D3DXMATRIX you can access later

Then, when you render the propeller subset:
D3DXMATRIX rot;
D3DXMatrixRotationZ(&rot,propellerAngle);
D3DXMATRIX meshWorld = translate*rot*translate2*currentWorld;
dev->SetTransform(D3DTS_WORLD,&meshWorld);

See if that helps.

Share this post


Link to post
Share on other sites
Righty, I've done most of what you said - it's still not quite right, the axis is still slightly off centre which is really annoying - but it's still just got this slight wobble now, whereby the lower half of the propeller will slice through the hull of the plane very slightly, but only on half of the rotation.

The bit that got it 99% there was as you suggested, was to not new the customvertex pointer, the vertex data is now correct, and it started to rotate - albeit with that wobble!

I've removed the mesh cloning part, and used getfvf to figure out the fvf for the mesh, and am now using it's vertex buffer directly - so that cuts out a fair bit of code! I've also put most of the calculations now stored as matrix variables but in my initialisation code, which obviously optimises things a fair bit, cos it's not recalculating everything every frame, only once.

So the code is now looking like this:

Init code:

struct CUSTOMVERTEX
{
float x, y, z;
D3DVECTOR NORMAL;
float u,v;
};

D3DXATTRIBUTERANGE* pAttribTable;
DWORD Size,Start, fvfflag = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
CUSTOMVERTEX* CustomVerts=NULL;

meshPlane->GetAttributeTable(NULL,&Size);
pAttribTable = new D3DXATTRIBUTERANGE[Size];
meshPlane->GetAttributeTable(pAttribTable,&Size);
Start = pAttribTable[5].VertexStart;

if(SUCCEEDED(meshPlane->LockVertexBuffer(D3DLOCK_DISCARD,(LPVOID*)&CustomVerts)))
{
D3DXVECTOR3 Vavg(0,0,0), vertPos;
for(WORD v=0; v<pAttribTable[5].VertexCount; v++)
{
vertPos.x= CustomVerts[v+Start].x;
vertPos.y= CustomVerts[v+Start].y;
vertPos.z= CustomVerts[v+Start].z;
Vavg += vertPos;
}
Vavg /= (float)pAttribTable[5].VertexCount;

D3DXMatrixTranslation(&TranslateMatrix,-Vavg.x,-Vavg.y,-Vavg.z);
D3DXMatrixTranslation(&UntranslateMatrix,Vavg.x,Vavg.y,Vavg.z);
meshPlane->UnlockVertexBuffer();
}
delete []pAttribTable;


Rendering the frame:

static float propeller=0.0f;
propeller+=0.08f;
if (propeller > D3DXToRadian(360)) propeller = 0.0f;
D3DXMATRIX PropMatrix;

for(DWORD i = 0; i < numMaterials; i++) // loop through each subset
{
if (i == 5)
{
D3DXMatrixRotationZ(&rotate,propeller);
PropMatrix = TranslateMatrix * rotate * UntranslateMatrix;
}
else D3DXMatrixIdentity(&PropMatrix);

d3ddev->SetTransform(D3DTS_WORLD, &(PropMatrix*matRotateY));
d3ddev->SetMaterial(&material[i]);
if(texture[i] != NULL)
d3ddev->SetTexture(0, texture[i]);
meshPlane->DrawSubset(i);
}


All I can think of now, is the calculation for the centre must be slightly off?

Share this post


Link to post
Share on other sites
It certainly looks like you're close.

Quick suggestion:

struct CUSTOMVERTEX
{
D3DXVECTOR3 pos;
D3DXVECTOR3 NORMAL;
D3DXVECTOR2 texCoords;
};
...
Vavg += CustomVertex[v+start].pos;
...

The only thing I can think of, for now, would be to render just the propeller and see if it's symmetrical. That attribute group may have some extra vertices in it that don't really belong. Not much you can do about that, unless you want to meticulously edit the file [shudder], but it may give you a hint.

Also, the "wobble" may be due to the Z-axis not being the exact axis-of-rotation for the prop. Perhaps rendering only the propeller without any rotation (or rotation of the plane) and with an identity world matrix would give you info. You could also draw a line from Vavg to Vavg+D3DXVECTOR3(0,0,5) to see how that local z-axis relates to the prop portion of the mesh.

Hah! You're learning all kinds of stuff, huh?!

EDIT: another approach to finding the center position would be to go through the attribute group and, rather than averaging, calculate min/maxX, min/maxY and min/maxZ. Then try minX+(maxX-minX)/2, minY+(maxY-minY)/2, and minZ+(maxZ-minZ)/2 for the center position.

Share this post


Link to post
Share on other sites
Quote:
Original post by Buckeye
It certainly looks like you're close.

Quick suggestion:

struct CUSTOMVERTEX
{
D3DXVECTOR3 pos;
D3DXVECTOR3 NORMAL;
D3DXVECTOR2 texCoords;
};
...
Vavg += CustomVertex[v+start].pos;
...

The only thing I can think of, for now, would be to render just the propeller and see if it's symmetrical. That attribute group may have some extra vertices in it that don't really belong. Not much you can do about that, unless you want to meticulously edit the file [shudder], but it may give you a hint.

Also, the "wobble" may be due to the Z-axis not being the exact axis-of-rotation for the prop. Perhaps rendering only the propeller without any rotation (or rotation of the plane) and with an identity world matrix would give you info. You could also draw a line from Vavg to Vavg+D3DXVECTOR3(0,0,5) to see how that local z-axis relates to the prop portion of the mesh.

Hah! You're learning all kinds of stuff, huh?!

EDIT: another approach to finding the center position would be to go through the attribute group and, rather than averaging, calculate min/maxX, min/maxY and min/maxZ. Then try minX+(maxX-minX)/2, minY+(maxY-minY)/2, and minZ+(maxZ-minZ)/2 for the center position.


I tell you what, this one exercise has made me learn more about all this stuff than anything I've read so far! The amount of research I've put into it, and obviously your very helpful advice as well (as it's also helping where my coding style may well be a little off for directx).... I just can't believe how complicated it all is to get something as simple as a propeller to spin lmao! I've got a whole new level of appreciation for the professional games I play!

I'm going to install 3DCanvas which can apparently edit .x files in a little more human way, so that will hopefully tell me whether the propeller is correctly symmetrical.

I'll also try drawing that line you suggested, and the different average approach...

Thanks again btw for your help so far, it's been invaluable, really appreciate it! Hopefully this thread can help someone else eventually as well, cos I've not found any advice anywhere else on this topic...

Share this post


Link to post
Share on other sites
Looking at airplane2.x in MeshViewer, it looks like the propeller is angled up a bit from the z-axis, so your propeller rotation axis will (probably) not be along the z-axis.

You can try something like:

D3DXQUATERNION quat;
D3DXVECTOR3 axis( 0, 0.1f, -1.0f ); // experiment with the amount of Y
D3DXVec3Normalize(&axis,&axis);
D3DXQuaternionRotationAxis(&quat, &axis, propAngle); // propAngle = propeller in your code, I think
D3DXMatrixRotationQuaternion( &rotate, &quat); // get a rotation matrix about axis


EDIT: Try that first. Vavg may be okay. Hope you see this before you modify your code too much.

Share this post


Link to post
Share on other sites
Quote:
Original post by Buckeye
Looking at airplane2.x in MeshViewer, it looks like the propeller is angled up a bit from the z-axis, so your propeller rotation axis will (probably) not be along the z-axis.

You can try something like:

D3DXQUATERNION quat;
D3DXVECTOR3 axis( 0, 0.1f, -1.0f ); // experiment with the amount of Y
D3DXVec3Normalize(&axis,&axis);
D3DXQuaternionRotationAxis(&quat, &axis, propAngle); // propAngle = propeller in your code, I think
D3DXMatrixRotationQuaternion( &rotate, &quat); // get a rotation matrix about axis


EDIT: Try that first. Vavg may be okay. Hope you see this before you modify your code too much.


Luckily I did see that first, I've just come home from work so didn't have time earlier to do anymore on this!!

But nice one it works great!!!! I hope you don't mind, but I did a little more research just now and found a direct3d function which will rotate about an arbitrary axis, so the following is the code with the quaternion bits commented out, and a Y value which seems to work pretty well:

//D3DXQUATERNION quat;
D3DXVECTOR3 axis( 0, 0.35f, -1.0f );
D3DXVec3Normalize(&axis,&axis);
//D3DXQuaternionRotationAxis(&quat, &axis, propeller);
//D3DXMatrixRotationQuaternion( &rotate, &quat);
//D3DXMatrixRotationZ(&rotate,propeller);
D3DXMatrixRotationAxis(&rotate, &axis, propeller);
PropMatrix = TranslateMatrix * rotate * UntranslateMatrix;

That works great too... the reason I changed it was cos I've not come across quaternions before so I did a wiki search, and the 3 letters "wtf??" came to mind, think I need to study more maths!! :P

Share this post


Link to post
Share on other sites
Well, for cryin' out loud! Of course that functions works! I'd been working with quats the last couple days and that was what I thought of first. Your method is a bit faster and easier on the eyes. Good work!

Share this post


Link to post
Share on other sites

This topic is 2855 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this