# Model orientations in 3D

• 10-31-2006
VirtualAce
Model orientations in 3D
I'm having a lot of problems getting my models to act correctly in 3D. Currently I'm using Euler angles via axis angle rotations.

The class for the camera works well and uses the standard up,right and look vector system.

To pitch I rotate about the right vector.
To yaw I rotate about the up vector.
To roll I rotate about the look vector.

This all works perfect for the camera and there is no gimbal lock or veering between angles (as angle axes begin to map onto one another). This tends to produce a bobbing sensation as the angles map onto one another and thus you lose a degree of freedom.

Now I took the camera class and used it for the COrient3D class. I figured hey it works for the camera why wouldn't it for the models? Boy was I way off.

Here are the problems:

• Models act fine until they reach negative pitch at which point the look vector and nose of the model do not line up.
• As you approach the +90 to -90 the effects of this can be seen quite clearly as your model begins to waiver about and no longer follow the look vector.
• If you yaw while in a positive or negative pitch it works fine until yaw reaches a certain point and then the vectors go haywire.
• The ship nose should point straight down the +z axis of the look vector, but the ship movement begins to be completely different from the direction it is pointing.

Now if I absolutely cannot do this type of game using euler angles then I will switch to quaternions, but I think something else is wrong.

Here is some of the code:

Code:

COrient3D::COrient3D()
{
m_vecRight=D3DXVECTOR3(1.0f,0.0f,0.0f);
m_vecUp=D3DXVECTOR3(0.0f,1.0f,0.0f);
m_vecLook=D3DXVECTOR3(0.0f,0.0f,1.0f);
m_vecPos=D3DXVECTOR3(0.0f,0.0f,0.0f);
m_iObjType=OBJECT_AIR;
m_fYaw=0.0f;
m_fRoll=0.0f;
m_fPitch=0.0f;
}

void COrient3D::Walk(float fUnits)
{
if (m_iObjType==OBJECT_GND)
m_vecPos+=D3DXVECTOR3(m_vecLook.x,0.0f,m_vecLook.z)*fUnits;

if (m_iObjType==OBJECT_AIR)
m_vecPos+=m_vecLook*fUnits;
}

void COrient3D::Roll(float fAngle)
{
m_fRoll+=fAngle;

D3DXMATRIX T;
D3DXMatrixRotationAxis(&T,&m_vecLook,fAngle);

D3DXVec3TransformCoord(&m_vecRight,&m_vecRight,&T);
D3DXVec3TransformCoord(&m_vecUp,&m_vecUp,&T);

}

void COrient3D::Pitch(float fAngle)
{
m_fPitch+=fAngle;

D3DXMATRIX T;
D3DXMatrixRotationAxis(&T,&m_vecRight,fAngle);

D3DXVec3TransformCoord(&m_vecUp,&m_vecUp,&T);
D3DXVec3TransformCoord(&m_vecLook,&m_vecLook,&T);

}

void COrient3D::Yaw(float fAngle)
{
m_fYaw+=fAngle;

D3DXMATRIX T;

if (m_iObjType==OBJECT_GND) D3DXMatrixRotationY(&T,fAngle);
if (m_iObjType==OBJECT_AIR) D3DXMatrixRotationAxis(&T,&m_vecUp,fAngle);

D3DXVec3TransformCoord(&m_vecRight,&m_vecRight,&T);
D3DXVec3TransformCoord(&m_vecLook,&m_vecLook,&T);

}

void COrient3D::GetRotMatrix(D3DXMATRIX *outMatrix)
{
D3DXVec3Normalize(&m_vecLook,&m_vecLook);

D3DXVec3Cross(&m_vecUp,&m_vecLook,&m_vecRight);
D3DXVec3Normalize(&m_vecRight,&m_vecRight);

D3DXVec3Cross(&m_vecRight,&m_vecUp,&m_vecLook);
D3DXVec3Normalize(&m_vecLook,&m_vecLook);

D3DXVECTOR3 vecPos=m_vecPos;

float x=D3DXVec3Dot(&m_vecRight,&vecPos);
float y=D3DXVec3Dot(&m_vecUp,&vecPos);
float z=D3DXVec3Dot(&m_vecLook,&vecPos);

(*outMatrix)(0,0)=m_vecRight.x;
(*outMatrix)(0,1)=m_vecUp.x;
(*outMatrix)(0,2)=m_vecLook.x;
(*outMatrix)(0,3)=0.0f;

(*outMatrix)(1,0)=m_vecRight.y;
(*outMatrix)(1,1)=m_vecUp.y;
(*outMatrix)(1,2)=m_vecLook.y;
(*outMatrix)(1,3)=0.0f;

(*outMatrix)(2,0)=m_vecRight.z;
(*outMatrix)(2,1)=m_vecUp.z;
(*outMatrix)(2,2)=m_vecLook.z;
(*outMatrix)(2,3)=0.0f;

(*outMatrix)(3,0)=0.0f;
(*outMatrix)(3,1)=0.0f;
(*outMatrix)(3,2)=0.0f;
(*outMatrix)(3,3)=1.0f;
}

