/****************************/
/*   METAOBJECTS.C		    */
/* (c)2002 Pangea Software  */
/*   By Brian Greenstone    */
/****************************/


/****************************/
/*    EXTERNALS             */
/****************************/

//#include <pictutils.h>

extern	float			gCurrentAspectRatio;
extern	int				gPolysThisFrame,gVRAMUsedThisFrame;
extern	Boolean			gMyState_Lighting,gIsPicking,gShowNormals;
extern	OGLMatrix4x4	gWorldToFrustumMatrix,gWorldToViewMatrix,gViewToFrustumMatrix;

/****************************/
/*    PROTOTYPES            */
/****************************/

static MetaObjectPtr AllocateEmptyMetaObject(u_long type, u_long subType);
static void SetMetaObjectToGroup(MOGroupObject *groupObj);
static void SetMetaObjectToMaterial(MOMaterialObject *matObj, MOMaterialData *inData);
static void SetMetaObjectToMesh(MOMeshObject *geoObj, MOMeshData *data);
static void SetMetaObjectToMatrix(MOMatrixObject *matObj, OGLMatrix4x4 *inData);
static void MO_DetachFromLinkedList(MetaObjectPtr obj);
static void MO_DisposeObject_Group(MOGroupObject *group);
static void MO_DeleteObjectInfo_Material(MOMaterialObject *obj);
static void MO_GetNewGroupReference(MOGroupObject *obj);
static void MO_GetNewMeshReference(MOMeshObject *obj);
static void MO_CalcBoundingBox_Recurse(MetaObjectPtr object, OGLBoundingBox *bBox);



/****************************/
/*    CONSTANTS             */
/****************************/





/*********************/
/*    VARIABLES      */
/*********************/

MetaObjectHeader	*gFirstMetaObject = nil;
MetaObjectHeader 	*gLastMetaObject = nil;
int					gNumMetaObjects = 0;

float				gGlobalTransparency = 1;			// 0 == clear, 1 = opaque
OGLColorRGB			gGlobalColorFilter = {1,1,1};
u_long				gGlobalMaterialFlags = 0;

MOMaterialObject	*gMostRecentMaterial;

u_long				gCurrentPickID = 0;


/***************** INIT META OBJECT HANDLER ******************/

void MO_InitHandler(void)
{
	gFirstMetaObject = nil;				// no meta object nodes yet
	gLastMetaObject = nil;
	gNumMetaObjects = 0;
}


#pragma mark -

/******************** GET NEW REFERENCE *********************/

MetaObjectPtr MO_GetNewReference(MetaObjectPtr mo)
{
MetaObjectHeader *h = mo;

	h->refCount++;
	
		/********************************/
		/* SEE IF NEED TO SUB INCREMENT */
		/********************************/
		
	switch(h->type)
	{
		case	MO_TYPE_GROUP:
				MO_GetNewGroupReference(mo);
				break;
				
				
		case	MO_TYPE_MESH:
				MO_GetNewMeshReference(mo);	
				break;
	}
	
	return(mo);
}


/**************** MO: GET NEW GROUP REFERENCE ******************/
//
// Recursively increment ref counts of everything inside the group
//

static void MO_GetNewGroupReference(MOGroupObject *obj)
{
MOGroupData	*data = &obj->objectData;
int			i;

	for (i = 0; i < data->numObjectsInGroup; i++)
	{
		MO_GetNewReference(data->groupContents[i]);
	}
}


/***************** MO: GET NEW MESH REFERENCE *************/

static void MO_GetNewMeshReference(MOMeshObject *obj)
{
MOMeshData	*data = &obj->objectData;

	MO_GetNewReference(data->material);
}


#pragma mark -

/****************** MO: CREATE NEW OBJECT OF TYPE ****************/
//
// INPUT:	type = type of mo to create
//			subType = subtype to create (optional)
//			data = pointer to any data needed to create the mo (optional)
//


