//
// Asura_Mesh.cpp
//
// Created by Richard Rice - 03/11/2003
// 
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//


#include "Core/Asura.h"
#include "Mesh/Asura_Mesh.h"

#ifdef ASURA_TOOLS

#include "Core/Memory/Asura_Mem.h"

#include "Mesh/Asura_Mesh_CollMeshGenData.h"
#include "MeshProcessing/Tools_MeshAdjacency.h"


Asura_Mesh::~Asura_Mesh()
{
	if (VertexDataPtr) delete [] VertexDataPtr;
	if (PolygonDataPtr) delete [] PolygonDataPtr;
	if (NormalsDataPtr) delete [] NormalsDataPtr;
	if (TextureDataPtr) delete [] TextureDataPtr;
	if (m_pxProcessedTextureInfo) delete [] m_pxProcessedTextureInfo;
	if (SmoothingGroupDataPtr) delete [] SmoothingGroupDataPtr;
	if (PrelightingDataPtr) delete [] PrelightingDataPtr;
	if (PolygonFlagDataPtr) delete [] PolygonFlagDataPtr;
	if (m_pxCollisionMesh) delete [] m_pxCollisionMesh;
	if (m_pxMergedEntityCollisionMesh) delete [] m_pxMergedEntityCollisionMesh;
	if (m_pxCollMeshGenData) delete m_pxCollMeshGenData;
	if (m_pxPolygonGuids) delete [] m_pxPolygonGuids;
	if (m_pxCollPolygonGuids) delete [] m_pxCollPolygonGuids;
	if (m_pxMergedEntities) delete [] m_pxMergedEntities;
	if (m_pxPolygonLights) delete [] m_pxPolygonLights;
	if (m_puPolygonLightIDs) delete [] m_puPolygonLightIDs;
	if ( m_pxPaintedColourDataPtr ) delete [] m_pxPaintedColourDataPtr;
	if ( m_piPolygonIndexDataPtr ) delete [] m_piPolygonIndexDataPtr;
	if (m_pxProjectPolygonData) delete [] m_pxProjectPolygonData;
	if (m_pxVertexFlags) delete [] m_pxVertexFlags;
	if (m_pxSelectionSets) delete [] m_pxSelectionSets;
}


void Asura_Mesh::CopyMiscDataFrom( const Asura_Mesh* const pxFrom )
{
	m_xExternalData = pxFrom->m_xExternalData;
	m_xLayer = pxFrom->m_xLayer;
}

static const Asura_Vector_3 g_xDefaultScale(1.0f, 1.0f, 1.0f);

bool Asura_Mesh::LOSTest(const Asura_Vector_3& xLOSOrigin, const Asura_Vector_3& xLOSDirection, const Asura_Vector_3& xPosition, const Asura_Matrix_3x3& xOrientation, float& fNearest, int& iPolygonIndex, const Asura_Vector_3* pxScale/* = NULL*/) const
{
	if(!BoundingBoxDataValid) { CalculateBoundingBox(); }

	const Asura_Vector_3& xScale = pxScale ? *pxScale : g_xDefaultScale;
	const float fMaxScale = Asura_Maths::Max(xScale.x, Asura_Maths::Max(xScale.y, xScale.z));

	//check distance
	if((xLOSOrigin - xPosition).Magnitude() > (BoundingRadius * fMaxScale) + fNearest)
	{
		return false;
	}

	//sphere intersection test
	Asura_Vector_3 xCentre = BoundingBox.CalculateCentre();
	xCentre.x *= xScale.x;
	xCentre.y *= xScale.y;
	xCentre.z *= xScale.z;

	xCentre = xPosition + (xCentre * xOrientation);
	if(!Asura_Maths::RayIntersectsSphere(xLOSOrigin, xLOSDirection, xCentre, BoundingRadius * fMaxScale))
	{
		return false;
	}

	//check for intersection with each poly
		
	bool bIsInLOS = false;
	
	for (int i=0; i<NumberOfPolygons; ++i)
	{
		const int iIterations = PolygonDataPtr[i].IsQuad() ? 2 : 1;

		for (int j=0; j<iIterations; ++j)
		{
			bool bFacingToward;
			Asura_Vector_3 xV0 = VertexDataPtr[PolygonDataPtr[i].VertexID[(j==0) ? 0 : 2]].Position.Vector_Multiply(xScale);
			Asura_Vector_3 xV1 = VertexDataPtr[PolygonDataPtr[i].VertexID[(j==0) ? 1 : 3]].Position.Vector_Multiply(xScale);
			Asura_Vector_3 xV2 = VertexDataPtr[PolygonDataPtr[i].VertexID[(j==0) ? 2 : 0]].Position.Vector_Multiply(xScale);
			xV0 = (xV0 * xOrientation) + xPosition;
			xV1 = (xV1 * xOrientation) + xPosition;
			xV2 = (xV2 * xOrientation) + xPosition;

			const float fDistance = Asura_Maths::RayIntersectsTri(xLOSOrigin, xLOSDirection, xV0, xV1, xV2, bFacingToward);

			if (bFacingToward && (fDistance >= 0.0f) && (fDistance < fNearest))
			{
				fNearest = fDistance;
				iPolygonIndex = i;
				bIsInLOS = true;
			}
		}
	}

	return bIsInLOS;
}