What I've done here is take the GetViewMatrix() function from CCamera and used it to create the final rotation matrix for the object. I cannot do a D3DXMatrixRotationYawPitchRoll() because that suffers from gimbal lock which would effectively 'undo' all my axis angle rotations.

You may notice the translation has been taken out of this matrix. It is done at a later time just in case I may want to scale the object prior to translation and I didn't think COrient3D should care about scale.

This code works fine for the camera but is completely backwards for the object. After negating certain vectors you can get the object to fly correctly, but the issues I mentioned above pop up quickly.

I tried code to rotate the object to always face down the positive Z axis of the look vector, but it did not change the object orientation leading me to believe it is already in this orientation.

• 11-01-2006
VirtualAce
Another problem.

Every time I attempt to set the camera's orientation to the object it's viewing - such as an in-cockpit view mode, the camera experiences gimbal lock as well as the ship. However I'm using axis angle representations for both camera and ship. For some reason when I set the camera's vectors to the object's vectors, gimbal lock occurs.

What a mess Euler angles are. I have no choice but to switch over to quaternions or otherwise I will have to do some complex math and gimbal equations all of which will suck precious CPU cycles.

In order to correctly orient my models so they don't gimbal lock I'm setting local up,look, and right vectors to their origin's and then performing 3 axis angle rotations to create the matrices and then multiplying the matrices together to arrive at the final rotation matrix. This is then concatenated with the translation matrix to arrive at the final world matrix. What a disaster. I cannot just extract the vectors from the object's current orientation because the angles are all wrong. It works until you reach 180 to 360 degrees of yaw at which point the up is down, down is up, right is left and left is right. Crazy.

I have no idea how these game companies did flight sims w/o using quaternions back in the early days. I've been trying to get around it by using axis angle rotations but I think I've come to my wit's end and all just to avoid stupid gimbal lock.

Back to the drawing board.

Here is a sample of my disastrous code:

Code:

void CShip::Render(float fTimeDelta)
{

m_pOrient->Reset();

m_pOrient->Pitch();
m_pOrient->Yaw();
m_pOrient->Roll();

if (m_spMyCamera->GetViewMode()==COCKPIT)
{

D3DXMATRIX matInvView;
D3DXMATRIX matView;
D3DXMATRIX matTrans;
D3DXMATRIX matWorld;
m_spDevice->GetTransform(D3DTS_VIEW,&matView);

D3DXMatrixInverse(&matInvView,NULL,&matView);
D3DXMatrixTranslation(&matTrans,0.0f,-2.0f,0.0f);

matWorld=matTrans*matInvView;

m_spDevice->SetTransform(D3DTS_WORLD,&matWorld);

}
else
{

D3DXMATRIX matWorld;
D3DXMATRIX matRot;
D3DXMATRIX matTilt;

D3DXMatrixIdentity(&matRot);
D3DXMatrixIdentity(&matTilt);

D3DXMATRIX matTrans;

D3DXVECTOR3 vecPos;
m_pOrient->GetPosition(&vecPos);
D3DXMatrixTranslation(&matTrans,vecPos.x,vecPos.y,vecPos.z);

D3DXVECTOR3 vecUp=D3DXVECTOR3(0.0f,1.0f,0.0f);
D3DXVECTOR3 vecRight=D3DXVECTOR3(1.0f,0.0f,0.0f);
D3DXVECTOR3 vecLook=D3DXVECTOR3(0.0f,0.0f,1.0f);

D3DXMATRIX matYaw;
D3DXMATRIX matPitch;
D3DXMATRIX matRoll;

D3DXMatrixRotationAxis(&matYaw,&vecUp,m_pOrient->GetYaw());
D3DXMatrixRotationAxis(&matPitch,&vecRight,m_pOrient->GetPitch());
D3DXMatrixRotationAxis(&matRoll,&vecLook,m_pOrient->GetRoll());

matRot=matYaw*matPitch*matRoll;

matWorld=matRot* matTrans;

m_spDevice->SetTransform(D3DTS_WORLD,&matWorld);
}

m_spDevice->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);

m_pMesh->Render(fTimeDelta);

if (m_spMyCamera->GetViewMode()==COCKPIT) m_pHUDSys->Update(fTimeDelta);
}

