Game Development Community

Rotation Math (Video added)

by Gregor Samsa · in Torque 3D Beginner · 10/17/2009 (3:07 pm) · 22 replies

How can I rotate an object (a security cam) as looking to a specific point(player)?

GO TO POST 18
Page «Previous 1 2
#1
10/19/2009 (9:57 pm)
not sure i understand the question here? What exactly are you trying to calculate? a specific coordinate on a rotating object? that is purely arbitrary a specific coordinates new location on a spinning object based on the objects radius and spin velocity and some new time? if this is the question then as far as the math goes its fairly simple, you would use either x=1/2(w0+w)t, or x=w0t+1/2at^2 or w^2=w0+2ax or x=wt-1/2at^2 were

x=angular displacement
w0=initial angular velocity
w=angular velocity
a=angular acceleration
t=time

the key here is to watch your units IE if you use ft/s your other variable units better also be in ft/s etc.

hope that helps, and btw next time it may be faster to just search the net, this stuff is some pretty elementary physics, and many many many locations have these equations accessible.
#2
10/20/2009 (2:29 pm)
Forget the rotating object and think that we have a security cam located somewhere on a wall and looking towards somewhere.

I want to use a key binding (lets say "ctrl k") to rotate the security cam. Every time I press the "ctrl + k" keys, the cam will rotate immediately as looking towards my player object.

I think it is more clear now.
#3
10/20/2009 (6:55 pm)
if your security camera only rotates around the vertical axis, check out this thread. if it can swivel up & down as well as side to side, the math is a bit tricker. actually the math is simpler, but it needs proper 4x4 matrices, which you need to have the engine to work with. if you do, this resource implements setForwardVector().
#4
10/22/2009 (2:20 pm)
Thank you Orion, I will check the links.

BTW, I don't have the engine, just the demo for now and I don't understand why I need the engine.

I know rotating an object is not that simple as it sounds, but I think it can be done without modifing the engine.

With torque script, I can get the transform matrices of both the security cam and the player objects and I can set a new transform to security cam. I just don't know how to calculate the new transform.
#5
10/22/2009 (2:57 pm)
like i said, if your camera only swivels side-to-side then this is easy to do in torquescript. if it also swivels up-and-down then you can still do it in TS but the math will be more involved, and would be simpler in the engine.
#6
10/22/2009 (3:48 pm)
I want to rotate the cam in both side-to-side and up-and-down so it will be pointing out (or looking at) the exact position of the target object.

Right now, I am trying to figure out the math from setForwardVector resource, but I think that won't help, there are many functions that I have no idea about what they does or what they returns.
#7
10/22/2009 (4:07 pm)
that resource will be difficult to translate into torquescript, as it takes a shortcut approach which simply isn't available in TS.

unfortunately i don't have time just now to describe how to do in TS.
it's a bit involved, and you'll need a fairly thorough understanding of transformations and the MatrixMul.. family of functions.
#8
11/01/2009 (6:12 pm)
I think I am very close to the solution, but I might be missing something.

Here what I do to rotate the camera towards the player:

function LookAt(%targetObj, %securityCam)
{
   //First, I need both current direction and desired direction
   //as unit vectors

   %objVec = %securityCam.getForwardVector(); //current direction vector
   
   %targetPos = %targetObj.getPosition();
   %camPos = %securityCam.getPosition();
   %desiredVec = VectorNormalize(VectorSub(%targetPos, %camPos)); //desired direction vector
   
   // then I need to find the vector to rotate around
   // (which is perpendicular to both current and desired directions)
   // and the amount of rotation (the angle between current and desired)

   %crossVec = VectorCross(%objVec, %desiredVec);
   %rotationVec = VectorNormalize(%crossVec); //vector to rotate around
   
   %dotProduct = VectorDot(%objVec, %desiredVec);
   %rotationAngle = mAcos(%dotProduct); //rotation amount
   %halfAngle = 0.5*%rotationAngle;
   
   // Now I can represent this axis-angle rotation as quaternion
   %quatW = mCos(%halfAngle);
   %quatX = mSin(%halfAngle)*getWord(%rotationVec, 0);
   %quatY = mSin(%halfAngle)*getWord(%rotationVec, 1);
   %quatZ = mSin(%halfAngle)*getWord(%rotationVec, 2);
   
   %rotationQuat = %quatX SPC %quatY SPC %quatZ SPC %quatW;

   // I need both the quaternion (q) and the conjugate of it (q')
   // to apply rotation (q*v*q')
   %rotQuatConj = (-1 * %quatX) SPC (-1 * %quatY) SPC (-1 * %quatZ) SPC %quatW;

   // Now I have both q and q' but not sure about v :(
   %initialRot = (%objVec SPC 0); //this must be the v
   //%initialRot = getWords(%rotateObj.getTransform(), 3, 6); // or this


   %resultRot = quatMult((quatMult(%rotationQuat, %initialRot)), %rotQuatConj); // q*v*q'
   
   %newTransform = %camPos SPC %resultRot;
   
   return %newTransform;
}

