//
// Tools_MultiElementSkin_Processor.cpp
//
// Created by KJL - 28/10/2003 12:42:32
//
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "MeshProcessing/Tools_MultiElementSkin_Processor.h"

#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Hierarchy.h"
#include "MeshProcessing/Asura_TriStripper.h"
#include "Mesh/Asura_Mesh.h"

u_int*										Tools_MultiElementSkin_Processor::s_puElementArray;
const Asura_Mesh*							Tools_MultiElementSkin_Processor::s_pxMesh;
const Asura_Hierarchy_Skin*					Tools_MultiElementSkin_Processor::s_pxSkin;
Tools_MultiElementSkin_Element*				Tools_MultiElementSkin_Processor::s_pxFirstElement;
Tools_MultiElementSkin_VertexAdjacencies*	Tools_MultiElementSkin_Processor::s_pxVertexAdjacencies;
Asura_Vector_3*								Tools_MultiElementSkin_Processor::s_pxFaceNormal;
u_int										Tools_MultiElementSkin_Processor::s_uNumberOfElements;
Tools_MultiElementSkin_Stripified*			Tools_MultiElementSkin_Processor::s_pxStripifiedSkin;

bool										Tools_MultiElementSkin_Processor::s_bInitialised = false;

// KJL - 28/10/2003 17:26:08 - Making a MultiElementSkin
// 
// First take a character mesh, which is a simple array of vertices with a triangle list, and separate it into 
// a number of elements. An 'element' is defined as a collection of polygons which are connected to each other
// by shared edges, e.g. each triangle in an element shares an edge with at least one other triangle. Any polygon
// that can be in an element is (rather than having a arbitrary limit), so a well connected mesh would result in
// just one element. This is the definition of an element used in 3DS Max, so (hopefully) it's a easy concept for
// artists to get to grips with, since they can be immediately visualised and selected when constructing models.
//
// Note that polygons sharing a vertex rather than an edge isn't enough to guarantee them being in the same
// element - it has to be an edge connection. However, we're interested in these vertex connections for the
// purpose of calculating vertex normals, since we'll need to know about the normal of every polygon that touches
// a vertex.
// 
// To complicate matters further we're also interested in polygons which *visually* share an edge, i.e. polygons
// of different elements who share an edge with identical vertex positions, but not actually the same vertices.
// e.g. if you detach a hand as a different element in Max, the vertices of the wrist still match the positions
// of the vertices at the end of the lower arm - but they're actually using different vertices. We need this info
// when calculating vertex normals so we don't get lighting discontinuities across elements, which could look 
// quite ugly and destroy the illusion of a solid smooth skin.