I've also tried some axis angle rotations on the X and Z plane for facing and the Y plane for tilt to cause the camera look, up, and right vectors to line up with the object's. It works but the gimbal lock problem still exists.
• 11-01-2006
Perspective
why are you inverting your view matrix? Matrix inversions are expensive, best to avoid if possible.
• 11-01-2006
BobMcGee123
Yeah, I'm really not entirely sure what's going on with all of that. I normally lock my camera's angles so that I cannot look straight up or straight down, but I changed it so that I COULD and it still worked. The best I can do is post for you how I implemented my camera...I just collect the angles about each axis first of all (in my Camera:: GatherInputOverFrames() function), and then I tell OpenGL to build the proper view matrix. I also calculate my View Strafe/Right and Up vectors in my Camera::MatrixCheckMouse function.

However, for aligning my models, I run the physics, which integrates a quaternion, and then converts it to a matrix. I'm guessing that posting my matrix3x3 to quaternion code would be useless seeing as how I'm in a different coordinate system.

Code:

void        FPSCam::MatrixCheckMouse()
{
GatherInputOverframes();
mView = Vector3(0, 0, -1);
mUp          = Vector3(0, 1, 0);

Matrix4x4 tempmatrix;
tempmatrix.TransformDir(&mView);

mStrafe = CrossProduct(mView, mYAxis);
mStrafe.Normalize();

mUp = CrossProduct(mStrafe, mView);
mUp.Normalize();
return;
}
...
void        FPSCam::GatherInputOverframes()
{
POINT MousePos;
GetCursorPos(&MousePos);

static        POINT        NewPos;

if(MousePos.x == Export.mVars.MiddleX && MousePos.y == Export.mVars.MiddleY)
{
Export.DisableState(MOUSEMOVED);
return;
}

SetCursorPos(Export.mVars.MiddleX, Export.mVars.MiddleY);

Export.SetState(MOUSEMOVED);

float YDeviation = (Export.mVars.MiddleY - MousePos.y);
float XDeviation = (Export.mVars.MiddleX - MousePos.x);

//FIXME: got rid of this when physics was removed
//        float        framespeed = (Export.mVars.TWOPI* 8) * gpPhysics->mFrameTime; //8 full rotations per second
float        framespeed = (Export.mVars.TWOPI*8) * .01;

//fixme: i know this used to use screenheight, changed on purpose so it goes same speed along each axis

/*
this is actually 89 degrees, gimbal lock is impossible in this scenario
however in order to have a valid move direction i sorta need to have X and Z components which is impossible
if you are looking straight upward
*/

/*
{
}

{
}
*/

}

...

...

void        R_GenerateViewMatrix(float        pitch, float yaw, float roll, Vector3 & translation)
{
if(pitch)
{
NT_GL(glRotatef(-pitch,1,0,0));
}
if(yaw)
{
NT_GL(glRotatef(-yaw,0,1,0));
}
if(roll)
{
NT_GL(glRotatef(-roll,0,0,1));
}
NT_GL(glTranslatef(-translation.x,-translation.y,-translation.z));
}

• 11-01-2006
VirtualAce
Quote:

why are you inverting your view matrix? Matrix inversions are expensive, best to avoid if possible.
If you set the camera position to some arbitrary 3D point and take an object at 0,0,0 in model space and transform it with the inverse view matrix it will move the object to the camera.

This is the only way I was able to get the ship to follow the camera and not experience all of the wild rotations.

To explain what's happening may require me sending a demo or something.

The object rotates correctly from an external viewpoint. The object camera also works correctly and pans around the object just fine. The fly-by camera will also work fine when it is imlpemented. What is not working is the in-cockpit camera.

So my question is how do I stick the camera in the cockpit of the ship? I thought I could orient the camera the same as the ship, thus setting the camera's up, look, and right vectors to that of the ship....however it does not work and gimbal lock occurs.

This problem will probably also affect chase view camera to an extent because that camera must to follow the ships' movements and always face down the positive Z axis of the ship or object look vector.

I'm implementing four camera modes for ALL objects in the game.

• Cockpit (not applicable to non-cockpit object such as planetary bodies, space stations, etc)
• Chase mode - applicable to all objects, but fairly boring with planetary bodies
• Object mode - applicable to all objects in the game (camera pans around object), will be used to render picture-in-picture images of targeted objects as well.
• Fly-by mode - camera is translated along the positive look vector some distance ahead and then a matrix is calculated to point the camera toward a certain position in the object. After object moves a certain distance from the camera, camera mode is then set to object mode.

The problem lies in the camera/object differences. My camera continually 'builds-up' it's vectors rather than begin with the original vectors each time and then rotating/translating to the correct point in space.

Objects, on the other hand, always start with their orientations reset. This means that their up, right and look vectors are set to:

1.0f,0.0f,0.0f
0.0f,1.0f,0.0f
0.0f,0.0f,1.0f

