Rotation based on normal vector

Started by
4 comments, last by _WeirdCat_ 10 years, 10 months ago

I'm trying to make my car 'hug' the terrain. I figure I should use the normal vector of the car's current surface(the terrain portion it stands on) in order to determine the orientation of the car. I have tried many different methods and exhausted all of them, and now I am out of ideas. I have a full understanding of vectors, normal, cross product, dot product, so feel free to suggest any idea you may have.

Given my car standing on a surface(triangle) whose normalized normal vector is (vn1, vn2, vn3), how do I arrive at rotations(in degrees) (rx1, ry2, rz3) such that my car stands as it should(in real life) on the surface?

I rotate my car with glRotatef:


glRotatef(rx1,1.0f,0.0f,0.0f);
glRotatef(ry2,0.0f,1.0f,0.0f);//not needed(I will specify this myself)
glRotatef(rz3,0.0f,0.0f,1.0f);

To give you an idea of how my game is laid out:(redline=surface normal, black line=ray trace to calculate which triangle the car stands on, car=sitting at rotation(0.0f,0.0f,0.0f))

2dt7gn8.jpg

Advertisement

You are essentially making the cars up axis, the terrain's up axis at a given point. So if your car is exported with the up-axis directly up (Y-Axis), you can figure out two angles needed to rotate that car (or just think of its up axis), to rotate and match the terrains up axis. Rotating the car on the Y-axis would only spin it and not manipulate the direction that "up" is. So you need to rotate some degrees on the x or z, then some on the y and your cars up will match the terrains up.

If you view down the z-axis (2d), your two vectors can drop the z-component so that you make it a 2d problem and you can dot product and find the angle with it by using the vectors (0,1,0) and (normal.x,normal.y, 0) , then spin it by that angle on the z-axis. You have to visualize it this way and you will understand. You then view the vectors top down from the y-axis. Use your new rotated vector, take the dot product between that vector and your normal vector dropping the y-components since you are viewing top down and don't care about the height. You then find the rotation to use on the y-axis to match your normal.

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal

Something like this, that might be wrong but close to it.

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal

You can also construct the rotation matrix from the terrain normal, car x vector (assuming z is forward) and the z vector ofc.

Kind of like you create a camera matrix given a look direction.

o3o

Also, once you do get it mapped correctly, turning left and right is actually a rotation on the cars y-axis, so while the cars up-axis matches (0,1,0), apply your steering rotation matrix by RotationGlobalY(), then apply the final matrices to map the car to the normal.

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal


















//---------------------------------------------------------------------------

#ifndef DxCRotationMathH
#define DxCRotationMathH
#include "SysUtils.hpp"
#include "Dialogs.hpp"
#include "Math.hpp"
#include "Math.h"
#include "gl/glew.h"
#include "gl/gl.h"
#include "DxcMath.h"

#include "Classes.hpp"

#include "string_unit.hpp"

//---------------------------------------------------------------------------

	   //rotation unit :) not quaterions etc just angle representation + pitch yaw roll matrix (not proper order)
