Game Development Community

MatrixF Rotate Player X axis only

by Swaroop Reddy · in Torque Game Engine · 11/13/2007 (5:18 pm) · 8 replies

I would like to stop the player from rotating along HIS own Y axis. I want him to rotate only along HIS own X axis. Can someone please tell me how I can modify the code below to do this. The code below will rotate player along both the x and y axis to conform him to the ground based on the slope he is standing on. If I set a value for rot.x and use the following line it rotates the player along the WORLD X axis not HIS own X axis:
mat.set(EulerF(rot.x, 0, rot.z));

void Player::setPosition(const Point3F& pos,const Point3F& rot)
{
MatrixF mat;
mat.set(EulerF(0, 0, rot.z));
mat.setColumn(3,pos);
if (mDataBlock && mDataBlock->conformToGround)
{
RayInfo surfaceInfo;
Point3F above = Point3F(pos.x,pos.y,pos.z + 0.5f);
Point3F below = Point3F(pos.x,pos.y,pos.z - 100.0f);
if (gClientContainer.castRay(above,below,TerrainObjectType | InteriorObjectType,&surfaceInfo))
{
Point3F x,y,z;
z = surfaceInfo.normal;
z.normalize();
mat.getColumn(1,&y);
mCross(y,z,&x);
x.normalize();
mCross(z,x,&y);
mat.setColumn(0,x);
mat.setColumn(1,y);
mat.setColumn(2,z);
}
}
Parent::setTransform(mat);
mRot = rot;
}

#1
11/18/2007 (2:17 am)
Surely the some math whiz Torquer that can solve this. I want the player to rotate only on HIS Y and Z axis and not HIS X axis.
#2
11/18/2007 (8:38 am)
I am unable to determine what you are trying to accomplish. I suspect that you haven't received any suggestions because what you want to obtain is not clearly described. The descriptions are confusing and apparently contradictory.

There are some things that can only be discussed correctly in precise technical language with standard terminology. Barring that, mathematics. This may be one of them. ;)

Perhaps if you tried explaining what your application is that needs the new mode.

I don't recognize this conformToGround mode. Are you trying to add this?
#3
11/19/2007 (7:36 am)
If you are doing the "horse walking on a slope" problem from your other thread, you want to perform a rot.z rotation then a rot.x (pitch) and no rot.y (roll). (no HIS Y ?)

Something like this, replacing the final few lines where mat.setColumn is done above:
EDIT: See below for corrected algorithm
#4
11/19/2007 (12:21 pm)
I found a problem with my previous suggestion during further testing. This one seems to work properly on all slopes. It adds a couple of more cross products to get rid of the "roll" rotation.

This version would be appropriate for the "horse on a slope" case. (For the "player prone on a slope" case, use the original algorithm that retains the roll rotation.)

void Player::setPosition(const Point3F& pos,const Point3F& rot)
{
   MatrixF mat;
   mat.set(EulerF(0, 0, rot.z));
   mat.setColumn(3,pos);

   if (mDataBlock && mDataBlock->conformToGround)
   {
      RayInfo surfaceInfo;
      Point3F above = Point3F(pos.x,pos.y,pos.z + 0.5f);
      Point3F below = Point3F(pos.x,pos.y,pos.z - 100.0f);
      if (gClientContainer.castRay(above,below,TerrainObjectType | InteriorObjectType,&surfaceInfo))
      {
         Point3F x,y,z;
         z = surfaceInfo.normal;
         z.normalize();
         mat.getColumn(1,&y); // horizontal heading
         mCross(y,z,&x);
         x.normalize();
         mCross(z,x,&y); 

#if 1
         // y is parallel to slope in the plane of the facing (rot.z)
         // desired pitch angle is the slope of the terrain 
         // in the direction we are facing = angle of y from horizontal
         // = PI/2 - angle of y from vertical (0 0 1) 
// ***OPTIMIZATION*** do the easy dot product manually
//         rot_x = PI_OVER_2 - mAcos( dot(y, ("0 0 1") ) );
         F32 PI_OVER_2 = (Float_Pi * 0.5f);
         F32 rot_x = -(PI_OVER_2 - mAcos(y.z));
         mRot.set(rot_x, rot.y, rot.z);
#endif

         VectorF up(0.0f,0.0f,1.0f);
         mCross(y,up,&x);
         mCross(x,y,&z);
         mat.setColumn(0,x);
         mat.setColumn(1,y);
         mat.setColumn(2,z);
         mat.setColumn(3,pos);
         Parent::setTransform(mat);

      } else {
         Parent::setTransform(mat);
         mRot = rot;
      }
   } else {
      Parent::setTransform(mat);
      mRot = rot;
   }

}

(Notice that at the top of the routine, mat.set(EulerF(0, 0, rot.z));
is used to initialize the mat rather than mat.set(EulerF(rot.x, 0, rot.z));
The rot.x isn't really needed to calculate the "y" axis vector and sending more than one non-zero value greatly increases the processing done in MatrixF::set since it then goes to a three rotation calculation.)

I included code to calculate mRot.x, if desired, trying to keep everything consistent.

Again, you would want to perform a similar calculation in setRenderPosition method also.
#5
11/22/2007 (8:37 pm)
Matthew

Thanks for the post. I am out of the country now. I will try this and let you know how this works out. I did not want the horse to slant to its right or left - "roll". Thanks again.
#6
11/27/2007 (6:19 am)
Matthew

The code is working great. Thanks for your help.
#7
03/31/2008 (3:55 pm)
Instead of doing a cast ray to get surfaceInfo.normal, if a vector is determined from a slope that is obtained from an 2dimensional array that contains z positions for current location and a location that is in front of unit. How can the code be modified to use this vector instead of surfaceInfo.normal? Cast ray seems to slow down progress in an RTS game and surfaceInfo is not giving the slope along the forward vector which is really what is needed.


if (mDataBlock && mDataBlock->conformToGround)
{
//RayInfo surfaceInfo;
//Point3F above = Point3F(pos.x,pos.y,pos.z + 0.5f);
//Point3F below = Point3F(pos.x,pos.y,pos.z - 100.0f);
//if (gClientContainer.castRay(above,below,TerrainObjectType | InteriorObjectType,&surfaceInfo))
//{
// HERE WE CAN ADD CODE TO CREATE A VECTOR FROM SLOPE OF LINE FROM UNIT Z LOC TO A Z LOC IN FRONT OF UNIT
// THEN WE SUBSTITUTE THE INFO AND LET THE FOLLOWING RUN IT WILL BE A LOT QUICKER
// HOW AND WHERE CAN WE PUT THE VECTOR / SLOPE INFO HERE
Point3F x,y,z;
//z = surfaceInfo.normal;
z.normalize();
mat.getColumn(1,&y);
mCross(y,z,&x);
x.normalize();
mCross(z,x,&y);

mat.setColumn(0,x);
mat.setColumn(1,y);
mat.setColumn(2,z);

//}
}
#8
04/07/2008 (9:55 am)
I have been thinking a lot of that realisation, i alse did a topic in the TGEA forum a few months ago.
The idea of the contact normal is NOT good (Swaroop Reddy is right), even it is slower.
In fact the terrain has a lot of normals (for each block), the terrain is not fluent so the transition between blocks is not good.
The original resource "Conform Player to Ground" did a discussion with an interpolation coefficient, but still not fluent.

I think the best idea is to create 3 animations - player walk normal, player walk up, player walk down.
Use the idea of determining the steap clone and play the relevant animation.

I also appreciate the hard work of Joe Maruschak.