bool Tools_MultiElementSkin_Processor::Initialise(const Asura_Mesh* pxMesh, const Asura_Hierarchy_Skin* pxSkin, const bool bRenumberBones)
{
	s_bInitialised = false;
	if (pxMesh == 0 || pxSkin == 0)
	{
		return false;
	}

	{
		Asura_Hierarchy_Skin* pxHackSkin = (Asura_Hierarchy_Skin* )pxSkin;
		pxHackSkin->CalculateSkinNormalData();
	}
	s_pxMesh = pxMesh;
	s_pxSkin = pxSkin;

	s_pxFirstElement = 0;
	s_puElementArray = 0;
	s_pxStripifiedSkin = 0;

	// KJL - 29/10/2003 17:02:38 - okay, let's calculate the face normals of all the polys
	u_int uNumberOfPolygons = s_pxMesh->NumberOfPolygons;
	s_pxFaceNormal = new Asura_Vector_3[uNumberOfPolygons];

	for (u_int u=0; u<uNumberOfPolygons; u++)
	{
		Asura_Vector_3& v0 = (s_pxMesh->VertexDataPtr[s_pxMesh->PolygonDataPtr[u].VertexID[0]].Position);
		Asura_Vector_3& v1 = (s_pxMesh->VertexDataPtr[s_pxMesh->PolygonDataPtr[u].VertexID[1]].Position);
		Asura_Vector_3& v2 = (s_pxMesh->VertexDataPtr[s_pxMesh->PolygonDataPtr[u].VertexID[2]].Position);

		s_pxFaceNormal[u] = (v1-v0)^(v2-v1);
		s_pxFaceNormal[u].Normalise();
	}

	
	// KJL - 29/10/2003 10:37:30 - right, for each vertex let's make a list of polygons that use that vertex
	s_pxVertexAdjacencies = new Tools_MultiElementSkin_VertexAdjacencies[s_pxMesh->NumberOfVertices];



	for (u_int u=0; u<uNumberOfPolygons; u++)
	{
		u_int uNumberOfVerticesInPolygon = s_pxMesh->PolygonDataPtr[u].NumberOfVertices();

		for (u_int v=0; v<uNumberOfVerticesInPolygon; v++)
		{
			u_int uVertexIndex = s_pxMesh->PolygonDataPtr[u].VertexID[v];

			s_pxVertexAdjacencies[uVertexIndex].AddAdjacentPolygon(u);
		}
	}
	
	// KJL - 10/11/2003 09:33:39 - add to that a list of coincident vertices
	// KJL - 10/11/2003 09:40:57 - this n squared loop is obviously an optimisation candidate!
	u_int uNumberOfVertices = s_pxMesh->NumberOfVertices;
	for (u_int u=0; u<uNumberOfVertices; u++)
	{
		for (u_int v=0; v<uNumberOfVertices; v++)
		{
			if (u==v) continue;

			if (s_pxMesh->VertexDataPtr[u].Position.ApproximatelyEquals(s_pxMesh->VertexDataPtr[v].Position))
			{
				s_pxVertexAdjacencies[u].AddAdjacentVertex(v);
			}
		}
	}

	// KJL - 29/10/2003 11:12:35 - now we use this to make a list of polygons sharing edges (or vertices)
	Tools_MultiElementSkin_PolyAdjacencies* pxPolyAdjacencies = new Tools_MultiElementSkin_PolyAdjacencies[s_pxMesh->NumberOfPolygons];

	for (u_int u=0; u<uNumberOfPolygons; u++)
	{
		u_int uNumberOfVerticesInPolygon = s_pxMesh->PolygonDataPtr[u].NumberOfVertices();

		for (u_int v=0; v<uNumberOfVerticesInPolygon; v++)
		{
			u_int uVertexIndex = s_pxMesh->PolygonDataPtr[u].VertexID[v];

			Tools_MultiElementSkin_IndexListNode* pxIndexNode = s_pxVertexAdjacencies[uVertexIndex].m_pxPolyList;

			while(pxIndexNode)
			{
				u_int uPolyIndex = pxIndexNode->m_uIndex;

				if (uPolyIndex != u)
				{
					pxPolyAdjacencies[u].AddAdjacentPolygon(uPolyIndex);
				}

				pxIndexNode = pxIndexNode->m_pxNext;
			};
		}
	}

	// KJL - 29/10/2003 14:49:04 - now we use this adjacency data to make elements
	s_puElementArray = new u_int[uNumberOfPolygons];

	for (u_int u=0; u<uNumberOfPolygons; u++)
	{
		s_puElementArray[u] = 0xffffffff;
	}

	s_uNumberOfElements = 0;

	for (u_int u=0; u<uNumberOfPolygons; u++)
	{
		// has this polygon been assigned to an element already?
		if(s_puElementArray[u] != 0xffffffff ) continue;

		// this polygon is the start of a new element
		// work out through the adjacencies from this polygon, adding polygons
		// to the element
		u_int uCurrent = u;		//current member of the element being looked at
		u_int uLastPolygon = u;	//the last polygon in the element

		do
		{
			const Tools_MultiElementSkin_PolyListNode* pxNode = pxPolyAdjacencies[uCurrent].m_pxPolyList;

			for (; pxNode; pxNode = pxNode->m_pxNext)
			{
				if (pxNode->m_uNumberOfSharedVertices < 2) continue; // only interested in shared edges, not vertices
				
				u_int uPolyIndex = pxNode->m_uIndex;

				if (s_puElementArray[uPolyIndex] != 0xffffffff) continue;
				if (uPolyIndex == uLastPolygon) continue;

				//link this polygon on to the end of the array
				s_puElementArray[uLastPolygon] = uPolyIndex;
				uLastPolygon = uPolyIndex;
			}

			uCurrent = s_puElementArray[uCurrent];

		}while(uCurrent!=0xffffffff);

		// done all the polygons in the element, mark as end
		s_puElementArray[uLastPolygon] = 0;

		AddNewElement(u);

		
		s_pxFirstElement->CalculateBoneUsage(s_pxSkin->NumberOfBones);

		if (bRenumberBones)	s_pxFirstElement->ConvertBoneIndices( s_pxSkin->NumberOfBones );
		
		s_uNumberOfElements++;
	}

	delete [] pxPolyAdjacencies;

	MergeSmallElements();

	s_bInitialised = true;


	return true;
}