void Asura_Mesh::CreateNormals()
{
	CreateFaceNormals();
	CreateVertexNormals();
}

void Asura_Mesh::CreateFaceNormals()
{
	if(NormalsDataPtr) delete [] NormalsDataPtr;
	NormalsDataPtr = new Asura_Mesh_Normals[NumberOfPolygons];
	m_bSmoothedAcrossModules = false;
	m_bVertexNormalsValid = false;

	for(int p=0; p<NumberOfPolygons; p++)
	{	
		Asura_Vector_3 v0 = VertexDataPtr[PolygonDataPtr[p].VertexID[0]].Position;
		Asura_Vector_3 v1 = VertexDataPtr[PolygonDataPtr[p].VertexID[1]].Position;
		Asura_Vector_3 v2 = VertexDataPtr[PolygonDataPtr[p].VertexID[2]].Position;

		Asura_Vector_3 n = (v1-v0)^(v2-v1);
        n.Normalise();

        // Prevent zero-length normals being generated from zero-area tris.
        if( n.MagnitudeSquared() < 0.0001f )
        {
            n.x = 1.0f;
            n.y = 0.0f;
            n.z = 0.0f;
        }

		NormalsDataPtr[p].FaceNormal = n;

	}

	m_bFaceNormalsValid = true;
}

void Asura_Mesh::CreateVertexNormals()
{
	if(!m_bFaceNormalsValid) CreateFaceNormals();
	m_bSmoothedAcrossModules = false;

	if(NumberOfPolygons<100)
	{
		//For a small number of polygons , just using a brute force method of working out which polygons
		//are used by each vertex is fastest. RAR 03/11/2003
		for(int p=0; p<NumberOfPolygons; p++)
		{
			int v=0;
			const Asura_Vector_3 xPolyNormal = NormalsDataPtr[p].FaceNormal;


			do
			{
				Asura_Vector_3 normals[16];
				int n = 0;

				int vID = PolygonDataPtr[p].VertexID[v];
			
				if(SmoothingGroupDataPtr[p]&0x7fffffff)
				{
					for(int i=0; i<NumberOfPolygons; i++)
					{
						if ((SmoothingGroupDataPtr[i]&SmoothingGroupDataPtr[p])&0x7fffffff)
						{
							int iVert = 0;
							
							//is the vertex used in this polygon?
							for(;iVert<4;iVert++)
							{
								if(vID == PolygonDataPtr[i].VertexID[iVert])
								{
									break;
								}
							}
							if(iVert == 4) continue;
							
							bool alreadyGotOne = false;

							Asura_Vector_3 xNormalToAdd = GetVertexNormalContribution(i,iVert);
	
							//Don't attempt to smooth with polygons that are facing too much away from each other ,
							//regardless of what the smoothing groups say.
							//RAR 24/10/2008
							if(xNormalToAdd * xPolyNormal < -0.5f)
							{
								continue;	
							}


							for (int j=0; j<n; j++)
							{
								if (normals[j] == xNormalToAdd)
								{
									alreadyGotOne = true; 
									break;
								}
							}

							if(!alreadyGotOne)
							{
								normals[n++] = xNormalToAdd;
								
								// KJL - 16-Mar-01 16:52:30 - defensive coding limit
								// replace with assert, perhaps
								if (n>15) n=15;
							}
						}

					}
				}
				else
				{
					normals[n++] = GetVertexNormalContribution(p,v);
				}
				
				// KJL - 16-Mar-01 16:53:49 - okay, we have all the normals of the faces
				// sharing this vertex and the same smoothing group
				
		        

				NormalsDataPtr[p].VertexNormal[v].Zero();
				
				for (int j=0; j<n; j++)
				{
					NormalsDataPtr[p].VertexNormal[v] += normals[j];
				}
				
				NormalsDataPtr[p].VertexNormal[v].Normalise();

				v++;
			}
			while(v<4 && PolygonDataPtr[p].VertexID[v]!=0xffff);
		}
	}
	else
	{
		//For large numbers of polygons , determine all the adjacencies first. RAR 03/11/2003
		Tools_MeshAdjacency xMeshAdj;
		xMeshAdj.Initialise(this);

		for(int i=0;i<NumberOfPolygons;i++)
		{
			Asura_Mesh_Polygon* pxPoly = &PolygonDataPtr[i];
			int iNumVerts = pxPoly->NumberOfVertices();

			const Asura_Vector_3 xPolyNormal = NormalsDataPtr[i].FaceNormal;
									   

			int iSmoothGroup = GetSmoothingGroup(i); 
			for(int j=0;j<iNumVerts;j++)
			{
				Asura_Vector_3& xNormal = NormalsDataPtr[i].VertexNormal[j];
				xNormal.Zero();

				
				if(iSmoothGroup)
				{
					Asura_Vector_3 xNormalsFound[16];
					int iNumNormalsFound = 0;

					//get a list of polygons using this vertex
					int iVertexID = pxPoly->VertexID[j];
					const Tools_Polygon_Adjacency* pxPolyAdj = xMeshAdj.GetVertexAdjacencies(iVertexID);

					for(const Tools_Adjacent_Polygon* pxAdj = pxPolyAdj->GetFirstAdjacency(); pxAdj; pxAdj = pxAdj->GetNext())
					{
						int iPolygonIndex2 = pxAdj->GetPolygonIndex();
						int iSmoothGroup2 = GetSmoothingGroup(iPolygonIndex2);
						
						//add on the face normal for this polygon , if any smoothing groups are shared
						if(iSmoothGroup & iSmoothGroup2)
						{
							Asura_Vector_3 xNormalToAdd = GetVertexNormalContribution(iPolygonIndex2,VertexDataPtr[pxPoly->VertexID[j]].Position);
							
							//Don't attempt to smooth with polygons that are facing too much away from each other ,
							//regardless of what the smoothing groups say.
							//RAR 24/10/2008
							if(xNormalToAdd * xPolyNormal < -0.5f)
							{
								continue;	
							}
							
							//make sure this normal hasn't already been added
							bool bAlreadyFound = false;
							for(int l=0;l<iNumNormalsFound;l++)
							{
								if(xNormalsFound[l] == xNormalToAdd)
								{
									bAlreadyFound = true;
									break;
								}

							}
							if(!bAlreadyFound)
							{
								xNormalsFound[iNumNormalsFound++] = xNormalToAdd;
								xNormal += xNormalToAdd;

								if(iNumNormalsFound>=16) iNumNormalsFound = 15;
							}
						}
				
					}
				}
				//normalise the vertex normal
				float fLength = xNormal.Magnitude();
				if(fLength)
				{
					xNormal /= fLength;
				}
				else
				{
					xNormal = GetVertexNormalContribution(i,j);
				}
			}
		}
	}

	m_bVertexNormalsValid = true;
	
}

void Asura_Mesh::ConvertPolygonFlags()
{
	if(!PolygonFlagDataValid) return;

	Asura_Mesh_PolygonFlags* pxFlags = PolygonFlagDataPtr;
	for(int i=0;i<NumberOfPolygons;i++,pxFlags++)
	{
		int iFlags = 0;
		if(pxFlags->Flags & OldAsuraPolygonFlag_FullBright) iFlags |= AsuraPolygonFlag_FullBright;
		if(pxFlags->Flags & OldAsuraPolygonFlag_Floor) iFlags |= AsuraPolygonFlag_Floor;
		if(pxFlags->Flags & OldAsuraPolygonFlag_DontExport) iFlags |= AsuraPolygonFlag_DontExport;
		if(pxFlags->Flags & OldAsuraPolygonFlag_DontBlockLight) iFlags |= AsuraPolygonFlag_DontBlockLight;
		if(pxFlags->Flags & OldAsuraPolygonFlag_Step) iFlags |= AsuraPolygonFlag_Step;
		if(pxFlags->Flags & OldAsuraPolygonFlag_NoCollisions) iFlags |= AsuraPolygonFlag_NoCollisions;
		if(pxFlags->Flags & OldAsuraPolygonFlag_IgnoreLOS) iFlags |= AsuraPolygonFlag_IgnoreLOS;
		if(pxFlags->Flags & OldAsuraPolygonFlag_IgnoreNormalForLighting) iFlags |= AsuraPolygonFlag_IgnoreNormalForLighting;
		if(pxFlags->Flags & OldAsuraPolygonFlag_IgnoreWhenWaypointing) iFlags |= AsuraPolygonFlag_IgnoreWhenWaypointing;
		if(pxFlags->Flags & OldAsuraPolygonFlag_DontRender) iFlags |= AsuraPolygonFlag_DontRender;
		if(pxFlags->Flags & OldAsuraPolygonFlag_DoubleSided) iFlags |= AsuraPolygonFlag_DoubleSided;
		if(pxFlags->Flags & OldAsuraPolygonFlag_IgnoreAmbientLight) iFlags |= AsuraPolygonFlag_IgnoreAmbientLight;

		pxFlags->Flags = iFlags;
	}
}

