peekb

I think I'm being dense today, but I can't figure this out for the life of me.

I have a model that I want to rotate across the X and Y axis. My issue is that when I rotate across the Y axis, I always want the rotation to occur as if the axis was vertical in terms of world space.

If I multiply by my X rotation first, obviously the Y axis moves with it, so rotating about the Y axis will rotate it about its transformed axis.

If I multiply by my Y rotation first, the X axis moves with it, so my X rotation is now turned along with it.

I basically want to rotate downward and then left/right independently from what the actual transformed axes become...or, rotate left/right and then downward independently.  When I rotate about the X, it should always rotate straight down, and when I rotate about the Y, it should always rotate left/right, no matter which rotation came first. The axes from local space should remain the same forever. If that makes any sense. :)

It seems like I need to do a Matrix.CreateFromAxisAngle() (or the quaternion equivalent), however I cannot seem to find the parameters which make it do what I want.

I'm sure I'm just being stupid...any help appreciated.

Thanks!


Re: XNA Framework Rotation help

parlance

What are you referring to is the gimbal lock effect of using subsequent matrix multiplications to represent rotation. In the common order for rotation combination using this method (YXZ) if you pitch an object such that it is facing straight up, all yaw rotations would become cancelled and you lose a degree of freedom (at that point the only rotations that would affect it's orientation would be pitch and roll). Quaternions are the solution to this problem. Create 2 quaternions, one for each rotation, and use those to generate a final rotation matrix instead.



Re: XNA Framework Rotation help

Fluxtah

I think I understand what your after but not quite sure, I distilled my tranformations into one class for the sake of clarity, I havent had the time to optimise it yet and not even fully sure if its without problems, however I think it does what you need it to do, I am just going to paste the entire class so you can have a look at how I am dealing with Quaternion based rotations, I am by no means an expert and any advice on this is much appreciated.

At the bottom of the class you will see the 3 methods that deal with applying pitch, yaw and roll to the tranformation.

 

  public class Transform
  {
    private Vector3 _Rotation;
    Matrix _World;
    private bool _Dirty;
    public bool Dirty
    {
      get
      {
        return _Dirty;
      }
      set
      {
        _Dirty = value;
      }
    }
    private Matrix _MatScale;
    private Quaternion _Orientation;
    private Quaternion _OrientationDelta;
    private Matrix _MatRotation;
    private Matrix _MatTranslation;
    private Vector3 _Left = Vector3.Left;
    private Vector3 _Up = Vector3.Up;
    private Vector3 _Forward = Vector3.Forward;
    public Transform()
    {
      _Scale = Vector3.One;
      _Position = Vector3.Zero;
      _Rotation = Vector3.Zero;
      _Orientation = Quaternion.Identity;
      _Dirty = true;
    }
    public Matrix World
    {
      get
      {  
        if (_Dirty)
        {
          Matrix.CreateScale(ref _Scale, out _MatScale);
          Matrix.CreateFromQuaternion(ref _Orientation, out _MatRotation);
          Matrix.CreateTranslation(ref _Position, out _MatTranslation);

          _World = _MatScale *
                 _MatRotation *
                 _MatTranslation;

          _Dirty = false;
        }

        return _World;
      }

    }
    #region Position
    private Vector3 _Position;

    ///<summary>
    ///Set the position
    ///</summary>
    ///<param name="x"></param>
    ///<param name="y"></param>
    ///<param name="z"></param>
    ///<returns></returns>
    public void SetPosition(float x, float y, float z)
    {
      _Position.X = x;
      _Position.Y = y;
      _Position.Z = z;
      _Dirty = true;
    }
    public void SetPosition(Vector3 position)
    {
      _Position = position;
      _Dirty = true;
    }

    public void SetPosition(ref Vector3 position)
    {
      _Position.X = position.X;
      _Position.Y = position.Y;
      _Position.Z = position.Z;
      _Dirty = true;
    }
    ///<summary>
    ///Get the position
    ///</summary>
    public Vector3 Position
    {
      get
      {
        return _Position;
      }
    }

    ///<summary>
    ///Set the X component of position
    ///</summary>
    public float PositionX
    {
      get
      {
        return _Position.X;
      }
      set
      {
        _Position.X = value;
        _Dirty = true;
      }
    }

    ///<summary>
    ///Set the Y component of position
    ///</summary>
    public float PositionY
    {
      get
      {
        return _Position.Y;
      }
      set
      {
        _Position.Y = value;
        _Dirty = true;
      }
    }

    ///<summary>
    ///Set the Z component of position
    ///</summary>
    public float PositionZ
    {
      get
      {
        return _Position.Z;
      }
      set
      {
        _Position.Z = value;
        _Dirty = true;
      }
    }
    #endregion
    #region Scale
    private Vector3 _Scale;

    ///<summary>
    ///Set the Scale
    ///</summary>
    ///<param name="x"></param>
    ///<param name="y"></param>
    ///<param name="z"></param>
    ///<returns></returns>
    public void SetScale(float x, float y, float z)
    {
      _Scale.X = x;
      _Scale.Y = y;
      _Scale.Z = z;
      _Dirty = true;
    }

    ///<summary>
    ///Get the Scale
    ///</summary>
    public Vector3 Scale
    {
      get
      {
        return _Scale;
      }
    }

    ///<summary>
    ///Set the X component of Scale
    ///</summary>
    public float ScaleX
    {
      get
      {
        return _Scale.X;
      }
      set
      {
        _Scale.X = value;
        _Dirty = true;
      }
    }

    ///<summary>
    ///Set the Y component of Scale
    ///</summary>
    public float ScaleY
    {
      get
      {
        return _Scale.Y;
      }
      set
      {
        _Scale.Y = value;
        _Dirty = true;
      }
    }

    ///<summary>
    ///Set the Z component of Scale
    ///</summary>
    public float ScaleZ
    {
      get
      {
        return _Scale.Z;
      }
      set
      {
        _Scale.Z = value;
        _Dirty = true;
      }
    }
    #endregion

    public void AddPitch(float pitch)
    {
      Quaternion.CreateFromAxisAngle(ref _Left, pitch, out _OrientationDelta);
      _Orientation *= _OrientationDelta;
      _Dirty = true;
    }
    public void AddYaw(float yaw)
    {
      Quaternion.CreateFromAxisAngle(ref _Up, yaw, out _OrientationDelta);
      _Orientation *= _OrientationDelta;
      _Dirty = true;

    }
    public void AddRoll(float roll)
    {
      Quaternion.CreateFromAxisAngle(ref _Forward, roll, out _OrientationDelta);
      _Orientation *= _OrientationDelta;
      _Dirty = true;
    }
  }




Re: XNA Framework Rotation help

peekb

I don't think gimbal lock is what I'm experiencing. My rotations are occurring exactly as I would have expected them to. What I'm asking for is a different behavior.

I have tried managing my rotations using quaternions and I am seeing the same result.

I'm not having an issue with what it's doing...it's correct. I just need something else. :)

Thanks!




Re: XNA Framework Rotation help

LaViMa

Let me see if understand you correctly. You have model which you render at a fixed offset from the camera Or is it that you want to make first person camera. Give me a bit more information on the scene and I think I can help. Usually it's just a question of multiplying the matrices in the correct order.



Re: XNA Framework Rotation help

Mike36

Hi:

Your question sounds like a problem I had to solve that simulates orbiting the Earth along an equitorial orbit inclined 28.5 degrees (and a polar orbit too), while simulating the Earth rotating throughout its day. I used a combination of Matrix.CreateRotationY to simulating the satellite's orbit around the planet, and Quaternion.CreateFromRotationMatrix (passing in the rotation matrix from Matrix.CreateRotationY) to simulate Earth rotating about its own axis. If you're interested, check out my Earth Orbit Demo at http://www.spellflight.com/GameModels/EarthOrbitDemo.zip and look at Planet.cs' Render function to see how I did it.

Hope this helps.





Re: XNA Framework Rotation help

Tyrathect

You're trying to do a first person camera, right

Look up "Camera" in the help and you'll see an article called Making a First Person Camera.

I think that article is a little hard to follow, so here's a simple (though maybe not particularly efficient) way

// create the matrix as if the camera was there

Matrix camera_mat = Matrix.Multiply(Matrix.RotationX(pitch),Matrix.RotationY(yaw)).Translate(head_position);

// invert it so you can use it as a world transformation matrix

camera_mat.Invert();





Re: XNA Framework Rotation help

LeeC22

I'm not sure if I know the answer to your problem yet but let me see if I am thinking along the right lines... this is a strange association but it seems similar to what you were describing...

Your model is a pole dancer and your Y-axis is the pole. Regardless of the orientation of the dancer, she will always rotate around the vertical pole if you do a Y-axis based rotation. Therefore, your Y-axis is not relative to the model, it is always straight up.

Is this what you are describing






Re: XNA Framework Rotation help

peekb

<ding!>

Correct.

Additionally, note that if the pole dancer were to flip herself (forgive the sexism) on her head and grab the pole with her legs, all through her flip, grasp, and return to her feet, she'd always be upright.

Mike36's response above is closest to what I'm looking for, yet I haven't been able to get his code to do what I want in my app. With it in place, I'm actually still seeing the same behavior! :)