const Tools_MultiElementSkin_Element* Tools_MultiElementSkin_Processor::GetFirstElement(void)
{
	if (!s_bInitialised)
	{
		Asura_Assert( false, "Not yet initialised" );
		return 0;
	}

	return s_pxFirstElement;
}

const Tools_MultiElementSkin_Stripified* Tools_MultiElementSkin_Processor::GetStripifiedSkin(void)
{
	if (!s_bInitialised)
	{
		Asura_Assert( false, "Not yet initialised" );
		return 0;
	}

	// KJL - 04/11/2003 17:19:02 - make stripified skin and keep a pointer to it so we can deallocate it on 
	// Shutdown. (We don't allow the user any create/destroy rights to any of these classes, to avoid any
	// memory mishaps.)
	
	s_pxStripifiedSkin = new Tools_MultiElementSkin_Stripified(s_uNumberOfElements, s_pxFirstElement);

	return s_pxStripifiedSkin;
}

void Tools_MultiElementSkin_Processor::Shutdown(void)
{
	if (!s_bInitialised)
	{
		Asura_Assert( false, "Not yet initialised" );
		return;
	}

	delete [] s_pxFaceNormal;
	delete [] s_puElementArray;
	delete [] s_pxVertexAdjacencies;

	Tools_MultiElementSkin_Element* pxNode = s_pxFirstElement;
	while (pxNode)
	{
		Tools_MultiElementSkin_Element* pxNextNode = pxNode->m_pxNextElement;
		
		delete pxNode;

		pxNode = pxNextNode;
	}

	delete s_pxStripifiedSkin;

	s_bInitialised = false;
}

void Tools_MultiElementSkin_Processor::MergeSmallElements(void)
{
	Tools_MultiElementSkin_Element* pxElement = s_pxFirstElement;

	while(pxElement)
	{
		Tools_MultiElementSkin_Element* pxNextElement = pxElement->m_pxNextElement;

		if (pxElement->GetNumberOfTriangles() < 50)
		{
			FindBestElementToMergeTo(pxElement)->MergeInElement(pxElement);
			DeleteElement(pxElement);
			s_uNumberOfElements--;
		}

		pxElement = pxNextElement;
	}
}

Tools_MultiElementSkin_Element* Tools_MultiElementSkin_Processor::FindBestElementToMergeTo(Tools_MultiElementSkin_Element* pxSubject)
{
	Tools_MultiElementSkin_Element* pxElement = s_pxFirstElement;

	u_int uMostTrisSoFar = 50;
	Tools_MultiElementSkin_Element* pxBest = 0;

	while(pxElement)
	{
		if (pxElement != pxSubject)
		{
			if (pxElement->GetNumberOfTriangles() > uMostTrisSoFar)
			{
				u_int uMaxNumberOfBones = s_pxSkin->NumberOfBones;

				bool bShareBones = true;
	
				for (u_int u=0; u<uMaxNumberOfBones; u++)
				{
					if (pxSubject->m_pbUsedBones[u])
					{
						if (!(pxElement->m_pbUsedBones[u]))
						{
							bShareBones = false;
							break;
						}
					}
				}	

				if (bShareBones)
				{
					uMostTrisSoFar = pxElement->GetNumberOfTriangles();
					pxBest = pxElement;
				}
			}
		}

		pxElement = pxElement->m_pxNextElement;
	}

	return pxBest;
}