#ifndef TOOLS_IF_POLYGON_HAS_THESE_FLAGS_THEN_DONT_ADD_IGNORE_LOS_EVEN_IF_DONT_RENDER_IS_SET
#define TOOLS_IF_POLYGON_HAS_THESE_FLAGS_THEN_DONT_ADD_IGNORE_LOS_EVEN_IF_DONT_RENDER_IS_SET 0
#endif


int Asura_Mesh::ConvertSinglePolygonFlag_AddIgnoreLOS(int iFlags)
{
	if((iFlags & AsuraPolygonFlag_DontRender) &&
	!(iFlags & TOOLS_IF_POLYGON_HAS_THESE_FLAGS_THEN_DONT_ADD_IGNORE_LOS_EVEN_IF_DONT_RENDER_IS_SET))
	{
		return (iFlags | AsuraPolygonFlag_IgnoreLOS);
	}
	return iFlags;
}

void Asura_Mesh::ConvertPolygonFlags_AddIgnoreLOS()
{
	if(!PolygonFlagDataValid) return;

	Asura_Mesh_PolygonFlags* pxFlags = PolygonFlagDataPtr;
	for(int i=0;i<NumberOfPolygons;i++,pxFlags++)
	{
		pxFlags->Flags = ConvertSinglePolygonFlag_AddIgnoreLOS(pxFlags->Flags);
	}
}

int Asura_Mesh::ConvertSinglePolygonFlag_AddWalkableSurface(int iFlags)
{
	if(iFlags & (AsuraPolygonFlag_Step|AsuraPolygonFlag_Floor))
	{
		return (iFlags | AsuraPolygonFlag_Physics_WalkableSurface);
	}
	return iFlags;
}

void Asura_Mesh::ConvertPolygonFlags_AddWalkableSurface()
{
	if(!PolygonFlagDataValid) return;

	Asura_Mesh_PolygonFlags* pxFlags = PolygonFlagDataPtr;
	for(int i=0;i<NumberOfPolygons;i++,pxFlags++)
	{
		pxFlags->Flags = ConvertSinglePolygonFlag_AddWalkableSurface(pxFlags->Flags);
	}
}


bool Asura_Mesh::IsMeshWellFormed( void) const
{
	#define Asura_Assertf_Return( _A, _B) Asura_Assertf( _A, _B); if( !(_A)) { bMeshValid = false;}
	
	bool bMeshValid = true;
	
	const u_int uVertexCountMax = 0x10000;
	
	Asura_Assertf( this->NumberOfVertices <= uVertexCountMax, ("Too many vertices (%d) in Mesh to be accessed. Only %d can be accessed.", this->NumberOfVertices, uVertexCountMax));
	
	for( uint32 uiIndexPolygon = 0; uiIndexPolygon < uint32( this->NumberOfPolygons); uiIndexPolygon += 1)
	{
		const Asura_Mesh_Polygon* const pxPolygon = &this->PolygonDataPtr[ uiIndexPolygon];
		
		Asura_Assertf_Return( pxPolygon->VertexID[0] != pxPolygon->VertexID[1], ("Mesh '%s' Polygon %d Has a Degenerate Edge", this->Name, uiIndexPolygon));
		Asura_Assertf_Return( pxPolygon->VertexID[0] != pxPolygon->VertexID[2], ("Mesh '%s' Polygon %d Has a Degenerate Edge", this->Name, uiIndexPolygon));
		Asura_Assertf_Return( (false == pxPolygon->IsQuad() || pxPolygon->VertexID[0] != pxPolygon->VertexID[3]), ("Mesh '%s' Polygon %d Has a Degenerate Edge", this->Name, uiIndexPolygon));
		Asura_Assertf_Return( pxPolygon->VertexID[1] != pxPolygon->VertexID[2], ("Mesh '%s' Polygon %d Has a Degenerate Edge", this->Name, uiIndexPolygon));
		Asura_Assertf_Return( (false == pxPolygon->IsQuad() || pxPolygon->VertexID[1] != pxPolygon->VertexID[3]), ("Mesh '%s' Polygon %d Has a Degenerate Edge", this->Name, uiIndexPolygon));
		Asura_Assertf_Return( (false == pxPolygon->IsQuad() || pxPolygon->VertexID[2] != pxPolygon->VertexID[3]), ("Mesh '%s' Polygon %d Has a Degenerate Edge", this->Name, uiIndexPolygon));
	}
	
	return( bMeshValid);
	
	#undef Asura_Assertf_Return
}


