/**
 * Creates a FStaticMeshRenderData from a D3DXMesh
 * @param DestMesh Destination mesh to extract to
 * @param NumUVs Number of UVs
 * @param Elements Elements array
 * @return Boolean representing success or failure
 */
bool ConvertD3DXMeshToRawMesh(
	TRefCountPtr<ID3DXMesh>& D3DMesh, 									  
	FRawMesh& DestMesh, 				  
	int32 NumUVs
	)
{
	// Extract simplified data to LOD
	FUtilVertex* D3DVertices;
	uint16*		 D3DIndices;
	::DWORD *		 D3DAttributes;
	D3DMesh->LockVertexBuffer(D3DLOCK_READONLY,(LPVOID*)&D3DVertices);
	D3DMesh->LockIndexBuffer(D3DLOCK_READONLY,(LPVOID*)&D3DIndices);
	D3DMesh->LockAttributeBuffer(D3DLOCK_READONLY,&D3DAttributes);

	int32 NumFaces = D3DMesh->GetNumFaces();
	int32 NumWedges = NumFaces * 3;

	DestMesh.FaceMaterialIndices.Init(NumFaces);
	DestMesh.FaceSmoothingMasks.Init(NumFaces);
	DestMesh.VertexPositions.Init(NumWedges);
	DestMesh.WedgeIndices.Init(NumWedges);
	DestMesh.WedgeColors.Init(NumWedges);
	DestMesh.WedgeTangentX.Init(NumWedges);
	DestMesh.WedgeTangentY.Init(NumWedges);
	DestMesh.WedgeTangentZ.Init(NumWedges);
	for (int32 UVIndex = 0; UVIndex < NumUVs; ++UVIndex)
	{
		DestMesh.WedgeTexCoords[UVIndex].Init(NumWedges);
	}

	for(int32 I=0;I<NumFaces;I++)
	{
		// Copy smoothing mask and index from any vertex into this triangle
		DestMesh.FaceSmoothingMasks[I] = D3DVertices[D3DIndices[I*3+0]].SmoothingMask;
		DestMesh.FaceMaterialIndices[I] = D3DAttributes[I];

		for(int UVs=0;UVs<NumUVs;UVs++)
		{
			DestMesh.WedgeTexCoords[UVs][I*3+0] = D3DVertices[D3DIndices[I*3+0]].UVs[UVs];
			DestMesh.WedgeTexCoords[UVs][I*3+1] = D3DVertices[D3DIndices[I*3+1]].UVs[UVs];
			DestMesh.WedgeTexCoords[UVs][I*3+2] = D3DVertices[D3DIndices[I*3+2]].UVs[UVs];
		}

		for(int32 K=0;K<3;K++)
		{
			DestMesh.WedgeIndices[I*3+K] = I*3+K;
			DestMesh.VertexPositions[I*3+K] = D3DVertices[D3DIndices[I*3+K]].Position;
			DestMesh.WedgeColors[I*3+K]   = D3DVertices[D3DIndices[I*3+K]].Color;
			DestMesh.WedgeTangentX[I*3+K] = D3DVertices[D3DIndices[I*3+K]].TangentX;
			DestMesh.WedgeTangentY[I*3+K] = D3DVertices[D3DIndices[I*3+K]].TangentY;
			DestMesh.WedgeTangentZ[I*3+K] = D3DVertices[D3DIndices[I*3+K]].TangentZ;
		}
	}

	D3DMesh->UnlockIndexBuffer();
	D3DMesh->UnlockVertexBuffer();
	D3DMesh->UnlockAttributeBuffer();
	return true;
}
/** Merges a set of D3DXMeshes. */
static void MergeD3DXMeshes(
	IDirect3DDevice9* Device,
	TRefCountPtr<ID3DXMesh>& OutMesh,TArray<int32>& OutBaseFaceIndex,const TArray<ID3DXMesh*>& Meshes)
{
	TArray<D3DVERTEXELEMENT9> VertexElements;
	GetD3D9MeshVertexDeclarations(VertexElements);
		
	// Count the number of faces and vertices in the input meshes.
	int32 NumFaces = 0;
	int32 NumVertices = 0;
	for(int32 MeshIndex = 0;MeshIndex < Meshes.Num();MeshIndex++)
	{
		NumFaces += Meshes[MeshIndex]->GetNumFaces();
		NumVertices += Meshes[MeshIndex]->GetNumVertices();
	}

	// Create mesh for source data
	VERIFYD3D9RESULT(D3DXCreateMesh(
		NumFaces,
		NumVertices,
		D3DXMESH_SYSTEMMEM,
		(D3DVERTEXELEMENT9*)VertexElements.GetData(),
		Device,
		OutMesh.GetInitReference()
		) );

	// Fill D3DXMesh
	FUtilVertex* ResultVertices;
	uint16*		 ResultIndices;
	::DWORD *		 ResultAttributes;
	OutMesh->LockVertexBuffer(0,(LPVOID*)&ResultVertices);
	OutMesh->LockIndexBuffer(0,(LPVOID*)&ResultIndices);
	OutMesh->LockAttributeBuffer(0, &ResultAttributes);

	int32 BaseVertexIndex = 0;
	int32 BaseFaceIndex = 0;
	for(int32 MeshIndex = 0;MeshIndex < Meshes.Num();MeshIndex++)
	{
		ID3DXMesh* Mesh = Meshes[MeshIndex];
				
		FUtilVertex* Vertices;
		uint16*		 Indices;
		::DWORD *		 Attributes;
		Mesh->LockVertexBuffer(0,(LPVOID*)&Vertices);
		Mesh->LockIndexBuffer(0,(LPVOID*)&Indices);
		Mesh->LockAttributeBuffer(0, &Attributes);

		for(uint32 FaceIndex = 0;FaceIndex < Mesh->GetNumFaces();FaceIndex++)
		{
			for(uint32 VertexIndex = 0;VertexIndex < 3;VertexIndex++)
			{
				*ResultIndices++ = BaseVertexIndex + *Indices++;
			}
		}
		OutBaseFaceIndex.Add(BaseFaceIndex);
		BaseFaceIndex += Mesh->GetNumFaces();

		FMemory::Memcpy(ResultVertices,Vertices,Mesh->GetNumVertices() * sizeof(FUtilVertex));
		ResultVertices += Mesh->GetNumVertices();
		BaseVertexIndex += Mesh->GetNumVertices();

		FMemory::Memcpy(ResultAttributes,Attributes,Mesh->GetNumFaces() * sizeof(uint32));
		ResultAttributes += Mesh->GetNumFaces();

		Mesh->UnlockIndexBuffer();
		Mesh->UnlockVertexBuffer();
		Mesh->UnlockAttributeBuffer();
	}

	OutMesh->UnlockIndexBuffer();
	OutMesh->UnlockVertexBuffer();
	OutMesh->UnlockAttributeBuffer();
}
/**
	* Creates a D3DXMESH from a FStaticMeshRenderData
	* @param Triangles The triangles to create the mesh from.
	* @param bRemoveDegenerateTriangles True if degenerate triangles should be removed
	* @param OutD3DMesh Mesh to create
	* @return Boolean representing success or failure
*/
bool ConvertRawMeshToD3DXMesh(
	IDirect3DDevice9* Device,
	FRawMesh& RawMesh,
	const bool bRemoveDegenerateTriangles,	
	TRefCountPtr<ID3DXMesh>& OutD3DMesh
	)
{
	TArray<D3DVERTEXELEMENT9> VertexElements;
	GetD3D9MeshVertexDeclarations(VertexElements);

	TArray<FUtilVertex> Vertices;
	TArray<uint16> Indices;
	TArray<uint32> Attributes;
	int32 NumWedges = RawMesh.WedgeIndices.Num();
	int32 NumTriangles = NumWedges / 3;
	int32 NumUVs = 0;
	for (; NumUVs < 8; ++NumUVs)
	{
		if (RawMesh.WedgeTexCoords[NumUVs].Num() != RawMesh.WedgeIndices.Num())
		{
			break;
		}
	}

	bool bHaveColors = RawMesh.WedgeColors.Num() == NumWedges;
	bool bHaveNormals = RawMesh.WedgeTangentZ.Num() == NumWedges;
	bool bHaveTangents = bHaveNormals && RawMesh.WedgeTangentX.Num() == NumWedges
		&& RawMesh.WedgeTangentY.Num() == NumWedges;

	for(int32 TriangleIndex = 0;TriangleIndex < NumTriangles;TriangleIndex++)
	{
		bool bTriangleIsDegenerate = false;

		if( bRemoveDegenerateTriangles )
		{
			// Detect if the triangle is degenerate.
			for(int32 EdgeIndex = 0;EdgeIndex < 3;EdgeIndex++)
			{
				const int32 Wedge0 = TriangleIndex * 3 + EdgeIndex;
				const int32 Wedge1 = TriangleIndex * 3 + ((EdgeIndex + 1) % 3);
				if((RawMesh.GetWedgePosition(Wedge0) - RawMesh.GetWedgePosition(Wedge1)).IsNearlyZero(THRESH_POINTS_ARE_SAME * 4.0f))
				{
					bTriangleIsDegenerate = true;
					break;
				}
			}
		}

		if(!bTriangleIsDegenerate)
		{
			Attributes.Add(RawMesh.FaceMaterialIndices[TriangleIndex]);
			for(int32 J=0;J<3;J++)
			{
				FUtilVertex* Vertex = new(Vertices) FUtilVertex;
				FMemory::Memzero(Vertex,sizeof(FUtilVertex));
				int32 WedgeIndex = TriangleIndex * 3 + J;
				Vertex->Position = RawMesh.GetWedgePosition(WedgeIndex);
				if (bHaveColors)
				{
					Vertex->Color = RawMesh.WedgeColors[WedgeIndex];
				}
				else
				{
					Vertex->Color = FColor::White;
				}
				//store the smoothing mask per vertex since there is only one per-face attribute that is already being used (materialIndex)
				Vertex->SmoothingMask = RawMesh.FaceSmoothingMasks[TriangleIndex];
				if (bHaveTangents)
				{
					Vertex->TangentX = RawMesh.WedgeTangentX[WedgeIndex];
					Vertex->TangentY = RawMesh.WedgeTangentY[WedgeIndex];
				}
				if (bHaveNormals)
				{
					Vertex->TangentZ = RawMesh.WedgeTangentZ[WedgeIndex];
				}
				for(int32 UVIndex = 0; UVIndex < NumUVs; UVIndex++)
				{
					Vertex->UVs[UVIndex] = RawMesh.WedgeTexCoords[UVIndex][WedgeIndex];
				}
	 
				Indices.Add(Vertices.Num() - 1);
			}
		}
	}

	// This code uses the raw triangles. Needs welding, etc.
	const int32 NumFaces = Indices.Num() / 3;
	const int32 NumVertices = NumFaces*3;

	check(Attributes.Num() == NumFaces);
	check(NumFaces * 3 == Indices.Num());

	// Create mesh for source data
	if (FAILED(D3DXCreateMesh(NumFaces,NumVertices,D3DXMESH_SYSTEMMEM,(D3DVERTEXELEMENT9 *)VertexElements.GetData(),Device,OutD3DMesh.GetInitReference()) ) )
	{
		UE_LOG(LogD3D9MeshUtils, Warning, TEXT("D3DXCreateMesh() Failed!"));
		return false;
	}

	// Fill D3DMesh mesh
	FUtilVertex* D3DVertices;
	uint16*		 D3DIndices;
	::DWORD *		 D3DAttributes;
	OutD3DMesh->LockVertexBuffer(0,(LPVOID*)&D3DVertices);
	OutD3DMesh->LockIndexBuffer(0,(LPVOID*)&D3DIndices);
	OutD3DMesh->LockAttributeBuffer(0, &D3DAttributes);

	FMemory::Memcpy(D3DVertices,Vertices.GetTypedData(),Vertices.Num() * sizeof(FUtilVertex));
	FMemory::Memcpy(D3DIndices,Indices.GetTypedData(),Indices.Num() * sizeof(uint16));
	FMemory::Memcpy(D3DAttributes,Attributes.GetTypedData(),Attributes.Num() * sizeof(uint32));

	OutD3DMesh->UnlockIndexBuffer();
	OutD3DMesh->UnlockVertexBuffer();
	OutD3DMesh->UnlockAttributeBuffer();

	return true;
}