www.pudn.com > 3dsMFCRender.rar > TriObject.cpp, change:1998-05-23,size:21172b


////////////////////////////////////////////////////////////////////// 
// 
//	TriObject.cpp: implementation of the TriObject class. 
//	TriObject class version 1.5 
//	(C) 1998 - Sivert L. Nielsen (sivni@qeocities.com) 
//	This code may be freely distributed and used in programs so long 
//	as this notice appears in the code. 
// 
//	One world, one mind. 
//	 
// 
////////////////////////////////////////////////////////////////////// 
#include "stdafx.h" 
//#include "3ds.h" 
#include "TriObject.h" 
 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
////////////////////////////////////////////////////////////////////// 
// Construction/Destruction 
////////////////////////////////////////////////////////////////////// 
 
TriObject::TriObject() 
{ 
	x = y = z = nx = ny = nz = NULL; 
	matfaces = faces = NULL; 
	materials = NULL; 
	poskeys = NULL; 
	rotkeys = NULL; 
	sclkeys = NULL; 
	splrotmover = NULL; 
	splposmover = NULL; 
	numvertices = 0; 
	numnormals = 0; 
	numfaces = 0; 
	nummatfacesapplied = 0; 
	i = 0; 
	nummaterials=0; 
	numposkeys=0; 
	numrotkeys=0; 
	numsclkeys=0; 
	animstart=0; 
	animend=0; 
	currentframe=0; 
	currentkeypostime=0; 
	currentkeyrottime=0; 
	currentkeyscltime=0; 
	numsplrotkeys = 0; 
	numsplposkeys = 0; 
	offset[0] = 0.0f; 
	offset[1] = 0.0f; 
	offset[2] = 0.0f; 
	pivot[0] = 0.0f; 
	pivot[1] = 0.0f; 
	pivot[2] = 0.0f; 
	pivotrot[0] = 0.0f; 
	pivotrot[1] = 0.0f; 
	pivotrot[2] = 0.0f; 
	pivotrot[3] = 0.0f; 
	normalapplied = FALSE; 
	bboxapplied = FALSE; 
	materialsapplied = FALSE; 
	poskeysapplied = FALSE; 
	rotkeysapplied = FALSE; 
	sclkeysapplied = FALSE; 
	displistapplied = FALSE; 
	splinesapplied = FALSE; 
	 
 
} 
 
TriObject::~TriObject() 
{ 
	delete [] x; 
	delete [] y; 
	delete [] z; 
	delete [] nx; 
	delete [] ny; 
	delete [] nz; 
	delete [] faces; 
	delete [] matfaces; 
	delete [] materials; 
	delete [] poskeys; 
	delete [] rotkeys; 
	delete [] sclkeys; 
	delete	  splrotmover; 
} 
 
void TriObject::applyNormals() 
{ 
	if (numfaces == 0) return; 
	delete [] nx; 
	delete [] ny; 
	delete [] nz; 
	nx = new float[numfaces/3]; 
	ny = new float[numfaces/3]; 
	nz = new float[numfaces/3]; 
	if( nx==NULL || ny==NULL || nz==NULL ) 
	{ 
		delete [] nx; 
		delete [] ny; 
		delete [] nz; 
		normalapplied = FALSE; 
		return; 
	} 
 
	float normal[3]; 
	for ( int i=0 ; i<numfaces/3 ; i++ )	//get to work 
	{ 
		CalcNormal(3*i, normal); 
		ReduceToUnit(normal); 
		nx[i] = normal[0]; 
		ny[i] = normal[1]; 
		nz[i] = normal[2]; 
	} 
	 
	normalapplied = TRUE; 
} 
 