void Tools_MultiElementSkin_Processor::DeleteElement(Tools_MultiElementSkin_Element* pxElement)
{
	if (pxElement->m_pxNextElement)
	{
		pxElement->m_pxNextElement->m_pxPrevElement = pxElement->m_pxPrevElement;
	}

	if (pxElement->m_pxPrevElement)
	{
		pxElement->m_pxPrevElement->m_pxNextElement = pxElement->m_pxNextElement;
	}

	if (s_pxFirstElement == pxElement)
	{
		s_pxFirstElement = pxElement->m_pxNextElement;
	}

	delete pxElement;
}

void Tools_MultiElementSkin_Processor::AddNewElement(const u_int uFirstPolyInElement)
{
	// KJL - 30/10/2003 11:01:02 - count the number of triangles
	u_int uNumberOfTris = 0;
	
	{
		u_int u = uFirstPolyInElement;
	
		do
		{
			uNumberOfTris += s_pxMesh->PolygonDataPtr[u].NumberOfVertices()-2;
			u = s_puElementArray[u];
		}
		while (u!=0);
	}

	
		
	s_pxFirstElement = new Tools_MultiElementSkin_Element(uNumberOfTris, s_pxFirstElement);
		
	u_int uPolyIndex = uFirstPolyInElement;

	do
	{
		u_int uNumberOfVertices = s_pxMesh->PolygonDataPtr[uPolyIndex].NumberOfVertices();
		u_int auNewVertexID[4];

		for (u_int v=0; v<uNumberOfVertices; v++)
		{
			u_int uVertexIndex = s_pxMesh->PolygonDataPtr[uPolyIndex].VertexID[v];
			
			Tools_MultiElementSkin_Vertex xVertex;

			xVertex.m_xPosition = s_pxMesh->VertexDataPtr[uVertexIndex].Position;

			const Asura_Mesh_TextureInfo* pxTexInfo = s_pxMesh->GetTextureInfoForExport(uPolyIndex);
			xVertex.m_fU		= pxTexInfo->U[0][v];
			xVertex.m_fV		= pxTexInfo->V[0][v];

			bool bFinished = false;
			for (u_int b=0; b<4; b++)
			{
				if (s_pxSkin->VertexData[uVertexIndex].Weight[b]>0 && !bFinished)
				{
					xVertex.m_afWeight[b] = s_pxSkin->VertexData[uVertexIndex].Weight[b]; 
					xVertex.m_acIndex[b]  =	s_pxSkin->VertexData[uVertexIndex].BoneID[b];
				}
				else
				{
					bFinished = true;
					xVertex.m_afWeight[b] = 0;
					xVertex.m_acIndex[b]  = 0;
				}
			}

	//		xVertex.m_xNormal = s_pxFaceNormal[uPolyIndex];
			Asura_Vector_3 axNormals[64];
			u_int uNumberOfNormals = 1;
			axNormals[0] = s_pxFaceNormal[uPolyIndex];

			// KJL - 10/11/2003 09:56:30 - scan adj polys and adj polys of coincident verts. This isn't that readable,
			// and needs to be tidied/optimised into the classes themselves.

			if(s_pxMesh->SmoothingGroupDataPtr[uPolyIndex]&0x7fffffff)
			{
				
				{
					Tools_MultiElementSkin_IndexListNode* pxNode = s_pxVertexAdjacencies[uVertexIndex].m_pxPolyList;

					while(pxNode)
					{
						u_int uThisPolyIndex = pxNode->m_uIndex;

						if (uPolyIndex != uThisPolyIndex)
						{
							if ( ((s_pxMesh->SmoothingGroupDataPtr[uPolyIndex])&(s_pxMesh->SmoothingGroupDataPtr[uThisPolyIndex]))&0x7fffffff )
							{
								bool bValidNewNormal = true;
								Asura_Vector_3& xNewNormal = s_pxFaceNormal[uThisPolyIndex];

								for (u_int n=0; n<uNumberOfNormals; n++)
								{
									if (axNormals[n].ApproximatelyEquals(xNewNormal))
									{
										bValidNewNormal = false;
										break;
									}
								}

								if (bValidNewNormal)
								{
									Asura_Assert( uNumberOfNormals<64, "Too many normals" );
									axNormals[uNumberOfNormals++] = xNewNormal;
								}
							}
						}

						pxNode = pxNode->m_pxNext;
					};
				}
				{
					Tools_MultiElementSkin_IndexListNode* pxVertexNode = s_pxVertexAdjacencies[uVertexIndex].m_pxVertexList;

					while(pxVertexNode)
					{
						u_int uThisVertexIndex = pxVertexNode->m_uIndex;

						{
							Tools_MultiElementSkin_IndexListNode* pxPolyNode = s_pxVertexAdjacencies[uThisVertexIndex].m_pxPolyList;

							while(pxPolyNode)
							{
								u_int uThisPolyIndex = pxPolyNode->m_uIndex;

								if (uPolyIndex != uThisPolyIndex)
								{
									if ( ((s_pxMesh->SmoothingGroupDataPtr[uPolyIndex])&(s_pxMesh->SmoothingGroupDataPtr[uThisPolyIndex]))&0x7fffffff )
									{
										bool bValidNewNormal = true;
										Asura_Vector_3& xNewNormal = s_pxFaceNormal[uThisPolyIndex];

										for (u_int n=0; n<uNumberOfNormals; n++)
										{
											if (axNormals[n].ApproximatelyEquals(xNewNormal))
											{
												bValidNewNormal = false;
												break;
											}
										}

										if (bValidNewNormal)
										{
											Asura_Assert( uNumberOfNormals<64, "Too many normals" );
											axNormals[uNumberOfNormals++] = xNewNormal;
										}
									}
								}

								pxPolyNode = pxPolyNode->m_pxNext;
							};
						}

						pxVertexNode = pxVertexNode->m_pxNext;
					};
				}
			}

			xVertex.m_xNormal.Zero();
		
			for (u_int n=0; n<uNumberOfNormals; n++)
			{
				xVertex.m_xNormal += axNormals[n];
			}							

			xVertex.m_xNormal.Normalise();

			auNewVertexID[v] = s_pxFirstElement->AddVertex(xVertex);
		}

		s_pxFirstElement->AddTriangle( auNewVertexID[0], auNewVertexID[1], auNewVertexID[2] );

		if (uNumberOfVertices == 4)
		{
			s_pxFirstElement->AddTriangle( auNewVertexID[2], auNewVertexID[3], auNewVertexID[0] );
		}

		uPolyIndex = s_puElementArray[uPolyIndex];
	}
	while (uPolyIndex!=0);

}




// KJL - 04/11/2003 12:39:16 - Tools_MultiElementSkin_Element private methods

Tools_MultiElementSkin_Element::Tools_MultiElementSkin_Element(u_int uNumberOfTriangles, Tools_MultiElementSkin_Element* pxNextElement)
{
	m_pxNextElement = pxNextElement;
	m_pxPrevElement = 0;

	if (pxNextElement)
	{
		pxNextElement->m_pxPrevElement = this;
	}

	m_pxVertices = new Tools_MultiElementSkin_Vertex[uNumberOfTriangles*3];
	m_puTriangleIndices = new u_int[uNumberOfTriangles*3]; // 3 verts per tri
	
	m_pbUsedBones = 0;
	m_puBoneConversionArray = 0;

	m_uNumberOfVertices = 0;
	m_uNumberOfTriangles = 0;
//	m_uNumberOfIndices = 0;
}

Tools_MultiElementSkin_Element::~Tools_MultiElementSkin_Element()
{
	delete [] m_pxVertices;
	delete [] m_puTriangleIndices;
	
	delete [] m_pbUsedBones;
	delete [] m_puBoneConversionArray;


	//delete m_pxNextElement;
}

u_int Tools_MultiElementSkin_Element::AddVertex(const Tools_MultiElementSkin_Vertex& xVertex)
{
	// KJL - 03/11/2003 11:05:15 - check to see if vertex already exists, and if so use that index
	// KJL - 03/11/2003 11:06:20 - inefficent simple loop for first pass of algorithm
	for (u_int u=0; u<m_uNumberOfVertices; u++)
	{
		if (m_pxVertices[u] == xVertex) return u;
	}
	
	// otherwise add a new vertex and return and then increment the vertex index counter
	m_pxVertices[m_uNumberOfVertices] = xVertex;
	return m_uNumberOfVertices++;
}