MetaObjectPtr	MO_CreateNewObjectOfType(u_long type, u_long subType, void *data)
{
MetaObjectPtr	mo;

			/* ALLOCATE EMPTY OBJECT */
			
	mo = AllocateEmptyMetaObject(type,subType);
	if (mo == nil)
		return(nil);


			/* SET OBJECT INFO */
			
	switch(type)
	{
		case	MO_TYPE_GROUP:
				SetMetaObjectToGroup(mo);
				break;
				
		case	MO_TYPE_MESH:
				SetMetaObjectToMesh(mo, data);
				break;
				
		case	MO_TYPE_MATERIAL:
				SetMetaObjectToMaterial(mo, data);
				break;
				
		case	MO_TYPE_MATRIX:
				SetMetaObjectToMatrix(mo, data);
				break;
				
	}
	
	return(mo);
}


/****************** ALLOCATE EMPTY META OBJECT *****************/
//
// allocates an empty meta object and connects it to the linked list
//

static MetaObjectPtr AllocateEmptyMetaObject(u_long type, u_long subType)
{
MetaObjectHeader	*mo;
int					size = 0;

			/* DETERMINE SIZE OF DATA TO ALLOC */
			
	switch(type)
	{
		case	MO_TYPE_GROUP:
				size = sizeof(MOGroupObject);
				break;
				
		case	MO_TYPE_MESH:
				size = sizeof(MOMeshObject);
				break;
				
		case	MO_TYPE_MATERIAL:
				size = sizeof(MOMaterialObject);
				break;
				
		case	MO_TYPE_MATRIX:
				size = sizeof(MOMatrixObject);
				break;

	}
			

			/* ALLOC MEMORY FOR META OBJECT */
			
	mo = AllocPtr(size);	


			/* INIT STRUCTURE */
			
	mo->cookie 		= MO_COOKIE;
	mo->type 		= type;
	mo->subType 	= subType;
	mo->data 		= nil;
	mo->nextNode 	= nil;		
	mo->refCount	= 1;							// initial reference count is always 1
	mo->parentGroup = nil;


		/***************************/
		/* ADD NODE TO LINKED LIST */
		/***************************/

		/* SEE IF IS ONLY NODE */
					
	if (gFirstMetaObject == nil)
	{
		if (gLastMetaObject)
			return(nil);
	
		mo->prevNode = nil;
		gFirstMetaObject = gLastMetaObject = mo;
		gNumMetaObjects = 1;
	}
	
		/* ADD TO END OF LINKED LIST */
		
	else
	{		
		mo->prevNode = gLastMetaObject;		// point new prev to last
		gLastMetaObject->nextNode = mo;		// point old last to new
		gLastMetaObject = mo;				// set new last
		gNumMetaObjects++;
	}

	return(mo);
}

/******************* SET META OBJECT TO GROUP ********************/
//
// INPUT:	mo = meta object which has already been allocated and added to linked list.
//

static void SetMetaObjectToGroup(MOGroupObject *groupObj)
{
		
			/* INIT THE DATA */
			
	groupObj->objectData.numObjectsInGroup = 0;
}


/***************** SET META OBJECT TO MESH ********************/
//
// INPUT:	mo = meta object which has already been allocated and added to linked list.
//

static void SetMetaObjectToMesh(MOMeshObject *geoObj, MOMeshData *data)
{
			/* INIT THE DATA */
		
	geoObj->objectData = *data;									// copy from input data		

	
		/* INCREASE MATERIAL REFERENCE COUNTS */
			
	if (data->material != nil)								// make sure this material ref is valid
		MO_GetNewReference(data->material);
}



/******************* SET META OBJECT TO MATERIAL ********************/
//
// INPUT:	mo = meta object which has already been allocated and added to linked list.
//
// This takes the given input data and copies it. 
//

static void SetMetaObjectToMaterial(MOMaterialObject *matObj, MOMaterialData *inData)
{	
	
		/* COPY INPUT DATA */
		
	matObj->objectData = *inData;

}


/******************* SET META OBJECT TO MATRIX ********************/
//
// INPUT:	mo = meta object which has already been allocated and added to linked list.
//
// This takes the given input data and copies it. 
//