void Asura_Mesh::AllocateVertexFlags()
{
	if(m_pxVertexFlags) return;
	
	m_pxVertexFlags = new Asura_Mesh_VertexFlags[NumberOfVertices];
	Asura_Mem::Set(m_pxVertexFlags,0,sizeof(Asura_Mesh_VertexFlags) * NumberOfVertices);
}

void Asura_Mesh::AllocateProjectPolygonData()
{
	if(m_pxProjectPolygonData) return;

	m_pxProjectPolygonData = new Asura_Mesh_ProjectPolygonData[NumberOfPolygons];
	Asura_Mem::Set(m_pxProjectPolygonData,0,sizeof(Asura_Mesh_ProjectPolygonData) * NumberOfPolygons);
}

Asura_Vector_3 Asura_Mesh::GetVertexNormalContribution(int iPolygonIndex,const Asura_Vector_3& xVertPos)
{
	if(!m_bFaceNormalsValid) CreateFaceNormals();

	if(GetPolygonFlags(iPolygonIndex) & AsuraPolygonFlag_SplayNormals)
	{
		//get the centre of the polygon
		const Asura_Mesh_Polygon* pxPoly = &PolygonDataPtr[iPolygonIndex];
		Asura_Vector_3 xCentre = VertexDataPtr[pxPoly->VertexID[0]].Position;
		int iNumVerts = pxPoly->NumberOfVertices();
		for(int i=1;i<iNumVerts;i++)
		{
			xCentre += VertexDataPtr[pxPoly->VertexID[i]].Position;
		}
		xCentre /= (float)iNumVerts;

		//get the normal towards the vertex
		Asura_Vector_3 xToVert = xVertPos;
		xToVert -= xCentre;

		if(xToVert != Asura_Maths::ZeroVector3)
		{
			xToVert.Normalise();

			//take the average of this and the face normal
			xToVert += NormalsDataPtr[iPolygonIndex].FaceNormal;
			if(xToVert != Asura_Maths::ZeroVector3)
			{
				xToVert.Normalise();
				return xToVert;
			}
		}
	}
	//just take the polygon normal
	return NormalsDataPtr[iPolygonIndex].FaceNormal;
}

void Asura_Mesh::AddPlatformMesh(Asura_Mesh* pxMesh)
{
	//Expecting this to be called for the root mesh
	//(Not that this is a problem for the function , but it means loading has gone wrong otherwise)
	Asura_Assert(!IsPlatformSpecificMesh(),"Should only be adding platform meshes to a non platform specific mesh");
	Asura_Assert(!pxMesh->m_pxNextPlatformMesh && !pxMesh->m_pxPrevPlatformMesh,"Platform mesh being added shouldn't be linked to other meshes yet");


	//Add the mesh to the end of the list , to prevent the ordering from reversing each time they are saved and loaded
	Asura_Mesh* pxLastMesh = this;
	while(pxLastMesh->m_pxNextPlatformMesh) pxLastMesh = pxLastMesh->m_pxNextPlatformMesh;

	pxLastMesh->m_pxNextPlatformMesh = pxMesh;
	pxMesh->m_pxPrevPlatformMesh = pxLastMesh;

}

void Asura_Mesh::GenerateProcessedTextureInfo()
{
	Asura_Assert(TextureDataPtr,"Original texture data doesn't exist");

	delete [] m_pxProcessedTextureInfo;
	m_pxProcessedTextureInfo = new Asura_Mesh_TextureInfo[NumberOfPolygons];

	for(int i=0;i<NumberOfPolygons;i++)
	{
		m_pxProcessedTextureInfo[i] = TextureDataPtr[i];
	}
}




#endif //ASURA_TOOLS