void Tools_MultiElementSkin_Element::AddTriangle(const u_int u0, const u_int u1, const u_int u2)
{
	u_int uIndex = 3 * m_uNumberOfTriangles;
	
	m_puTriangleIndices[uIndex+0] = u0;
	m_puTriangleIndices[uIndex+1] = u1;
	m_puTriangleIndices[uIndex+2] = u2;

	m_uNumberOfTriangles++;
}

void Tools_MultiElementSkin_Element::MergeInElement(const Tools_MultiElementSkin_Element* pxElement)
{
	Tools_MultiElementSkin_Vertex* pxVertices = new Tools_MultiElementSkin_Vertex[(m_uNumberOfVertices+pxElement->m_uNumberOfVertices)];
	u_int* puTriangleIndices = new u_int[(m_uNumberOfTriangles+pxElement->m_uNumberOfTriangles)*3]; // 3 verts per tri

	memcpy(pxVertices,m_pxVertices,sizeof(Tools_MultiElementSkin_Vertex)*m_uNumberOfVertices);
	memcpy(puTriangleIndices,m_puTriangleIndices,sizeof(u_int)*3*m_uNumberOfTriangles);

	delete [] m_pxVertices;
	delete [] m_puTriangleIndices;

	m_pxVertices = pxVertices;
	m_puTriangleIndices = puTriangleIndices;

	for (u_int u=0; u<pxElement->GetNumberOfTriangles(); u++)
	{
		AddTriangle
		(
			AddVertex( pxElement->m_pxVertices[ pxElement->m_puTriangleIndices[ u*3 + 0 ] ] ),
			AddVertex( pxElement->m_pxVertices[ pxElement->m_puTriangleIndices[ u*3 + 1 ] ] ),
			AddVertex( pxElement->m_pxVertices[ pxElement->m_puTriangleIndices[ u*3 + 2 ] ] )
		);
	}
}

void Tools_MultiElementSkin_Element::CalculateBoneUsage(const u_int uMaxNumberOfBones)
{
	m_pbUsedBones = new bool[uMaxNumberOfBones];

	for (u_int u=0; u<uMaxNumberOfBones; u++)
	{
		m_pbUsedBones[u] = false;
	}

	for (u_int v=0; v<m_uNumberOfVertices; v++)	
	{
		for (u_int b=0; b<4; b++)
		{
			if (m_pxVertices[v].m_afWeight[b]==0.0f) break;

			m_pbUsedBones[m_pxVertices[v].m_acIndex[b]] = true;
		}
	}

	m_uNumberOfBonesUsed = 0;

	for (u_int u=0; u<uMaxNumberOfBones; u++)
	{
		if (m_pbUsedBones[u]) m_uNumberOfBonesUsed++;
	}
}

void Tools_MultiElementSkin_Element::ConvertBoneIndices(const u_int uMaxNumberOfBones)
{
	m_puBoneConversionArray = new u_int[m_uNumberOfBonesUsed];
	u_int* puConversionToArray = new u_int[uMaxNumberOfBones];

	u_int uFreeIndex = 0;
	for (u_int u=0; u<uMaxNumberOfBones; u++)
	{
		if (m_pbUsedBones[u] == true)
		{
			puConversionToArray[u] = uFreeIndex;
			m_puBoneConversionArray[uFreeIndex] = u;

			uFreeIndex++;
		}
	}

	for (u_int v=0; v<m_uNumberOfVertices; v++)	
	{
		for (u_int b=0; b<4; b++)
		{
			if (m_pxVertices[v].m_afWeight[b]==0.0f) break;

			m_pxVertices[v].m_acIndex[b] = puConversionToArray[ m_pxVertices[v].m_acIndex[b] ];
		}
	}

	delete [] puConversionToArray;

}

// KJL - 04/11/2003 17:25:14 - Tools_MultiElementSkin_Stripified makes a stripified (PC/Xbox/GC style) version of the elements and wraps it all
// together in one vertex/index array pairing.


