[ProceduralMeshComponent] Strange behaviour

Hello !

I recently decided to create procedural destructibles. However, when I make a procedural mesh, and import from a StaticMesh, I get a strange result. It seems to have no shadows, or to don’t react to light at all.

The left one is procedural, the right one is a classic static mesh actor, both with the same StaticMesh

My code to import a StaticMesh in a ProceduralMeshComponent is:



void AProceduralDestructibleActor::SetStaticMeshToProcedural(UStaticMesh* StaticMesh, UProceduralMeshComponent* ProcMeshComp)
{
	if (StaticMesh && StaticMesh->RenderData)
	{
		const FStaticMeshLODResources& LOD = StaticMesh->RenderData->LODResources[0];
		uint32 SectionsCount = LOD.Sections.Num();

		uint32 SectionIndex = 0;
		for (; SectionIndex < SectionsCount; SectionIndex++)
		{
			TArray<uint32> UnsignedTrianglesIndices;
			LOD.IndexBuffer.GetCopy(UnsignedTrianglesIndices);

			TArray<int32> SignedTrianglesIndices;
			SignedTrianglesIndices.AddUninitialized(UnsignedTrianglesIndices.Num());
			for (int32 TriangleIndex = 0; TriangleIndex < UnsignedTrianglesIndices.Num() / 3; TriangleIndex++)
			{
				if ((uint32)TriangleIndex * 3 >= LOD.Sections[SectionIndex].FirstIndex && (uint32)TriangleIndex * 3 < LOD.Sections[SectionIndex].FirstIndex + LOD.Sections[SectionIndex].NumTriangles * 3)
				{
					SignedTrianglesIndices[TriangleIndex * 3] = UnsignedTrianglesIndices[TriangleIndex * 3];
					SignedTrianglesIndices[TriangleIndex * 3 + 1] = UnsignedTrianglesIndices[TriangleIndex * 3 + 1];
					SignedTrianglesIndices[TriangleIndex * 3 + 2] = UnsignedTrianglesIndices[TriangleIndex * 3 + 2];
				}
			}

			TArray<FVector> Vertices;
			TArray<FVector2D> UVs;
			TArray<FVector> Normals;
			TArray<FProcMeshTangent> Tangents;
			Vertices.AddUninitialized(LOD.PositionVertexBuffer.GetNumVertices());
			UVs.AddUninitialized(LOD.PositionVertexBuffer.GetNumVertices());
			Normals.AddUninitialized(LOD.PositionVertexBuffer.GetNumVertices());
			Tangents.AddUninitialized(LOD.PositionVertexBuffer.GetNumVertices());
			for (uint32 VertexIndex = 0; VertexIndex < LOD.PositionVertexBuffer.GetNumVertices(); VertexIndex++)
			{
				Vertices[VertexIndex] = LOD.PositionVertexBuffer.VertexPosition(VertexIndex);
				UVs[VertexIndex] = LOD.VertexBuffer.GetVertexUV(VertexIndex, 0);
				Normals[VertexIndex] = FVector::ForwardVector;
				Tangents[VertexIndex] = FProcMeshTangent(LOD.VertexBuffer.VertexTangentX(VertexIndex).operator FVector(), false);
			}

			TArray<FColor> VertexColors;
			// LOD.ColorVertexBuffer.GetVertexColors(VertexColors); Doesn't compile, but isn't important

			ProcMeshComp->CreateMeshSection(SectionIndex, Vertices, SignedTrianglesIndices, Normals, UVs, VertexColors, Tangents, true);
			ProcMeshComp->SetMaterial(LOD.Sections[SectionIndex].MaterialIndex, StaticMesh->GetMaterial(LOD.Sections[SectionIndex].MaterialIndex));
		}
	}
}


I’m trying to help you but first, how you are compiling this without error on the follow line?
Tangents[VertexIndex] = FProcMeshTangent(LOD.VertexBuffer.VertexTangentX(VertexIndex).operator FVector(), false);

Meanwhile i used a cast to FVector to make it work
Tangents[VertexIndex] = FProcMeshTangent((FVector)LOD.VertexBuffer.VertexTangentX(VertexIndex), false);

Back to your problem, looks like your Normals are wrong, in fact you are using FVector::ForwardVector that means all your normals are (1, 0, 0) and this is not correct (normals had to be perpendicular to the surface), look at the World Normal buffer visualization, on the left the procedural generated mesh, on the right the original mesh.

[spoiler]

[/spoiler]

To solve the problem you have to calculate the correct normals, i tried with

LOD.PositionVertexBuffer.VertexPosition(VertexIndex).GetSafeNormal();

the result is a little bit better but still not correct, need a better way to calculate it.

[spoiler]

[/spoiler]

if ill find a solution i will tell you, meanwhile i hope someone more expert than me jump in to help us :slight_smile:

Seems i solved!

Looks like the normals are stored as VertexTangentZ in the VertexBuffer, that means you have to use it like this

Normals[VertexIndex] = LOD.VertexBuffer.VertexTangentZ(VertexIndex);

as you can see now normals are good :slight_smile:

[spoiler]

[/spoiler]

My final working code here

[spoiler]



void AProceduralDestructibleActor::SetStaticMeshToProcedural(UStaticMesh* StaticMesh, UProceduralMeshComponent* ProcMeshComp)
{
	if (!(StaticMesh && StaticMesh->RenderData))
		return;

	const FStaticMeshLODResources& LOD = StaticMesh->RenderData->LODResources[0];
	uint32 SectionsCount = LOD.Sections.Num();
	uint32 SectionsIndex = 0;

	for (; SectionsIndex < SectionsCount; SectionsIndex++)
	{
		TArray<uint32> UnsignedTrianglesIndices;
		LOD.IndexBuffer.GetCopy(UnsignedTrianglesIndices);

		TArray<int32> SignedTrianglesIndices;
		SignedTrianglesIndices.AddUninitialized(UnsignedTrianglesIndices.Num());

		for (int32 TrianglesIndex = 0; TrianglesIndex < UnsignedTrianglesIndices.Num() / 3; TrianglesIndex++)
		{
			if ((uint32)TrianglesIndex * 3 >= LOD.Sections[SectionsIndex].FirstIndex && (uint32)TrianglesIndex * 3 < LOD.Sections[SectionsIndex].FirstIndex + LOD.Sections[SectionsIndex].NumTriangles * 3)
			{
				SignedTrianglesIndices[TrianglesIndex * 3] = UnsignedTrianglesIndices[TrianglesIndex * 3];
				SignedTrianglesIndices[TrianglesIndex * 3 + 1] = UnsignedTrianglesIndices[TrianglesIndex * 3 + 1];
				SignedTrianglesIndices[TrianglesIndex * 3 + 2] = UnsignedTrianglesIndices[TrianglesIndex * 3 + 2];
			}
		}

		TArray<FVector> Vertices;
		TArray<FVector2D> UVs;
		TArray<FVector> Normals;
		TArray<FProcMeshTangent> Tangents;

		Vertices.AddUninitialized(LOD.PositionVertexBuffer.GetNumVertices());
		UVs.AddUninitialized(LOD.PositionVertexBuffer.GetNumVertices());
		Normals.AddUninitialized(LOD.PositionVertexBuffer.GetNumVertices());
		Tangents.AddUninitialized(LOD.PositionVertexBuffer.GetNumVertices());

		for (uint32 VertexIndex = 0; VertexIndex < LOD.PositionVertexBuffer.GetNumVertices(); VertexIndex++)
		{
			Vertices[VertexIndex] = LOD.PositionVertexBuffer.VertexPosition(VertexIndex);
			UVs[VertexIndex] = LOD.VertexBuffer.GetVertexUV(VertexIndex, 0);
			Normals[VertexIndex] = LOD.VertexBuffer.VertexTangentZ(VertexIndex);								
			Tangents[VertexIndex] = FProcMeshTangent((FVector)LOD.VertexBuffer.VertexTangentX(VertexIndex), false);			
		}

		TArray<FLinearColor> VertexColors;
		//LOD.ColorVertexBuffer.GetVertexColors(VertexColors);

		ProcMeshComp->CreateMeshSection_LinearColor(SectionsIndex, Vertices, SignedTrianglesIndices, Normals, UVs, VertexColors, Tangents, true);
		ProcMeshComp->SetMaterial(LOD.Sections[SectionsIndex].MaterialIndex, StaticMesh->GetMaterial(LOD.Sections[SectionsIndex].MaterialIndex));
		

	}

}


[/spoiler]