Ejemplo n.º 1
0
/**
 * 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;
}
Ejemplo n.º 2
0
bool FD3D9MeshUtilities::GenerateUVs(
	struct FRawMesh& RawMesh,
	uint32 TexCoordIndex,
	float MinChartSpacingPercent,
	float BorderSpacingPercent,
	bool bUseMaxStretch,
	const TArray< int32 >* InFalseEdgeIndices,
	uint32& MaxCharts,
	float& MaxDesiredStretch,
	FText& OutError
	)
{
	OutError = FText();
	if(!IsValid())
	{
		OutError = LOCTEXT("GenerateUVs_FailedInvalid", "GenerateUVs failed, mesh was invalid.");
		return false;
	}

	int32 NumTexCoords = 0;
	for (int32 i = 0; i < MAX_MESH_TEXTURE_COORDS; ++i)
	{
		if (RawMesh.WedgeTexCoords[i].Num() != RawMesh.WedgeIndices.Num())
		{
			break;
		}
		NumTexCoords++;
	}

	if (TexCoordIndex > (uint32)NumTexCoords)
	{
		OutError = LOCTEXT("GenerateUVs_FailedUVs", "GenerateUVs failed, incorrect number of texcoords.");
		return false;
	}

	TRefCountPtr<ID3DXMesh> ChartMesh;
	TArray<uint32> AtlasAndChartAdjacency;
	TArray<int32> AtlasAndChartTriangleCharts;
	{
		const bool bUseFalseEdges = InFalseEdgeIndices != NULL;

		// When using false edges we don't remove degenerates as we want our incoming selected edge list to map
		// correctly to the D3DXMesh.
		const bool bRemoveDegenerateTriangles = !bUseFalseEdges;

		// Create a D3DXMesh for the triangles being charted.
		TRefCountPtr<ID3DXMesh> SourceMesh;
		if (!ConvertRawMeshToD3DXMesh(Device, RawMesh,bRemoveDegenerateTriangles,SourceMesh))
		{
			OutError = LOCTEXT("GenerateUVs_FailedConvert", "GenerateUVs failed, couldn't convert to a D3DXMesh.");
			return false;
		}

		//generate adjacency info for the mesh, which is needed later
		TArray<uint32> Adjacency;
		GenerateAdjacency(SourceMesh,Adjacency,FFragmentedAdjacencyFilter());

		// We don't clean the mesh as this can collapse vertices or delete degenerate triangles, and
		// we want our incoming selected edge list to map correctly to the D3DXMesh.
		if( !bUseFalseEdges )
		{
			//clean the mesh
			TRefCountPtr<ID3DXMesh> TempMesh;
			TArray<uint32> CleanedAdjacency;
			CleanedAdjacency.AddUninitialized(SourceMesh->GetNumFaces() * 3);
			if( FAILED(D3DXCleanMesh( D3DXCLEAN_SIMPLIFICATION, SourceMesh, (::DWORD *)Adjacency.GetTypedData(), TempMesh.GetInitReference(), 
				(::DWORD *)CleanedAdjacency.GetTypedData(), NULL ) ) )
			{
				OutError = LOCTEXT("GenerateUVs_FailedClean", "GenerateUVs failed, couldn't clean mesh.");
				return false;
			}
			SourceMesh = TempMesh;
			Adjacency = CleanedAdjacency;
		}


		// Setup the D3DX "false edge" array.  This is three DWORDS per face that define properties of the
		// face's edges.  Values of -1 indicates that the edge may be used as a UV seam in a the chart.  Any
		// other value indicates that the edge should never be a UV seam.  This essentially allows us to
		// provide a precise list of edges to be used as UV seams in the new charts.
		uint32* FalseEdgeArray = NULL;
		TArray<uint32> FalseEdges;
		if( bUseFalseEdges )
		{
			// -1 means "always use this edge as a chart UV seam" to D3DX
 			FalseEdges.AddUninitialized( SourceMesh->GetNumFaces() * 3 );
			for( int32 CurFalseEdgeIndex = 0; CurFalseEdgeIndex < (int32)SourceMesh->GetNumFaces() * 3; ++CurFalseEdgeIndex )
			{
				FalseEdges[ CurFalseEdgeIndex ] = -1;
			}

			// For each tagged edge
			for( int32 CurTaggedEdgeIndex = 0; CurTaggedEdgeIndex < InFalseEdgeIndices->Num(); ++CurTaggedEdgeIndex )
			{
				const int32 EdgeIndex = ( *InFalseEdgeIndices )[ CurTaggedEdgeIndex ];
					
				// Mark this as a false edge by setting it to a value other than negative one
				FalseEdges[ EdgeIndex ] = Adjacency[ CurTaggedEdgeIndex ];
			}

			FalseEdgeArray = (uint32*)FalseEdges.GetTypedData();
		}

			
		// Partition the mesh's triangles into charts.
		TRefCountPtr<ID3DXBuffer> PartitionResultAdjacencyBuffer;
		TRefCountPtr<ID3DXBuffer> FacePartitionBuffer;
		HRESULT Result = D3DXUVAtlasPartition(
			SourceMesh,
			bUseMaxStretch ? 0 : MaxCharts,				// Max charts (0 = use max stretch instead)
			MaxDesiredStretch,
			TexCoordIndex,
			(::DWORD *)Adjacency.GetTypedData(),
			(::DWORD *)FalseEdgeArray,	// False edges
			NULL,		// IMT data
			&GenerateUVsStatusCallback,
			0.01f,		// Callback frequency
			NULL,			// Callback user data
			D3DXUVATLAS_GEODESIC_QUALITY,
			ChartMesh.GetInitReference(),
			FacePartitionBuffer.GetInitReference(),
			NULL,
			PartitionResultAdjacencyBuffer.GetInitReference(),
			&MaxDesiredStretch,
			&MaxCharts
			);
		if (FAILED(Result))
		{
			UE_LOG(LogD3D9MeshUtils, Warning, 
				TEXT("D3DXUVAtlasPartition() returned %u with MaxDesiredStretch=%.2f, TexCoordIndex=%u."),
				Result,
				MaxDesiredStretch,
				TexCoordIndex
				);
			OutError = LOCTEXT("GenerateUVs_FailedPartition", "GenerateUVs failed, D3DXUVAtlasPartition failed.");
			return false;
		}

		// Extract the chart adjacency data from the D3DX buffer into an array.
		for(uint32 TriangleIndex = 0;TriangleIndex < ChartMesh->GetNumFaces();TriangleIndex++)
		{
			for(int32 EdgeIndex = 0;EdgeIndex < 3;EdgeIndex++)
			{
				AtlasAndChartAdjacency.Add(*((uint32*)PartitionResultAdjacencyBuffer->GetBufferPointer()+TriangleIndex*3+EdgeIndex));
			}
		}

		// Extract the triangle chart data from the D3DX buffer into an array.
		uint32* FacePartitionBufferPointer = (uint32*)FacePartitionBuffer->GetBufferPointer();
		for(uint32 TriangleIndex = 0;TriangleIndex < ChartMesh->GetNumFaces();TriangleIndex++)
		{
			AtlasAndChartTriangleCharts.Add(*FacePartitionBufferPointer++);
		}

		// Scale the partitioned UVs down.
		FUtilVertex* LockedVertices;
		ChartMesh->LockVertexBuffer(0,(LPVOID*)&LockedVertices);
		for(uint32 VertexIndex = 0;VertexIndex < ChartMesh->GetNumVertices();VertexIndex++)
		{
			LockedVertices[VertexIndex].UVs[TexCoordIndex] /= 2048.0f;
		}
		ChartMesh->UnlockVertexBuffer();
	}

	if(ChartMesh)
	{
		// Create a buffer to hold the triangle chart data.
		TRefCountPtr<ID3DXBuffer> MergedTriangleChartsBuffer;
		VERIFYD3D9RESULT(D3DXCreateBuffer(
			AtlasAndChartTriangleCharts.Num() * sizeof(int32),
			MergedTriangleChartsBuffer.GetInitReference()
			));
		uint32* MergedTriangleChartsBufferPointer = (uint32*)MergedTriangleChartsBuffer->GetBufferPointer();
		for(int32 TriangleIndex = 0;TriangleIndex < AtlasAndChartTriangleCharts.Num();TriangleIndex++)
		{
			*MergedTriangleChartsBufferPointer++ = AtlasAndChartTriangleCharts[TriangleIndex];
		}

		const uint32 FakeTexSize = 1024;
		const float GutterSize = ( float )FakeTexSize * MinChartSpacingPercent * 0.01f;

		// Pack the charts into a unified atlas.
		HRESULT Result = D3DXUVAtlasPack(
			ChartMesh,
			FakeTexSize,
			FakeTexSize,
			GutterSize,
			TexCoordIndex,
			(::DWORD *)AtlasAndChartAdjacency.GetTypedData(),
			&GenerateUVsStatusCallback,
			0.01f,		// Callback frequency
			NULL,
			0,
			MergedTriangleChartsBuffer
			);
		if (FAILED(Result))
		{
			UE_LOG(LogD3D9MeshUtils, Warning, 
				TEXT("D3DXUVAtlasPack() returned %u."),
				Result
				);
			OutError = LOCTEXT("GenerateUVs_FailedPack", "GenerateUVs failed, D3DXUVAtlasPack failed.");
			return false;
		}

		int32 NewNumTexCoords = FMath::Max<int32>(NumTexCoords, TexCoordIndex + 1);
		FRawMesh FinalMesh;

		if (!ConvertD3DXMeshToRawMesh(ChartMesh, FinalMesh, NewNumTexCoords))
		{
			OutError = LOCTEXT("GenerateUVs_FailedSimple", "GenerateUVs failed, couldn't convert the simplified D3DXMesh back to a UStaticMesh.");
			return false;
		}

		// Scale/offset the UVs appropriately to ensure there is empty space around the border
		{
			const float BorderSize = BorderSpacingPercent * 0.01f;
			const float ScaleAmount = 1.0f - BorderSize * 2.0f;

			for( int32 CurUVIndex = 0; CurUVIndex < MAX_MESH_TEXTURE_COORDS; ++CurUVIndex )
			{
				int32 NumWedges = FinalMesh.WedgeTexCoords[CurUVIndex].Num();
				for( int32 WedgeIndex = 0; WedgeIndex < NumWedges; ++WedgeIndex )
				{
					FVector2D& UV = FinalMesh.WedgeTexCoords[CurUVIndex][WedgeIndex];
					UV.X = BorderSize + UV.X * ScaleAmount;
					UV.Y = BorderSize + UV.Y * ScaleAmount;
				}
			}
		}
		RawMesh = FinalMesh;
	}
	return true; 
}
Ejemplo n.º 3
0
/** 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();
}
Ejemplo n.º 4
0
/**
	* 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;
}