static void SetMetaObjectToMatrix(MOMatrixObject *matObj, OGLMatrix4x4 *inData)
{	
	
		/* COPY INPUT DATA */
		
	matObj->matrix = *inData;
}




#pragma mark -


/********************** MO: APPEND TO GROUP ****************/
//
// Attach new object to end of group
//

void MO_AppendToGroup(MOGroupObject *group, MetaObjectPtr newObject)
{
int	i;

			/* VERIFY COOKIE */

	if ((group->objectHeader.cookie != MO_COOKIE) || (((MetaObjectHeader *)newObject)->cookie != MO_COOKIE))
		return;


	i = group->objectData.numObjectsInGroup++;		// get index into group list
	if (i >= MO_MAX_ITEMS_IN_GROUP)
		return;

	MO_GetNewReference(newObject);					// get new reference to object
	group->objectData.groupContents[i] = newObject;	// save object reference in group
}

/**************** MO: ATTACH TO GROUP START **************/
//
// Attach new object to START of group
//

void MO_AttachToGroupStart(MOGroupObject *group, MetaObjectPtr newObject)
{
int	i,j;

			/* VERIFY COOKIE */

	if ((group->objectHeader.cookie != MO_COOKIE) || (((MetaObjectHeader *)newObject)->cookie != MO_COOKIE))
		return;


	i = group->objectData.numObjectsInGroup++;		// get index into group list
	if (i >= MO_MAX_ITEMS_IN_GROUP)
		return;

	MO_GetNewReference(newObject);					// get new reference to object
	
	
			/* SHIFT ALL EXISTING OBJECTS UP */
			
	for (j = i; j > 0; j--)
		group->objectData.groupContents[j] = group->objectData.groupContents[j-1];
	
	group->objectData.groupContents[0] = newObject;	// save object ref into group
}


#pragma mark -


/******************** MO: DRAW OBJECT ***********************/
//
// This recursive function will draw any objects submitted and parses groups.
//

void MO_DrawObject(const MetaObjectPtr object, const OGLSetupOutputType *setupInfo)
{
MetaObjectHeader	*objHead = object;
MOMeshObject	*vObj;

			/* VERIFY COOKIE */

	if (objHead->cookie != MO_COOKIE)
		return;


			/* HANDLE TYPE */

	switch(objHead->type)
	{
		case	MO_TYPE_MESH:
				vObj = object;
				MO_DrawGeometry_VertexArray(&vObj->objectData);				
				break;
	
		case	MO_TYPE_MATERIAL:
				MO_DrawMaterial(object);				
				break;

		case	MO_TYPE_GROUP:
				MO_DrawGroup(object, setupInfo);				
				break;
				
		case	MO_TYPE_MATRIX:
				MO_DrawMatrix(object, setupInfo);
				break;
	}
}


/******************** MO_DRAW GROUP *************************/

void MO_DrawGroup(const MOGroupObject *object, const OGLSetupOutputType *setupInfo)
{
int	numChildren,i;

				/* VERIFY OBJECT TYPE */
				
	if (object->objectHeader.type != MO_TYPE_GROUP)
		return;
		

			/*************************************/
			/* PUSH CURRENT STATE ON STATE STACK */
			/*************************************/


	OGL_PushState();


				/***************/
				/* PARSE GROUP */
				/***************/

	numChildren = object->objectData.numObjectsInGroup;			// get # objects in group
	
	for (i = 0; i < numChildren; i++)
	{
		MO_DrawObject(object->objectData.groupContents[i], setupInfo);
	}


			/******************************/
			/* POP OLD STATE OFF OF STACK */
			/******************************/
			
	OGL_PopState();
}


/******************** MO: DRAW GEOMETRY - VERTEX ARRAY *************************/

