Game Development Community

Torque Rotation, convert from Euler.

by PuG · in Torque Game Engine · 02/25/2008 (12:34 am) · 8 replies

Hi, we're trying to take Euler rotation output (rotation around OX, OY and OZ) from another application and convert into Torque's rotation format, but at the moment struggling to get a correct output.

So far we have tried taking it to quaternion then, over to Torque but always end up with problems.

Does anyone have any code snippets on how to do this, or any tips?

Best Regards,

#1
02/25/2008 (2:03 am)
I wrote some code to do this from lightwave. however, it only works when you rotate on 2 or few axis.
// Convesion to Quaterions
	MPI = 3.14159265358979323846;

//Heading
	radX=((rot.x * MPI) / (180));
//Pitch
	radY=((rot.y * MPI) / (180));
//Bank
	radZ=((rot.z * MPI) / (180));

	c1 = cos(radY/2);
	s1 = sin(radY/2);
	c2 = cos(radZ/2);
	s2 = sin(radZ/2);
	c3= cos(radX/2);
	s3 = sin(radX/2);

	c1c2 = c1*c2;
	s1s2 = s1*s2; 

	w =c1c2*c3 - s1s2*s3;
	x =c1c2*s3 + s1s2*c3;
	y =s1*c2*c3 + c1*s2*s3;
	z =c1*s2*c3 - s1*c2*s3;
	angle = 2 * (acos((c1*c2*c3) - (s1*s2*s3)));

	//Keeping the angle a degree
	angle = (angle*180)/MPI;


	norm = (x*x)+(y*y)+(z*z);
	if (norm < 0.001){ 
		x=1;
		y=z=0;
	}
	else {
		norm = sqrt(norm);
		x = x / norm;
		y = y / norm;
		z = z / norm;
	}


	x = round(x,4);
	y = round(y,4);
	z = round(z,4);

	result = (string(y) + " " + string(z) + " " + string(x) + " " + string(angle));

it's not written with torquescript. but you might be able to figure something out from this... probably not.
#2
02/25/2008 (2:46 am)
Thanks will go through it, well its in a standalone app, so C++. We're struggling on two of the four Torque axis's, being the first two are fine, the third doesn't function and the heading we have is a preset which is manageable.

This is what we've got so far ourselfs:

void CTorqueMisSyntaxDlg::WriteOut(LPCSTR file)
{
 FILE *fp;
 if(Common.Setting.AppendOutput)
  fp = fopen(file,"a+b");
 else
  fp = fopen(file,"wb");
 if(!fp)
 {
  MessageBox("Unable to write to file",file,MB_ICONWARNING);
  return;
 }

 for(UINT i = 0; i < nTotalBlocks; i++)
 {
  if(ParseBlock[i]->Position[0] > 0) 
   ParseBlock[i]->Position[Common.Setting.PosOrder[0]] += ParseBlock[i]->Tran[Common.Setting.TransOrder[0]];
  else         
   ParseBlock[i]->Position[Common.Setting.PosOrder[0]] -= ParseBlock[i]->Tran[Common.Setting.TransOrder[0]];
  if(ParseBlock[i]->Position[1] > 0) 
   ParseBlock[i]->Position[Common.Setting.PosOrder[1]] += ParseBlock[i]->Tran[Common.Setting.TransOrder[1]];
  else        
   ParseBlock[i]->Position[Common.Setting.PosOrder[1]] -= ParseBlock[i]->Tran[Common.Setting.TransOrder[1]];
  if(ParseBlock[i]->Position[2] > 0) 
   ParseBlock[i]->Position[Common.Setting.PosOrder[2]] += ParseBlock[i]->Tran[Common.Setting.TransOrder[2]];
  else        
   ParseBlock[i]->Position[Common.Setting.PosOrder[2]] -= ParseBlock[i]->Tran[Common.Setting.TransOrder[2]];
   
  /* Temp code */
  float fTotal = (ParseBlock[i]->Rotation[0] + ParseBlock[i]->Rotation[1] + ParseBlock[i]->Rotation[2]);
  USHORT RotationOffsetVal = 0;
  if(fTotal == 0.0f)
  {
   ParseBlock[i]->Rotation[2] = 1;
   RotationOffsetVal          = Common.Setting.RotationOffsetValue;
  }
  else
  {
   float Temp = 0.f;
   Temp = (ParseBlock[i]->Rotation[0] / Common.Setting.RotationMultiplier[0]);
   ParseBlock[i]->Rotation[0] = fToPositive(Temp);

   ParseBlock[i]->Rotation[1] = Temp;
   Temp = (float)(ParseBlock[i]->Rotation[2] / Common.Setting.RotationMultiplier[2]);

      ParseBlock[i]->Rotation[2] = fToPositive(Temp);

   RotationOffsetVal  = Common.Setting.RotationOffsetValue;
  }

  // Grome temp - Ignore
  t_float3 gFloat3;
  gFloat3.x = ParseBlock[i]->Rotation[0];
  gFloat3.y = ParseBlock[i]->Rotation[1];
  gFloat3.z = ParseBlock[i]->Rotation[2];

  t_quat gQuat;
  float test = 1.0f;
  gQuat2AxisRot2(gFloat3,test,gQuat);
  
  /* End temp code - Ignore*/

  fprintf(fp,"new TSStatic() {\r\n");
  fprintf(fp,"\tcanSaveDynamicFields = \"1\";\r\n");

  fprintf(fp,"\tposition = \"%f %f %f\";\r\n",ParseBlock[i]->Position[Common.Setting.PosOrder[0]],
   ParseBlock[i]->Position[Common.Setting.PosOrder[1]],ParseBlock[i]->Position[Common.Setting.PosOrder[2]]);

  if(Common.Setting.RotationOffset == 1)
   fprintf(fp,"\trotation = \"%f %f %f -%d\";\r\n",ParseBlock[i]->Rotation[Common.Setting.RotOrder[0]],
    ParseBlock[i]->Rotation[Common.Setting.RotOrder[1]],ParseBlock[i]->Rotation[Common.Setting.RotOrder[2]],
    RotationOffsetVal);
  else
   fprintf(fp,"\trotation = \"%f %f %f %d\";\r\n",ParseBlock[i]->Rotation[Common.Setting.RotOrder[0]],
   ParseBlock[i]->Rotation[Common.Setting.RotOrder[1]],ParseBlock[i]->Rotation[Common.Setting.RotOrder[2]],
   RotationOffsetVal);

  fprintf(fp,"\tscale = \"%f %f %f\";\r\n",ParseBlock[i]->Scale[Common.Setting.ScaOrder[0]],
   ParseBlock[i]->Scale[Common.Setting.ScaOrder[1]],ParseBlock[i]->Scale[Common.Setting.ScaOrder[2]]);

  fprintf(fp,"\tinternalName = \"%s\";\r\n",ParseBlock[i]->Name);
  fprintf(fp,"\tshapeName = \"%s%s.dts\";\r\n",Common.Setting.ShapeName,ParseBlock[i]->Template);
  fprintf(fp,"\treceiveSunLight = \"1\";\r\n");
  fprintf(fp,"\treceiveLMLighting = \"1\";\r\n");
  fprintf(fp,"\tuseCustomAmbientLighting = \"0\";\r\n");
  fprintf(fp,"\tcustomAmbientLighting = \"0 0 0 1\";\r\n");
  fprintf(fp,"};\r\n");

  free(ParseBlock[i]);
 }
 fclose(fp);
 nTotalBlocks = 0;
}

(90% is just read-in to read out)

Rotation:

float fTotal = (ParseBlock[i]->Rotation[0] + ParseBlock[i]->Rotation[1] + ParseBlock[i]->Rotation[2]);
  USHORT RotationOffsetVal = 0;
  if(fTotal == 0.0f)
  {
   ParseBlock[i]->Rotation[2] = 1;
   RotationOffsetVal          = Common.Setting.RotationOffsetValue;
  }
  else
  {
   float Temp = 0.f;
   Temp = (ParseBlock[i]->Rotation[0] / Common.Setting.RotationMultiplier[0]);
   ParseBlock[i]->Rotation[0] = fToPositive(Temp);

   ParseBlock[i]->Rotation[1] = Temp;
   Temp = (float)(ParseBlock[i]->Rotation[2] / Common.Setting.RotationMultiplier[2]);

      ParseBlock[i]->Rotation[2] = fToPositive(Temp);

   RotationOffsetVal  = Common.Setting.RotationOffsetValue;
  }

(it just devide mathematicaly with the input numbers, fTotal is to check if rotation has any value , if not use a default set of values - so if all axis are 0x0000 it goes to default values )

