Download

Issue with creating a mesh in run time in UE4 using data from .obj and RuntimeMeshComponent

Hello Folks,
I am trying to create meshes from point clouds at run time, I can do the pre-processing and create a textured mesh with uvs etc. mapped and create .obj file format like structure. Such a structure is standard .obj wavefront format and I can load in UE4 via the editor where it looks like this :
catInUE4.jpg

Using the C++ pipeline at run time:

  1. https://github.com/Koderz/RuntimeMeshComponent , and
  2. https://github.com/Koderz/RuntimeMeshComponent-Examples

I came up with the following C++ code, but the result of matching vertices, normals, uvs between the obj file and the run time mesh component structure looks messed up. I don’t know why the triangulations are so strange - some correspondence issue perhaps. I wonder what any expert in the community thinks about this. How can I fix this ? What does the creator of RuntimeMeshCompoenent, @Koderz - Chris Conway thinks ? Can someone shine a light on this issue to help me understand and fix this please ?

I have attached all the files, including the original CatMesh.obj.txt files (just remove the .txt if you want to test) along with some separate derived files from it that I use in my code.

RunningInUE4.jpg

MeshObjectExample.h



UCLASS()
class RMC_EXAMPLES_API AMeshObjectExample : public ARuntimeMeshObjectBase
{
    GENERATED_BODY()

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

    void ReadOBJFileVertices(TArray<FVector>& VerticesOBJ);
    void ReadOBJFileVerticesNormals(TArray<FVector>& VerticesNormalsOBJ);

    void ReadOBJFileFaces(TArray<int32>& TrianglesOBJ);
    void ReadOBJFileUVs(TArray<FVector2D>& UVs);

};


MeshObjectExample.cpp



#include "RMC_Examples.h"
#include "MeshObjectExample.h"


void AMeshObjectExample::ReadOBJFileVerticesNormals(TArray<FVector>& VerticesNormalsOBJ)
{
    FString path = "F:\\RuntimeMeshComponent-Examples\\Binaries\\Win64\\VerticesNormal.txt";
    TArray<FString> take;
    FFileHelper::LoadANSITextFileToStrings(*path, NULL, take);


    for (int i = 0; i < take.Num(); i++)
    {
        FString aString = take*;

        TArray<FString> stringArray = {};

        aString.ParseIntoArray(stringArray, TEXT(" "), false);

        FVector temp;
        temp.X = FCString::Atof(*stringArray[0]);
        temp.Y = FCString::Atof(*stringArray[1]);
        temp.Z = FCString::Atof(*stringArray[2]);


        VerticesNormalsOBJ.Add(temp);
    }

}


void AMeshObjectExample::ReadOBJFileVertices(TArray<FVector>& VerticesOBJ)
{
    FString path = "F:\\RuntimeMeshComponent-Examples\\Binaries\\Win64\\Vertices.csv.txt";
    TArray<FString> take;
    FFileHelper::LoadANSITextFileToStrings(*path, NULL, take);


    for (int i = 0; i < take.Num(); i++)
    {
        FString aString = take*;

        TArray<FString> stringArray = {};

        aString.ParseIntoArray(stringArray, TEXT(","), false);

        FVector temp;
        temp.X = FCString::Atof(*stringArray[0]);
        temp.Y = FCString::Atof(*stringArray[1]);
        temp.Z = FCString::Atof(*stringArray[2]);


        VerticesOBJ.Add(temp);
    }

}

void AMeshObjectExample::ReadOBJFileFaces(TArray<int32>& TrianglesOBJ)
{
    FString path = "F:\\RuntimeMeshComponent-Examples\\Binaries\\Win64\\Faces.txt";
    TArray<FString> take;
    FFileHelper::LoadANSITextFileToStrings(*path, NULL, take);


    for (int i = 0; i < take.Num(); i++)
    {
        FString aString = take*;

        TArray<FString> stringArray = {};

        aString.ParseIntoArray(stringArray, TEXT(" "), false);

        TrianglesOBJ.Add(FCString::Atoi(*stringArray[0]));
        TrianglesOBJ.Add(FCString::Atoi(*stringArray[2]));
        TrianglesOBJ.Add(FCString::Atoi(*stringArray[4]));
    }
}

void AMeshObjectExample::ReadOBJFileUVs(TArray<FVector2D>& UVs)
{
    FString path = "F:\\RuntimeMeshComponent-Examples\\Binaries\\Win64\\UVs.txt";
    TArray<FString> take;
    FFileHelper::LoadANSITextFileToStrings(*path, NULL, take);


    for (int i = 0; i < take.Num(); i++)
    {
        if (i % 3 == 0)
        {
            FString aString = take*;

            TArray<FString> stringArray = {};

            aString.ParseIntoArray(stringArray, TEXT(" "), false);

            UVs.Add(FVector2D(FCString::Atof(*stringArray[0]), FCString::Atof(*stringArray[2])));
        }
    }
}

void AMeshObjectExample::BeginPlay()
{
    Super::BeginPlay();

    TArray<FVector> Vertices;
    TArray<int32> Triangles;
    TArray<FVector> Normals;
    TArray<FVector2D> UVs;
    TArray<FRuntimeMeshTangent> Tangents;

    ReadOBJFileVertices(Vertices);
    ReadOBJFileVerticesNormals(Normals);
    ReadOBJFileFaces(Triangles);
    ReadOBJFileUVs(UVs);

    URuntimeMeshLibrary::CalculateTangentsForMesh(Vertices, Triangles, UVs, Normals, Tangents);

    RuntimeMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UVs, TArray<FColor>(), Tangents, true, EUpdateFrequency::Infrequent);

}