Game Development Community

Quaternion rotation, rotate one vector to another, problems

by Ross Pawley · in Technical Issues · 09/24/2007 (11:41 am) · 3 replies

So I'm doing some quat based rotation, and that works fine. I basically have two vectors, the target and the current vector. I need to rotate one to the other. I'm using the Quat class' shortestArc function to determine how far to rotate my object to line the current vector up with the target vector. However, shortestArc doesn't determine the *direction* to rotate in, which is actually determined by the axis of rotation. In my case, I may need to rotate in the other direction if my current vector is in front of the target vector (I get both cases, and I need to be able to have both).

The problem arises because I can't limit the solution to a particular axis (i.e., the axis of rotation) to decide when to flip the order of vectors passed in the shortestArc function. Using the right vector of the two vectors to determine when to flip is problematic because in some cases, it's not in the direction of the rotation axis. A dot product between the two vectors doesn't help, since they're going to always or almost always lie on the same side of the positive/negative plane.

As you can see from the image, in the case of the lower knee node, the current vector (in green) is behind the target vector, while for the thigh node above it, the opposite is true. The problem I need to solve is how to detect (correctly, every time) when to flip the order of the vectors passed into shortestArc in order to rotate the current vector to the target vector every time.

I've tried a bunch of different things involving the dot of the object space right vector and my right vectors, the dot of my vectors and my right vector...etc etc. I really need to solve this and any help is much appreciated.

I don't know if I'm explaining this well, so if not, post and berate me about it and I'll elaborate.

Image:
farm2.static.flickr.com/1124/1433682449_41b1333367.jpg

#1
09/24/2007 (3:00 pm)
Doesn't just a simple quat interpolation find the shortest and correct rotation regardless the situation you describe (assuming this needs to occur over time)?

aka:
TSTransform::interpolate(qCurrent,qTarget,timeAsPercent,&qFinal);

you may also consider converting quats into matrices, working with them there, and extracting the resultant quat. sometimes this approach works for me.

i am also may be misunderstanding your needs ;)
#2
09/27/2007 (12:56 pm)
Here is how I solved this problem in the past in a different graphics engine.
Represent the vectors as sets of angles in reference to the 0,0,0 -> 1,1,1 vector.
Now calculate with reference to time the x, y, z angles from the start angles (starting vector) to the end angles (ending vector). Use the time sliced calculated angles to set the node rotation on each frame render.
This will do a rotation from vector 1 to vector 2 with the x, y and z angles changing at a constant rate.
example code:
//! constructor
CSceneNodeAnimatorRotationFixed::CSceneNodeAnimatorRotationFixed(const core::vector3df& startRotation,
                     const core::vector3df& endRotation, u32 timeForWay,
                     bool loop, u32 now)
: Start(startRotation), End(endRotation), StartTime(now), TimeForWay(timeForWay), Loop(loop)
{
   

   recalculateImidiateValues();
}


void CSceneNodeAnimatorRotationFixed::recalculateImidiateValues()
{
   Vector = End - Start;
   WayLength = (f32)Vector.getLength();
   Vector.normalize();

   TimeFactor = WayLength / TimeForWay;
}



//! destructor
CSceneNodeAnimatorRotationFixed::~CSceneNodeAnimatorRotationFixed()
{
}



//! animates a scene node
void CSceneNodeAnimatorRotationFixed::animateNode(ISceneNode* node, u32 timeMs)
{
   if (!node)
      return;

   u32 t = (timeMs-StartTime);

   core::vector3df rot = Start;

   if (!Loop && t >= TimeForWay)
      rot = End;
   else
      rot += Vector * (f32)fmod((f32)t, (f32)TimeForWay) * TimeFactor;
   
   node->setRotation(rot);
}

This is code I wrote to modify Irrlicht to aid in Network synchronization of node rotations. It is posted here as an example and is not intended as a complete solution in Torque
#3
09/27/2007 (11:26 pm)
@CodeDog, thanks for you reply. Actually managed to get this fixed a couple of days ago now by doing a few things different from what I was.

Essentially, when messing with the nodes via the callback, they're in bone-relative space. The best way to get around this is to convert everything you need to object space, do your work, then convert back to the relative space.

For anyone that might find this looking for examples of quaternion use in Torque, here's a bit of sample code.

// You can get the current rotation 
// quaternion out of the smCurrentRotations.
 
// Then you want to convert that 
// to object space using the 
// object space transform of your node's parent.
 
QuatF rot( currentRot );
QuatF parentRot( parentNodeObjectTransform );
 
rot *= parentRot;
  
// Then you can do your work on it, 
// basically construct a quat from 
// your axis of rotation, and the arc cosine 
// of a dot product to get the angle in radians.  
// You have to use the AngAxisF class 
// to store that, then construct the quat from that.
 
AngAxisF angRot( axisVec, angle );
QuatF newRot( angRot );
 
// Rotate by newRot
rot *= newRot;
  
// The last step is to convert your rotation
// and position back into bone-relative space, 
// and store it into the mNodeTransforms array.