void MO_DrawGeometry_VertexArray(const MOMeshData *data)
{
Boolean		useTexture = false;
u_long 	materialFlags;


			/**********************/
			/* SETUP VERTEX ARRAY */
			/**********************/
			
	glEnableClientState(GL_VERTEX_ARRAY);				// enable vertex arrays
	glVertexPointer(3, GL_FLOAT, 0, data->points);		// point to point array

	if (OGL_CheckError())
		return;


			/************************/
			/* SETUP VERTEX NORMALS */
			/************************/
			
	if (data->normals)									// do we have normals
	{
		glNormalPointer(GL_FLOAT, 0, data->normals);
		glEnableClientState(GL_NORMAL_ARRAY);			// enable normal arrays
		

			/* SHOW VERTEX NORMALS */
			
		if (gShowNormals)
		{
			int	i;
			
			OGL_PushState();
			OGL_DisableLighting();
			glDisable(GL_TEXTURE_2D);
			SetColor4f(1,1,0,1);
			
			for (i = 0; i < data->numPoints; i++)
			{
				glBegin(GL_LINES);
		
				glVertex3fv((GLfloat *)&data->points[i]);
				glVertex3f(data->points[i].x + data->normals[i].x * 10.0f,
							data->points[i].y + data->normals[i].y * 10.0f,
							data->points[i].z + data->normals[i].z * 10.0f);
		
				glEnd();
			}
			OGL_PopState();
		}
		
	}
	else
		glDisableClientState(GL_NORMAL_ARRAY);			// disable normal arrays

	if (OGL_CheckError())
		return;


			/***********************/
			/* SETUP VERTEX COLORS */
			/***********************/

			/* IF LIGHTING, THEN USE COLOR FLOATS */
			
	if (gMyState_Lighting)
	{
		if (data->colorsFloat)									// do we have float colors?
		{
			glColorPointer(4, GL_FLOAT, 0, data->colorsFloat);
			glEnableClientState(GL_COLOR_ARRAY);				// enable color arrays
		}
		else
		if (data->colorsByte)									// no floats, so check bytes
		{
			glColorPointer(4, GL_UNSIGNED_BYTE, 0, data->colorsByte);
			glEnableClientState(GL_COLOR_ARRAY);				// enable color arrays
		}
		else
			glDisableClientState(GL_COLOR_ARRAY);				// no color data at all, so disable
	}
	
			/* IF NOT LIGHTING, THEN USE COLOR BYTES */
			
	else
	{		
		if (data->colorsByte)									// do we have byte colors?
		{
			glColorPointer(4, GL_UNSIGNED_BYTE, 0, data->colorsByte);
			glEnableClientState(GL_COLOR_ARRAY);				// enable color arrays
		}
		else
		if (data->colorsFloat)									// no bytes, so check floats
		{
			glColorPointer(4, GL_FLOAT, 0, data->colorsFloat);
			glEnableClientState(GL_COLOR_ARRAY);				// enable color arrays
		}
		else
			glDisableClientState(GL_COLOR_ARRAY);				// no color data at all, so disable
	}

	if (OGL_CheckError())
		return;


			/****************************/
			/* SEE IF ACTIVATE MATERIAL */
			/****************************/

	if (data->material != nil)										// are there any materials?	
	{
		
		MO_DrawMaterial(data->material);					// submit material


			/* IF TEXTURED, THEN ALSO ACTIVATE UV ARRAY */
			
		materialFlags = gMostRecentMaterial->objectData.flags;	// get material flags
		if (materialFlags & BG3D_MATERIALFLAG_TEXTURED)
		{
			if (data->uvs)
			{

				glTexCoordPointer(2, GL_FLOAT, 0,data->uvs);
				glEnableClientState(GL_TEXTURE_COORD_ARRAY);	// enable uv arrays
				
				useTexture = true;

				if (OGL_CheckError())
					return;
			}			
		}
	}	
	else
		glDisable(GL_TEXTURE_2D);						// no materials, thus no texture, thus turn this off
		

		/* WE DONT HAVE ENOUGH INFO TO DO TEXTURES, SO DISABLE */
								
	if (!useTexture)
	{
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		if (OGL_CheckError())
			return;
	}



			/***********/
			/* DRAW IT */
			/***********/
						
	glDrawElements(GL_TRIANGLES,data->numTriangles*3,GL_UNSIGNED_INT,&data->triangles[0]);

	if (OGL_CheckError())
		return;

	gPolysThisFrame += data->numPoints;					// inc poly counter
	
	
}



