3D Math Madness: Get Transform from Position and Vector? Result!
by Steve Acaster · in Technical Issues · 04/14/2011 (1:48 pm) · 7 replies
The title says it all ...
Technical terms:
I have a (XYZ) position (POSITION1) and a (3 element) Vector pointing from that position to another XYZposition (POSITION2), and I want to work out what the equivalent (PosX PosY PosZ RotX RotY RotZ theta - theta no doubt radians but perfectly convertable to degrees) transform for an object would be ...
In leyman terms: I want to spawn an object at Position1 that is facing Position2.
eg:
... and I'm stumped ...
... ideas, pointers, working math, heckling, welcome ...
Technical terms:
I have a (XYZ) position (POSITION1) and a (3 element) Vector pointing from that position to another XYZposition (POSITION2), and I want to work out what the equivalent (PosX PosY PosZ RotX RotY RotZ theta - theta no doubt radians but perfectly convertable to degrees) transform for an object would be ...
In leyman terms: I want to spawn an object at Position1 that is facing Position2.
eg:
//POSITION1 %pos1 = -25.854 107.102 339.979;//aim from %facingVector = 98.4598 17.483 -100;//aiming vector %pos2 = 72.6058 124.585 239.979;//aim at this %vectorTowardsPos2 = VectorAdd(%pos2, VectorScale(%facingVector_normalized, 200)); %vectorTowardsPos2 = 72.6058 124.585 239.979; //and this is what I'm trying to do, visualized (note debugdraw is bust in 1.1 Preview) debugdraw.togglefreeze(); debugdraw.drawline(%pos1, %vectorTowardsPos2, "1 0 1");
... and I'm stumped ...
... ideas, pointers, working math, heckling, welcome ...
About the author
One Bloke ... In His Bedroom ... Making Indie Games ...
#2
Works!
And a few alterations (just to get degrees nothing more fancy)
Thanks, Mr.Hall! 8D
04/14/2011 (3:18 pm)
Now those functions look awfully familiar ... guess I shouldn't have edited my "useful utiliy scripts list" ...Works!
And a few alterations (just to get degrees nothing more fancy)
$Pi = 3.14159;
$TwoPi = 6.28319;
// This lil function generates the rotation required for an object at PosOne to
// point at PosTwo (z rot only)
//yorks, then convert radian to degrees
function pointToXYPosDegree(%posOne, %posTwo)
{
%vec = VectorSub(%posOne, %posTwo);
//get the angle
%rotAngleZ = mATan( firstWord(%vec), getWord(%vec, 1) );
//add pi to the angle
%rotAngleZ += $Pi;
//make this rotation a proper torque game value, anything more than 240
// degrees is negative
if(%rotAngleZ > 4.18879)//yorks you don't actually need this but if it ain't broke don't fix it
{
//the rotation scale is seldom negative, instead make the axis value negative
%modifier = -1;
//subtract 2pi from the value, then make sure its positive
%rotAngleZ = mAbs(%rotAngleZ - $TwoPi);
//sigh, if only this were all true
}
else
%modifier = 1;
//assemble the rotation and send it back
// return "0 0" SPC %modifier SPC %rotAngleZ;//yorks out if you don't want radians - put back in if you do!
%rotAngleZ = mRadToDeg(%rotAngleZ);//yorks in - for returning a degree angle, take out if you want radians
return "0 0" SPC %modifier SPC %rotAngleZ;
}
// And this lil function generates the rotation required for an object at posOne
// to point at PosTwo for X, Y And Z axis.
//yorks, then convert final radian to degrees
function pointToPosDegree(%posOne, %posTwo)
{
//sub the two positions so we get a vector pointing from the origin in the
// direction we want our object to face
%vec = VectorSub(%posTwo, %posOne);
// pull the values out of the vector
%x = firstWord(%vec);
%y = getWord(%vec, 1);
%z = getWord(%vec, 2);
//this finds the distance from origin to our point
%len = vectorLen(%vec);
//---------X-----------------
//given the rise and length of our vector this will give us the angle in radians
%rotAngleX = mATan(%z, %len);
//---------Z-----------------
//get the angle for the z axis
%rotAngleZ = mATan(%x, %y);
//create 2 matrices, one for the z rotation, the other for the x rotation
%matrix = MatrixCreateFromEuler("0 0" SPC %rotAngleZ * -1);
%matrix2 = MatrixCreateFromEuler(%rotAngleX SPC "0 0");
//now multiply them together so we end up with the rotation we want
%finalMat = MatrixMultiply(%matrix, %matrix2);
//yorks
//let's change the radian to an angle here
//makes it easier for us to just pass the final %finalMat directly on to an object's rotation
%radAngle = getWord(%finalMat, 6);
%degAngle = mRadToDeg(%radAngle);
%finalMat = setWord(%finalMat, 6, %degAngle);
//we're done, send the proper numbers back
return getWords(%finalMat, 3, 6);
}Thanks, Mr.Hall! 8D
#3
library.cs found in Zod's MGStarter Kit has a lot of great utility functions like these... and I've seen many of them floating around the forums over the years - hard to say were credit goes but glad it helped.
04/14/2011 (3:24 pm)
Cool :)library.cs found in Zod's MGStarter Kit has a lot of great utility functions like these... and I've seen many of them floating around the forums over the years - hard to say were credit goes but glad it helped.
#4
That should get you what you want. Note that you MUST normalize the direction vector before passing it into this function, UNLESS you modify your createOrientFromDir() function in engine/math/mathUtils.cc to always normalize the direction passed to it (which is the solution I recommend):
04/14/2011 (3:28 pm)
I don't remember when, but I guess I had a need at some point to create a transform facing a point, because I apparently added this helpful function to my engine/math/mathTypes.cc file:ConsoleFunction( MatrixCreateFromDir, const char*, 2, 3, "(Vector dir, [Point position]) Creates a matrix facing the given direction")
{
VectorF dir;
dSscanf(argv[1], "%g %g %g", &dir.x, &dir.y, &dir.z);
MatrixF mat = MathUtils::createOrientFromDir(dir);
Point3F pos(0,0,0);
if (argc == 3)
{
dSscanf(argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z);
}
AngAxisF aa(mat);
char* ret = Con::getReturnBuffer(256);
dSprintf(ret, 255, "%g %g %g %g %g %g %g", pos.x, pos.y, pos.z, aa.axis.x, aa.axis.y, aa.axis.z, aa.angle);
return ret;
}That should get you what you want. Note that you MUST normalize the direction vector before passing it into this function, UNLESS you modify your createOrientFromDir() function in engine/math/mathUtils.cc to always normalize the direction passed to it (which is the solution I recommend):
MatrixF createOrientFromDir(const Point3F &direction )
{
Point3F j = direction;
Point3F k(0.0f, 0.0f, 1.0f);
Point3F i;
j.normalize(); // < Normalize this to avoid returning a bad matrix
mCross( j, k, &i );
if( i.isZero() )
{
i = Point3F( 0.0f, -1.0f, 0.0f );
}
...
#5
[edit]
And got library.cs, too.
[double edit]
And after a quick faff ... and then ending up not using the rotation onto an object after all ... but hey it'll be real useful for something else! - but I did use the XY rotation which solved the 180 degree flipping issue I'd reported as a bug ... I've got my "scriptObject" faked airstrikes working.
I've been improving a bit since that vid.
04/14/2011 (3:54 pm)
Ah! Cool stuff, Scott. :)[edit]
And got library.cs, too.
[double edit]
And after a quick faff ... and then ending up not using the rotation onto an object after all ... but hey it'll be real useful for something else! - but I did use the XY rotation which solved the 180 degree flipping issue I'd reported as a bug ... I've got my "scriptObject" faked airstrikes working.
I've been improving a bit since that vid.
Associate Michael Hall
Distracted...
$Pi = 3.14159; $TwoPi = 6.28319; // This lil function generates the rotation required for an object at PosOne to // point at PosTwo (z rot only) function pointToXYPos(%posOne, %posTwo) { %vec = VectorSub(%posOne, %posTwo); //get the angle %rotAngleZ = mATan( firstWord(%vec), getWord(%vec, 1) ); //add pi to the angle %rotAngleZ += $Pi; //make this rotation a proper torque game value, anything more than 240 // degrees is negative if(%rotAngleZ > 4.18879) { //the rotation scale is seldom negative, instead make the axis value negative %modifier = -1; //subtract 2pi from the value, then make sure its positive %rotAngleZ = mAbs(%rotAngleZ - $TwoPi); //sigh, if only this were all true } else %modifier = 1; //assemble the rotation and send it back return "0 0" SPC %modifier SPC %rotAngleZ; } // And this lil function generates the rotation required for an object at posOne // to point at PosTwo for X, Y And Z axis. function pointToPos(%posOne, %posTwo) { //sub the two positions so we get a vector pointing from the origin in the // direction we want our object to face %vec = VectorSub(%posTwo, %posOne); // pull the values out of the vector %x = firstWord(%vec); %y = getWord(%vec, 1); %z = getWord(%vec, 2); //this finds the distance from origin to our point %len = vectorLen(%vec); //---------X----------------- //given the rise and length of our vector this will give us the angle in radians %rotAngleX = mATan(%z, %len); //---------Z----------------- //get the angle for the z axis %rotAngleZ = mATan(%x, %y); //create 2 matrices, one for the z rotation, the other for the x rotation %matrix = MatrixCreateFromEuler("0 0" SPC %rotAngleZ * -1); %matrix2 = MatrixCreateFromEuler(%rotAngleX SPC "0 0"); //now multiply them together so we end up with the rotation we want %finalMat = MatrixMultiply(%matrix, %matrix2); //we're done, send the proper numbers back return getWords(%finalMat, 3, 6); }