// quaternion multiplication
function quatMult(%quatA, %quatB)
{
   %quatAX = getWord(%quatA, 0);
   %quatAY = getWord(%quatA, 1);
   %quatAZ = getWord(%quatA, 2);
   %quatAW = getWord(%quatA, 3);
   %quatBX = getWord(%quatB, 0);
   %quatBY = getWord(%quatB, 1);
   %quatBZ = getWord(%quatB, 2);
   %quatBW = getWord(%quatB, 3);
   
   %quatCX = %quatAW * %quatBX + %quatAX * %quatBW + %quatAY * %quatBZ - %quatAZ * %quatBY;
   %quatCY = %quatAW * %quatBY - %quatAX * %quatBZ + %quatAY * %quatBW + %quatAZ * %quatBX;
   %quatCZ = %quatAW * %quatBZ + %quatAX * %quatBY - %quatAY * %quatBX + %quatAZ * %quatBW;
   %quatCW = %quatAW * %quatBW - %quatAX * %quatBX - %quatAY * %quatBY - %quatAZ * %quatBZ;
   
   return (%quatCX SPC %quatCY SPC %quatCZ SPC %quatCW);
}
#9
11/02/2009 (1:22 am)
wow looks like you've done some nice work.
i can't tell if it's correct or not,
but i have one or two comments.

first, i believe that torque quaternions may not match the mathematical definition of a quaternion, but it looks like you're on top of that.

second, just to describe another approach, if this were with full 3x3 (or 4x4) transformation matrices, a very simple solution is:
* assuming that the object's "Y" vector is the forward direction,
* choose a "forward" vector that's the direction you want the object to point. this would be %desiredVec.
* calculate a "right" vector as vForward CROSS vUp. (vUp probably = 0 0 1)
* calculate a Z vector as vRight CROSS vForward.
* then just copy those vectirs into a matrix where the X-column is vRight, the Y-column is vForward, and the Z-column is vZ, and Bob's your uncle.

you could do this math in script, but afaik there isn't a torquescript function to convert a 3x3 (or 4x4) matrix into a torquescript transform, so you'd have to find some code to convert an affine transformation matrix to a quaternion.
#10
11/03/2009 (4:02 pm)
I think torque script rotation is not a quaternion, but an axis angle. I will try to convert my resultRot quaternion to axis angle before applying it.