void TriObject::CalcNormal(int entry, float out [ 3 ]) 
{ 
	float v1[3],v2[3]; 
 
	// Calculate two vectors from the three points 
	v1[0] = x[ faces[entry] ] - x[ faces[entry+1] ]; 
	v1[1] = y[ faces[entry] ] - y[ faces[entry+1] ]; 
	v1[2] = z[ faces[entry] ] - z[ faces[entry+1] ]; 
	 
	v2[0] = x[ faces[entry+1] ] - x[ faces[entry+2] ]; 
	v2[1] = y[ faces[entry+1] ] - y[ faces[entry+2] ]; 
	v2[2] = z[ faces[entry+1] ] - z[ faces[entry+2] ]; 
 
	// Take the cross product of the two vectors to get 
	// the normal vector which will be stored in out 
	out[0] = v1[1]*v2[2] - v1[2]*v2[1]; 
	out[1] = v1[2]*v2[0] - v1[0]*v2[2]; 
	out[2] = v1[0]*v2[1] - v1[1]*v2[0]; 
} 
 
void TriObject::ReduceToUnit(float vector [ 3 ]) 
{ 
	float length; 
	 
	// Calculate the length of the vector		 
	length = (float)sqrt((vector[0]*vector[0]) +  
						(vector[1]*vector[1]) + 
						(vector[2]*vector[2])); 
 
	// Keep the program from blowing up by providing an exceptable 
	// value for vectors that may calculated too close to zero. 
	if(length == 0.0f) 
		length = 1.0f; 
 
	// Dividing each element by the length will result in a 
	// unit normal vector. 
	vector[0] /= length; 
	vector[1] /= length; 
	vector[2] /= length; 
} 
 
void TriObject::drawGL() 
{ 
	value	 = currentframe *numsplrotkeys / (float)animend; 
	valuepos = currentframe *numsplposkeys / (float)animend; 
	if(poskeysapplied)  
	{ 
		::glPushMatrix(); 
		 
		if(rotkeysapplied) 
		{ 
			 
			if(splinesapplied) 
			{ 
				::glTranslatef(offset[0] + splposmover->GetValueAtTime(0, valuepos), offset[1] + splposmover->GetValueAtTime(1, valuepos), offset[2] + splposmover->GetValueAtTime(2, valuepos) ); 
				::glRotatef( splrotmover->GetValueAtTime(3, value), splrotmover->GetValueAtTime(0, value), splrotmover->GetValueAtTime(1, value), splrotmover->GetValueAtTime(2, value) ); 
			} 
			else 
			{ 
				::glTranslatef(pivot[0]+poskeys[currentkeypostime].pos[0], pivot[1]+poskeys[currentkeypostime].pos[1], pivot[2]+poskeys[currentkeypostime].pos[2]); 
				::glRotatef( rotkeys[currentkeyrottime].angle, rotkeys[currentkeyrottime].axis[0], rotkeys[currentkeyrottime].axis[1], rotkeys[currentkeyrottime].axis[2] ); 
			} 
			::glTranslatef(pivot[0],pivot[1],pivot[2]); 
		} 
	} 
 
	 
	if (normalapplied) 
	{ 
		int j; 
		glBegin(GL_TRIANGLES); 
			for (i=0; i <numfaces/3; i++) 
			{ 
				j = 3*i; 
				if(materialsapplied) glColor4f( materials[matfaces[i]].diffuseColor[0], materials[matfaces[i]].diffuseColor[1], materials[matfaces[i]].diffuseColor[2], 1/materials[matfaces[i]].transparency ); 
				else glColor3f( 0.0f, 0.0f, 1.0f ); 
				::glNormal3f( nx[i], ny[i], nz[i]); 
				::glVertex3f( x[faces[j]]  ,  y[faces[j]]  ,  z[faces[j]]); 
				::glVertex3f( x[faces[j+1]],  y[faces[j+1]],  z[faces[j+1]]); 
				::glVertex3f( x[faces[j+2]],  y[faces[j+2]],  z[faces[j+2]]); 
 
			} 
		glEnd(); 
	} 
	else 
	{ 
		glBegin(GL_TRIANGLES); 
			for (i=0; i <numfaces; i+=3) 
			{ 
				if(materialsapplied) glColor3f( materials[matfaces[i/3]].diffuseColor[0], materials[matfaces[i/3]].diffuseColor[1], materials[matfaces[i/3]].diffuseColor[2] ); 
				else glColor3f( 0.0f, 0.0f, 1.0f ); 
				glVertex3f( x[faces[i]]  ,  y[faces[i]]  ,  z[faces[i]]); 
				glVertex3f( x[faces[i+1]],  y[faces[i+1]],  z[faces[i+1]]); 
				glVertex3f( x[faces[i+2]],  y[faces[i+2]],  z[faces[i+2]]); 
			} 
		glEnd(); 
	} 
	 
	if(poskeysapplied) ;glPopMatrix(); 
} 
 
