locked
Vector to rotation, ie - point models 'forward' to their direction.

    Question

  • Imagine you have a model of say a plane, with a position (x,y,z) and a vector for it's movement.  On each update you're adding the movement vector to the position - ok easy enough.

    You want to have the plane model always be rendered to move with it's nose forward.  is there a simple way to translate the movement vector into corrolating rotation x,y,z.  My high-school cartisian trig is a bit sketchy, but as I recall on a 2d plane from a normalized vector it would be like rotation.x = csc(y/x) (i probably have the trig identity wrong,but you get the geist of it). 

    Want to translate normalized vectors v(1,0.5,0) into a rotation so models are facing the correct way for their movement. - anything built into xna for this?

     

    Tuesday, February 13, 2007 8:21 PM

Answers

  • When you're working in 3D it's generally best to avoid thinking in terms of rotations as angles. Trying to extract angle rotations lands you in a world of pain working out complex trig equations, which are not at all my idea of fun! This sort of thing can generally be done much more easily using vector and matrix math.

    Ultimately, you want a matrix that describes the rotation of your object, but you can get there from a couple of vectors. One vector alone is not enough to describe a full orientation, though, because that still leaves one axis of freedom that the object could rotate to any angle around that vector.

    Let's start out by choosing a front vector for our orientation: you can get this by normalizing your movement vector.

    Now we need vectors pointing right and up. To avoid squashing the object, these need to be perpendicular (at right angles to) to the front vector. We could guess that up might be roughly Vector3.Up, but that might need some adjustment to make it stay perpendicular to our front vector. Here's how to do that:

    - Compute a right vector by taking the cross product of your front vector and Vector3.Up, and normalizing that result

    - Compute a properly perpendicular up vector by taking the cross product of the right vector that you just calculated and your front vector.

    Now you have three vectors, all perpendicular to each other, that describe the orientation of your object. Turning these into a matrix is simple: start off with Matrix.Identity, then assign your new values to its Forward, Right, and Up properties.

    I might have got some of those cross products the wrong way round (I'm terrible at remembering which way they go) so if your model comes out inverted, just swap the order of the arguments to the cross product calls.

    The framework actually has a built in method that does almost the same thing as the above, in the Matrix.CreateLookAt method. You could pass Vector3.Zero as cameraPosition, your movement vector as cameraTarget, and Vector3.Up as cameraUpVector, and it will give back almost the same thing as the matrix I described above, only backward. Call Matrix.Transpose on the result, and you can use that for your object orientation.
    Wednesday, February 14, 2007 5:46 PM
  • Thank you, that's the best example.  I took your 2nd suggestion as I'd prefer a builtin method rather than futz around with too much trig.  .This general solution would be equally good for rockets, thrown footballs, planes, and even people walking around on a flat plane - basically anytime you want a mesh oriented the 'correct' way.

    I have a Character class, with a vector3 Movement - which predictably is that characters direction of movement.  I added a private matrix RotationMatrix which is used on Draw().  For demonstration purposes I call it just on the initalization, but the method should be called anytime to re-orient the character towards their heading. 

     

    public void PointTowardsMovementDirection()
    {
    RotationMatrix = Matrix.CreateLookAt(Vector3.Zero, Movement, Vector3.Up);
    RotationMatrix = Matrix.Transpose(RotationMatrix);
    }

     http://farm1.static.flickr.com/145/390808110_bd9a3fa127.jpg?v=0  (a screen shot, the green fellow in the middle is controlled by the joystick.  The other 50 characters are being thrown away from him they're all flying head first).

    Thursday, February 15, 2007 4:16 AM

All replies

  • I have a little model I steer using 'steering forces', and I've got to do the same thing. I have two methods, steerleft and steerright, that when called do two things:

    - update the heading vector to steer left or right using by vector addition with a vector that's orthogonal to the current heading.

    - update the model rotation to keep it facing the heading vector. Do this by finding the angle between oldVelocity and velocity by doing Vector3.Dot(velocity, oldVelocity). That will give you the cosine of the angle between the two headings. You then know how much to rotate your model by to keep it in line with your change of heading.

    I suppose the relevant bit to you is the latter. If you store your current velocity before you update it, you can use it to calculate the change in angle you require.

    Looks something like this:

    public void SteerLeft()
            {
                Vector3 oldVelocity = velocity;
                velocity = Vector3.Normalize(velocity);
                Vector3 ortho = new Vector3(0, 0, 1f);
                Vector3 steer = Vector3.Cross(ortho, velocity);
                steer = Vector3.Normalize(steer);
                velocity += 0.05f * steer;
                velocity = Vector3.Normalize(velocity);
                oldVelocity = Vector3.Normalize(oldVelocity);
                float d = ((Vector3.Dot(velocity, oldVelocity)));
                double theta = Math.Acos(d);
                angle += MathHelper.ToDegrees((float)theta);
                tilt -= 0.08f;
            }

            public void SteerRight()
            {
                Vector3 oldVelocity = velocity;
                velocity = Vector3.Normalize(velocity);
                Vector3 ortho = new Vector3(0, 0, 1f);
                Vector3 steer = Vector3.Cross(velocity, ortho);
                steer = Vector3.Normalize(steer);
                velocity += 0.05f * steer;
                velocity = Vector3.Normalize(velocity);
                oldVelocity = Vector3.Normalize(oldVelocity);
                float d = ((Vector3.Dot(oldVelocity, velocity)));
                double theta = Math.Acos(d);
                angle -= MathHelper.ToDegrees((float)theta);
                tilt += 0.08f;
            }

    Tuesday, February 13, 2007 8:48 PM
  • I realise that's a bit over complicated. So I'll simplify.

    Say your current heading vector is Vector3 velocity (x,y,z). Store that in a variable.  Then do whatever you do to update your heading however you like.

    Then, take your new velocity (x2,y2,z2) and take the dot product of that and the old velocity. That will give you the cosine of the angle between them. You can use that to determine the angle you need to rotate your model by to keep it aligned with its heading.

    The only thing you have to watch is determining which way to rotate the model, because a dot b is the same as b dot a - this isn't a problem in the code I posted above because each method is applying a 'left' force and a 'right' force when I change my velocity, so I always know which way to rotate. You'll need some way to distinguish the correct direction of rotation, as well as size of rotation.
    Tuesday, February 13, 2007 8:56 PM

  • http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1187025&SiteID=1
    ^^ except instead of finding a rotation between normals, you are finding a rotation between velocity vectors (with your target vector (0,0,-1))
    Tuesday, February 13, 2007 10:17 PM
  • I sort of get what you're saying -but I'm not going really from one vector to another. e.g.

    Model, who's origin is given a point (10,0,0) given a movement Vector of (0,0,3) -moving upwards should be translated from it's 'inital state of 'front on X axis' to be pointing upwards.  If the movment vector were (1,0,1) then it' shoudl be at a 45' inclination.  It's not the 'incremental' rotation I need but the absolute final rotation you'd be at.  If that makes sense.

    Don't quite understand the matrix from the axis & angle.  I don't see why you can just say

    Make a new vector of the normalized MovementVector and get a rotation translation from that.  Why are the cross and dot produts nessecary?  Sorry if this is remedial trig, again it's been ages.

    Wednesday, February 14, 2007 5:26 AM
  • There's really no difference between doing it incrementally and absolutely. All you would do is compare to an initial starting vector every time, instead of the last known heading vector.

    Let's keep it to the XZ plane as you describe in the example above. So you start off heading right. Your heading vector is (1,0,0). Your model is being translated by a rotation matrix of 0 degrees (i.e., it's not being rotated - your default position).

    So you suddenly change velocity to, say, (1,0,1) to fly diagonally up right. If you're only needing to fly in certain directions, you could just store the necessary rotation angles for these flying positions. So for a velocity of (1,0,1), you know to rotate the model 45 degrees. Not flexible, however. Probably too simple for your purposes.

    What you need is two velocities to compare and work with. Let's say you were at (1,0,0) and moved suddenly to (1,0,1). You could just find the angle between them however you wanted - dot product of the vectors, trig, whatever.You certainly have enough info to work on. Dot product is easiest, however, there's supported methods for doing it. So, the dot product of two vectors returns the cosine of the angle between them.

    You should notice that there's no difference in the result between (1,0,0) dot (1,0,1) and (1,0,0) dot and (1,0,-1). You'll just get back cos 45 in each case. So you need to know which direction you turn in when you apply rotation. If you're always aware of the relative direction of turn of your plane that's not so important, you'll be able to control the plus/minus of the rotation angle yourself as appropriate. But cross product is helpful in certain situations. It returns the vector perpendicular to two others. If you measured the dot product of your NEW heading with the two vectors perpendicular to your ORIGINAL heading, you could compare the two angles you get back. If one is smaller than the other, you've just turned more in that direction, so you know which sign to give the angle.








    Wednesday, February 14, 2007 7:32 AM
  • appologies for quickly skimming over the replies, but in answer to the OP, if you have a movement vector then you already have the direction and velocity components.

    You could take the dot product of the vector the object is currently facing and the velocity vector, then derive the angle from the dot product. I am not a math genius and forget exactly how to do this but if you need more help I can post the exact solution later as I have limited time right now I am just heading out :)

    quickly from my book the angle A = acos (a.b) where a and b are unit vectors. not sure if this is the right solution but I am sure I have got it to work before for the same problem, the ship basically adjusts its facing direction based on the direction its currently going.

    Wednesday, February 14, 2007 10:36 AM
  • When you're working in 3D it's generally best to avoid thinking in terms of rotations as angles. Trying to extract angle rotations lands you in a world of pain working out complex trig equations, which are not at all my idea of fun! This sort of thing can generally be done much more easily using vector and matrix math.

    Ultimately, you want a matrix that describes the rotation of your object, but you can get there from a couple of vectors. One vector alone is not enough to describe a full orientation, though, because that still leaves one axis of freedom that the object could rotate to any angle around that vector.

    Let's start out by choosing a front vector for our orientation: you can get this by normalizing your movement vector.

    Now we need vectors pointing right and up. To avoid squashing the object, these need to be perpendicular (at right angles to) to the front vector. We could guess that up might be roughly Vector3.Up, but that might need some adjustment to make it stay perpendicular to our front vector. Here's how to do that:

    - Compute a right vector by taking the cross product of your front vector and Vector3.Up, and normalizing that result

    - Compute a properly perpendicular up vector by taking the cross product of the right vector that you just calculated and your front vector.

    Now you have three vectors, all perpendicular to each other, that describe the orientation of your object. Turning these into a matrix is simple: start off with Matrix.Identity, then assign your new values to its Forward, Right, and Up properties.

    I might have got some of those cross products the wrong way round (I'm terrible at remembering which way they go) so if your model comes out inverted, just swap the order of the arguments to the cross product calls.

    The framework actually has a built in method that does almost the same thing as the above, in the Matrix.CreateLookAt method. You could pass Vector3.Zero as cameraPosition, your movement vector as cameraTarget, and Vector3.Up as cameraUpVector, and it will give back almost the same thing as the matrix I described above, only backward. Call Matrix.Transpose on the result, and you can use that for your object orientation.
    Wednesday, February 14, 2007 5:46 PM
  • I hear what you're saying about not using angles and instead thinking in terms of matrices, but what if you want to pitch your aircraft up 20 degrees while it is moving 1 unit per update call? When I think in those terms, it's easy to picture what the craft is doing, and then create a rotationy matrix with current angle + 20. It's harder for me to picture how you'd define a movement vector such that, when all is said and done with Matrix.CreateLookAt and Matrix.Transpose, results in an aircraft pitched up 20 degrees.

    Chalk it up to inexperience with matrix math, guess I need to find a primer.

    Thursday, February 15, 2007 1:02 AM
  • You can think of that kind of operation in terms of modifying the front and up vectors for your object.

    Rather than thinking "add 10 degrees to my pitch angle", think "rotate my front vector around my right vector by 10 degrees".
    Thursday, February 15, 2007 1:23 AM
  • hm.. so something like:

    //Pitch event handler

    m_mtxDesiredRotations *= Matrix.CreateFromAxisAngle(m_mtxCurRotations.Right, 10f);

    ...

    //Somewhere in update method

    if (m_mtxCurRotations != m_mtxDesiredRotations)

        m_mtxCurRotations =  m_mtxDesiredRotations.Lerp(m_mtxCurRotations, m_mtxDesiredRotations, fLerpSpeed);

    Thursday, February 15, 2007 1:42 AM
  • Thank you, that's the best example.  I took your 2nd suggestion as I'd prefer a builtin method rather than futz around with too much trig.  .This general solution would be equally good for rockets, thrown footballs, planes, and even people walking around on a flat plane - basically anytime you want a mesh oriented the 'correct' way.

    I have a Character class, with a vector3 Movement - which predictably is that characters direction of movement.  I added a private matrix RotationMatrix which is used on Draw().  For demonstration purposes I call it just on the initalization, but the method should be called anytime to re-orient the character towards their heading. 

     

    public void PointTowardsMovementDirection()
    {
    RotationMatrix = Matrix.CreateLookAt(Vector3.Zero, Movement, Vector3.Up);
    RotationMatrix = Matrix.Transpose(RotationMatrix);
    }

     http://farm1.static.flickr.com/145/390808110_bd9a3fa127.jpg?v=0  (a screen shot, the green fellow in the middle is controlled by the joystick.  The other 50 characters are being thrown away from him they're all flying head first).

    Thursday, February 15, 2007 4:16 AM