struct typrNota {
	public:
	 TStringList*s;
	 bool save;
//t3dpoint p;//position
t3dpoint front;
t3dpoint right;
t3dpoint up;

t3dpoint rf;
t3dpoint ru;
t3dpoint rr;

t3dpoint pos;

float * AIR_MATRIX;
float ax,ay,az,bx,by,bz,cx,cy,cz,xt,yt,zt,xp,yp,zp;
float cs,sn;			// sine and cosine of the rotation angle
float rcs,rsn;			//roll sine and cosine


float GetLongitude(t3dpoint cpos)  //yaw
{
 long double       angle;
 angle = n2dGetPolarCoordAngleAD(cpos.x-pos.x,cpos.z-pos.z) / 6.2731860;


angle = -360.0*angle;
angle = VALIDUJD(angle)+90.0;
angle = VALIDUJD(angle);
	   return float(angle);


}


float GetLatitude(t3dpoint cpos)   //pitch
{

t3dpoint cpn;
cpn = vectorAB(pos,cpos);
cpn = Normalize(cpn);

long double t;
t = (long double)cpn.x*(long double)cpn.x +(long double)cpn.z*(long double)cpn.z;
t = sqrtl(t);
					  if (cpn.y == 0.0f) return 0.0f;
long double tanges = t / (long double)cpn.y;
long double angle;
			angle = atanl(tanges);
			  angle = RadToDeg( angle );
			  if (angle < 0) angle = -1.0*(90.0 + angle); else
			  angle = 90.0 - angle;
		return float(angle);
}

t3dpoint __fastcall matrix_mult2(t3dpoint adr_ptr)
{
t3dpoint p;
   p.x = ax*adr_ptr.x + ay* adr_ptr.y  + az* adr_ptr.z;
   p.y = bx*adr_ptr.x + by* adr_ptr.y  + bz* adr_ptr.z;
   p.z = cx*adr_ptr.x + cy* adr_ptr.y  + cz* adr_ptr.z;
return p;
}



void pitch(float cs, float sn)
{
   float temp;

   temp = ay*cs - az*sn;
   az   = ay*sn + az*cs;
   ay = temp;

   temp = by*cs - bz*sn;
   bz   = by*sn + bz*cs;
   by = temp;

   temp = cy*cs - cz*sn;
   cz   = cy*sn + cz*cs;
   cy = temp;
}
void yaw(float cs, float sn)
{
   float temp;

   temp = az*cs - ax*sn;
   ax   = az*sn + ax*cs;
   az = temp;

   temp = bz*cs - bx*sn;
   bx   = bz*sn + bx*cs;
   bz = temp;

   temp = cz*cs - cx*sn;
   cx   = cz*sn + cx*cs;
   cz = temp;
}
void roll(float cs, float sn)
{
   float temp;

   temp = ax*cs - ay*sn;
   ay   = ax*sn + ay*cs;
   ax = temp;

   temp = bx*cs - by*sn;
   by   = bx*sn + by*cs;
   bx = temp;

   temp = cx*cs - cy*sn;
   cy   = cx*sn + cy*cs;
   cx = temp;
}





void DoRotation()
{

rf =	  matrix_mult2(		front	  	);
ru =	  matrix_mult2(		up	  		);
rr =	  matrix_mult2(		right	  	);



			AIR_MATRIX[0] = rr.x; //right
			AIR_MATRIX[1] = rr.y;
			AIR_MATRIX[2] = rr.z;
			AIR_MATRIX[3] = 0.0f;
			AIR_MATRIX[4] = ru.x; //up
			AIR_MATRIX[5] = ru.y;
			AIR_MATRIX[6] = ru.z;
			AIR_MATRIX[7] = 0.0f;
			AIR_MATRIX[8]  = -rf.x; //front
			AIR_MATRIX[9]  = -rf.y;
			AIR_MATRIX[10] = -rf.z;
			AIR_MATRIX[11] = 0.0f;
			AIR_MATRIX[12] =  0.0f;  //point
			AIR_MATRIX[13] =  0.0f;
			AIR_MATRIX[14] =  0.0f;
			AIR_MATRIX[15] = 1.0f;


}






void set_init_vals(t3dpoint nfront, t3dpoint nup, t3dpoint nright, t3dpoint center)
{

pos.x = center.x;

pos.y = center.y;

pos.z = center.z;

//right.x = nright.x;
//right.y = nright.y;
//right.z = nright.z;
//
//up.x = nup.x;
//up.y = nup.y;
//up.z = nup.z;
//
//front.x = -nfront.x;
//front.y = -nfront.y;
//front.z = -nfront.z;


ax = nright.x;
ay = nright.y;
az = nright.z;


bx = nup.x;
by = nup.y;
bz = nup.z;

   cx = nfront.x;
   cy = nfront.y;
   cz = nfront.z;
//   DoRotation(); jeszcze nei sprawdzalem

}


~typrNota() {

}


typrNota()	{
save=false;
 //p.x = 0.0f; p.y = 0.0f; p.z = 0.0f;


right.x = 1.0f;
right.y = 0.0f;
right.z = 0.0f;

up.x = 0.0f;
up.y = 1.0f;
up.z = 0.0f;

front.x = 0.0f;
front.y = 0.0f;
front.z = -1.0f;



AIR_MATRIX = new float[16];

  xt = 0.0;
	   yt = 0.0;
	   zt = 0.0;

   cs = cos(3.0f*imopi);
   sn = sin(3.0f*imopi);


   rcs = cos(4.0f*imopi);
   rsn = sin(4.0f*imopi);


   ax = 1.00;
   ay = 0.0;
   az = 0.0;

   bx = 0.0;
   by = 1.00;
   bz = 0.0;

   cx = 0.0;
   cy = 0.0;
   cz = 1.00;
   pos.x = 0.0f;
   pos.y = 0.0f;
   pos.z = 0.0f;
DoRotation();

//---
}





void perform_rotation_to_vector(t3dpoint vec, bool normalized)
{
t3dpoint v;
  if (normalized == false) v = Normalize( vec ); else v = vec;



  t3dpoint temp; temp = vector_multiple(v, 10000.0f);


  t3dpoint temp2; temp2 = vector_multiple(rf, 10000.0f);



float act_pitch;    act_pitch = GetLatitude( temp2 );
float act_yaw;      act_yaw	  = GetLongitude( temp2 );




 act_pitch 	= VALIDUJ( act_pitch );
 act_yaw 	= VALIDUJ( act_yaw );


//ShowMessage("act pitch: "+FloatToStr(act_pitch));
//ShowMessage("act yaw: "+FloatToStr(act_yaw));



float new_pitch;    new_pitch = GetLatitude( temp );
float new_yaw;      new_yaw	  = GetLongitude( temp );

 new_pitch 	= VALIDUJ( new_pitch );
 new_yaw 	= VALIDUJ( new_yaw );
//
//ShowMessage("new pitch: "+FloatToStr(new_pitch));
//ShowMessage("new yaw: "+FloatToStr(new_yaw));
	  float yawsign = 1.0f; float pitchsign = 1.0f;

 float mpitch;
if (act_pitch > new_pitch)
   mpitch = -(act_pitch - new_pitch);
else        {
   mpitch =   new_pitch - act_pitch;
   pitchsign = -1.0f;
	   }


 float myaw;
if (act_yaw > new_yaw)
   myaw = -(act_yaw - new_yaw);
else    {
   myaw =   new_yaw - act_yaw;

        yawsign = -1.0f;
		 }
   float mpsine; float mpcosine;
   mpsine 	= sin( mpitch * imopi );
   mpcosine = cos( mpitch * imopi );


   float mysine; float mycosine;
   mysine 	= sin( myaw * imopi );
   mycosine = cos( myaw * imopi );




   if (mpitch 	< 0) 	pitchsign 	= -1.0f;
   if (myaw 	< 0) 	yawsign 	= -1.0f;


   yaw(mycosine,yawsign*mysine);
   DoRotation();
   pitch(mpcosine,pitchsign*mpsine);
   DoRotation();



 temp2 = vector_multiple(rf, 10000.0f);

act_pitch = GetLatitude( temp2 );
act_yaw	  = GetLongitude( temp2 );

//ShowMessage("act pitch: "+FloatToStr(act_pitch));
//ShowMessage("act yaw: "+FloatToStr(act_yaw));

}



void perform_roll_to_vector(t3dpoint vec, bool normalized)
{
t3dpoint v;
  if (normalized == false) v = Normalize( vec ); else v = vec;



  t3dpoint temp; temp = vector_multiple(v, 10000.0f);


  t3dpoint temp2;
  temp2.x = bx;
  temp2.y = by;
  temp2.z = bz;
  temp2 = vector_multiple(temp2, 10000.0f);
float angela = RadToDeg( AngleBetweenVectors(temp, temp2) ) + 180.0f;


   float mpsine; float mpcosine;
   mpsine 	= sin( angela * imopi );
   mpcosine = cos( angela * imopi );

      float yawsign = 1.0f;
   
roll(mpcosine,mpsine);
   DoRotation();

}





//*****************************		VECTOR PITCH YAW DETERMINATION     ****************************
//*****************************		VECTOR PITCH YAW DETERMINATION     ****************************
//*****************************		VECTOR PITCH YAW DETERMINATION     ****************************







//*****************************		VECTOR PITCH YAW DETERMINATION     ****************************
//*****************************		VECTOR PITCH YAW DETERMINATION     ****************************
//*****************************		VECTOR PITCH YAW DETERMINATION     ****************************





};




	void copyNOTA(typrNota in, typrNota & d)
		{
//			d.p.x = in.x;
//			d.p.y = in.y;
//			d.p.z = in.z;


			d.ax 	= in.ax;
			d.ay	= in.ax;
			d.az	= in.az;

			d.bx 	= in.bx;
			d.by	= in.bx;
			d.bz	= in.bz;

			d.cx 	= in.cx;
			d.cy	= in.cx;
			d.cz	= in.cz;

			 d.front.x = in.front.x;
			 d.front.y = in.front.y;
			 d.front.z = in.front.z;

			 d.up.x = in.up.x;
			 d.up.y = in.up.y;
			 d.up.z = in.up.z;

			 d.right.x = in.right.x;
			 d.right.y = in.right.y;
			 d.right.z = in.right.z;

			 d.pos.x = in.pos.x;
			 d.pos.y = in.pos.y;
			 d.pos.z = in.pos.z;


			 int i;
			 for (i=0;i<16;i++)			 d.AIR_MATRIX[i] = in.AIR_MATRIX[i]; //is result created yet no so there will be a problemo
		  d.DoRotation();
		}