void TriObject::drawGLBbox() 
{ 
	value	 = currentframe *numsplrotkeys / (float)animend; 
	valuepos = currentframe *numsplposkeys / (float)animend; 
 
	if(bboxapplied) 
	{ 
		if(poskeysapplied)  
		{ 
			::glPushMatrix(); 
			if(rotkeysapplied) 
			{ 
				if(splinesapplied) 
				{ 
					::glTranslatef(pivot[0] + splposmover->GetValueAtTime(0, valuepos), pivot[1] + splposmover->GetValueAtTime(1, valuepos), pivot[2] + splposmover->GetValueAtTime(2, valuepos) ); 
					::glRotatef( splrotmover->GetValueAtTime(3, value), splrotmover->GetValueAtTime(0, value), splrotmover->GetValueAtTime(1, value), splrotmover->GetValueAtTime(2, value) ); 
				} 
				else 
				{ 
					::glTranslatef(pivot[0]+poskeys[currentkeypostime].pos[0], pivot[1]+poskeys[currentkeypostime].pos[1], pivot[2]+poskeys[currentkeypostime].pos[2]); 
					::glRotatef( rotkeys[currentkeyrottime].angle, rotkeys[currentkeyrottime].axis[0], rotkeys[currentkeyrottime].axis[1], rotkeys[currentkeyrottime].axis[2] ); 
				} 
				::glTranslatef(pivot[0],pivot[1],pivot[2]); 
			} 
		} 
		if(materialsapplied) glColor3f( materials[matfaces[0]].diffuseColor[0], materials[matfaces[0]].diffuseColor[1], materials[matfaces[0]].diffuseColor[2] ); 
		else glColor3f( 0.0f, 0.0f, 1.0f ); 
		 
		glBegin(GL_TRIANGLES); 
			glNormal3f(1.0f,0.0f, 0.0f); 
			glVertex3fv( bbox[5]); 
			glVertex3fv( bbox[0]); 
			glVertex3fv( bbox[1]); 
 
			glVertex3fv( bbox[1]); 
			glVertex3fv( bbox[6]); 
			glVertex3fv( bbox[5]); 
 
			 
			glNormal3f(-1.0f,0.0f, 0.0f); 
			glVertex3fv( bbox[2]); 
			glVertex3fv( bbox[3]); 
			glVertex3fv( bbox[4]); 
 
			glVertex3fv( bbox[4]); 
			glVertex3fv( bbox[7]); 
			glVertex3fv( bbox[2]); 
 
			 
			glNormal3f(0.0f,0.0f, 1.0f); 
			glVertex3fv( bbox[4]); 
			glVertex3fv( bbox[3]); 
			glVertex3fv( bbox[0]); 
 
			glVertex3fv( bbox[0]); 
			glVertex3fv( bbox[5]); 
			glVertex3fv( bbox[4]); 
 
			 
			glNormal3f(0.0f,0.0f, -1.0f); 
			glVertex3fv( bbox[6]); 
			glVertex3fv( bbox[1]); 
			glVertex3fv( bbox[2]); 
 
			glVertex3fv( bbox[2]); 
			glVertex3fv( bbox[7]); 
			glVertex3fv( bbox[6]); 
 
			 
			glNormal3f(0.0f,-1.0f, 0.0f); 
			glVertex3fv( bbox[1]); 
			glVertex3fv( bbox[0]); 
			glVertex3fv( bbox[3]); 
 
			glVertex3fv( bbox[3]); 
			glVertex3fv( bbox[2]); 
			glVertex3fv( bbox[1]); 
 
			 
			glNormal3f(0.0f,1.0f, 0.0f); 
			glVertex3fv( bbox[6]); 
			glVertex3fv( bbox[7]); 
			glVertex3fv( bbox[4]); 
			 
			glVertex3fv( bbox[4]); 
			glVertex3fv( bbox[5]); 
			glVertex3fv( bbox[6]); 
			 
		glEnd(); 
		 
		if(poskeysapplied) glPopMatrix(); 
	} 
} 
 
