// Tools_SkinLOD.cpp
//
// Created by Richard Rice - 30/08/2002
// 
// Copyright (C) 2002 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

#if !defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER

// This file's header
#include "Hierarchy/Tools_SkinLOD.h"

// Asura headers
#include "Animation/Asura_Hierarchy.h"
#include "Animation/Asura_Animation.h"
#include "Debugging/Asura_Assert.h"
#include "Mesh/Asura_Mesh.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Rendering/Asura_PlatformRenderCharacter.h"

Tools_SkinLOD::Tools_SkinLOD(Asura_Hierarchy_Skin* pxSkin)
{
	Asura_Assert(pxSkin,"");

	m_pxSkin = pxSkin;
	m_pxMesh = Asura_Mesh_System::GetMesh(pxSkin->Name);
	Asura_Assert(m_pxMesh,"");//Nb , non-skinned models will currently cause this to die horribly


	int iTotalIndices = m_pxMesh->NumberOfTriangles*3;

	int iNumberOfUniqueVertices = 0;
	int iIndex = 0;

	Asura_Direct3D_CharacterVertex* pxWorkspace = new Asura_Direct3D_CharacterVertex[iTotalIndices];

	unsigned short* psIndexData = new unsigned short[iTotalIndices];
	int* piVertexIndices = new int[iTotalIndices];
	int* piPolygonIndices = new int[m_pxMesh->NumberOfTriangles];

	if(!m_pxMesh->AreNormalsValid()) m_pxSkin->CalculateSkinNormalData();
	

	for (int p=0; p<m_pxMesh->NumberOfPolygons; p++)
	{	
		int v=0;
		int aiIndexNumber[4];
		
		do
		{
		
			int vID =  m_pxMesh->PolygonDataPtr[p].VertexID[v];
			Asura_Vector_3& pos = m_pxMesh->VertexDataPtr[vID].Position;
			Asura_Vector_3& n = m_pxMesh->NormalsDataPtr[p].VertexNormal[v];

			Asura_Direct3D_CharacterVertex xVertexData;

			xVertexData.m_xPosition = pos;
			xVertexData.m_xNormal = n;

			xVertexData.m_fU = m_pxMesh->TextureDataPtr[p].U[0][v];
			xVertexData.m_fV = m_pxMesh->TextureDataPtr[p].V[0][v];

			bool bLastBone = false;
			for (int b=0; b<4; b++)
			{
				if (m_pxSkin->VertexData[vID].Weight[b]>0 && !bLastBone)
				{
					xVertexData.m_afWeight[b] = m_pxSkin->VertexData[vID].Weight[b];
					xVertexData.m_afIndex[b] = (float)m_pxSkin->VertexData[vID].BoneID[b]*3.0f;
				}
				else
				{
					bLastBone = true;
					xVertexData.m_afWeight[b] = 0;
					xVertexData.m_afIndex[b] = 0;
				}
			}

			int i = iNumberOfUniqueVertices;
			bool bVertexNotUnique = false;
			while(i)
			{
				i--;
				if (xVertexData == pxWorkspace[i])
				{
					bVertexNotUnique = true;
					break;
				}
			}
	
			if (bVertexNotUnique)
			{
				aiIndexNumber[v] = i;
			}
			else
			{
				aiIndexNumber[v] = iNumberOfUniqueVertices;

				pxWorkspace[iNumberOfUniqueVertices] = xVertexData;
				piVertexIndices[iNumberOfUniqueVertices] = vID;
				iNumberOfUniqueVertices++;
			}
			v++;
		}
		while(v<4 && m_pxMesh->PolygonDataPtr[p].VertexID[v]!=0xffff);

		piPolygonIndices[iIndex/3] = p;
		psIndexData[iIndex++] = (unsigned short)(aiIndexNumber[0]);
		psIndexData[iIndex++] = (unsigned short)(aiIndexNumber[1]);
		psIndexData[iIndex++] = (unsigned short)(aiIndexNumber[2]);
	
		if (m_pxMesh->PolygonDataPtr[p].IsQuad())
		{
			piPolygonIndices[iIndex/3] = p;
			psIndexData[iIndex++] = (unsigned short)(aiIndexNumber[2]);
			psIndexData[iIndex++] = (unsigned short)(aiIndexNumber[3]);
			psIndexData[iIndex++] = (unsigned short)(aiIndexNumber[0]);
		}
	}

	m_iNumberOfTriangles = m_pxMesh->NumberOfTriangles;
	m_iNumberOfVertices = iNumberOfUniqueVertices;
	m_iMaterialID = m_pxMesh->TextureDataPtr[0].m_iMaterialID;


	m_piOriginalVertexIndices = new int[m_iNumberOfVertices];
	m_piOriginalPolygonIndices = new int[m_iNumberOfTriangles];

	m_xVipm.CurrentNumberOfTriangles = m_iNumberOfTriangles;
	m_xVipm.CurrentNumberOfVertices = m_iNumberOfVertices;
	m_xVipm.Create(pxWorkspace,psIndexData,m_iNumberOfVertices,m_iNumberOfTriangles*3,m_piOriginalVertexIndices,m_piOriginalPolygonIndices);

	//Update the vertex ordering array so that it refers to the original emsh vertices , instead of the 
	//vertices in pxWorkspace.
	for(int i=0;i<m_iNumberOfVertices;i++)
	{
		m_piOriginalVertexIndices[i] = piVertexIndices[m_piOriginalVertexIndices[i]];
	}
	//and for the polygons
	for(int i=0;i<m_iNumberOfTriangles;i++)
	{
		m_piOriginalPolygonIndices[i] = piPolygonIndices[m_piOriginalPolygonIndices[i]];
	}


	delete [] pxWorkspace;
	delete [] psIndexData;
	delete [] piVertexIndices;
	delete [] piPolygonIndices;

}