#endif



/*
     // computes pB ON line B closest to line A
    // computes pA ON line A closest to line B
    // return: 0 if parallel; 1 if coincident; 2 if generic (i.e., skew)
    int
    line_line_closest_points3d (
        POINT *pA, POINT *pB,			// computed points
        const POINT *a, const VECTOR *adir,		// line A, point-normal form
        const POINT *b, const VECTOR *bdir )	// line B, point-normal form
    {
    static VECTOR Cdir, *cdir = &Cdir;
    static PLANE Ac, *ac = &Ac, Bc, *bc = &Bc;
    
        // connecting line is perpendicular to both
        vcross ( cdir, adir, bdir );
    
        // check for near-parallel lines
        if ( !vnorm( cdir ) )   {
            *pA = *a;	// all points are closest
            *pB = *b;
            return 0;	// degenerate: lines parallel
            }
    
        // form plane containing line A, parallel to cdir
        plane_from_two_vectors_and_point ( ac, cdir, adir, a );
    
        // form plane containing line B, parallel to cdir
        plane_from_two_vectors_and_point ( bc, cdir, bdir, b );
    
        // closest point on A is line A ^ bc
        intersect_line_plane ( pA, a, adir, bc );
    
        // closest point on B is line B ^ ac
        intersect_line_plane ( pB, b, bdir, ac );
    
        // distinguish intersecting, skew lines
        if ( edist( pA, pB ) < 1.0E-5F )
            return 1;     // coincident: lines intersect
        else
            return 2;     // distinct: lines skew
    }


*/

















