Problems with Procedural Mesh and Materials

/// More Updates in the answers below!! ///


Currently i’m having a problem where my mesh isn’t getting any sort of material. I’ve tried alot, and this last bit of code is based from the recent plugin SiggiG posted in the forums. But even like so i can’t get it to work…
This is all supposed to work during runtime.

Right now it looks like this:

I’ve changed the material while it’s running so you can see that it isn’t on the respective mesh.

And the code for that is like this:

FbxActor.h

UCLASS()
class AFbxActor : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AFbxActor();

	bool GenerateActorFromFile(const FString& inFileName);

	//void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent);

	// Called when the game starts or when spawned
	//virtual void BeginPlay() override;

	// Called every frame
	virtual void Tick(float DeltaSeconds) override;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Materials)
	UMaterialInterface * LeMaterial;

	//UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Mesh)
	UStaticMeshComponent * StaticMeshObj;

	UProceduralMeshComponent * GenMesh;

};

FbxActor.cpp

AFbxActor::AFbxActor() :
	StaticMeshObj(NULL),
	GenMesh(NULL)
{
	
	RootComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("RootComponent"));

	//RootComponent = StaticMeshObj;

	GenMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh"));
	GenMesh->SetMobility(EComponentMobility::Movable);
	GenMesh->AttachTo(RootComponent);

	LeMaterial = UMaterial::GetDefaultMaterial(MD_Surface);
	
	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
}

    bool AFbxActor::GenerateActorFromFile(const FString& inFileName) {
    
    	UE_LOG(FbxActorLog, Log, TEXT("Generating actor from file %s"), *inFileName);
    
    	GenMesh->SetWorldLocation(this->GetActorLocation());
    	GenMesh->SetWorldRotation(this->GetActorRotation());
    	GenMesh->SetWorldScale3D(this->GetActorScale3D());
    
    	ImpFbx::FFbxImport currFbxImport = ImpFbx::FFbxImport::FFbxImport();
    
    	if (!currFbxImport.GetFbxScene(inFileName)) {
    
    		UE_LOG(FbxActorLog, Error, TEXT("Error creating scene from file..."));
    
    		return false;
    	}
    
    	currFbxImport.DoYourMagic(this->GenMesh, this->RootComponent);
    
    	UMaterialInstanceDynamic * InstanceMaterial = UMaterialInstanceDynamic::Create(LeMaterial, this);
    	
    	GenMesh->SetMaterial(1, InstanceMaterial);
    	
    	//this->Tick(1);
    
    	return true;
    }

I have placed the material interface as material, the “GenMesh->AttachTo(RootComponent)” after the set material, but it’s still the same result.

The set material is for index 1, because in the part where i create the mesh section is for index 1 as well.

GenMesh->CreateMeshSection(1, CtrlPointsArray, TrianglesArray, NormalsArray, UVsArray, ColorsArray, TangentsArray, true);

Am I doing something wrong? I have been trying to work around this problem for the whole week… I really don’t see where the problem is. Can someone help me please?

UPDATE:

I’ve discovered that the material is correctly being assigned to the mesh. The problem is that the material does not appear to be placed correctly on the mesh, as in, the edges of the object are not visible.
Here you can see the mesh is the same (the left one is the import with editor, and the one on the right is the runtime import):

and the same material applied:

You can also see the size differences in each picture, on the first one is selected the mesh imported in runtime and on the second is the fbx imported in the editor… don’t know if that might also be affecting something and is actually an error or not…

What might me causing this incorrect placement? Am I missing information in the mesh? Building the mesh wrong? Or maybe it’s in the material?

1 Like

Hello KittyT,
I’m having trouble getting even this far. If you don’t mind my asking, is there any way I could have a look at your DoYourMagic function? Have you uploaded it somewhere?
If not, could you please point me to some resources where I could get some help? I’m having trouble spawning a procedural mesh after parsing an fbx file. Despite populating the vertices and normals arrays, I get a bunch of missng triangles. There’s very little documentation online, so I’ll be grateful for any help you can offer.
Thanks!

I can’t see where you set UVsArray, but I expect your problem lies there. UV coordinates tell the renderer how to wrap the 2D material around your 3D object, so if you don’t provide them, your entire object will be a single color, unlike the texture you intended

