• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0

Rotation based on normal vector

5 posts in this topic

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(ry2,0.0f,1.0f,0.0f);//not needed(I will specify this myself)

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

Share this post

Link to post
Share on other sites

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.


Share this post

Link to post
Share on other sites
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.

Share this post

Link to post
Share on other sites

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.


Share this post

Link to post
Share on other sites


#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 {
	 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()	{
 //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;


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;


 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;


//*****************************		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


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


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[

normupB =  FCOpenGL->earthp.PLANET_MODEL->VBO_V[
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.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;




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

else {

p = p->next;


Edited by ___

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  
Followers 0