/************************ DRAW MATERIAL **************************/

void MO_DrawMaterial(MOMaterialObject *matObj)
{
MOMaterialData		*matData;
OGLColorRGBA		*diffuseColor,diffColor2;
Boolean				alreadySet;
u_long				matFlags;

					
	matData = &matObj->objectData;									// point to material data


				/****************************/
				/* SEE IF TEXTURED MATERIAL */
				/****************************/
				
	matFlags = matData->flags | gGlobalMaterialFlags;				// check flags of material & global
				
	if (matFlags & BG3D_MATERIALFLAG_TEXTURED)
	{
	
				/* ACTIVATE MATERIAL */
				
		alreadySet = (matObj == gMostRecentMaterial);
		if (alreadySet)												// see if even need to bother resetting this
		{
			glEnable(GL_TEXTURE_2D);	
		}
		else
		{		
			OGL_Texture_SetOpenGLTexture(matData->textureName);	// set this texture active			
		}
		
				/*****************************/
				/* SET TEXTURE WRAPPING MODE */
				/*****************************/
				
						/* U */
						
		if (matFlags & BG3D_MATERIALFLAG_CLAMP_U)										// we want to clamp the U
		{
			if (!(matData->flags & BG3D_MATERIALFLAG_CLAMP_U_TRUE))						// see if clamping needs to be enabled
			{
			    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);	// nope, so set clamping
			    matData->flags |= BG3D_MATERIALFLAG_CLAMP_U_TRUE;						// and remember that we set it
			}
		}
		else																			// we DONT want to clamp U
		{
			if (matData->flags & BG3D_MATERIALFLAG_CLAMP_U_TRUE)						// see clamping is still enabled
			{
			    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
			    matData->flags &= ~BG3D_MATERIALFLAG_CLAMP_U_TRUE;						// and remember that we cleared it
			}
		}

						/* V */
						
		if (matFlags & BG3D_MATERIALFLAG_CLAMP_V)										// we want to clamp the V
		{
			if (!(matData->flags & BG3D_MATERIALFLAG_CLAMP_V_TRUE))						// see if clamping needs to be enabled
			{
			    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);	// nope, so set clamping
			    matData->flags |= BG3D_MATERIALFLAG_CLAMP_V_TRUE;						// and remember that we set it
			}
		}
		else																			// we DONT want to clamp V
		{
			if (matData->flags & BG3D_MATERIALFLAG_CLAMP_V_TRUE)						// see clamping is still enabled
			{
			    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
			    matData->flags &= ~BG3D_MATERIALFLAG_CLAMP_V_TRUE;						// and remember that we cleared it
			}
		}
	
	}
	else
		glDisable(GL_TEXTURE_2D);	
	

			/********************/
			/* COLORED MATERIAL */
			/********************/

	diffuseColor = &matData->diffuseColor;			// point to diffuse color
		
	if (gGlobalTransparency != 1.0f)				// see if need to factor in global transparency
	{
		diffColor2.r = diffuseColor->r;
		diffColor2.g = diffuseColor->g;
		diffColor2.b = diffuseColor->b;
		diffColor2.a = diffuseColor->a * gGlobalTransparency;		
	}
	else
		diffColor2 = *diffuseColor;					// copy to local so we can apply filter to it without munging original


			/* APPLY COLOR FILTER */
			
	diffColor2.r *= gGlobalColorFilter.r;
	diffColor2.g *= gGlobalColorFilter.g;
	diffColor2.b *= gGlobalColorFilter.b;

	glColor4fv((GLfloat *)&diffColor2);						// set current diffuse color


		/* SEE IF NEED TO ENABLE BLENDING */

		
	if ((diffColor2.a != 1.0f) || (matFlags & BG3D_MATERIALFLAG_ALWAYSBLEND))		// if has alpha, then we need blending on	
		glEnable(GL_BLEND);	
	else
		glDisable(GL_BLEND);	


			/* SAVE THIS STUFF */
			
	gMostRecentMaterial = matObj;
}