BOOL TriObject::isDataAlive() 
{ 
	return ( x != NULL && y != NULL && z != NULL ); 
} 
 
void TriObject::applyBbox() 
{ 
	if (isDataAlive()) 
	{ 
		float minx=x[0], maxx=x[0], miny=y[0], maxy=y[0], minz=z[0], maxz = z[0]; 
		for( int i=1; i<numvertices; i++) 
		{ 
			minx = (x[i]  minx) ? x[i] : minx; 
			maxx = (x[i] > maxx) ? x[i] : maxx; 
			miny = (y[i]  miny) ? y[i] : miny; 
			maxy = (y[i] > maxy) ? y[i] : maxy; 
			minz = (z[i]  minz) ? z[i] : minz; 
			maxz = (z[i] > maxz) ? z[i] : maxz; 
		} 
		 
		bbox[0][0] = maxx; 
		bbox[1][0] = maxx; 
		bbox[5][0] = maxx; 
		bbox[6][0] = maxx; 
 
		bbox[2][0] = minx; 
		bbox[3][0] = minx; 
		bbox[4][0] = minx; 
		bbox[7][0] = minx; 
 
		bbox[4][1] = maxy; 
		bbox[5][1] = maxy; 
		bbox[6][1] = maxy; 
		bbox[7][1] = maxy; 
 
		bbox[0][1] = miny; 
		bbox[1][1] = miny; 
		bbox[2][1] = miny; 
		bbox[3][1] = miny; 
 
		bbox[0][2] = maxz; 
		bbox[3][2] = maxz; 
		bbox[4][2] = maxz; 
		bbox[5][2] = maxz; 
 
		bbox[1][2] = minz; 
		bbox[2][2] = minz; 
		bbox[6][2] = minz; 
		bbox[7][2] = minz;		//....phew!.. 
		 
		 
		offset[0] = (maxx - minx)/2 + minx ; 
		offset[1] = (maxy - miny)/2 + miny; 
		offset[2] = (maxz - minz)/2 + minz; 
 
		bboxapplied = TRUE; 
	} 
	else  
	{ 
		bboxapplied = FALSE; 
	} 
} 
 
 
int TriObject::addMaterial(tMaterial * _material) 
{ 
	tMaterial * tmp; 
	nummaterials++; 
	 
	if (nummaterials==1) 
	{ 
		materials = new tMaterial[nummaterials]; 
		if (materials == NULL) 
		{ 
			nummaterials = 0; 
			materialsapplied = FALSE; 
			return -1; 
		} 
	} 
	else 
	{	 
		tmp = materials;	 
		materials = new tMaterial[nummaterials]; 
		if (materials == NULL) 
		{ 
			delete [] tmp; 
			nummaterials = 0; 
			materialsapplied = FALSE; 
			return -1; 
		} 
		for (i=0; i<nummaterials-1;i++)	 
		{ 
			materials[i] = tmp[i]; //Copy existing materials into the newly created array 
		} 
	} 
 
	//Insert the new material last in the array 
	materials[nummaterials-1].ambientColor[0] = _material->ambientColor[0]; 
	materials[nummaterials-1].ambientColor[1] = _material->ambientColor[1]; 
	materials[nummaterials-1].ambientColor[2] = _material->ambientColor[2]; 
 
	materials[nummaterials-1].diffuseColor[0] = _material->diffuseColor[0]; 
	materials[nummaterials-1].diffuseColor[1] = _material->diffuseColor[1]; 
	materials[nummaterials-1].diffuseColor[2] = _material->diffuseColor[2]; 
 
	materials[nummaterials-1].specularColor[0] = _material->specularColor[0]; 
	materials[nummaterials-1].specularColor[1] = _material->specularColor[1]; 
	materials[nummaterials-1].specularColor[2] = _material->specularColor[2]; 
 
	materials[nummaterials-1].emissiveColor[0] = _material->emissiveColor[0]; 
	materials[nummaterials-1].emissiveColor[1] = _material->emissiveColor[1]; 
	materials[nummaterials-1].emissiveColor[2] = _material->emissiveColor[2]; 
 
	materials[nummaterials-1].shininess = _material->shininess; 
	materials[nummaterials-1].transparency = _material->transparency; 
	 
	materialsapplied = TRUE; 
 
	return (nummaterials -1); //return what index the new material got into. 
} 
 