For the understanding of Fbx Sdk, this post was very VERY helpful http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/how-to-work-with-fbx-sdk-r3582

It also gives you the source code. So check that out, cause the function DoYourMagic was just a call to a private function that starts the reading of the fbx file. About the triangles, what i opted to do was call the triangulate function from the sdk, that applies to a node attribute instead of the whole mesh. Then you just need to add to your Triangles array your current Control Point Index.

If you still have problems just say, although i don’t think i can help much. I still feel very stupid with this.

I forgot to update in the meantime, but yes i got to the same point where i think my problem lies in the UV’s, but for the past two weeks i’ve been checking and trying to get the UV’s from file in different ways, all to get to the same state.

In theory and according to my output, the UV’s i’m getting are within 0-1, so that gets an okay. The channel i get them from is 0, so that’s an okay as well. I’ve read somewhere that the triangulation through an automatic method might screw the information of the file, so i tried with an already triangulated mesh, so that i don’t have to call a function to do it, but still to no avail, as i get the same result.

I’m seriously wondering if i don’t have to send the whole information of UV for every material that i want to apply to the respective mesh…

Attach a debugger and look at FRawMesh::WedgeTexCoords right before you call RawMeshBulkData::SaveRawMesh. I have a hunch that you have a helper function populating WedgeTexCoords but are accidentally passing your mesh or UV coordinates by value instead of by reference, in which case WedgeTexCoords[0] will be empty when you call SaveRawMesh.

the answer is to big so i’m going to write in a comment

The problem here is i’m not using the FRawMesh but the Procedural Mesh, so i’m just passing the arrays required for the UProceduralMeshComponent::CreateMeshSection()

I’m trying to avoid the classes in the Editor and Developer folders, so that i don’t have to worry much with the copyright or EULA of UE.

Here is how it looks :
GenMesh->ClearAllMeshSections();

	GenMesh->CreateMeshSection(1, MeshData.Vertices, MeshData.Triangles, MeshData.Normals, MeshData.UVs, MeshData.VertexColor, MeshData.Tangents, true);	

	UMaterialInstanceDynamic * InstanceMaterial = UMaterialInstanceDynamic::Create(LeMaterial, this);
	
	GenMesh->SetMaterial(1, InstanceMaterial);

	GenMesh->UpdateMeshSection(1, MeshData.Vertices, MeshData.Normals, MeshData.UVs, MeshData.VertexColor, MeshData.Tangents);

	GenMesh->AttachTo(RootComponent);

with MeshData being a struct to save the information of the mesh arrays.

Here is my function to get the uvs from the fbx file:

TArray<FVector2D> FFbxImport::GetUVsFromFBX(FbxMesh * Mesh, int CtrlPointIndex, int UVVertexIndex) {

	FVector2D OutUVs;
	TArray<FVector2D> OutUVsArray;

	int UVsCount = Mesh->GetElementUVCount();

	//if (UVsCount > 1);

	FbxStringList UVSetList;
	Mesh->GetUVSetNames(UVSetList);


	//get lUVSetIndex-th uv set
	char* UVSetName = UVSetList.GetStringAt(0);

	FbxGeometryElementUV * UVElement = Mesh->GetElementUV(UVSetName);
	int UVIndex = 0;

	switch (UVElement->GetMappingMode()) {

	case FbxGeometryElement::eByControlPoint:

		switch (UVElement->GetReferenceMode()) {
		case FbxGeometryElement::eDirect:

			UVIndex = CtrlPointIndex;

			break;

		case FbxGeometryElement::eIndexToDirect:

			UVIndex = UVElement->GetIndexArray().GetAt(CtrlPointIndex);

			break;
		}

		break;

	case FbxGeometryElement::eByPolygonVertex:

		switch (UVElement->GetReferenceMode()) {
		case FbxGeometryElement::eDirect:
		case FbxGeometryElement::eIndexToDirect:

			UVIndex = UVVertexIndex;

			break;

		}
		break;
	}

	FbxVector2 fUV = UVElement->GetDirectArray().GetAt(UVVertexIndex);

	UE_LOG(FbxMeshLog, Log, TEXT("Uvs: %f  |  %f "), fUV.mData[0], fUV.mData[1]);

	OutUVs.Set(fUV.mData[0], fUV.mData[1]);

	OutUVsArray.Add(OutUVs);


	return OutUVsArray;
}

