Generate Procedural Mesh

[=dmacesic;9953]
:cool:

I thought I had published that yesterday, did you do anything to make it public?
[/]

To clarify:

“His tutorial is now up and available here!”

I was just excitedly letting people know you already posted it :slight_smile:

Hey guys, just wanted to share what I’ve been tinkering around with on this stuff to be able to set UV’s for the custom meshes:

First, I split off the FCustomMeshTriangle struct into these two structs:


USTRUCT(BlueprintType)
struct FCustomMeshTriangleVertex
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, Category = Triangle)
	FVector Position;

	UPROPERTY(EditAnywhere, Category = Triangle)
	float U;

	UPROPERTY(EditAnywhere, Category = Triangle)
	float V;
};

USTRUCT(BlueprintType)
struct FCustomMeshTriangle
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, Category=Triangle)
	FCustomMeshTriangleVertex Vertex0;

	UPROPERTY(EditAnywhere, Category=Triangle)
	FCustomMeshTriangleVertex Vertex1;

	UPROPERTY(EditAnywhere, Category=Triangle)
	FCustomMeshTriangleVertex Vertex2;
};

Then, the constructor for the FCustomMeshSceneProxy class would change to this:


FCustomMeshSceneProxy(UCustomMeshComponent* Component)
		: FPrimitiveSceneProxy(Component)
		, MaterialRelevance(Component->GetMaterialRelevance())
	{
		const FColor VertexColor(255,255,255);

		// Add each triangle to the vertex/index buffer
		for(int TriIdx=0; TriIdx<Component->CustomMeshTris.Num(); TriIdx++)
		{
			FCustomMeshTriangle& Tri = Component->CustomMeshTris[TriIdx];

			const FVector Edge01 = (Tri.Vertex1.Position - Tri.Vertex0.Position);
			const FVector Edge02 = (Tri.Vertex2.Position - Tri.Vertex0.Position);

			const FVector TangentX = Edge01.SafeNormal();
			const FVector TangentZ = (Edge02 ^ Edge01).SafeNormal();
			const FVector TangentY = (TangentX ^ TangentZ).SafeNormal();

			FDynamicMeshVertex Vert0;
			Vert0.Position = Tri.Vertex0.Position;
			Vert0.Color = VertexColor;
			Vert0.SetTangents(TangentX, TangentY, TangentZ);
			Vert0.TextureCoordinate.Set(Tri.Vertex0.U, Tri.Vertex0.V);
			int32 VIndex = VertexBuffer.Vertices.Add(Vert0);
			IndexBuffer.Indices.Add(VIndex);

			FDynamicMeshVertex Vert1;
			Vert1.Position = Tri.Vertex1.Position;
			Vert1.Color = VertexColor;
			Vert1.SetTangents(TangentX, TangentY, TangentZ);
			Vert1.TextureCoordinate.Set(Tri.Vertex1.U, Tri.Vertex1.V);
			VIndex = VertexBuffer.Vertices.Add(Vert1);
			IndexBuffer.Indices.Add(VIndex);

			FDynamicMeshVertex Vert2;
			Vert2.Position = Tri.Vertex2.Position;
			Vert2.Color = VertexColor;
			Vert2.SetTangents(TangentX, TangentY, TangentZ);
			Vert2.TextureCoordinate.Set(Tri.Vertex2.U, Tri.Vertex2.V);
			VIndex = VertexBuffer.Vertices.Add(Vert2);
			IndexBuffer.Indices.Add(VIndex);
		}

		// Init vertex factory
		VertexFactory.Init(&VertexBuffer);

		// Enqueue initialization of render resource
		BeginInitResource(&VertexBuffer);
		BeginInitResource(&IndexBuffer);
		BeginInitResource(&VertexFactory);

		// Grab material
		Material = Component->GetMaterial(0);
		if(Material == NULL)
		{
			Material = UMaterial::GetDefaultMaterial(MD_Surface);
		}
	}

Lastly, to test this out I added a couple functions:


bool UCustomMeshComponent::ClearCustomMeshTriangles()
{
	CustomMeshTris.Empty();
	// Need to recreate scene proxy to send it over
	MarkRenderStateDirty();

	return true;
}

bool UCustomMeshComponent::AddCustomBox(const float& Size, const FVector& Position)
{
	// make vertex positions
	FVector p0 = FVector(Position.X, Position.Y, Position.Z);
	FVector p1 = FVector(Position.X, Position.Y, Position.Z + Size);
	FVector p2 = FVector(Position.X + Size, Position.Y, Position.Z + Size);
	FVector p3 = FVector(Position.X + Size, Position.Y, Position.Z);
	FVector p4 = FVector(Position.X + Size, Position.Y + Size, Position.Z);
	FVector p5 = FVector(Position.X + Size, Position.Y + Size, Position.Z + Size);
	FVector p6 = FVector(Position.X, Position.Y + Size, Position.Z + Size);
	FVector p7 = FVector(Position.X, Position.Y + Size, Position.Z);

	FCustomMeshTriangleVertex v0;
	FCustomMeshTriangleVertex v1;
	FCustomMeshTriangleVertex v2;
	FCustomMeshTriangleVertex v3;
	v0.U = 0; v0.V = 0;
	v1.U = 0; v1.V = .5;
	v2.U = .5; v2.V = .5;
	v3.U = .5; v3.V = 0;

	FCustomMeshTriangle t1;
	FCustomMeshTriangle t2;

	// front face
	v0.Position = p0;
	v1.Position = p1;
	v2.Position = p2;
	v3.Position = p3;
	t1.Vertex0 = v0;
	t1.Vertex1 = v1;
	t1.Vertex2 = v2;
	t2.Vertex0 = v0;
	t2.Vertex1 = v2;
	t2.Vertex2 = v3;
	CustomMeshTris.Add(t1);
	CustomMeshTris.Add(t2);

	//back face
	v0.Position = p4;
	v1.Position = p5;
	v2.Position = p6;
	v3.Position = p7;
	t1.Vertex0 = v0;
	t1.Vertex1 = v1;
	t1.Vertex2 = v2;
	t2.Vertex0 = v0;
	t2.Vertex1 = v2;
	t2.Vertex2 = v3;
	CustomMeshTris.Add(t1);
	CustomMeshTris.Add(t2);

	// left face
	v0.Position = p7;
	v1.Position = p6;
	v2.Position = p1;
	v3.Position = p0;
	t1.Vertex0 = v0;
	t1.Vertex1 = v1;
	t1.Vertex2 = v2;
	t2.Vertex0 = v0;
	t2.Vertex1 = v2;
	t2.Vertex2 = v3;
	CustomMeshTris.Add(t1);
	CustomMeshTris.Add(t2);

	// right face
	v0.Position = p3;
	v1.Position = p2;
	v2.Position = p5;
	v3.Position = p4;
	t1.Vertex0 = v0;
	t1.Vertex1 = v1;
	t1.Vertex2 = v2;
	t2.Vertex0 = v0;
	t2.Vertex1 = v2;
	t2.Vertex2 = v3;
	CustomMeshTris.Add(t1);
	CustomMeshTris.Add(t2);

	// top face
	v0.Position = p1;
	v1.Position = p6;
	v2.Position = p5;
	v3.Position = p2;
	t1.Vertex0 = v0;
	t1.Vertex1 = v1;
	t1.Vertex2 = v2;
	t2.Vertex0 = v0;
	t2.Vertex1 = v2;
	t2.Vertex2 = v3;
	CustomMeshTris.Add(t1);
	CustomMeshTris.Add(t2);

	// bottom face
	v0.Position = p3;
	v1.Position = p4;
	v2.Position = p7;
	v3.Position = p0;
	t1.Vertex0 = v0;
	t1.Vertex1 = v1;
	t1.Vertex2 = v2;
	t2.Vertex0 = v0;
	t2.Vertex1 = v2;
	t2.Vertex2 = v3;
	CustomMeshTris.Add(t1);
	CustomMeshTris.Add(t2);

	MarkRenderStateDirty();

	return true;
}

With all that, and using the AddCustomBox function, applying materials to the custom mesh components should now look something like this:

and then I went a little crazy with for loops:

[=FireOnHigh;10597]
and then I went a little crazy with for loops
[/]

Must have been a long loop… :smiley:
Nice work!

[=FireOnHigh;10597]
Hey guys, just wanted to share what I’ve been tinkering around with on this stuff to be able to set UV’s for the custom meshes:

[/]

Cool stuff, feel free to adjust the wiki article if you wish: A new, community-hosted Unreal Engine Wiki - Announcements - Epic Developer Community Forums

Or perhaps make your own since I renamed the class.

This is very cool. And I have got it working with a dev/editor build. However, a shipping build with a cooked assets folder causes a crash as soon as the custom geometry creation is triggered.

How would one go about making procedural geometry work in a non-editor build?

I think another user actually found this bug yesterday, we’ll try and get a fix in for the next release!

Oh yeah, I just cooked my stuff and it does crash. Is there a bug report somewhere I can look at and perhaps add to?

[=dmacesic;11766]
Oh yeah, I just cooked my stuff and it does crash. Is there a bug report somewhere I can look at and perhaps add to?
[/]

My initial assumption was that I may be missing some specific material-assigning in my mesh-gen code, which in an editor-build, default materials/textures would be applied in places that I missed, but in a cooked build any undefined material refs would result in null (or random address) mat/tex pointers.

The problem with packaged builds is that it dereferences GEngine->WireframeMaterial, which is NULL after packaging the game without editor support. You can fix the problem by checking if WITH_EDITOR is true before dereferencing WireframeMaterial.

You can see the fix I made to my similar primitive component implementation here: https://.com/AndrewScheidecker/BrickGame/commit/767adc91a7881f149e9488df7b691e354b36728a

[=AndrewJSch;12131]
The problem with packaged builds is that it dereferences GEngine->WireframeMaterial, which is NULL after packaging the game without editor support. You can fix the problem by checking if WITH_EDITOR is true before dereferencing WireframeMaterial.

You can see the fix I made to my similar primitive component implementation here: https://.com/AndrewScheidecker/BrickGame/commit/767adc91a7881f149e9488df7b691e354b36728a
[/]

Yep! That’s fixed it! :slight_smile:

I’m trying to confirm this, but I’m getting a bunch on link errors on cook (works fine in editor), investigating. E.g.