long double __fastcall n2dGetPolarCoordAngleAD(float x,float y)
{
if ( (x == 0) && (y > 0) )  { 	return 3.1415926535897932384626433832795/2.0;                   }

if ( (x == 0) && (y < 0) )  { 	return 3.0*3.1415926535897932384626433832795/2.0;                 }

if (x == 0) return - 1000.0;
long double k; k = (long double)y / (long double)x;
if ( (x > 0) && (y >= 0) ) { 	return ArcTan(k);        }

if ( (x > 0) && (y < 0) )  { 	return ArcTan(k)+2.0*3.1415926535897932384626433832795;  	  }

if  (x < 0)                {	return ArcTan(k)+3.1415926535897932384626433832795;   	  }




//last versions were without .0 just simple 2 division
return - 1000.0;
}

















float __fastcall VALIDUJ(float angle2)

{
				   float angle = angle2;

int kat=(int)angle2;

kat=kat/360;

if (angle < 0 )

   {

   angle = angle + (float)(kat+1)*360.0f;

   }

if (angle >= 360)

   {

   angle = angle - (float)kat*360.0f;

   }



return angle;

}









float __fastcall AngleBetweenVectors(t3dpoint Vector1,t3dpoint  Vector2)
{
float  dotProduct 		= 0.0f;
float  vectorsMagnitude = 0.0f;
float  angle 			= 0.0f;

	dotProduct = Dot(Vector1, Vector2);


	vectorsMagnitude = magnitude(Vector1) * magnitude(Vector2) ;


	angle = ArcCos( dotProduct / vectorsMagnitude );


		if(IsNan(angle) == true) return 0.0f;

 return angle;
}