Regards,
#3
02/27/2008 (11:46 pm)
No other help? it is for a tool thats going to be released to the community as soon as the rotation is figured out.
#4
02/28/2008 (8:05 am)
If you could describe what you want to do, in mathematical terminology, then people can help you accurately.
So far we know that you want to convert a rotation from some totally unknown Euler angle sequence to a Torque rotation. Since there are 12 different (in type I only) Euler angle sequences to choose from, I am exhausted just thinking of all the questions that could be asked to figure out what you really need.

Here's several:

1. what is the Euler rotation sequence you are converting the attitudes from
a. The order of the rotations.
b. Is the axis system right handed?

2. Presumably you are going to rotate objects in Torque's body format with +Y forward, +X right and +Z up. If your original objects used some other setup, the conversion is more complicated to include that effect also.

3. Presumably you want to end up with an attitude in the TorqueScript "transform" format?

4. Script only, or do you have access to the source code?

Info:
the 4 words 3..6 of a TorqueScript "transform" form an axis-angle rotationspecification: %transform = "10 20 30 0 0 1 0" is positioned at (10,20,30) and uses the axis 0 0 1 and rotates an angle zero about it. (an identity rotation)

It shouldn't be too hard to convert a Quaternion format to axis angle. You might have to flip the sign of the rotation angle, and possibly the units depending on exactly how you insert it into Torque.
#5
02/28/2008 (9:26 am)
Sorry, im not the one coding it myself you see (hence the vague question).

Ok, well the program were outputting from is using Euler rotation (rotation around OX, OY and OZ) which I think answers the first question?

As for the second, not sure - what ever is default.

For the 3rd, all we need to end up with is correct rotation for static shapes to go in the mission file.

Lastly this is our own application to take objects placed in another program and convert (rot = -0.785398 -1.570796 0.000000) its output syntax to torque mission format.

Everything is completed accept for the rotation part of it.
#6
02/28/2008 (11:42 am)
You could add something like this in mathTypes.cc,
altho i'm not sure i trust it because going from matrix to eulers and back doesn't seem idempotent.
ConsoleFunction( EulersToMatrix, const char*, 2, 2, "(X Y Z) Retrieve tranformation matrix from Euler angles.")
{
   EulerF eulers;

   dSscanf(argv[1], "%g %g %g", &eulers.x, &eulers.y, &eulers.z);
   MatrixF matrix(eulers);

   AngAxisF aa(matrix);

   aa.axis.normalize();

   char* ret = Con::getReturnBuffer(256);
   dSprintf(ret, 255, "%g %g %g %g %g %g %g", 0.0f, 0.0f, 0.0f, aa.axis.x, aa.axis.y, aa.axis.z, aa.angle);

   return ret;
}

or you could look at m_matF_set_euler_C() in mMath_C.cc.
#7
02/28/2008 (11:45 am)
Note that whatever method you use,
you may run into trouble depending on what "order" the source application composed the eulers in.

that is,
there is more than one correct interpretation of three axis rotations.

so you might need to write custom stuff choosing one of the four interpretations in m_matF_set_euler_C().
#8
02/28/2008 (12:23 pm)
The code PuG posted above looks to me at first glance to be treating the torque rotation fields as Euler angle sets: remember, Torque rotations are axis-angle sets. (a three vector and an angle scalar)

I would suggest converting to your internal quaternion attitude representation, then converting the quat to the Torque axis-angle representation, since quaternions and axis angles are so close. That avoids any question about Euler angle rotation orders and so on, since you would have solved that question entirely in the export program's conventions when converting to the quaternion form.

Torque's Quat to Axis Angle looks like this, in engine/math/mQuat.cc:
AngAxisF & AngAxisF::set( const QuatF & q )
{
   angle = mAcos( q.w ) * 2.0f;
   F32 sinHalfAngle = mSqrt(1.0f - q.w * q.w);
   if (sinHalfAngle != 0.0f)
   	axis.set( q.x / sinHalfAngle, q.y / sinHalfAngle, q.z / sinHalfAngle );
   else
      axis.set(1.0f,0.0f,0.0f);
   return *this;
}

Your quaternion format is possibly a bit different but it should be straight forward to perform this conversion.

Once you have the axis (a three vector) and the angle from the quaternion, a rotation is just "x y z angle", with the values separated by spaces. In mission files, the angle is expressed in degrees, not radians, IIRC. You might try some test objects, because Torque's format could easily be the transpose of yours, in which case you would have to invert the angles sign also.

Good luck!