Orion, your matrix approach seems a bit problematic because it assumes that the camera has no initial rotation or at least the camera is not looking upwards (or bob's my uncle). It fails when forward and up vectors are same.

Maybe VectorOrthoBasis function can do a better job for such matrix rotation.
#11
11/03/2009 (4:20 pm)
> It fails when forward and up vectors are same.
true.
choosing a reasonable "up" vector is a bit of an art.
if you know the previous vZ (assuming a reasonable previous condition), you could try to making the new vUp be the old vZ, but then you'll eventually get drift. you could do something like:
dot = vForward DOT vUp;
if (dot > 0.8) // 0.8 chosen arbitrarily
{
   f   = (dot - 0.8) / (1 - 0.8);
   vUp = vUp * (1 - f) + vZPrev * f;
}
#12
11/03/2009 (4:21 pm)
note there's also problems if vForward = vDown,
but the same approach would work for that case.
#13
11/05/2009 (5:16 pm)
Everything seems perfect and security cam (the buggy chasis in the video) rotating as player moves but for some reason it is rotating twice more than it should. :(


function securityCam::LookAt(%thisDB, %player, %securityCam)
{
//  First, I need both current direction and desired direction  
//  as unit vectors  

   %currentVec = %securityCam.getForwardVector(); //current direction

   %targetPos = %player.getPosition();
   %securityCamPos = %securityCam.getPosition();
   
   %desiredVec = VectorNormalize(VectorSub(%targetPos, %securityCamPos)); //desired direction

// then I need to find the vector to rotate around
// (which is perpendicular to both current and desired directions)
// and the amount of rotation (the angle between current and desired) 
      
   %crossVec = VectorCross(%currentVec, %desiredVec);
   %rotationVec = VectorNormalize(%crossVec); //vector(axis) to rotate around
   
   %dotProduct = VectorDot(%currentVec, %desiredVec);
   %rotationAngle = mAcos(%dotProduct); //rotation amount(angle)

// Now I can represent this axis-angle rotation as quaternion  
   %rotationAxisAngle = %rotationVec SPC %rotationAngle;
   %rotationQuat = axisAngleToQuat(%rotationAxisAngle); //quaternion q

// I need both the quaternion (q) and the conjugate of it (q')  
// to apply rotation (q*v*q')

   %rotQuatConj = quatConjugate(%rotationQuat);

// To make the quaternion multiplication I need to add a fourth 
// component(zero as real component) to my current vector(v)
   %initialRot = (%currentVec SPC 0);

   %resultQuat = quatMult((quatMult(%rotationQuat, %initialRot)), %rotQuatConj); // q*v*q'

// the result is quaternion so I need to convert it to axis angle
   %resultRot = quatToAxisAngle(%resultQuat);
   
// It's time to apply the rotation  
   %newTransform = %securityCamPos SPC %resultRot;
   %securityCam.setTransform(%newtransform);
   %thisDB.schedule(200, "LookAt", %player, %securityCam);
}


function axisAngleToQuat(%axisAngle)
{
// --- Axis Angle to Quaternion Conversion ---
   %halfAngle = 0.5 * GetWord(%axisAngle, 3);
   
   %quatW = mCos(%halfAngle);
   %quatX = mSin(%halfAngle)*getWord(%axisAngle, 0);
   %quatY = mSin(%halfAngle)*getWord(%axisAngle, 1);
   %quatZ = mSin(%halfAngle)*getWord(%axisAngle, 2);
   
   %quat = %quatX SPC %quatY SPC %quatZ SPC %quatW;
   return %quat;
}


// --- Quaternion to Axis Angle Conversion ---
function quatToAxisAngle(%quat)
{
   %quat = quatNormalize(%quat);
   
   %quatX = GetWord(%quat, 0);
   %quatY = GetWord(%quat, 1);
   %quatZ = GetWord(%quat, 2);
   %quatW = GetWord(%quat, 3);
   
   %scale = mSqrt(%quatX*%quatX + %quatY*%quatY + %quatZ*%quatZ);

   %axisX = %quatX / %scale;
   %axisY = %quatY / %scale;
   %axisZ = %quatZ / %scale;

   %axisA = mAcos(%quatW)*2;
   
   %axisAngle = %axisX SPC %axisY SPC %axisZ SPC %axisA;
   return %axisAngle;
}


function quatMult(%quatA, %quatB)
{
//---Quaternion Multiplication (q1*q2)---
   %quatAX = getWord(%quatA, 0);
   %quatAY = getWord(%quatA, 1);
   %quatAZ = getWord(%quatA, 2);
   %quatAW = getWord(%quatA, 3);
   %quatBX = getWord(%quatB, 0);
   %quatBY = getWord(%quatB, 1);
   %quatBZ = getWord(%quatB, 2);
   %quatBW = getWord(%quatB, 3);
   
   %quatCX = %quatAW * %quatBX + %quatAX * %quatBW + %quatAY * %quatBZ - %quatAZ * %quatBY;
   %quatCY = %quatAW * %quatBY - %quatAX * %quatBZ + %quatAY * %quatBW + %quatAZ * %quatBX;
   %quatCZ = %quatAW * %quatBZ + %quatAX * %quatBY - %quatAY * %quatBX + %quatAZ * %quatBW;
   %quatCW = %quatAW * %quatBW - %quatAX * %quatBX - %quatAY * %quatBY - %quatAZ * %quatBZ;
   
   %quatC = %quatCX SPC %quatCY SPC %quatCZ SPC %quatCW;
   return %quatC;
}


function quatConjugate(%quat)
{
   %quatX = GetWord(%quat, 0);
   %quatY = GetWord(%quat, 1);
   %quatZ = GetWord(%quat, 2);
   %quatW = GetWord(%quat, 3);
   
   %conjugate = (-1 * %quatX) SPC (-1 * %quatY) SPC (-1 * %quatZ) SPC %quatW;
   return %conjugate;  
}
#14
11/05/2009 (6:03 pm)
nice progress!

those are some seriously handy quaternion utils you've written, too.

a 2x rotation should be easy to track down.
i would suggest using some hard-coded data for which you know what the results should be and testing various parts of the math to see where things become unexpected, starting with the three utility fn's.
#15
11/06/2009 (8:31 pm)
Orion, I tested the code over and over again and I am convinced that there must be a logic error that I can't overcome with my math knowledge. I am just a 3d artist who don't really know anything about quaternion math. Thank you for your suggestions, but I am done trying to fix this.

Actually the most disappointing part here is that I asked for help, explained my problem, shared my code, captured a video, but I can't get any real help from the great community of Garage Games.

I know, this is a public forum, but I don't think the private ones are different. I looked at many similar topics from TGE and TGEA forums, which they were private once and I saw that they are full of unanswered questions and unsolved issues.
#16
11/06/2009 (8:36 pm)
hey Gregor -
not sure how you consider yourself unhelped in this thread, but i'm sorry you hit a wall.

unfortunately i don't have the time to dig into yr code.

you could try finding some code to convert a 3x3 matrix into a axis-angle rotation and trying the approach i outlined.
#17
11/07/2009 (7:01 pm)
I am unhelped Orion, no one is telling me that how stupid I am!
The code below doing exactly the same thing with all the code on post #13.

function securityCam::LookAt2(%thisDB, %targetObj, %rotateObj)
{
   %targetObjPos = %targetObj.getPosition();
   %rotateObjPos = %rotateObj.getPosition();
   %desiredVec = VectorNormalize(VectorSub(%targetObjPos, %rotateObjPos));
   %currentAngle = getWord(%rotateObj.getTransform(), 6);
   
   %newTransform = %rotateObjPos SPC %desiredVec SPC %currentAngle;
   %rotateObj.setTransform(%newtransform);
   %thisDB.schedule(200, "LookAt2", %targetObj, %rotateObj);
}

Still rotating twice more than expected, just like in the video :/
#18
11/07/2009 (9:14 pm)
this line is probably not doing what you think:
%newTransform = %rotateObjPos SPC %desiredVec SPC %currentAngle;

%desiredVec is the vector from %this to %target,
but that line uses %desiredVec as the axis of a rotation for %this,
so %this is going to have a hard time aligning itself to %desiredVec.


try this. untested.
function directionToAxisAngle(%vDirection)
{
   // orion elenzil
   // we are going to compose a 3x3 rotation matrix
   // which orients the Y-axis of an incoming identity transform to the %vDirection vector.
   // it's as simple as just setting the y-column of the matrix to be %vDirection
   // and then determining the x- and z-columns as mutually perpendicular.
   // it's well worth the time to hand-work some multiplications of this matrix against various
   // incoming vectors to get a feel for the whole matrix thing.

   %vUp      = "0 0 1"; // let's keep it simple for now. fight gimble-lock later.

   %vMatrixX = VectorCross(%vDirection, %vUp);    // the x-column of our 3x3 matrix. perpendicular to both %vDirection and %vUp.
   %vMatrixY = %vDirection;                       // the y-column of our 3x3 matrix. we're just saying "be what i want".
   %vMatrixZ = VectorCross(%vMatrixX, %vMatrixY); // the z-column of our 3x3 matrix. perpendicular to X and Y.

   // note that the order of the components matters in a cross-product,
   // and the proper order varies from 3D system to system,
   // but there's only two choices, so if you find your object pointing
   // 180-degrees in the wrong direction, try switching the order of
   // the arguments in the two cross-products.

   %matrix    = %vMatrixX SPC %vMatrixY SPC %vMatrixZ;
   %transform = "0 0 0" SPC rotationMatrixToAxisAngle(%matrix);
   
   return %transform;
}



function rotationMatrixToAxisAngle(%m)
{
   // orion elenzil
   // %m is a string representing a rotation matrix who's columns are: x[n] = m[n], y[n] = m[3 + n], z[n] = m[6 + n].
   // this is code untested, and the math comes from
   // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle
   // this will fail if the matrix contains scaling, shearing, or is non-affine.
   
   %m00 = getWord(%m, 0);
   %m01 = getWord(%m, 1);
   %m02 = getWord(%m, 2);
   %m10 = getWord(%m, 3);
   %m11 = getWord(%m, 4);
   %m12 = getWord(%m, 5);
   %m20 = getWord(%m, 6);
   %m21 = getWord(%m, 7);
   %m22 = getWord(%m, 8);
   
   // some systems use row-matrices and some use column-matrices.
   // i'm not sure which one was chosen by euclideanspace.com,
   // so it's possible that the above code should be:
   // %m00 = getWord(%m, 0);
   // %m10 = getWord(%m, 1);
   // %m20 = getWord(%m, 2);
   // %m01 = getWord(%m, 3);
   // %m11 = getWord(%m, 4);
   // %m21 = getWord(%m, 5);
   // %m01 = getWord(%m, 6);
   // %m11 = getWord(%m, 7);
   // %m21 = getWord(%m, 8);
   
   %theta = mAcos((%m00 + %m11 + %m22 - 1) / 2);
   
   %epsilon = 0.0001;
   if (mAbs(%theta) < %epsilon)
   {
      // singularity at zero
      return "0 0 0 0 0 1 0";
   }
   else if (mAbs(3.14159265359 - %theta) < %epsilon)
   {
      // ditto at 180, but i leave it to you to grok the whole largest-diagonal thing at that site.
      return "0 0 0 0 0 1 0";
   }

   %m21_minus_m12 = %m21 - %m12;
   %m02_minus_m20 = %m02 - %m20;
   %m10_minus_m01 = %m10 - %m01;
   %denominator   = mSqrt(
                          (%m21_minus_m12 * %m21_minus_m12) +
                          (%m02_minus_m20 * %m02_minus_m20) +
                          (%m10_minus_m01 * %m10_minus_m01)
                         );

   %x = %m21_minus_m12 / %denominator;
   %y = %m02_minus_m20 / %denominator;
   %z = %m10_minus_m01 / %denominator;
   
   %axisAngle %x SPC %y SPC %z SPC %theta;
   
   return %axisAngle;
}
#19
11/08/2009 (12:05 pm)
Thank you so much Orion! It is working as exactly as it should.

Now I am a bit confused. You used a new term, "direction", but I was thinking that the torque orientation is an axis-angle which the axis part is a vector representing the direction that the object facing, and the angle part is a roll angle around that vector and it says the object is straighten up, upside down or canted.

So I tried to set my %desiredVec (which is a unit vector pointing from object to target) as the axis part of the transform, without changing the position and the angle parts and expected to see the object facing to the target.

What is wrong with that approach?
#20
11/08/2009 (12:40 pm)
cool, glad the code worked. note there's still a singularity when vDirection becomes parallel or anti-parallel to vUp.

Quote:I was thinking that the torque orientation is an axis-angle which the axis part is a vector representing the direction that the object facing, and the angle part is a roll angle around that vector and it says the object is straighten up, upside down or canted.

nope. as i alluded to in my previous post,
the axis part is a vector around which the object is rotated.
so "1 0 0 <angle>" means a rotation around the x-axis, which is very different than aligning the object to the x-axis.

i believe this is the standard meaning of the phrase.
Page «Previous 1 2