float __fastcall magnitude(t3dpoint Vector)
{

  long double k;
  k = (Vector.x*Vector.x) +(Vector.y*Vector.y) +(Vector.z*Vector.z);
  k = sqrtl( k );



  float s = float(k);


return s;
}







float __fastcall Dot(t3dpoint vVector1,t3dpoint  vVector2)
{
return ( (vVector1.x * vVector2.x) + (vVector1.y * vVector2.y) + (vVector1.z * vVector2.z) );
}

i dont call set init vals or they are set as specified in code

now the code that inserts a model onto a face:














t3dpoint normupA;
t3dpoint normupB;
t3dpoint normup;
normupA =  FCOpenGL->earthp.PLANET_MODEL->VBO_V[
FCOpenGL->earthp.PLANET_MODEL->VBO_BE[
FCOpenGL->earthp.selected_face].INDEX_START];


normupB =  FCOpenGL->earthp.PLANET_MODEL->VBO_V[
FCOpenGL->earthp.PLANET_MODEL->VBO_BE[
FCOpenGL->earthp.selected_face].INDEX_START	+	1];
//get the pitch info - we find out how much we must rotate model to fit the face pitch)
normup = vectorAB(normupA,normupB);

lasto->MATRIX.perform_rotation_to_vector(normup,false); //rotate pitch

  t3dpoint xnormal;
xnormal =  FCOpenGL->earthp.PLANET_MODEL->VBO_N[
FCOpenGL->earthp.PLANET_MODEL->VBO_BE[
FCOpenGL->earthp.selected_face].INDEX_START]; //and thats the face normal

lasto->MATRIX.perform_roll_to_vector(xnormal,true); //and we roll our model

but it works when model is rotated to have z or -z front side i cant remember ;] if not you need to rotate it first;x

the video shows that it work:

https://www.youtube.com/watch?feature=player_embedded&v=nkLu1NBYgJg

ahh ofc you need to find a point on plane in order to place there a model

drawing goes like this:

through all objects in scene:


void __fastcall TEditor::draw_mission_scene()
{
		  if (FCOpenGL->earthp.ican == false) return;

TMissionObject * p;
p = gmiss.FIRST_MISSION_OBJECT;


          glPushMatrix();
		  glLoadIdentity();

	  gluLookAt(FCOpenGL->old.x,FCOpenGL->old.y,FCOpenGL->old.z,FCOpenGL->old.x+FCOpenGL->SUPERPOINT.x,
FCOpenGL->old.y-FCOpenGL->SUPERPOINT.y,FCOpenGL->old.z-FCOpenGL->SUPERPOINT.z,
0.0,1.0,0.0);
glColor3f(1,0,0);

//Textures::CreateSphere(FCOpenGL->dq1.x,FCOpenGL->dq1.y,FCOpenGL->dq1.z,10000.0f,30);
//Textures::CreateSphere(FCOpenGL->dq2.x,FCOpenGL->dq2.y,FCOpenGL->dq2.z,10000.0f,30);
//Textures::CreateSphere(FCOpenGL->dq3.x,FCOpenGL->dq3.y,FCOpenGL->dq3.z,10000.0f,30);
//Textures::CreateSphere(FCOpenGL->dq4.x,FCOpenGL->dq4.y,FCOpenGL->dq4.z,10000.0f,30);

while (p!=NULL) {

if (p->model != NULL)    if (p->blank == false)
{
if ((p->friendly == true)&& (p->neutral == false))  glColor3f(0,0,1);
if ((p->friendly == false)&& (p->neutral == true)) glColor3f(1,1,1);
if ((p->friendly == false)&& (p->neutral == false)) glColor3f(1,0,0);
if (CheckBox18->Checked == false) {
Textures::CreateSphere(p->position.x,p->position.y,p->position.z,1000.0f,30);

}
else {
											glPushMatrix();
//
											glTranslatef(
											p->position.x,
											p->position.y,
											p->position.z);
								glPushMatrix();
								glMultMatrixf(p->MATRIX.AIR_MATRIX);
p->model->DrawSimpleModel();
								glPopMatrix();
glPopMatrix();
}
}

p = p->next;

}


      glPopMatrix();
		glColor3f(1,1,1);
}

This topic is closed to new replies.

Advertisement