/************************ DRAW MATRIX **************************/

void MO_DrawMatrix(const MOMatrixObject *matObj, const OGLSetupOutputType *setupInfo)
{
const OGLMatrix4x4		*m;

#pragma unused (setupInfo)
	
	m = &matObj->matrix;							// point to matrix

				/* MULTIPLY CURRENT MATRIX BY THIS */
	
	glMultMatrixf((GLfloat *)m);


}



#pragma mark -

/********************** MO DISPOSE OBJECT REFERENCE ******************************/
//
// NOTE: this is a recursive function in the case of group objects
//
// This will dispose of a single reference to the input object and any sub-objects in the
// case of a group.  If the refCount is 0, then the object is freed.
//

void MO_DisposeObjectReference(MetaObjectPtr obj)
{
MetaObjectHeader	*header = obj;
MOMeshObject	*vObj;

	if (obj == nil)
		return;

	if (header->cookie != MO_COOKIE)					// verify cookie			
		return;


			/*************************************/
			/* HANDLE SPECIFIC DISPOSE FUNCTIONS */
			/*************************************/
			//
			// This handles the decrementing of sub references such as objects
			// in a group or material references.
			//

	switch(header->type)
	{
		case	MO_TYPE_GROUP:
				MO_DisposeObject_Group(obj);
				break;
				
		case	MO_TYPE_MESH:
				vObj = obj;
				MO_DisposeObject_Geometry_VertexArray(&vObj->objectData);
				break;
	}

			/**************************************/
			/* DEC REFERENCE COUNT OF THIS OBJECT */
			/**************************************/
	
	header->refCount--;									// dec ref count
	
	if (header->refCount < 0)							// see if over did it
		return;
		
	if (header->refCount == 0)							// see if we can DELETE this node
	{
			/* DELETE OBJECT'S SPECIFIC INFO */

		switch(header->type)
		{
			case	MO_TYPE_MESH:
					vObj = obj;
					MO_DeleteObjectInfo_Geometry_VertexArray(&vObj->objectData);
					break;
					
			case	MO_TYPE_MATERIAL:
					MO_DeleteObjectInfo_Material(obj);
					break;		
					
		}
	
			/* DELETE THE OBJECT NODE */
			
		MO_DetachFromLinkedList(obj);					// detach from linked list

		header->cookie = 0xdeadbeef;					// devalidate cookie
		DisposePtr(obj);								// free memory used by object
		return;
	}		
}



/***************** DETACH FROM LINKED LIST *********************/

static void MO_DetachFromLinkedList(MetaObjectPtr obj)
{
MetaObjectHeader	*header = obj;
MetaObjectHeader	*prev,*next;


			/* VERIFY COOKIE */

	if (header->cookie != MO_COOKIE)
		return;


	prev = header->prevNode;
	next = header->nextNode;
	
			/* SEE IF WAS 1ST NODE IN LIST */
			
	if (prev == nil)
	{
		gFirstMetaObject = next;
		if (gFirstMetaObject)
			gFirstMetaObject->prevNode = nil;
	}
	
			/* SEE IF WAS LAST NODE IN LIST */
			
	if (next == nil)
	{
		gLastMetaObject = prev;	
		if (gLastMetaObject)
			gLastMetaObject->nextNode = nil;
	}
	
			/* SOMEWHERE IN THE MIDDLE */
	
	else		
	if (prev != nil)
	{
		prev->nextNode = next;
		next->prevNode = prev;
	}
	
	gNumMetaObjects--;	
	
	if (gNumMetaObjects < 0)
		return;
		
}


/**************** DISPOSE OBJECT:  GROUP ************************/
//
// Decrement the references of all objects in the group.
//

