#include "MAXProxyHeaders.hpp"

//
//############################################################################
//###########################    MAXPolygonMesh    ############################
//############################################################################
//

MemoryBlock*
	MAXPolygonMesh::AllocatedMemory = NULL;
MAXPolygonMesh::ClassData*
	MAXPolygonMesh::DefaultData = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::InitializeClass()
{
	Verify(!AllocatedMemory);
	AllocatedMemory =
		new MemoryBlock(
			sizeof(MAXPolygonMesh),
			10,
			10,
			"MAXPolygonMesh"
		);
	Register_Object(AllocatedMemory);

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			MAXPolygonMeshClassID,
			"MAXPolygonMesh",
			PolygonMeshProxy::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	Unregister_Object(AllocatedMemory);
	delete AllocatedMemory;
	AllocatedMemory = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::Destroy()
{
	Check_Object(this);
	Verify(referenceCount == 1);
	Verify(activePolygonProxies.IsEmpty());
	Check_Pointer(proxiedMesh);
	if (needToDelete)
	{
		delete meshData;
		needToDelete = false;
	}
	if (newMesh)
	{
		Unregister_Pointer(newMesh);
		delete newMesh;
		newMesh = NULL;
	}
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MAXPolygonMesh::MAXPolygonMesh(
	MAXScene *scene,
	MAXGroup *parent,
	INode *mesh,
	TimeValue t,
	int index,
	bool children
):
	Proxies::PolygonMeshProxy(DefaultData, scene, parent),
	proxiedMesh(mesh),
	time(t),
	childIndex(index),
	pseudoChild(children)
{
	newMesh = NULL;
	bmeshInitialized = false;
	Check_Pointer(this);
	Check_Pointer(proxiedMesh);
	//
	// we get the data for the mesh by making it a tri object
	//
	needToDelete = FALSE;
	Object *obj = proxiedMesh->EvalWorldState(time).obj;
	Verify(obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)));
	meshData = (TriObject *) obj->ConvertToType(time,Class_ID(TRIOBJ_CLASS_ID, 0));
	//meshData->mesh.buildNormals();

	//
	// now that we have the local in max space let
	// get the offset in max space and make them
	// our space
	//
	Point3 offset_trans = proxiedMesh->GetObjOffsetPos();
	Quat   offset_rot   = proxiedMesh->GetObjOffsetRot();

	Point3D offset_translation;
	offset_translation=ConvertMaxToMW(offset_trans);

	UnitQuaternion offset_rotation;
	offset_rotation=ConvertMaxToMW(offset_rot);

	meshToPivot.BuildRotation(offset_rotation);
	meshToPivot.BuildTranslation(offset_translation);

	// Note that the TriObject should only be deleted
	// if the pointer to it is not equal to the object
	// pointer that called ConvertToType()
	if (obj != meshData) needToDelete = TRUE;
	if (pseudoChild)
	{
		childIndex = 0;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MAXPolygonMesh::~MAXPolygonMesh()
{
	Check_Object(this);
	if (needToDelete)
	{
		delete meshData;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
	Check_Pointer(proxiedMesh);
	MAXScene *scene_proxy = Cast_Pointer(MAXScene*, sceneProxy);
	Check_Object(scene_proxy);
	const INode *scene = scene_proxy->GetProxiedScene();
	Check_Pointer(scene);
	Object *obj = proxiedMesh->EvalWorldState(time).obj;
	Verify(obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::Copy(Proxies::PolygonMeshProxy *mesh)
{
	Check_Object(this);
	Check_Object(mesh);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::TransferAndAppendToParentGroup(Proxies::GroupProxy *parent)
{
	Check_Object(this);
	Check_Pointer(proxiedMesh);
	//
	// detach the node but do not allow the position
	// to be move ie. 1 as the second arg
	//
	proxiedMesh->Detach(time,1);
	MAXGroup *old_parent = GetParentGroupProxy();
	if (old_parent)
	{
		Check_Object(old_parent);
		old_parent->DetachChildProxy(this);
	}
	else
	{
		Check_Object(GetSceneProxy());
		GetSceneProxy()->DetachChildProxy(this);
	}
	INode *parent_rec = NULL;
	if (parent)
	{
		MAXGroup *max_parent = Cast_Object(MAXGroup*, parent);
		parent_rec = max_parent->GetProxiedGroup();
		parent->AttachChildProxy(this);
	}
	else
	{
		MAXScene *max_scene = GetSceneProxy();
		Check_Object(max_scene);
		parent_rec = max_scene->GetProxiedScene();
		max_scene->AttachChildProxy(this);
	}
	Check_Pointer(parent_rec);
	proxiedMesh->AttachChild(parent_rec,1);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Proxies::ChildProxy*
	MAXPolygonMesh::UseNextSiblingProxy()
{


	Check_Object(this);
	Check_Pointer(proxiedMesh);
	INode *parent = proxiedMesh->GetParentNode();
	Check_Pointer(parent);

	unsigned children = parent->NumberOfChildren();
	Verify(children>0);

	INode *next = NULL;

	unsigned index;
	if (pseudoChild)
	{
		children = proxiedMesh->NumberOfChildren();
		Verify(children>0);
		index = 0;
		next = proxiedMesh->GetChildNode(index);
	}
	else
	{
		if (childIndex+1 < children)
		{
			index = childIndex+1;
			next = parent->GetChildNode(index);
		}
	}
	if (next)
	{	
		return
			GetSceneProxy()->InterpretRecord(GetParentGroupProxy(), next, index, true);
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Proxies::ChildProxy*
	MAXPolygonMesh::UsePreviousSiblingProxy()
{

	Check_Object(this);
	Check_Pointer(proxiedMesh);
	INode *parent = proxiedMesh->GetParentNode();
	Check_Pointer(parent);

	unsigned children = parent->NumberOfChildren();;
	INode *prev = NULL;

	if (!children)
	{
		return NULL;
	}
	if (childIndex-1 >= 0)
	{
		prev = parent->GetChildNode(childIndex-1);
	}
	if (prev)
	{
		return
			GetSceneProxy()->InterpretRecord(GetParentGroupProxy(), prev, childIndex-1,true);
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MAXPolygonMesh::GetName(MString *name)
{
	Check_Object(this);
	Check_Object(name);

	TCHAR *string = proxiedMesh->GetName();
	int len = strlen(string);
	if (len != 0)
	{
		*name = string;
		return true;
	}
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::SetName(const char* name)
{
	Check_Object(this);
	Check_Pointer(name);
	TCHAR *string = (TCHAR *)name;
	proxiedMesh->SetName(string);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MAXPolygonMesh::GetLocalToParent(LinearMatrix4D *matrix)
{
	Check_Object(this);
	Check_Pointer(matrix);
	Check_Pointer(proxiedMesh);


	Matrix3 LocalToWorld = proxiedMesh->GetNodeTM(time,NULL);
	Matrix3 ParentToWorld = proxiedMesh->GetParentTM(time);
	Matrix3 LocalToParent =  LocalToWorld * Inverse(ParentToWorld);
  
	//
	// now decompose the max matrix and make our linear matrix
	//
	if (pseudoChild)
	{
		*matrix = LinearMatrix4D::Identity;
	}
	else
	{
		AffineParts parts;
		decomp_affine(LocalToParent, &parts);

		Point3D translation;
		translation=ConvertMaxToMW(parts.t);

		UnitQuaternion rotation;
		rotation=ConvertMaxToMW(parts.q);

		matrix->BuildRotation(rotation);
		matrix->BuildTranslation(translation);
	}

	return *matrix != LinearMatrix4D::Identity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::SetLocalToParent(const LinearMatrix4D &matrix)
{
	Check_Object(this);
	Check_Object(&matrix);
//	Matrix3	*mat3 = matrix;
//	SetNodeTM(time, mat3);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MAXPolygonMesh::GetOBB(OBB *obb)
{
	Check_Object(this);
	Check_Pointer(obb);
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::SetOBB(const OBB &obb)
{
	Check_Object(this);
	Check_Object(&obb);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::SetBoundingSphere(const Sphere &sphere)
{
	Check_Object(this);
	Check_Object(&sphere);
	STOP(("Not implemented"));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	MAXPolygonMesh::UsePolygonArray(DynamicArrayOf<Proxies::PolygonProxy*> *polygons)
{
	Check_Object(this);
	Check_Object(polygons);

	Check_Pointer(meshData);
	InitMesh();
	unsigned face_count = face_array.GetLength();
	polygons->SetLength(face_count);

	for (unsigned i=0; i<face_count; ++i)
	{
		(*polygons)[i] = MAXPolygon::MakeProxy(this,&(face_array[i]),i);
		Register_Object((*polygons)[i]);
	}
	return face_count;
}


Quat ConvertMWToMax(UnitQuaternion &quat)
{

Quat rotation;
	rotation.x	= quat.x;
	rotation.y	= -quat.z;
	rotation.z	= quat.y;
	rotation.w	= -quat.w;

return rotation;
}

Point3 ConvertMWToMax(Point3D &pnt)
{
Point3 translation;
	translation.x = pnt.x;
	translation.y = -pnt.z;
	translation.z = pnt.y;

return translation;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::AddPolygons(Proxies::Process *process, DynamicArrayOf<Proxies::PolygonProxy*> &source_polygons)
{
	Check_Object(this);
	Check_Object(process);
	Check_Object(&source_polygons);

	Check_Pointer(meshData);
	//
	//------------------------------------------------------------------
	// Make an array out of both the source polys and the existing polys
	//------------------------------------------------------------------
	//
	DynamicArrayOf<Proxies::PolygonProxy*> existing_polygons;
	unsigned existing_polys = UsePolygonArray(&existing_polygons);
	unsigned source_polys = source_polygons.GetLength();
	unsigned total_polys = existing_polys + source_polys;
	DynamicArrayOf<Proxies::PolygonProxy*> new_polys(total_polys);
	unsigned i;
	for (i=0; i<existing_polys; ++i)
		new_polys[i] = existing_polygons[i];
	for (i=0; i<source_polys; ++i)
		new_polys[i+existing_polys] = source_polygons[i];


	//
	//-------------------------------------------------
	// Make the vertex index array
	//-------------------------------------------------
	
	DynamicArrayOf<Face> faces(total_polys);
	DynamicArrayOf< DynamicArrayOf<TVFace> > tfaces(total_polys);
	//tfaces[face_number][mutlitexture_number]

	DynamicArrayOf<Point3> vertices;
	DynamicArrayOf<Point3> normals;
	DynamicArrayOf<Point3> tvertices;
	DynamicArrayOf<VertColor> colors;
	DynamicArrayOf<int> indices;	// non optimized, the number of indices is the total number of vertices and normals - many duplicates

	int current_index = 0;
	int total_index = 0;
	int current_tindex = 0;
	int total_tindex = 0;

	DynamicArrayOf<StdMat *>material_array;
	int new_statecount = 0;
	material_array.SetLength(0);

	Proxies::StateLibrary *state_library = GetSceneProxy()->GetStateLibrary();
	// TODO: Get the ProxiedMesh's material - should be a multi-material.  Append these materials to it.
	for (i=0;i<total_polys;++i)
	{
		int j;
		// Get the material state
		int matid = 0;
		Proxies::MultiState multistate;
		int state_count = new_polys[i]->UseMultiState(&multistate);
		for (j=0;j<state_count;++j)
		{
			Proxies::StateProxy *state = state_library->UseMatchingStateProxy(multistate[j]);
			matid = ((MAXProxies::MAXState *)state)->matID;
			state->DetachReference();
		}
		multistate.DetachReferences();



		// Get the geometry stuff

		DynamicArrayOf<Proxies::IndexProxy *> poly_indices;

		int index_count = new_polys[i]->UseIndexArray(&poly_indices);
		Verify(3 == index_count);

		total_index += index_count;
		indices.SetLength(total_index);
		vertices.SetLength(total_index);
		normals.SetLength(total_index);
		colors.SetLength(total_index);
		
		for (j=0;j<index_count;++j)
		{
			Proxies::VertexProxy *vertex = poly_indices[j]->GetVertexProxy();

			Stuff::Point3D position;
			vertex->GetPosition(&position);

			Stuff::Normal3D normal;
			vertex->GetNormal(&normal);

			Stuff::RGBAColor color;
			vertex->GetColor(&color);


			// Get the UV - Texture information
			Stuff::DynamicArrayOf<Stuff::Vector2DOf<Stuff::Scalar> > tvector;
			vertex->GetUVs(&tvector);		// this is an array for the multitexture support
			int tvector_count = tvector.GetLength();
			total_tindex += tvector_count;

			tvertices.SetLength(total_tindex);
			tfaces[i].SetLength(tvector_count);
			for (int k=0;k<tvector_count;++k)
			{
				tvertices[current_tindex].x = tvector[k].x;
				tvertices[current_tindex].y = 1.0f - tvector[k].y;
				tvertices[current_tindex].z = 0.0;

				tfaces[i][k].t[j] = current_tindex;
				current_tindex++;
			}


			faces[i].v[j] = current_index;
			faces[i].flags = EDGE_ALL | HAS_TVERTS;
			faces[i].setMatID(matid);

			// Convert position and normal from MW to MAX
			vertices[current_index] = ConvertMWToMax(position);
			normals[current_index] = ConvertMWToMax((Stuff::Point3D)normal);

			colors[current_index].x = color.red;
			colors[current_index].y = color.green;
			colors[current_index].z = color.blue;

			indices[current_index] = current_index;			

			//faces[i].smGroup = smGroup

			current_index++;
		}
		new_polys[i]->DetachArrayReferences(&poly_indices);
	}

	// Optimize the data

	// TODO: reduce face vertex sharing

	// Create the MAX materials
	MAXScene *scene_proxy = Cast_Pointer(MAXScene*, sceneProxy);
	Check_Object(scene_proxy);
	scene_proxy->GetInterface()->GetMaterialLibrary().RemoveDuplicates();
	// Copy the data into the mesh
	meshData->mesh.setNumFaces(total_polys);
	meshData->mesh.setNumTVFaces(total_polys);
	for (i=0;i<total_polys;++i)
	{
		meshData->mesh.faces[i] = faces[i];
		int total_uvs = tfaces[i].GetLength();
		// Should be either 1 or 2
		for (int j=0;j<total_uvs;++j)
		{
			switch (j)
			{
			case 0:
				{
					meshData->mesh.tvFace[i] = tfaces[i][0];
					break;
				}
			case 1:
			default:
				{
				}
			}
		}
	}

	int total_vertices = vertices.GetLength();
	meshData->mesh.setNumVerts(total_vertices);
	for (i=0;i<total_vertices;++i)
	{
		meshData->mesh.setVert(i,vertices[i]);
		meshData->mesh.setNormal(i,normals[i]);
	}

	int total_tvertices = tvertices.GetLength();
	meshData->mesh.setNumTVerts(total_tvertices);
	for (i=0;i<total_tvertices;++i)
	{
		meshData->mesh.setTVert(i,tvertices[i]);
	}

	int total_colors = colors.GetLength();
	meshData->mesh.setNumVertCol(total_colors);
	for (i=0;i<total_colors;++i)
	{
		meshData->mesh.vertCol[i] = colors[i];
	}

	/*int total_uv_faces = uvs.GetLength();
	meshData->mesh.setNumTVerts(total_uvs);
	for (i=0;i<total_uvs;++i)
	{
		int total_TVerts = uvs[i].GetLength();
		for (int j=0;j<total_TVerts;++j)
		{
			switch (j)
			{
			case 0:meshData->mesh.setTVert(i,uvs[i][0].x,uvs[i][0].y,0.0);break;
			case 1:
			default:
				{
					// too many textures (for multitexture support)
				}
			}
		}
	}
*/
	DetachArrayReferences(&existing_polygons);
	//
	//-------------------------------------------------
	// Now do the state analysis of the new polygon set
	//-------------------------------------------------
	//
/*	DynamicArrayOf<unsigned> match;
	DynamicArrayOf<unsigned> count;
	DynamicArrayOf<Proxies::MultiState*> multi_states;
	unsigned unique_combinations =
		UseMultiStateArray(&multi_states, &match, &count, new_polys);
	Verify(unique_combinations>0);

	//
	//----------------------------------------------------------------
	// We need to create one MLRPolyMesh object per unique combination
	//----------------------------------------------------------------
	//
/*	gos_PushCurrentHeap(MidLevelRenderer::Heap);
	MLRShape *new_shape = new MLRShape(unique_combinations);
	Register_Object(new_shape);
	DynamicArrayOf<MLRPrimitiveBase*> meshes(unique_combinations);
	ElementSceneProxy *scene = GetSceneProxy();
	Check_Object(scene);
	StateLibrary *state_library = scene->GetStateLibrary();
	Check_Object(state_library);
	int primitive;
	for (primitive=0; primitive<unique_combinations; ++primitive)
	{
		Verify(	multi_states[primitive]->GetLength() >= 0 && 
				multi_states[primitive]->GetLength() <= 2);

		if(multi_states[primitive]->GetLength() < 2)
		{
			meshes[primitive] = new MLR_I_L_PMesh;
		}
		else
		{
			meshes[primitive] = new MLR_I_L_DT_PMesh;
		}

		Register_Object(meshes[primitive]);
		new_shape->Add(meshes[primitive]);

		//
		//--------------------------------------------------------------------
		// For each unique combination, we need to create the mesh data
		// structures, so we need to first identify the polygons to be grouped
		// together
		//--------------------------------------------------------------------
		//
		Verify(count[primitive]>0);
		DynamicArrayOf<PolygonProxy*> polygons(count[primitive]);
		unsigned i;
		unsigned poly_count = 0;
		for (i=0; i<total_polys && poly_count<count[primitive]; ++i)
		{
			PolygonProxy *polygon = new_polys[i];
			Check_Object(polygon);
			if (match[i] == primitive)
			{
				if (!poly_count)
				{
					Check_Object(multi_states[primitive]);


					if(multi_states[primitive]->GetLength() > 0)
					{
						MLRStateProxy *state =
							Cast_Object(
								MLRStateProxy*,
								state_library->UseMatchingStateProxy((*multi_states[primitive])[0])
							);
						Check_Object(state);
						MLRState mlr_state;
						meshes[primitive]->SetReferenceState(state->GetMLRState());
						state->DetachReference();
					}

					if(multi_states[primitive]->GetLength() > 1)
					{
						MLRStateProxy *state =
							Cast_Object(
								MLRStateProxy*,
								state_library->UseMatchingStateProxy((*multi_states[primitive])[1])
							);
						Check_Object(state);
						MLRState mlr_state;
						if(multi_states[primitive]->isInverted == false)
						{
							meshes[primitive]->SetReferenceState(state->GetMLRState(), 1);
						}
						else
						{
							meshes[primitive]->SetReferenceState(meshes[primitive]->GetReferenceState(), 1);
							meshes[primitive]->SetReferenceState(state->GetMLRState(), 0);
						}
						state->DetachReference();
					}
				}
				polygons[poly_count++] = polygon;
			}
		}
		Verify(poly_count == count[primitive]);

		if(multi_states[primitive]->GetLength() < 2)
		{
			SetPolyMeshArrays(
				mlrShape,
				Cast_Pointer(MLR_I_L_PMesh*, meshes[primitive]),
				polygons,
				process
			);
		}
		else
		{
			SetPolyMeshArrays(
				mlrShape,
				Cast_Pointer(MLR_I_L_DT_PMesh*, meshes[primitive]),
				polygons,
				process,
				multi_states[primitive]->isInverted
			);
		}

		multi_states[primitive]->DetachReferences();
		meshes[primitive]->DetachReference();
	}

	//
	//-----------------------------------------------------------------------
	// Now we need to clean out the shape and stick the new primitives inside
	// it
	//-----------------------------------------------------------------------
	//
	DetachArrayReferences(&existing_polygons);
	mlrShape->DetachReference();
	Check_Object(proxiedShape);
	proxiedShape->SetMLRShape(new_shape);
	mlrShape = new_shape;
	mlrShape->AttachReference();

	gos_PopCurrentHeap();
*/
	//
	//----------------------------------------------------------
	// Clean up the proxy arrays, then load up the useful arrays
	//----------------------------------------------------------
	//
//	LoadArrays();
}

void
	MAXPolygonMesh::SetToMatchMultiState(Proxies::MultiState* multi_state)
{
	STOP(("Not implemented"));
}

#if 0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MAXPolygonMesh::SetToMatchStateProxy(Proxies::StateProxy* state)
{
	STOP(("Not implemented"));
}
#endif

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	MAXPolygonMesh::UseVertexArray(DynamicArrayOf<Proxies::VertexProxy*> *vertices)
{
	Check_Object(this);
	Check_Object(vertices);
	
	Check_Pointer(meshData);
	InitMesh();
	unsigned vertex_count = vertex_array.GetLength();
	vertices->SetLength(vertex_count);
	for (unsigned i=0; i<vertex_count; ++i)
	{
		(*vertices)[i] = MAXVertex::MakeProxy(this, 
												i,
												0,
												&(vertex_array[i]),-1);
		Register_Object((*vertices)[i]);
	}
	return vertex_count;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
INode 
	*MAXPolygonMesh::GetNextChild()
{
	Check_Pointer(proxiedMesh);
	INode *parent = proxiedMesh->GetParentNode();
	Check_Pointer(parent);
	unsigned children = parent->NumberOfChildren();;

	if (!children)
	{
		return NULL;
	}
	if (childIndex+1 < children)
	{
		INode *child = parent->GetChildNode(childIndex);
		childIndex++;
		return child;
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
INode 
	*MAXPolygonMesh::GetPreviousChild()
{
	Check_Pointer(proxiedMesh);
	INode *parent = proxiedMesh->GetParentNode();
	Check_Pointer(parent);
	unsigned children = parent->NumberOfChildren();;

	if (!children)
	{
		return NULL;
	}
	if (childIndex > -1)
	{
		INode *child = parent->GetChildNode(childIndex);
		childIndex--;
		return child;
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MAXPolygonMesh::GetNumberTextureMaps(int face_index)
{
	Check_Pointer(proxiedMesh);

	Mtl* m = proxiedMesh->GetMtl();
	int index = 0;

	if (face_index >= 0)
	{
		Face face = meshData->mesh.faces[face_index];
		index = face.getMatID();
	}

	int num_sub = 0;
	Mtl* mat = NULL;
	if (m)
	{
		num_sub = m->NumSubMtls();
		if (index < num_sub)
		{
			 mat = m->GetSubMtl(index);
		}
		else
		{
			mat = m;
		}
	}
	if (mat == NULL) return -1;
	Check_Pointer(mat);
	int texture_count = 0;
	for (int i=0;i<NTEXMAPS;i++)
	{
		if (i==ID_OP) continue; // skip over the opacity map cause we use it
		Texmap* texmap = NULL;
		texmap = m->GetSubTexmap(i);
		if (texmap!=NULL)
		{
			texture_count++;
		}
	}
	return texture_count;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MAXPolygonMesh::GetUVTypeOfMap(int texture_index, int face_index)
{
	Check_Pointer(proxiedMesh);

	Mtl* m = proxiedMesh->GetMtl();
	Check_Pointer(m);

	int index = 0;
	if (face_index >= 0)
	{
		Face face = meshData->mesh.faces[face_index];
		index = face.getMatID();
	}

	int num_sub = 0;
	Mtl* mat = NULL;
	if (m)
	{
		num_sub = m->NumSubMtls();
		if (index < num_sub)
		{
			 mat = m->GetSubMtl(index);
		}
		else
		{
			mat = m;
		}
	}
	Check_Pointer(mat);

	int texture_count = 0;
	int uv_type = UVWSRC_EXPLICIT;

	for (int i=0;i<NTEXMAPS;i++)
	{
		if (i==ID_OP) continue; // skip over the opacity map cause we use it
		Texmap* texmap = NULL;
		texmap = mat->GetSubTexmap(i);
		if (texmap!=NULL)
		{
			uv_type = texmap->GetUVWSource();
			texture_count++;
			if (texture_index == texture_count) break;
		}
	}
	return uv_type;
}

int MAXPolygonMesh::InitMesh()
{
	if (!bmeshInitialized)
	{
		//JKYLE - defer this work until it is needed
		meshData->mesh.buildNormals();
		//Split the mesh by smoothing group
		// Create a mesh for each smoothing group
		int face_count = meshData->mesh.numFaces;
		int vertex_count = meshData->mesh.numVerts;
		
		// Create a hash table of mappings from the original vertex index to the multiple copies of the new vertex index
		// each copy of the vertex belongs to a unique smoothing group
		DynamicArrayOf< DynamicArrayOf<DWORD> > hash_table;
		hash_table.SetLength(vertex_count);
		vertex_array.SetLength(vertex_count);
		vertex_ref_array.SetLength(vertex_count);
		face_array.SetLength(face_count);
		vertex_revmap.SetLength(vertex_count);

		for (int iVertex = 0;iVertex < vertex_count;iVertex++)
		{
			vertex_array[iVertex] = meshData->mesh.verts[iVertex];
			vertex_ref_array[iVertex] = -1;
			vertex_revmap[iVertex] = iVertex;
			hash_table[iVertex].SetLength(1);
			hash_table[iVertex][0] = iVertex;
		}


		for (int iFace = 0;iFace < face_count;iFace++)
		{
			face_array[iFace] = meshData->mesh.faces[iFace];
			Face *pFace = &face_array[iFace];

			DWORD group = meshData->mesh.faces[iFace].smGroup;
			// We are done with the meshData for this iteration, use pFace and group

			// find or create the group index
			int group_count = group_array.GetLength();
			for (int iGroup = 0;iGroup < group_count;iGroup++)
			{
				if (group_array[iGroup] == group)
					break;	// Found group
			}
			if (iGroup == group_count)
			{
				// did not find group, add one
				group_count++;
				group_array.SetLength(group_count);
				group_array[iGroup] = group;
			}
			// iGroup is now the index in group_array that equals group

			// Set the vertices in the face to the matching vertex in vertex_array that matches the smoothing group
			for (int i = 0;i<3;i++)
			{
				DWORD iVertex = pFace->v[i];
				Verify(iVertex < vertex_count);
				
				// Find the vertex index in the hash table such that the vertex is in the same smoothing group
				int hash_count = hash_table[iVertex].GetLength();
				for (int iHash = 0;iHash < hash_count;iHash++)
				{
					DWORD hash_vert = hash_table[iVertex][iHash];
					if (-1 == vertex_ref_array[hash_vert])
					{
						//This is the first time this vertex is used, so it has no reference to a group
						vertex_ref_array[hash_vert] = iGroup;
						break;
					}
					else
					{
						DWORD group_ref = vertex_ref_array[hash_vert];
						if (iGroup == vertex_ref_array[hash_vert])
						{
							// we found the vertex
							break;
						}
					}
				}

				// 
				if (iHash == hash_count)
				{
					// we did not find the same vertex in the right smoothing group, so add it

					// first make a new vertex, copy of the old vertex
					DWORD new_vertex = vertex_count;
					vertex_count++;
					vertex_array.SetLength(vertex_count);
					vertex_ref_array.SetLength(vertex_count);
					
					vertex_array[new_vertex] = vertex_array[iVertex];
					vertex_ref_array[new_vertex] = iGroup;
					vertex_revmap.SetLength(vertex_count);
					vertex_revmap[new_vertex] = iVertex;

					// add the new entry to the hash table
					hash_count++;
					hash_table[iVertex].SetLength(hash_count);
					hash_table[iVertex][iHash] = new_vertex;
				}
				pFace->v[i] = hash_table[iVertex][iHash];
			}
		}
		bmeshInitialized = true;
	}
	return 1;
}

Point3 *MAXPolygonMesh::GetVertex(DWORD index)
{
	InitMesh();
	Verify(index < vertex_array.GetLength());
	return &(vertex_array[index]);
}

DWORD MAXPolygonMesh::GetVertexGroup(DWORD index)
{
	InitMesh();
	// If returns 0 then this is flat shaded
	return group_array[vertex_ref_array[index]];
}

int MAXPolygonMesh::GetNumVertices()
{
	InitMesh();
	return vertex_array.GetLength();
}

TVFace *MAXPolygonMesh::GetTVFace(int index)
{
	Verify(index < meshData->mesh.numFaces);
	return &meshData->mesh.tvFace[index];
}

int MAXPolygonMesh::GetnumTVerts()
{
	return meshData->mesh.numTVerts;
}

UVVert MAXPolygonMesh::GetTVert(DWORD index)
{
	Verify(index < meshData->mesh.numTVerts);
	return meshData->mesh.tVerts[index];
}

void MAXPolygonMesh::SetTVert(DWORD index, UVVert tvert)
{
	meshData->mesh.setTVert(index,tvert);
}

int MAXPolygonMesh::GetnumCVerts()
{
	return meshData->mesh.numCVerts;
}

VertColor *MAXPolygonMesh::GetvertColor(DWORD index)
{
	return &meshData->mesh.vertCol[index];
}

Point3 MAXPolygonMesh::GetFaceNormal(DWORD index)
{
	InitMesh();
	// These are flat shaded vertex normals
	// Index is the face index
	Point3 p0 = vertex_array[face_array[index].v[0]];
	Point3 p1 = vertex_array[face_array[index].v[1]];
	Point3 p2 = vertex_array[face_array[index].v[2]];

	Point3 norm = CrossProd(p1-p0,p2-p1);
	return norm;
}

Point3 MAXPolygonMesh::GetVertexNormal(DWORD index)
{
	InitMesh();
	// These are gourand shaded vertex normals
	// Which index is this & how is it calculated?
	//DWORD dwIndex = vertex_revmap[index];
	//Verify(-1 != dwIndex);
	if (!newMesh)
	{
		newMesh = new Mesh();
		Register_Pointer(newMesh);

		newMesh->setNumVerts(vertex_array.GetLength());
		for (int x=0;x<vertex_array.GetLength();x++)
		{
			newMesh->setVert(x,vertex_array[x]);
		}
		newMesh->setNumFaces(face_array.GetLength());
		for (x=0;x<face_array.GetLength();x++)
		{
			newMesh->faces[x] = face_array[x];
		}
		newMesh->buildNormals();
	}
	return newMesh->getNormal(index);
}

void MAXPolygonMesh::SetNormal(DWORD index,Point3 norm)
{
	meshData->mesh.setNormal(index,norm);
}

int MAXPolygonMesh::GetNumVertCol()
{
	return meshData->mesh.getNumVertCol();
}