LogPlayLevel:Display: UnrealBuildTool:    Creating library C:\dev\Unreal\Projects\MyProject5\Binaries\Win64\MyProject5.lib and object C:\dev\Unreal\Projects\MyProject5\Binaries\Win64\MyProject5.exp
LogPlayLevel:Display: UnrealBuildTool: MyProject5.h.obj : warning LNK4217: locally defined symbol ??0FName@@QEAA@PEBDW4EFindName@@_N@Z (public: __cdecl FName::FName(char const *,enum EFindName,bool)) imported in function "void __cdecl `dynamic initializer for 'NAME_DeviceType''(void)" (??__ENAME_DeviceType@@YAXXZ)
LogPlayLevel:Display: UnrealBuildTool: GameHUD.cpp.obj : warning LNK4049: locally defined symbol ??0FName@@QEAA@PEBDW4EFindName@@_N@Z (public: __cdecl FName::FName(char const *,enum EFindName,bool)) imported
LogPlayLevel:Display: UnrealBuildTool: MyProject5.h.obj : warning LNK4049: locally defined symbol ?ZeroVector@FVector@@2V1@B (public: static class FVector const FVector::ZeroVector) imported
LogPlayLevel:Display: UnrealBuildTool: GameHUD.cpp.obj : warning LNK4049: locally defined symbol ?ZeroVector@FVector@@2V1@B (public: static class FVector const FVector::ZeroVector) imported
LogPlayLevel:Display: UnrealBuildTool: GeneratedMeshComponent.cpp.obj : warning LNK4049: locally defined symbol ?ZeroVector@FVector@@2V1@B (public: static class FVector const FVector::ZeroVector) imported
LogPlayLevel:Display: UnrealBuildTool: MyProject5PlayerController.cpp.obj : warning LNK4049: locally defined symbol ?ZeroVector@FVector@@2V1@B (public: static class FVector const FVector::ZeroVector) imported
LogPlayLevel:Display: UnrealBuildTool: MyProject5PlayerController.cpp.obj : warning LNK4049: locally defined symbol ?Logf__VA@FMsg@@SAXPEBDHAEBVFName@@W4Type@ELogVerbosity@@PEB_WZZ (public: static void __cdecl FMsg::Logf__VA(char const *,int,class FName const &,enum ELogVerbosity::Type,wchar_t const *,...)) imported
LogPlayLevel:Display: UnrealBuildTool: GameGeneratedActor.cpp.obj : warning LNK4217: locally defined symbol ?Logf__VA@FMsg@@SAXPEBDHAEBVFName@@W4Type@ELogVerbosity@@PEB_WZZ (public: static void __cdecl FMsg::Logf__VA(char const *,int,class FName const &,enum ELogVerbosity::Type,wchar_t const *,...)) imported in function "public: class UGeneratedMeshComponent * __cdecl FPostConstructInitializeProperties::CreateDefaultSubobject&lt;class UGeneratedMeshComponent,class UGeneratedMeshComponent&gt;(class UObject *,class FName,bool,bool,bool)const " (??$CreateDefaultSubobject@VUGeneratedMeshComponent@@V1@@FPostConstructInitializeProperties@@QEBAPEAVUGeneratedMeshComponent@@PEAVUObject@@VFName@@_N22@Z)
LogPlayLevel:Display: UnrealBuildTool: GameHUD.cpp.obj : warning LNK4049: locally defined symbol ?Logf__VA@FMsg@@SAXPEBDHAEBVFName@@W4Type@ELogVerbosity@@PEB_WZZ (public: static void __cdecl FMsg::Logf__VA(char const *,int,class FName const &,enum ELogVerbosity::Type,wchar_t const *,...)) imported

etc...


In your code it should simply be a single line change in GeneratedMeshComponent.cpp: -

[]