Some more info: I'm rotating a sphere mesh projected with an orthogonal projection.

Thanks for all of the responses so far!




Re: XNA Framework Rotation help

LeeC22

If you reset the object back to default (Matrix.identity() ) and then apply the new transforms in a rotationY then rotationX order, will that work That way the Y rotation is always working on the straight up axis before the object gets rotated off it.

I'm not the best at 3D so I could be way off the mark here.






Re: XNA Framework Rotation help

Fluxtah

Hey thats not a bad idea, it makes sense that it should work.





Re: XNA Framework Rotation help

tportz

If you are using standard rotation matrices, the rotations will always be performed in world space. The order in which the matrices are applied still matters however. To get a matrix that rotates around the world's y-axis and then the world's x-axis, all you need is:

Matrix transform = Matrix.Multiply(Matrix.CreateRotationY(yAngle), Matrix.CreateRotationX(xAngle));

The situation you describe where the x rotation is done around the rotated x-axis would require the new x-axis to be extracted from the y rotation matrix and used as the axis of rotation in Matrix.CreateFromAxisAngle. That would look like this:

Matrix rotateY = Matrix.CreateRotationY(yAngle);

Vector3 xAxis = new Vector3(rotateY.M11, rotateY.M12, rotateY.M13);

Matrix transform = Matrix.Multiply(rotateY, Matrix.CreateFromAxisAngle(xAxis, xAngle));