void TriObject::addPosKey(Poskey pkey) 
{ 
	Poskey* tmp; 
	numposkeys ++; 
	 
	if (numposkeys==1) 
	{ 
		poskeys = new Poskey[numposkeys]; 
		if (poskeys == NULL) 
		{ 
			numposkeys = 0; 
			poskeysapplied = FALSE; 
		} 
	} 
	else 
	{	 
		tmp = poskeys;	 
		poskeys = new Poskey[numposkeys]; 
		if (poskeys == NULL) 
		{ 
			delete [] tmp; 
			numposkeys = 0; 
			poskeysapplied = FALSE; 
		} 
		for (i=0; i<numposkeys-1;i++)	 
		{ 
			poskeys[i] = tmp[i]; //Copy existing keys into the newly created array 
		} 
	} 
 
	//Insert the new key last in the array 
	memcpy(&poskeys[numposkeys-1], &pkey, sizeof(Poskey)); 
 
	poskeysapplied = TRUE; 
} 
 
long TriObject::advanceFrame(int step, int direction, long frame) 
{ 
	if(frame == -1){ 
		if((currentkeypostime + direction) >-1 && (currentkeypostime + direction)  numposkeys+1) 
		{ 
			if (poskeys[currentkeypostime+direction].time == currentframe +direction) 
			currentkeypostime += direction; 
		} 
		if((currentkeyrottime + direction) >-1 && (currentkeyrottime + direction)  numrotkeys+1) 
		{ 
			if (rotkeys[currentkeyrottime+direction].time == currentframe +direction) 
			currentkeyrottime += direction; 
		} 
		if((currentkeyscltime + direction) >-1 && (currentkeyscltime + direction)  numsclkeys+1) 
		{ 
			if (sclkeys[currentkeyscltime+direction].time == currentframe +direction) 
			currentkeyscltime += direction; 
		} 
		return currentframe += direction; 
	} 
	else if(frame == -2) 
	{ 
		currentkeypostime = 0; 
		currentkeyrottime = 0; 
		currentkeyscltime = 0; 
		return currentframe = animstart; 
	} 
	else if(frame == -3) 
	{ 
		currentkeypostime = numposkeys-1; 
		currentkeyrottime = numposkeys-1; 
		currentkeyscltime = numposkeys-1; 
		return currentframe = animend; 
	} 
	else  
	{ 
		//should set frame instead of 0 ... check this sometime 
		currentkeypostime = 0; 
		currentkeyrottime = 0; 
		currentkeyscltime = 0; 
		return currentframe = frame; 
	} 
 
} 
 
