Game Development Community

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:
//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 ...

#1
04/14/2011 (2:20 pm)
Maybe these will help. Pulled from a library of useful stuff that I haven't used in a while... but I think they would still work - if not it's at least an idea ;)

$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);
}
#2
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
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
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
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.
#6
04/15/2011 (2:09 pm)
@Scott
Why not add that code to the resources section!

----------------------------

Also, airstrikes resourced:
part1
part2
... stupid word count limit ... /nerdraeg

#7
04/15/2011 (2:35 pm)
Cool work Steve!

And nice bit of code from Scott as well.