virtual void DrawDynamicElements(FPrimitiveDrawInterface* PDI, const FSceneView* View)
{
	QUICK_SCOPE_CYCLE_COUNTER(STAT_GeneratedMeshSceneProxy_DrawDynamicElements);

	const bool bWireframe = View-&gt;Family-&gt;EngineShowFlags.Wireframe;

	FColoredMaterialRenderProxy WireframeMaterialInstance(
		//GEngine-&gt;WireframeMaterial-&gt;GetRenderProxy(IsSelected()),
		WITH_EDITOR ? GEngine-&gt;WireframeMaterial-&gt;GetRenderProxy(IsSelected()) : NULL,
		FLinearColor(0, 0.5f, 1.f)
		);

[/]

[=AndrewJSch;12131]
The problem with packaged builds is that it dereferences GEngine->WireframeMaterial, which is NULL after packaging the game without editor support. You can fix the problem by checking if WITH_EDITOR is true before dereferencing WireframeMaterial.

You can see the fix I made to my similar primitive component implementation here: https://.com/AndrewScheidecker/BrickGame/commit/767adc91a7881f149e9488df7b691e354b36728a
[/]

Good work, I’ve updated the Procedural Mesh Generation code example on the wiki with your solution.

And now to figure out why my Launch button won’t build properly and gives me unresolved externals when cooking and running in the editor work fine… (not related to this fix)

Hi ,

I based my work on CustomMeshComponent to allow per-vertex and uv animation.
Everything started quite well and i can now animate like expected.

Unfortunately i am stuck to one big issue : i cant see the applied material to this mesh.
My mesh is rendered in plain color (ranging from white to black depending on the lighting and … luck… ).

I added support for UVs just like HighOnFire did, the material is attached and gettable with GetMaterial(0)…
If you guys ran into these kind of issues and know how to adapt the code for it to work, i would really appreciate :slight_smile:

Thanks


**UVs For GeneratedMesh**

You have to modify the procedure mesh code to use DynamicMeshVertex data rather than just FVector

Then when you cache the vertices the UVs will be included!

Here's the section you need to look at, with my modified code (it wont look the same as what you have currently)



```

FGeneratedMeshSceneProxy(UVictoryMeshComponent* Component)
		: FPrimitiveSceneProxy(Component)
		, MaterialRelevance(Component->GetMaterialRelevance())
	{
		
 
		// Add each triangle to the vertex/index buffer
		for(int TriIdx=0; TriIdx<Component->GeneratedMeshTris.Num(); TriIdx++)
		{
			FVMeshTri& Tri = Component->GeneratedMeshTris[TriIdx];
			
			IndexBuffer.Indices.Add(
				VertexBuffer.Vertices.Add(Tri.Vertex0) //returns new index
			);
		
			IndexBuffer.Indices.Add(
				VertexBuffer.Vertices.Add(Tri.Vertex1) //returns new index
			);
			
			IndexBuffer.Indices.Add(
				VertexBuffer.Vertices.Add(Tri.Vertex2) //returns new index
			);
		}
 
		// Init vertex factory
		VertexFactory.Init(&VertexBuffer);

```



I modified my triangle struct to look like this (which is the unit for GeneratedMeshTris)



```

USTRUCT()
struct FVMeshTri
{
	GENERATED_USTRUCT_BODY()
 
	
	FDynamicMeshVertex Vertex0;
	FDynamicMeshVertex Vertex1;
	FDynamicMeshVertex Vertex2;
	
	FVMeshTri()
	{
	}
};

```




**DynamicMeshVertex has support for UVs in its TextureCoordinate**


So now pass the entire DynamicMeshVertex into VertexBuffer.Vertices.Add(

and UVs will work correctly!

**I've already proven this in my own project,** resulting in fact that now I can make dynamic UV'ed meshes with collision (thanks Dmacesic!)

https://www.mediafire.com/convkey/e081/27c39zzh11o41z06g.jpg



PS: UPROPERTY() doesnt work with DynamicMeshVertex so I took that out

Thank you for the input .
I finally found out the issue… again something stupid and tricky…
I was actually using FDynamicMeshVertex to build the vertex buffer BUT instead of adding the whole structure i was only adding the FVector position component…:eek:
I wish someone told me about it at compile time :slight_smile:

How can we add the support for simulating physics for this generated mesh?

[=arkenthera;17531]
How can we add the support for simulating physics for this generated mesh?
[/]

I cant post the code here for legal reasons, but check out SphereComponent.cpp

and the AggGeom.

adding to AggGeom you can create a physics composite shape to match your mesh!

This does require a lot of careful manual arrangement, just like the UE4 Physics Editor / Phat.

So you will have to do this per shape that you create :slight_smile:

[=;17621]
I cant post the code here for legal reasons, but check out SphereComponent.cpp

and the AggGeom.

adding to AggGeom you can create a physics composite shape to match your mesh!

This does require a lot of careful manual arrangement, just like the UE4 Physics Editor / Phat.

So you will have to do this per shape that you create :slight_smile:

[/]

Thank you! Will try this for sure and post here if I could achieve it

Hi ,

Is there a way to create multiple instances of the same CustomMeshComponent? Like InstancedStaticMeshComponent?

Thanks