void TriObject::setAnim(long start, long end) 
{ 
	animstart = start; 
	animend	= end; 
	currentframe = start; 
} 
 
void TriObject::addRotKey(Rotkey rkey) 
{ 
	Rotkey* tmp; 
	numrotkeys ++; 
	 
	if (numrotkeys==1) 
	{ 
		rotkeys = new Rotkey[numrotkeys]; 
		if (rotkeys == NULL) 
		{ 
			numrotkeys = 0; 
			rotkeysapplied = FALSE; 
		} 
	} 
	else 
	{	 
		tmp = rotkeys;	 
		rotkeys = new Rotkey[numrotkeys]; 
		if (rotkeys == NULL) 
		{ 
			delete [] tmp; 
			numrotkeys = 0; 
			rotkeysapplied = FALSE; 
		} 
		for (i=0; i<numrotkeys-1;i++)	 
		{ 
			rotkeys[i] = tmp[i]; //Copy existing keys into the newly created array 
		} 
	} 
 
	//Insert the new key last in the array 
	memcpy(&rotkeys[numrotkeys-1], &rkey, sizeof(Rotkey)); 
 
	rotkeysapplied = TRUE; 
} 
 
void TriObject::addSclKey(Poskey skey) 
{ 
	Poskey* tmp; 
	numsclkeys ++; 
	 
	if (numsclkeys==1) 
	{ 
		sclkeys = new Poskey[numsclkeys]; 
		if (sclkeys == NULL) 
		{ 
			numsclkeys = 0; 
			sclkeysapplied = FALSE; 
		} 
	} 
	else 
	{	 
		tmp = sclkeys;	 
		sclkeys = new Poskey[numsclkeys]; 
		if (sclkeys == NULL) 
		{ 
			delete [] tmp; 
			numsclkeys = 0; 
			sclkeysapplied = FALSE; 
		} 
		for (i=0; i<numsclkeys-1;i++)	 
		{ 
			sclkeys[i] = tmp[i]; //Copy existing keys into the newly created array 
		} 
	} 
 
	//Insert the new key last in the array 
	memcpy(&sclkeys[numsclkeys-1], &skey, sizeof(Poskey)); 
 
	sclkeysapplied = TRUE; 
} 
 
void TriObject::setId(int _id) 
{ 
	id = _id; 
} 
 
int TriObject::compileDisplayList() 
{ 
	glNewList(id, GL_COMPILE); 
 
		if (normalapplied) 
		{ 
			int j; 
			glBegin(GL_TRIANGLES); 
				for (i=0; i <numfaces/3; i++) 
				{ 
					j = 3*i; 
					if(materialsapplied) glColor4f( materials[matfaces[i]].diffuseColor[0], materials[matfaces[i]].diffuseColor[1], materials[matfaces[i]].diffuseColor[2], 1/materials[matfaces[i]].transparency ); 
					else glColor3f( 0.0f, 0.0f, 1.0f ); 
					::glNormal3f( nx[i], ny[i], nz[i]); 
					::glVertex3f	( x[faces[j]]  ,  y[faces[j]]  ,  z[faces[j]]); 
					::glVertex3f( x[faces[j+1]],  y[faces[j+1]],  z[faces[j+1]]); 
					::glVertex3f( x[faces[j+2]],  y[faces[j+2]],  z[faces[j+2]]); 
				} 
			glEnd(); 
		} 
		else 
		{ 
			glBegin(GL_TRIANGLES); 
				for (i=0; i <numfaces; i+=3) 
				{ 
					if(materialsapplied) glColor3f( materials[matfaces[i/3]].diffuseColor[0], materials[matfaces[i/3]].diffuseColor[1], materials[matfaces[i/3]].diffuseColor[2] ); 
					else glColor3f( 0.0f, 0.0f, 1.0f ); 
					glVertex3f( x[faces[i]]  ,  y[faces[i]]  ,  z[faces[i]]); 
					glVertex3f( x[faces[i+1]],  y[faces[i+1]],  z[faces[i+1]]); 
					glVertex3f( x[faces[i+2]],  y[faces[i+2]],  z[faces[i+2]]); 
				} 
			glEnd(); 
		} 
	 
	glEndList(); 
	displistapplied = TRUE; 
	return id; 
} 
 