Tools_MultiElementSkin_Stripified::Tools_MultiElementSkin_Stripified(const u_int uNumberOfStrips, const Tools_MultiElementSkin_Element* pxFirstElement)
{
	m_uNumberOfElementStrips = uNumberOfStrips;
	m_pxElementStrip = new Tools_MultiElementSkin_ElementStrip[uNumberOfStrips];


	m_uTotalNumberOfIndices = 0;
	m_uTotalNumberOfVertices = 0;

	u_short**	ppusStripIndices = new u_short*[uNumberOfStrips];
	u_short**	ppusPermutationTable = new u_short*[uNumberOfStrips];
		
	{
		const Tools_MultiElementSkin_Element* pxElement = pxFirstElement;

		for (u_int u=0; u<uNumberOfStrips; u++)
		{
			Asura_Assert( pxElement!=0, "invalid element" );

			DWORD uNumberOfStrippedIndices;

			u_int uNumberOfInputIndices = pxElement->GetNumberOfIndices();
			u_int uNumberOfTris = pxElement->GetNumberOfTriangles();
			
			u_short* pusInputIndices = new u_short[uNumberOfInputIndices];
			
			for (u_int v=0; v<uNumberOfInputIndices; v++)
			{
				pusInputIndices[v] = (u_short) (pxElement->GetIndices()[v]);
			}

			Stripify
			(
				uNumberOfTris,
				pusInputIndices,
				&(uNumberOfStrippedIndices),
				&(ppusStripIndices[u])
			);

			delete [] pusInputIndices;

			ComputeVertexPermutation
			(
				uNumberOfStrippedIndices,
				ppusStripIndices[u],
				pxElement->GetNumberOfVertices(),
				&(ppusPermutationTable[u])
			);

			m_pxElementStrip[u].m_iMaterialID = 0;
			m_pxElementStrip[u].m_uFirstIndexInStrip = m_uTotalNumberOfIndices;
			m_pxElementStrip[u].m_uFirstVertexInStrip = m_uTotalNumberOfVertices;

			m_pxElementStrip[u].m_uNumberOfIndicesInStrip = uNumberOfStrippedIndices;
			m_pxElementStrip[u].m_uNumberOfVerticesInStrip = pxElement->GetNumberOfVertices();

			m_uTotalNumberOfVertices += pxElement->GetNumberOfVertices();
			m_uTotalNumberOfIndices  += uNumberOfStrippedIndices;

			pxElement = pxElement->GetNextElement();
		}
	}

	m_puIndices = new u_int[m_uTotalNumberOfIndices];
	m_pxVertices = new Tools_MultiElementSkin_Vertex[m_uTotalNumberOfVertices];

	{
		const Tools_MultiElementSkin_Element* pxElement = pxFirstElement;

		for (u_int u=0; u<m_uNumberOfElementStrips; u++)
		{
			for (u_int v=0; v<m_pxElementStrip[u].m_uNumberOfVerticesInStrip; v++)
			{
				u_int uVertexID = ppusPermutationTable[u][v];

				m_pxVertices[m_pxElementStrip[u].m_uFirstVertexInStrip + v] = pxElement->GetVertices()[uVertexID];
			}
			for (u_int v=0; v<m_pxElementStrip[u].m_uNumberOfIndicesInStrip; v++)
			{
				m_puIndices[m_pxElementStrip[u].m_uFirstIndexInStrip + v] = m_pxElementStrip[u].m_uFirstVertexInStrip + ppusStripIndices[u][v];
			}

			pxElement = pxElement->GetNextElement();
		}
	}

	for (u_int u=0; u<uNumberOfStrips; u++)
	{
		delete ppusPermutationTable[u];
		delete ppusStripIndices[u];
	}

	delete [] ppusStripIndices;
	delete [] ppusPermutationTable;

}
		
Tools_MultiElementSkin_Stripified::~Tools_MultiElementSkin_Stripified(void)
{
	delete [] m_pxElementStrip;
	delete [] m_puIndices;
	delete [] m_pxVertices;
}