Tools_SkinLOD::~Tools_SkinLOD()
{
	delete [] m_piOriginalVertexIndices;
	delete [] m_piOriginalPolygonIndices;
}

bool Tools_SkinLOD::GenerateMesh(Asura_Mesh*& pxNewMesh,int*& piVertConv)
{
	#if 0
	Asura_String xName = "test name";
	Asura_Mesh* pxMesh = new Asura_Mesh(xName);
	#else
	char temp[200];
	sprintf(temp,"%s_%d",m_pxMesh->Name.GetString(),m_xVipm.CurrentNumberOfTriangles);
	
	Asura_String xName = temp;
	Asura_Mesh* pxMesh = Asura_Mesh_System::GetMesh(xName,true);
	if(pxMesh->VertexDataValid)
	{
		pxNewMesh = pxMesh;
		return false;
	}
	#endif

	//Determine the number of unique vertices (considering only position and bone weightings)
	piVertConv = new int[m_xVipm.CurrentNumberOfVertices];
	int iNumberOfVertices = 0;
	for(int i=0;i<m_xVipm.CurrentNumberOfVertices;i++)
	{
		bool bFoundMatch = false;
		Asura_Direct3D_CharacterVertex* pxVert1 = &m_xVipm.VertexList[i];
		for(int j=0;j<i;j++)
		{
			Asura_Direct3D_CharacterVertex* pxVert2 = &m_xVipm.VertexList[j];

			if(	pxVert1->m_xPosition == pxVert2->m_xPosition && 
				pxVert1->m_afIndex[0] == pxVert2->m_afIndex[0] && 	
				pxVert1->m_afIndex[1] == pxVert2->m_afIndex[1] && 	
				pxVert1->m_afIndex[2] == pxVert2->m_afIndex[2] && 	
				pxVert1->m_afIndex[3] == pxVert2->m_afIndex[3] && 	
				pxVert1->m_afWeight[0] == pxVert2->m_afWeight[0] && 	
				pxVert1->m_afWeight[1] == pxVert2->m_afWeight[1] && 	
				pxVert1->m_afWeight[2] == pxVert2->m_afWeight[2] && 	
				pxVert1->m_afWeight[3] == pxVert2->m_afWeight[3])
			{
				bFoundMatch = true;
				piVertConv[i] = piVertConv[j];
				break;
			}
		}
		if(!bFoundMatch)
		{
			piVertConv[i] = iNumberOfVertices++;
		}
	}

	//Vertices
	pxMesh->VertexDataValid = true;
	pxMesh->NumberOfVertices = iNumberOfVertices;
	pxMesh->VertexDataPtr = new Asura_Mesh_Vertex[iNumberOfVertices];

	for(int i=0;i<m_xVipm.CurrentNumberOfVertices;i++)
	{
		pxMesh->VertexDataPtr[piVertConv[i]].Position	= m_xVipm.VertexList[i].m_xPosition;
	}

	//Polygons
	pxMesh->PolygonDataValid = true;
	pxMesh->NumberOfPolygons = m_xVipm.CurrentNumberOfTriangles;
	pxMesh->NumberOfTriangles = pxMesh->NumberOfPolygons;
	pxMesh->PolygonDataPtr = new Asura_Mesh_Polygon[pxMesh->NumberOfPolygons];

	for(int i=0;i<pxMesh->NumberOfPolygons;i++)
	{
		pxMesh->PolygonDataPtr[i].VertexID[0] = (u_short)piVertConv[m_xVipm.IndexList[i*3]];
		pxMesh->PolygonDataPtr[i].VertexID[1] = (u_short)piVertConv[m_xVipm.IndexList[i*3+1]];
		pxMesh->PolygonDataPtr[i].VertexID[2] = (u_short)piVertConv[m_xVipm.IndexList[i*3+2]];
		pxMesh->PolygonDataPtr[i].VertexID[3] = 0xffff;
	}

	//Texturing
	pxMesh->TextureDataValid = false;
	pxMesh->TextureDataPtr = new Asura_Mesh_TextureInfo[pxMesh->NumberOfPolygons];

	for(int i=0;i<pxMesh->NumberOfPolygons;i++)
	{
		Asura_Mesh_TextureInfo* pxTex = &pxMesh->TextureDataPtr[i];
        
        
		pxTex->m_iMaterialID = m_iMaterialID;

		for(int iChan=0;iChan<ASURA_MATERIAL_MAXTEXTURES;iChan++)
		{
			for(int j=0;j<3;j++)
			{
				int iVert =	m_xVipm.IndexList[i*3+j];
				pxTex->U[iChan][j] = m_xVipm.VertexList[iVert].m_fU;
				pxTex->V[iChan][j] = m_xVipm.VertexList[iVert].m_fV;
			}
			pxTex->U[iChan][3] = 0;
			pxTex->V[iChan][3] = 0;
		}
	}

	pxMesh->TextureDataValid = true;

	//Smoothing
	if(m_pxMesh->SmoothingGroupDataValid)
	{
		pxMesh->SmoothingGroupDataValid = true;
		pxMesh->SmoothingGroupDataPtr = new int[pxMesh->NumberOfPolygons];

		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			pxMesh->SmoothingGroupDataPtr[i] = m_pxMesh->SmoothingGroupDataPtr[m_piOriginalPolygonIndices[i]];
		}
	}

	#if 0
	//Normals
	if(m_pxMesh->NormalsDataValid)
	{
		m_pxMesh->NormalsDataValid = true;
		m_pxMesh->NormalsDataPtr = new Asura_Mesh_Normals[pxMesh->NumberOfPolygons];

		for(int i=0;i<pxMesh->NumberOfPolygons;i++)
		{
			pxMesh->NormalsDataPtr[i].FaceNormal = m_pxMesh->NormalsDataPtr[m_piOriginalPolygonIndices[i]].FaceNormal;
			for(int j=0;j<3;j++)
			{
				int iVert =	pxMesh->PolygonDataPtr[i].VertexID[j];
			}
		}
	}
	#else
	pxMesh->CreateNormals();
	//check for 0-normals
	for(int i=0;i<pxMesh->NumberOfPolygons;i++)
	{
		if(pxMesh->NormalsDataPtr[i].FaceNormal.Magnitude()<.5f)
		{
			//just choose an arbitrary value
			pxMesh->NormalsDataPtr[i].FaceNormal.x = 1.0f;
			pxMesh->NormalsDataPtr[i].FaceNormal.y = 0.0f;
			pxMesh->NormalsDataPtr[i].FaceNormal.z = 0.0f;
		}
		for(int j=0;j<3;j++)
		{
			if(pxMesh->NormalsDataPtr[i].VertexNormal[j].Magnitude()<.5f)
			{
				//just use the face normal for the vertex normal in that case
				pxMesh->NormalsDataPtr[i].VertexNormal[j] = pxMesh->NormalsDataPtr[i].FaceNormal;

			}
		}
	

	}
	#endif


	pxNewMesh = pxMesh;
	return true;
}