void TriObject::drawGLDispList() 
{ 
	value	 = currentframe *numsplrotkeys / (float)animend; 
	valuepos = currentframe *numsplposkeys / (float)animend; 
 
	if(poskeysapplied)  
	{ 
		::glPushMatrix(); 
		 
		if(rotkeysapplied) 
		{ 
			 
			if(splinesapplied) 
			{ 
				::glTranslatef(pivot[0] + splposmover->GetValueAtTime(0, valuepos), pivot[1] + splposmover->GetValueAtTime(1, valuepos), pivot[2] + splposmover->GetValueAtTime(2, valuepos) ); 
				::glRotatef( splrotmover->GetValueAtTime(3, value), splrotmover->GetValueAtTime(0, value), splrotmover->GetValueAtTime(1, value), splrotmover->GetValueAtTime(2, value) ); 
			} 
			else 
			{ 
				::glTranslatef(pivot[0]+poskeys[currentkeypostime].pos[0], pivot[1]+poskeys[currentkeypostime].pos[1], pivot[2]+poskeys[currentkeypostime].pos[2]); 
				::glRotatef( rotkeys[currentkeyrottime].angle, rotkeys[currentkeyrottime].axis[0], rotkeys[currentkeyrottime].axis[1], rotkeys[currentkeyrottime].axis[2] ); 
			} 
			::glTranslatef(pivot[0],pivot[1],pivot[2]); 
		} 
	} 
	glCallList(id); 
	if(poskeysapplied) ::glPopMatrix(); 
} 
 
void TriObject::buildSplineAnimation() 
{	 
	 
	//build rotation splines 
	//ahem. The first rotation key sets the pivotrotation 
	pivotrot[0] = -rotkeys[0].axis[0]; 
	pivotrot[1] = -rotkeys[0].axis[1]; 
	pivotrot[2] = -rotkeys[0].axis[2]; 
	pivotrot[3] = rotkeys[0].angle * 180 / (float)M_PI; 
 
	//set null key to 0,0,0,0 
	splrotx.Add(0); 
	splroty.Add(0); 
	splrotz.Add(0); 
	splrotangle.Add(0); 
	splrottime.Add(0); 
	 
 
	for(i=1; i  numrotkeys; i++ ) 
	{ 
		if(i!=1)  
		{ 
			addQuaternions(&rotkeys[i-1], &rotkeys[i]); 
		} 
		splrotx.Add(-rotkeys[i].axis[0]); 
		splroty.Add(-rotkeys[i].axis[1]); 
		splrotz.Add(-rotkeys[i].axis[2]); 
		splrotangle.Add(rotkeys[i].angle * 180 / (float)M_PI); 
		splrottime.Add(rotkeys[i].time *(numrotkeys-1)/(float)animend); 
		TRACE("X:%f Y:%f Z:%f ANGLE:%f TIME:%d \n",-rotkeys[i].axis[0],-rotkeys[i].axis[1],-rotkeys[i].axis[2],rotkeys[i].angle * 180 / (float)M_PI,rotkeys[i].time); 
	} 
	 
	splrotmover = new LSmoothMover(4, &splrottime); 
	if (splrotmover == NULL) return; 
	splrotmover->SetupParamN(0,&splrotx); 
	splrotmover->SetupParamN(1,&splroty); 
	splrotmover->SetupParamN(2,&splrotz); 
	splrotmover->SetupParamN(3,&splrotangle); 
	numsplrotkeys = i-2; 
 
	//build position splines 
	for(i=0; i  numposkeys; i++ ) 
	{ 
		splposx.Add(poskeys[i].pos[0]); 
		splposy.Add(poskeys[i].pos[1]); 
		splposz.Add(poskeys[i].pos[2]); 
		splpostime.Add(poskeys[i].time *(numposkeys)/(float)animend); 
	} 
	splposmover = new LSmoothMover(3, &splpostime); 
	if (splposmover == NULL) return; 
	splposmover->SetupParamN(0,&splposx); 
	splposmover->SetupParamN(1,&splposy); 
	splposmover->SetupParamN(2,&splposz); 
	splinesapplied = TRUE; 
	numsplposkeys = i-1; 
} 
 
void TriObject::QuatToAxisAngle(Rotkey* rkey) 
{ 
	float scale,tw; 
	tw = (float)acos(rkey->angle) * 2; 
	scale = (float)sin(tw / 2.0); 
	rkey->axis[0] = rkey->axis[0] / scale; 
	rkey->axis[1] = rkey->axis[1] / scale; 
	rkey->axis[2] = rkey->axis[2] / scale; 
	//convert the angle back to degrees 
	rkey->angle = (tw * (360 / 2)) / (float)M_PI; 
} 
 
 
void TriObject::NormalizeQuaternion(Rotkey *rkey) 
{ 
	float magnitude; 
 
	// find the magnitude 
	magnitude = (rkey->axis[0] * rkey->axis[0]) +  
				(rkey->axis[1] * rkey->axis[1]) +  
				(rkey->axis[2] * rkey->axis[2]) +  
				(rkey->angle   * rkey->angle); 
	 
	//catch a null quaternion 
	if(magnitude == 0)  
	{ 
		magnitude = 1.0f; 
		rkey->axis[0] = 1.0f; 
	} 
 
 
	// Divide by the magnitude to normalize 
	rkey->axis[0] = rkey->axis[0] / magnitude; 
	rkey->axis[1] = rkey->axis[1] / magnitude; 
	rkey->axis[2] = rkey->axis[2] / magnitude; 
	rkey->angle   = rkey->angle   / magnitude; 
} 
 
void TriObject::addQuaternions(Rotkey* rkeyin, Rotkey* rkeyout) 
{ 
	float dirin  = 0.0f; 
	float dirout = 0.0f; 
	 
	dirin  = rkeyin->axis[0] + rkeyin->axis[1] + rkeyin->axis[2]; 
	dirout = rkeyout->axis[0] + rkeyout->axis[1] + rkeyout->axis[2]; 
	 
	if( dirout  0) 
	{ 
		rkeyout->angle  = rkeyin->angle - rkeyout->angle; 
		if (rkeyout->angle  0) 
			rkeyout->angle = M_2PI - (-rkeyout->angle); 
	}	 
	if( dirout >= 0) 
	{ 
		rkeyout->angle  = rkeyin->angle + rkeyout->angle; 
		if (rkeyout->angle > M_2PI) 
			rkeyout->angle = rkeyout->angle - M_2PI; 
	} 
	 
} 
 
// 
//                 APPLYOFFSET 
// This function will center the object around 0,0,0 
// It is needed for correct rotation animation 
// It must be called AFTER applybox() 
// 
void TriObject::applyOffset() 
{	 
	//return, no offset value is set 
	if(!bboxapplied) return; 
 
	//transform object 
	for(i=0; i<numvertices; i++) 
	{ 
		x[i] -= offset[0]; 
		y[i] -= offset[1]; 
		z[i] -= offset[2]; 
	} 
 
	//transform bounding box 
	for(i=0; i<8; i++) 
	{ 
		bbox[i][0] -= offset[0]; 
		bbox[i][1] -= offset[1]; 
		bbox[i][2] -= offset[2]; 
	} 
}