with UVVertexIndex being Mesh->GetTextureUVIndex(i, j) with i being the current polygon and j the current vertex of the current polygon.

Here is how it is being processed:

void FFbxImport::DoYourMagic(FProceduralMeshData * MeshData) {

	ProcessFbxGeometry(Scene->GetRootNode(), MeshData, 0);
}

.

void FFbxImport::ProcessFbxGeometry(FbxNode * Node, FProceduralMeshData * MeshData, int32 index) {

	FbxNodeAttribute * NodeAttri = Node->GetNodeAttribute();

	if (NodeAttri) {

		switch (NodeAttri->GetAttributeType()) {

		case FbxNodeAttribute::eMesh:

			NodeAttri = GeometryConverter->Triangulate(NodeAttri, false);
			FbxNode * temp = NodeAttri->GetNode();
			ProcessFbxMesh(temp, MeshData, index);
			//ProcessFbxMaterials(temp);

			break;
		}

	}

	for (int i = 0; i < Node->GetChildCount(); i++) {
		UE_LOG(FbxMeshLog, Warning, TEXT("New Node"));
		ProcessFbxGeometry(Node->GetChild(i), MeshData, i);
	}
}

.

void FFbxImport::ProcessFbxMesh(FbxNode * Node, FProceduralMeshData * MeshData, int32 index) {

	UE_LOG(FbxMeshLog, Warning, TEXT("Reading object data..."));

	FbxMesh * Mesh = Node->GetMesh();

	MeshData->Vertices = GetControlPointsFromFBX(Mesh);

	int PolyCount = Mesh->GetPolygonCount();

	UE_LOG(FbxMeshLog, Warning, TEXT("PolyCount : %d"), PolyCount);

	int VertexIndex = 0;
	int aux = 0;

	for (int i = 0; i < PolyCount; i++) {

		int PolySize = Mesh->GetPolygonSize(i);

		UE_LOG(FbxMeshLog, Warning, TEXT("PolyCount : %d"), PolySize);

		for (int j = 0; j < PolySize; j++) {

			int CtrlPointIndex = Mesh->GetPolygonVertex(i, j);

			MeshData->Normals.Append(GetNormalsFromFBX(Mesh, CtrlPointIndex, VertexIndex));

			MeshData->UVs.Append(GetUVsFromFBX(Mesh, CtrlPointIndex, Mesh->GetTextureUVIndex(i, j)));

			MeshData->VertexColor.Append(GetVertexColorsFromFBX(Mesh, CtrlPointIndex, VertexIndex));

			MeshData->Tangents.Append(GetTangentsFromFBX(Mesh, CtrlPointIndex, VertexIndex));

			//UE_LOG(FbxMeshLog, Warning, TEXT("NormalsArray: %f %f %f"), NormalsArray[VertexIndex][0], NormalsArray[VertexIndex][1], NormalsArray[VertexIndex][2]);

			MeshData->Triangles.Add(CtrlPointIndex);

			VertexIndex++;
		}
	}

	//TreatTriangles(MeshData->Triangles);

	//for (int i = 0; i < MeshData->Triangles.Num(); i++) 
		//UE_LOG(FbxMeshLog, Warning, TEXT("TrianglesArray: %d"), MeshData->Triangles[i]);


	UE_LOG(FbxMeshLog, Log, TEXT("CtrlPts Count: %d  \nTriangles Count: %d  \nNormals Count: %d"),
		MeshData->Vertices.Num(), MeshData->Triangles.Num(), MeshData->Normals.Num());
	UE_LOG(FbxMeshLog, Log, TEXT("Uvs Count: %d  \nVertex Colors Count: %d  \nTangents Count: %d"),
		MeshData->UVs.Num(), MeshData->VertexColor.Num(), MeshData->Tangents.Num());

}

Okay and what are the actual values you see for UVElement->GetMappingMode() and UVElement->GetReferenceMode()?

Sorry for the late reply (had to study for an yesterdays exam)

The values for GetMappingMode will be FbxGeometryElement::eByControlPoint or FbxGeometryElement::eByPolygonVertex
GetReferenceMode will be FbxGeometryElement::eDirect or FbxGeometryElement::eIndexToDirect.

These are data within the fbx file, for the UV’s, almost always, the mapping mode will be eByPolygonVertex with reference mode eIndexToDirect.