#if 0
#include "Mesh/Asura_Mesh_System.h"
void Tools_MultiElementSkin_Processor::MakeNewMeshesAndSkins(void)
{
	const Tools_MultiElementSkin_Element* pxElement = s_pxFirstElement;
	
	for (u_int u=0; u<s_uNumberOfElements; u++, pxElement = pxElement->m_pxNextElement)
	{
		char szElementName[256];

		sprintf(szElementName, "%s_Element%d", s_pxSkin->Name.GetString(), u);


		u_int uNumberOfVertices  = pxElement->GetNumberOfVertices();
		u_int uNumberOfTriangles = pxElement->GetNumberOfTriangles();


			// KJL - 10/11/2003 17:09:40 - create a new mesh
		Asura_Mesh* pxMesh = Asura_Mesh_System::GetMesh(szElementName, true);

		// KJL - 10/11/2003 17:21:43 - vertices
		pxMesh->VertexDataPtr = new Asura_Mesh_Vertex[uNumberOfVertices];
		
		for (u_int v=0; v<uNumberOfVertices; v++)
		{
			pxMesh->VertexDataPtr[v].Position = pxElement->GetVertices()[v].m_xPosition;
		}

		// KJL - 10/11/2003 17:21:48 - polygons (tri indices, normals, UVs)
		pxMesh->PolygonDataPtr = new Asura_Mesh_Polygon[uNumberOfTriangles];
		pxMesh->NormalsDataPtr = new Asura_Mesh_Normals[uNumberOfTriangles];
		pxMesh->TextureDataPtr = new Asura_Mesh_TextureInfo[uNumberOfTriangles];

		for (u_int v=0; v<uNumberOfTriangles; v++)
		{
			u_short usIndex0 = (u_short) pxElement->GetIndices()[v*3+0];
			u_short usIndex1 = (u_short) pxElement->GetIndices()[v*3+1];
			u_short usIndex2 = (u_short) pxElement->GetIndices()[v*3+2];

			pxMesh->PolygonDataPtr[v].VertexID[0] = usIndex0;
			pxMesh->PolygonDataPtr[v].VertexID[1] = usIndex1;
			pxMesh->PolygonDataPtr[v].VertexID[2] = usIndex2;
			pxMesh->PolygonDataPtr[v].VertexID[3] = 0xffff;

			pxMesh->NormalsDataPtr[v].VertexNormal = new Asura_Vector_3[4];
			// do we need to set pxMesh->NormalsDataPtr[v].FaceNormal ?
			pxMesh->NormalsDataPtr[v].VertexNormal[0] = pxElement->GetVertices()[usIndex0].m_xNormal;
			pxMesh->NormalsDataPtr[v].VertexNormal[1] = pxElement->GetVertices()[usIndex1].m_xNormal;
			pxMesh->NormalsDataPtr[v].VertexNormal[2] = pxElement->GetVertices()[usIndex2].m_xNormal;

			pxMesh->TextureDataPtr[v].AllocateUVs();
			pxMesh->TextureDataPtr[v].U[0] = pxElement->GetVertices()[usIndex0].m_fU;
			pxMesh->TextureDataPtr[v].U[1] = pxElement->GetVertices()[usIndex1].m_fU;
			pxMesh->TextureDataPtr[v].U[2] = pxElement->GetVertices()[usIndex2].m_fU;
			pxMesh->TextureDataPtr[v].V[0] = pxElement->GetVertices()[usIndex0].m_fV;
			pxMesh->TextureDataPtr[v].V[1] = pxElement->GetVertices()[usIndex1].m_fV;
			pxMesh->TextureDataPtr[v].V[2] = pxElement->GetVertices()[usIndex2].m_fV;
			pxMesh->TextureDataPtr[v].m_iMaterialID = s_pxMesh->TextureDataPtr[0].m_iMaterialID;
	
		}

		pxMesh->VertexDataValid = true;
		pxMesh->m_bFaceNormalsValid = true;
		pxMesh->m_bVertexNormalsValid = true;
		pxMesh->TextureDataValid = true;

		// KJL - 10/11/2003 17:09:51 - create a new skin
		Asura_Hierarchy_Skin* pxSkin;
		Asura_Animation_System::NewSkin(szElementName, pxSkin);

		pxSkin->


	}
	
}
#endif