Pitch, yaw and roll are 'built-up' by moving the mouse. Once it is time to render, the object is then rotated by the amounts on those 3 axes. Then the actual object vectors are actually rotated as well. I have not found a suitable way to use the vectors as the rotation values so I'm having to store pitch, yaw, and roll due to this problem.
1 rotation rotates the 3D model, and 1 rotation rotates the vectors for the model.
I thought I could take this matrix and use it as the rotation for both model and vectors, but it does not work for the model.

Right.x,Right.y,Right.z,0.0f
Up.x,Up.y,Up.z,0.0f
Look.x,Look.y,Look.z,0.0f
Pos.x,Pos.y,Pos.z,1.0f
• 11-04-2006
VirtualAce
I think I found the problem. My ship is not rotating correctly and is still experiencing gimbal lock. So my new question is how do you rotate an object or a 3D model using axis angle orientations and yet not experiencing gimbal lock?

Since my COrient3D class always maintains 3 vectors then it make sense that my up, right, and look vectors never become aligned and thus should never gimbal lock.

I have looked at quaternions but it seems that they are a tool used to interpolate between orientations w/o having the problems of Euler angles. However in the end even quats must be stuck back into matrix form to be used by the hardware. As of right now the quaternions look great for my camera class and chase cameras, etc, but not for actual object orientations. Several websites have recommended AGAINST using quaternions for all object
orientations.

I've done some experimenting with rotating one vector onto another vector (or making one vector point the same direction as another) and it works but my results have been rather 'jerky' which is a known issue with my system.
Quaternions will solve this but they won't solve my current problem.

Sorry for asking this but all of you know if I'm asking this I'm also hard at work on finding a solution. Any help is and will be appreciated.

Also, the internet seems to be really good at showing how to do a camera or some other isolated operation but most if not all the sites are absolutely terrible at explaining how to integrate these into an engine. Several camera tutorials out there but very few model orientation tutorials or explanations.
• 11-07-2006
VirtualAce
I've almost got it but I'm still not orienting the ship correctly. So far cockpit and chase mode both work but when you switch to object view the ship is not pointing in the direction of travel as evidenced by the stars flying by it.

Also in flyby view mode, which currently just detaches the camera from the object, the object is NOT facing the direction of travel.

I'm going to try to orient the vehicle to point towards it's look vector because any rotations I do just do not orient the model correctly. I'm not sure why this is. I have no idea what is going to happen when I get various ships in this system all flying around your ship. Right now most of it works for 1 ship but I may not be able to display other ships correctly.

Some of this is starting to make sense but I'm still a bit puzzled by other portions.

To do a chase camera I'm doing this:

1. Get the current view matrix
2. Get it's inverse
3. Translating to a position relative to 0,0,0 since that is where the inverse puts the object relative to the camera - so a 0.0f,-2.0f,18.0f will put the camera 2 units above, and 18 units behind the actual object.
4. World=Trans*InvView

To do a cockpit camera:
1. Get the current view matrix
2. Get the inverse of it
3. Translate to the cockpit location relative to 0,0,0 or the center of the model.
4. Render the virtual cockpit around the player at this location. (TODO: Make the virtual cockpit model)
5. World=Trans*InvView

To do an object camera:
1. Translate from the object a set distance.
2. Render
• 11-09-2006
VirtualAce
I fixed it.

Here is a screenshot....not much diff....but it works. :)

The fix is this:

Camera matrix
Right.x,Up.x,Look.x,0
Right.y,Up.y,Look.y,0
Right.z,Up.z,Look.z,0
-dot(Right,cameraPos),-dot(Up,cameraPos),-dot(Look,cameraPos),0

Object matrix
Right.x,Right.y,Right.z,0
Up.x,Up.y,Up.z,0
Look.x,Look.y,Look.z,0
Pos.x,Pos.y,Pos.z,1
• 11-09-2006
Perspective
Nice, ship looks better too (I don't think I could make that out of paper). Are you using smooth shading? I can see the polys.
• 11-09-2006
VirtualAce
The gouraud shading is on, but not working for the ship. Not sure what's going on there but it doesn't matter b/c I'm about to scrap it and use my per-pixel shader anyways.
• 11-09-2006
Dark_Phoenix
Looking good Bubba. Maybe sometime soon I'll understand enough DirectX so I can get my game idea started.

Its not hard, I just don't have a lot of time to spend on it....
• 11-14-2006
VirtualAce
Note to all who use Blender

If you perform a subsurf divide on your model and/or a smooth you MUST perform a remove doubles on the model or you will have more than one normal per vertex. This will cause Direct3D to mess up and will only perform flat-shading. This was the problem in the screenshots above. You can really tell if your model is correct or not by using the DX model viewer and setting it to render normals. If you have 2 or 3 normals per vertex, you have this problem and need to remove doubles. You should only have 1 normal per vertex in DX model viewer.

Here is a corrected screenshot.