View more

View more

View more

### Image of the Day Submit

IOTD | Top Screenshots

### The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

# Rotation based on normal vector

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

5 replies to this topic

### #1Shawn619  Members

Posted 18 June 2013 - 07:36 PM

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))

Edited by Shawn619, 18 June 2013 - 07:40 PM.

Posted 19 June 2013 - 12:33 PM

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.

Posted 19 June 2013 - 12:35 PM

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

### #4Waterlimon  Members

Posted 19 June 2013 - 01:14 PM

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

Posted 19 June 2013 - 04:54 PM

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.

### #6WiredCat  Members

Posted 21 June 2013 - 01:09 AM



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

#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 = -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);
if (angle < 0) angle = -1.0*(90.0 + angle); else
angle = 90.0 - angle;
return float(angle);
}

{
t3dpoint p;
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:

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();

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);
}


Edited by ___, 21 June 2013 - 01:33 AM.

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.