Since the objects i’m testing with have all followed that path mode, i’m replacing, for the meantime while i’m testing the issue, directly the UVVertexIndex variable where i’m getting the UV data from the fbx file FbxVector2 fUV = UVElement->GetDirectArray().GetAt(UVVertexIndex);

Hmmm… This is a little hard without stepping through the code myself, but I think you’re interpreting eIndexToDirect incorrectly. In FbxStaticMeshImport.cpp, they are getting the UV index as TriangleIndex*3+CornerIndex:

int lControlPointIndex = Mesh->GetPolygonVertex(TriangleIndex, CornerIndex);
int UVMapIndex = (UVMappingMode[UVLayerIndex] == FbxLayerElement::eByControlPoint) ? lControlPointIndex : TriangleIndex*3+CornerIndex;
int32 UVIndex = (UVReferenceMode[UVLayerIndex] == FbxLayerElement::eDirect) ? 
UVMapIndex : LayerElementUV[UVLayerIndex]->GetIndexArray().GetAt(UVMapIndex);

Whereas you are improperly indexing into the IndexArray with CtrlPointIndex:

case FbxGeometryElement::eIndexToDirect:
  UVIndex = UVElement->GetIndexArray().GetAt(CtrlPointIndex);

Also, beware the fallthrough in your case FbxGeometryElement::eDirect. It’s not going to affect your code with eIndexToDirect, but if your FBX file ever actually, uses eDirect, it is an accident waiting to happen.

I am actually doing that code snippet in my code, except for the part where instead of having TriangleIndex*3+CornerIndex i have the variable UVVertexIndex that is Mesh->GetTextureUVIndex(i,j), where i is the current polygon( TriangleIndex ) and j is the current vertex of that polygon ( CornerIndex )

Never the less, i went to check the whole code around that snippet. At first i just tried to replace the UVVertexIndex with TriangleIndex*3+CornerIndex, but that didn’t work. I then noticed that it that the UV element was in the form of Layers, so after reviewing the whole process they have for the UV’s i rearranged my code for it, but again same output, still wrong.

Then I realized that grabbing the methods used in the FbxStaticMeshImport.cpp isn’t going to work for me in this case, as the process is completely different from what i’m doing. While in the FbxStaticMeshImport.cpp they are reading and saving the information they receive from the fbx, directly to the mesh object in unreal (that is RawMesh in this case), while going through the whole mesh, on the other hand, i’m reading and saving that information in an array from a struct i created, called MeshData, so that after processing the whole information from the mesh, i pass it on to the constructor ( UProceduralMeshComponent::CreateMeshSection() ) of the mesh object in unreal (that is ProceduralMeshComponent in this case).

Basically, FbxStaticMeshImport.cpp is usng RawMesh and keeping that updated, and I’m using a simple struct, called MeshData, just to save the information from the mesh, so that i can pass it after everything is processed, on to the constructor of the mesh of UProceduralMeshComponent.

What i’m doing in my code is just like from what i’ve seen from other people’s code around the web, for the receiving of information from the fbx and the creation of the mesh for the UProceduralMeshComponent.

What i think the major issue here is, either i’m really failing to notice a simple(or not) mistake, or the since RawMesh actually saves the information in it and when giving the material to the mesh created from RawMesh, it grabs the UV information from it so the material knows how to wrap itself on the mesh.
UProceduralMeshCompenent should be able to keep that information, since i saw that it was working fine with other people. But i think in my case it’s not keeping the UV information with it, and that is the major flaw i have… This is just a theory i got, so it might be very wrong as well…

Okay so I finally found the problem with the textures!!

When using UProceduralMeshComponent::CreateMeshSection(), the UV Array (as well as the Normals, Tangents and Vertex Color Array) has to be of the same size as the Vertices Array, and if it’s not of the same size, the function will create a zeroed UV array.

Such a simple thing… and i missed it over and over again… x-x So much time wasted… I’m so sorry…

Well either way, i’m still failing a bit on the UVs. I have this now:

From what I understand, each index of UV Array corresponds to each index of Vertices Array. So now I know my problem has to do with reading the UV’s from the Fbx file.

Either way I’ll mark this as resolved since the problem is technically resolved and now it has to do with reading the Fbx.

Thanks everyone~