int Tools_SkinLOD::SetNumberOfPolygons(int iDesiredPolygons)
{
	//If the model currently has too few triangles , move up until the model
	//has more triangles than needed
	while(iDesiredPolygons>m_xVipm.CurrentNumberOfTriangles)
	{
		if(!m_xVipm.UpOneLevel()) break;
	}

	//Drop down levels to find the highest version that is less than or equal to the desired level
	//(Ignore last collaspe to avoid the risk of going down to 0 polygons)
	while(iDesiredPolygons<m_xVipm.CurrentNumberOfTriangles && m_xVipm.CurrentNumberOfCollapses<m_xVipm.NumberOfCollapses-1)
	{
		if(!m_xVipm.DownOneLevel()) break;
	}

	return m_xVipm.CurrentNumberOfTriangles;
}

int Tools_SkinLOD::GenerateSkin(int iDesiredPolygons,float fDistance)
{
	SetNumberOfPolygons(iDesiredPolygons);

	Asura_Mesh* pxMesh;
	int* piVertConv;
	if (!GenerateMesh(pxMesh,piVertConv)) return pxMesh->NumberOfTriangles;


	Asura_Hierarchy_Skin* pxSkin = Asura_Hierarchy_System::Project_AllocateSkin();
	pxSkin->Name = pxMesh->Name;
	pxSkin->m_uNameHashID = Asura_GetHashID(pxMesh->Name.GetString());

	//Vertices
	pxSkin->NumberOfVertices = pxMesh->NumberOfVertices;
	pxSkin->VertexData = new Asura_Hierarchy_Skin_Vertex[pxSkin->NumberOfVertices];

	for(int i=0;i<m_xVipm.CurrentNumberOfVertices;i++)
	{
		pxSkin->VertexData[piVertConv[i]] = m_pxSkin->VertexData[m_piOriginalVertexIndices[i]];
	}
	delete [] piVertConv;
	piVertConv = 0;

	#if 0
	//Normals
	if(m_pxSkin->NormalData)
	{
		Asura_ResourceAllocateArray(Asura_ResourceAllocator::ANIMATION_RESOURCE, pxSkin->NormalData, pxSkin->NumberOfVertices);
		for(int i=0;i<pxSkin->NumberOfVertices;i++)
		{
			pxSkin->NormalData[i] = m_pxSkin->NormalData[m_piOriginalVertexIndices[i]];
		}
	}
	#endif

	pxSkin->CopyFromRootSkin(m_pxSkin);


	//insert the skin into the linked list
	Asura_Hierarchy_Skin* pxParentSkin = m_pxSkin;
	while(pxParentSkin->m_pxNextLowerSkin)
	{
		if(pxSkin->NumberOfVertices>pxParentSkin->m_pxNextLowerSkin->NumberOfVertices) break;
		pxParentSkin = pxParentSkin->m_pxNextLowerSkin;
	}

	pxSkin->m_pxNextHigherSkin =pxParentSkin;
	pxSkin->m_pxNextLowerSkin = pxParentSkin->m_pxNextLowerSkin;
	pxParentSkin->m_pxNextLowerSkin = pxSkin;
	if(pxSkin->m_pxNextLowerSkin)
	{
		pxSkin->m_pxNextLowerSkin->m_pxNextHigherSkin = pxSkin;		
	}
	
	pxSkin->m_fMinDistance = fDistance;

	return pxMesh->NumberOfTriangles;
}

#endif //!defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER

