Thread: Rotation independent of preceding rotations (DirectX)

  1. #1
    Registered User
    Join Date
    Sep 2011
    Posts
    25

    Rotation independent of preceding rotations (DirectX)

    I've been struggling with this concept for the past few weeks. Let me set up an example to demonstrate what I'm trying to do...

    If you imagine we are looking from (0, 0, -1, y-up) at an airplane model, I want a user to be able to rotate the airplane in such a way that despite previous rotations, X rotations will always appear as the far point of the plane coming to the bottom, Y rotations will always appear as the far point coming to the right, and Z rotations always appear as the right point coming over the top.

    In example, if we've already rotated the model to have the nose looking to the left and the left wing to the bottom, no matter what rotations we applied to get the model in that position, an X rotation of 90 degrees is guaranteed to cause the left wing to point toward us, OR a Y rotation of 90 is guaranteed to have the nose point away from us (the left wing still downward), OR a Z rotation is guaranteed to have the nose pointing downward (the left wing now point right).

    I hope that isn't too confusing. I'm not sure how to phrase this question beyond presenting an example case. Does this make sense? Is this type of rotation possible in DirectX?

  2. #2
    Registered User ledow's Avatar
    Join Date
    Dec 2011
    Posts
    435
    I know how to do it in OpenGL, but in DirectX I shouldn't imagine it's any harder at all.

    Rotate the object and view separately. The user input rotates the object only. The view window is only rotated when they want to rotate the view. The total rotation actually applied to the models is the combination of both rotations (the matrix math for this is quite easy, but often you can cheat and use shortcut functions to do it for you).

    What you're looking at and what you're rotating are two different things and when you treat them as such (i.e. you don't always rotate the view itself), then it works as you describe. If you'll notice, that's how every 3D program does it too (e.g. in a flight sim, you control the rotation of the OBJECT, but the view can change independently of that via other controls - players pressing to change the view - or automatically e.g. fly-past cameras, etc.).

    - Compiler warnings are like "Bridge Out Ahead" warnings. DON'T just ignore them.
    - A compiler error is something SO stupid that the compiler genuinely can't carry on with its job. A compiler warning is the compiler saying "Well, that's bloody stupid but if you WANT to ignore me..." and carrying on.
    - The best debugging tool in the world is a bunch of printf()'s for everything important around the bits you think might be wrong.

  3. #3
    Registered User
    Join Date
    Sep 2011
    Posts
    25
    Sorry, I should have made it more clear that I want to rotate the single model alone, not the entire scene or the viewpoint. So I don't think your solution is applicable here (unless I misunderstood).

    From my research, the closest answer I've gotten has been in the form of "a 3D ball rolling across a plane", which is pretty close to the effect I was to have, minus distance traveled and all. Unfortunately, the quality of the answers to that question are either really poorly written or expect the reader to know enough math to fill in big blanks on their own.

    Perhaps it's easier to visualize my problem if I ask for a solution to a ball rolling across a plane?

  4. #4
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,739
    If I understood correctly, you want your object to rotate around the absolute/global X, Y and Z axes and not the objects', right?
    I remember it has something to do with rotation order. Meaning rotating (X, Y, Z) is different than (Z, Y, X) , but it's been long since I've done graphics...
    Devoted my life to programming...

  5. #5
    Registered User
    Join Date
    Sep 2011
    Posts
    25
    That's correct. I realize that X * Y * Z is not the same as Z * Y * X. However, the problem exists no matter what order you multiply in. The first rotation axis will be offset by the next two, the second offset by the third, and the third is the only one unaffected. So the question remains; How do we stop those offsets from occuring?

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You mean you want to avoid gimbal lock or when a rotation about one axis results in an unintended rotation around another axis. You can either use axis-angle rotations or use quaternions and then create a matrix from the resulting quaternion. There is no mathematical way to avoid gimbal lock if you are creating rotation matrices for X, Y and Z and then concatenating...regardless of order.

    An example of gimbal lock is when pitching up to 90 degrees and then attempting to bank left or right. The banking will actually roll the camera about the z axis and will stop rolling at some point. In fact you can continue to bank left but the camera will be gimbal locked and will not change its orientation.

    With axis-angle and quaternion rotations you can pitch up to 90 degrees and bank left which will bank the camera left but will not result in any roll and at no point will the camera experience gimbal lock where your control inputs seem to be ignored completely.
    Last edited by VirtualAce; 09-29-2012 at 12:33 AM.

  7. #7
    Registered User
    Join Date
    Sep 2011
    Posts
    25
    Thanks for the insight, VirtualAce. After searching more about avoiding gimbal lock, I found some tips to use quaternions and even found a related thread on this board. I'm pretty confused as to how to actually apply quaternions, but will give it a shot. If anyone has the time and patience, I would greatly appreciate some type of example implementation in the vein of what I'm trying to do. Otherwise, I'll just post up more specific questions as I run into problems.

  8. #8
    Registered User
    Join Date
    Sep 2011
    Posts
    25
    I found some great information from an article on cprogramming.com ( ) where quaternions were explained in such a way that didn't confuse me with all the mathematics involved. Quaternions and 3D Rotations - Cprogramming.com

    I'm trying to use the D3DXQUATERNION structure in C++, and having some problems...

    Code:
    D3DXQUATERNION totalRot; // Hold the total rotation
    D3DXMATRIX meshMat, meshTranslate, meshRotate;
    
    void init()
    {
     ...
     // The article says to initialize quaternions to (w=1, x=0, y=0, z=0). D3D places the w parameter as the fourth, not first.
     totalRot = D3DXQUATERNION(0.0f, 0.0f, 0.0f, 1.0f);
    
     D3DXMatrixTranslation(&meshTranslate, 0, 0, 0);
     ...
    }
    
    void update(float delta)
    {
     // The rotation to apply from this frame
     D3DXQUATERNION tempRot(0.0f, 0.0f, 1.0f, D3DXToRadian(45*delta));
     D3DXQuaternionNormalize(&tempRot, &tempRot);
    
     // Apply it (total = temp * total)
     D3DXQuaternionMultiply(&totalRot, &totalRot, &tempRot);
    
     // Convert to a matrix for rendering
     D3DXMatrixRotationQuaternion(&meshRotate, &totalRot);
     meshMat = meshTranslate * (meshRotate);
    }
    
    void render()
    {
     ...
     pd3dDevice->SetTransform(D3DTS_WORLD, &meshMat);
     ...
    }
    What I expect to happen is that the model will rotate around the Z axis at the specified rate (45 degrees per second). What I get instead is the model rotating around the Z axis at a much faster rate. In fact, even if I change tempRot.w to 0.0f, it still rotates at a fast rate around Z. How is that happening even when I've set the rotation amount to 0? What's going on here?

  9. #9
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I see one problem for sure. Remember that a transform is Scale * Rotation * Translation. You are doing Translation * Rotation which will cause the object to rotate around the point you translated from instead of rotating about it's center.

    tempRot.w is not the Z rotation. tempRot.z is the Z rotation. tempRot.w just makes the math come out correctly. You have specified 1.0f for Z rotation and have not multiplied it by the delta. This means you will rotate 1 radian per frame or 60 radians in 1 second which is just shy of 10 complete revolutions. Note that your object may also be close to the Nyquist frequency of your display. This will make the object either appear to rock back and forth or not rotate at all...even though it is. I believe you are also missing a quaternion transformation to make the math work.
    Last edited by VirtualAce; 09-30-2012 at 02:36 AM.

  10. #10
    Registered User
    Join Date
    Sep 2011
    Posts
    25
    I changed the transform order to the correct order. I also changed tempRot to (0.0f, 0.0f, D3DXToRadian(45)*delta, 1.0f), which seems to rotate in the expected manner. I was under the impression that tempRot.x, y, and z define the axis about which the rotation occurs, and tempRot.w is the actual amount to rotate by. This is apparently incorrect. Could you explain what tempRot.w actually represents?

    Also, could you elaborate on what sort of quaternion transformation I'm missing? I thought multiplying tempRot and totalRot together, converting the result to a matrix, and then applying transformations to the matrix was all I needed.

  11. #11
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Have a look at this code. It specifies the axis of rotation but not as you would expect.
    Code:
    void X3DCamera::Pitch(float angle)
    {
        D3DXQUATERNION quat = m_qRot;
        D3DXQuaternionRotationAxis(&quat,TransformVector(&m_qRot,&D3DXVECTOR3(1.0f, 0.0f, 0.0f)),angle);
        
        m_qRot *= quat;
        D3DXQuaternionNormalize(&m_qRot,&m_qRot);
        m_inTransition = true;
    }
    
    void X3DCamera::Yaw(float angle)
    {
        m_yaw += angle;
        D3DXMATRIX matRot;
        D3DXQUATERNION quat;
        D3DXMatrixRotationY(&matRot,m_yaw);
        D3DXQuaternionRotationMatrix(&quat,&matRot);
    
        D3DXQuaternionRotationAxis(&quat,TransformVector(&quat,&D3DXVECTOR3(0.0f, 1.0f, 0.0f)),angle);
        
        m_qRot *= quat;
        D3DXQuaternionNormalize(&m_qRot,&m_qRot);
        m_inTransition = true;
    }
    
    void X3DCamera::Roll(float angle)
    {
        D3DXQUATERNION quat = m_qRot;
        D3DXQuaternionRotationAxis(&quat,TransformVector(&m_qRot,&D3DXVECTOR3(0.0f, 0.0f, 1.0f)),angle);  
        m_qRot *= quat;
        D3DXQuaternionNormalize(&m_qRot , &m_qRot);
        m_inTransition = true;
    }
    
    void X3DCamera::GetViewMatrix(D3DXMATRIX *outMatrix)
    {
        D3DXMATRIX matRot;
        D3DXQuaternionNormalize(&m_qRot,&m_qRot);
        D3DXMatrixRotationQuaternion(&matRot,&D3DXQUATERNION(-m_qRot.x,-m_qRot.y,-m_qRot.z,m_qRot.w));
        
        D3DXVECTOR3 vecLook(matRot._13,matRot._23,matRot._33);
        D3DXVECTOR3 vecOrbitPos = m_vecTargetPos + vecLook * -m_fCamDist;
        D3DXMATRIX matOrbit;
        D3DXMatrixTranslation(&matOrbit,-vecOrbitPos.x,-vecOrbitPos.y,-vecOrbitPos.z);
        
        D3DXMATRIX matTrans;
        D3DXMatrixTranslation(&matTrans,-Pos.x,-Pos.y,-Pos.z);
        
        m_matView = matOrbit * matRot * matTrans;
    
        *outMatrix = m_matView;
    }
    
    D3DXVECTOR3* X3DCamera::TransformVector(D3DXQUATERNION *pOrientation, D3DXVECTOR3 *pAxis)
    {
        D3DVECTOR vNewAxis;
        D3DXMATRIX matRotation;
        // Build a matrix from the quaternion.
    
        D3DXMatrixRotationQuaternion(&matRotation, pOrientation); 
    
        // Transform the queried axis vector by the matrix.
        vNewAxis.x = pAxis->x * matRotation._11 + pAxis->y * matRotation._21 + pAxis->z * matRotation._31 + matRotation._41; 
        vNewAxis.y = pAxis->x * matRotation._12 + pAxis->y * matRotation._22 + pAxis->z * matRotation._32 + matRotation._42;
        vNewAxis.z = pAxis->x * matRotation._13 + pAxis->y * matRotation._23 + pAxis->z * matRotation._33 + matRotation._43;
    
        memcpy(pAxis, &vNewAxis, sizeof(vNewAxis)); // Copy axis.
    
        return(pAxis);
    }
    In essence this code is doing exactly the same thing as the axis-angle version. The axis angle version transforms the orientation vectors according to yaw, pitch and roll and the resulting matrix formed by these vectors is the final view matrix. Note that the orientation vectors always start at their original values IE: right: 1 0 0, up 0 1 0 0 and look 0 0 1. This is because if you transform the vectors and then transform them from there that would be the same as transforming an object from local to world space and then from world space to another world space. Camera orientation vectors always start at the original vectors and are transformed from there. Note that the approach shown is only one way to create a quaternion based camera. There are other ways to do this.

    Also note that if you take this code and create the transpose of the final view matrix it creates you can use this as a quaternion-based frame class for orienting all of your 3D objects.
    Last edited by VirtualAce; 10-01-2012 at 08:10 PM.

  12. #12
    Registered User
    Join Date
    Sep 2011
    Posts
    25
    Thanks for that snippet. One thing I notice is that the result of the quat multiplication is normalized, not the quat being multiplied by. In other words, multiplying a unit quat by a non-unit quat and then normalizing the result, as compared to multiplying a unit quat by another unit quat. Do these two situations result in different accuracies? Is there a preference to which one should be used?

  13. #13
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    If you multiply two normalized quats you get a normalized quat. If you mulitply a non-normalized and a normalized and you want a normalized result you must normalize the result. But regardless if the goal is a normalized quaternion both of those will yield the same result.

    It is the same with vectors:

    Given a unit vector A and non-unit vector B:

    normalize(A * B) = A * (normalize(B))

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. 2d rotations
    By beginner.alx in forum Game Programming
    Replies: 11
    Last Post: 09-19-2008, 08:57 PM
  2. DirectX rotation matrices
    By confuted in forum Game Programming
    Replies: 7
    Last Post: 08-10-2003, 03:05 PM
  3. Help with DX9 rotations
    By darcome in forum Game Programming
    Replies: 0
    Last Post: 03-05-2003, 12:55 PM
  4. preceding zeros in int variables
    By AshFooYoung in forum C Programming
    Replies: 2
    Last Post: 09-23-2001, 02:28 PM
  5. Preceding 0
    By morbuz in forum C++ Programming
    Replies: 2
    Last Post: 09-19-2001, 12:15 PM