Download

Generate Procedural Mesh

looking at the picture … Only coders will understand! :smiley:

Aha! You guys didn’t notice my normals were inverted… (Unreal seems to like clockwise vertices) I will share my code on the wiki to get you started with quick and dirty instructions, although it will not be to Rama-sama standards :wink: I’ll post here when it’s up.

Here’s another with more segments and proper normals:

Here’s the example. A new, community-hosted Unreal Engine Wiki - Announcements and Releases - Unreal Engine Forums

I haven’t been able to embed images (I’m a wiki noob and short on time), but there are links. Here’s what the blueprint portion looks like, really standard and simple:

very cool, ill see if i can make my voxel chunk object like that.

Right now this geometry is slightly slower than a regular static mesh, and doesn’t support things like precomputed lighting.

Wow this is awesome!

Amazing work Dmacesic!

Please do share the tutorial!

Amazing things will come of it!

EDIT

His tutorial is now up and available here!

Procedural Mesh

:slight_smile:

Rama

: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:

Rama

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:

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

Cool stuff, feel free to adjust the wiki article if you wish: A new, community-hosted Unreal Engine Wiki - Announcements and Releases - Unreal Engine 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?

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: Fixed crash in packaged build due to GEngine->WireframeMaterial==NULL · AndrewScheidecker/BrickGame@767adc9 · GitHub

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: -

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)