Re: XNA Framework Rotation help

peekb

Back for more.

First, thank you to everyone who has responded so far. I've attempted all of the things listed here, however I still cannot get the rotation I'm after.

So, to help with my explanation, I've created a bare-bones Windows solution with my sphere model, and the original drawing code I am using. There's also a pre-built .exe in there. You can get it here.

Use the arrow keys to rotate the sphere about it's X (up/down arrows) and Y (left/right arrows) axes.

You'll see it move just how'd you expect. If you rotate the sphere downward a tad and then rotate it to the left/right, you'll see it rotate on its transformed axis...that is, it turns as though you were holding a globe toward you and spinning it.

Rotate it about its Y axis first and then its X, and you'll see it rotate about its transformed axis.

All as expected.

So, once again, what I'm after is a solution that will, no matter what rotation the sphere is in, will *always* spin the sphere straight up/down and straight left/right relative to the screen. That is, if you start the app and hold the left arrow key, or start the app and hold the up arrow key. It should always spin on those axis no matter how far its been turned in either direction.

Hopefully this will make it a bit easier to understand what I'm trying to do. I dunno why this is so hard for me. :)

Thanks again,
Brian




Re: XNA Framework Rotation help

tportz

It appears that the the Y rotation is done in the X rotation's coordinate space. You need to transform the world's y-axis into the sphere's coordinate space before you can do the Y rotation. If you transform Vector3.Up by the inverse of the X rotation matrix (and the Z rotation matrix if the Z rotation is non-zero), it will give you an axis you can use with Matrix.CreateFromAxisAngle for the Y rotation.



Re: XNA Framework Rotation help

peekb

tportz: Is this what you're suggesting

Matrix m = Matrix.CreateRotationX(this._rotation.X);
Vector3 v = Vector3.Transform(Vector3.Up, Matrix.Transpose(m));

effect.World = Matrix.CreateFromAxisAngle(v, this._rotation.Y) *
m *
Matrix.CreateRotationZ(this._rotation.Z) *
Matrix.CreateTranslation(_position);

If so, this gives me an always right/left rotation about the Y axis, but the X-axis is still transformed and the sphere spins about its transformed axis. I need it to go in both directions... :)

I've tried creating the "inverse" of the above (creating a based on the Y rotation and using that with CreateFromAxisAngle) and multiplying by that, but that did not solve it either.

Is the above correct or am I way off

Thanks!