static void MO_DisposeObject_Group(MOGroupObject *group)
{
int	i,n;

	n = group->objectData.numObjectsInGroup;				// get # objects in group

	for (i = 0; i < n; i++)
	{
		MO_DisposeObjectReference(group->objectData.groupContents[i]);	// dispose of this object's ref
	}
}



/****************** DISPOSE OBJECT: GEOMETRY : VERTEX ARRAY *******************/
//
// Decrement references to all of the materials used in this geometry.
//

void MO_DisposeObject_Geometry_VertexArray(MOMeshData *data)
{
	if (data->material != nil)
		MO_DisposeObjectReference(data->material);	// dispose of this object's ref
}


/****************** DELETE OBJECT INFO: GEOMETRY : VERTEX ARRAY *******************/
//
// Assumes the contents (the materials) have already been dereferenced!
//

void MO_DeleteObjectInfo_Geometry_VertexArray(MOMeshData *data)
{
#pragma unused (data)

		/* DISPOSE OF VARIOUS ARRAYS */
		//
		// NOTE:  In this plug-in WE DO NOT DISPOSE OF THIS since these
		//			are actually pointers into the Bins which are disposed of
		//			elsewhere!
		//

}



/****************** DELETE OBJECT INFO: MATERIAL *******************/

static void MO_DeleteObjectInfo_Material(MOMaterialObject *obj)
{
MOMaterialData		*data = &obj->objectData;

		/* DISPOSE OF TEXTURE NAMES */
				
	if (data->hasTexture)
	{
		if (data->texturePixels)										// see if still have source buffer, then delete it too
			DisposePtr(data->texturePixels);					
		glDeleteTextures(1, &data->textureName);						// nuke OpenGL's copy of texture
	}
}


#pragma mark -


/******************** MO: CALC BOUNDING BOX ***********************/

void MO_CalcBoundingBox(MetaObjectPtr object, OGLBoundingBox *bBox)
{
		/* INIT BBOX TO BOGUS VALUES */
		
	bBox->min.x = 
	bBox->min.y =
	bBox->min.z = 100000000;
	
	bBox->max.x = 
	bBox->max.y = 
	bBox->max.z = -bBox->min.x;

	bBox->isEmpty = false;

			/* RECURSIVELY CALC BBOX */
			
	MO_CalcBoundingBox_Recurse(object, bBox);
}


/******************** MO: CALC BOUNDING BOX: RECURSE ***********************/

static void MO_CalcBoundingBox_Recurse(MetaObjectPtr object, OGLBoundingBox *bBox)
{
MetaObjectHeader	*objHead = object;
MOMeshObject	*vObj;
MOGroupObject		*groupObject;
MOMeshData	*geoData;
int					numChildren,i;
float				x,y,z;

			/* VERIFY COOKIE */

	if (objHead->cookie != MO_COOKIE)
		return;

	switch(objHead->type)
	{
			/* CALC BBOX OF MESH */
			
		case	MO_TYPE_MESH:
				vObj = object;
				geoData = &vObj->objectData;
				
				for (i = 0; i < geoData->numPoints; i++)
				{
					x = geoData->points[i].x;
					y = geoData->points[i].y;
					z = geoData->points[i].z;
					
					if (x < bBox->min.x)
						bBox->min.x = x;
					if (x > bBox->max.x)
						bBox->max.x = x;
				
					if (y < bBox->min.y)
						bBox->min.y = y;
					if (y > bBox->max.y)
						bBox->max.y = y;

					if (z < bBox->min.z)
						bBox->min.z = z;
					if (z > bBox->max.z)
						bBox->max.z = z;							
				}							
				break;
	
	
			/* RECURSE THRU GROUP */
			
		case	MO_TYPE_GROUP:
				groupObject = object;
				numChildren = groupObject->objectData.numObjectsInGroup;				
				for (i = 0; i < numChildren; i++)
					MO_CalcBoundingBox_Recurse(groupObject->objectData.groupContents[i], bBox);
				break;

				
	}
}







