Generate Procedural Mesh

It’s might be a dump question.

But i wonder if there is a solution to asynchronously generate the mesh without blocking the render and the game thread?
Right now if i generate a bigger mesh, the editor freezes.

AndrewScheidecker did something to solve this issue:
https://.com/AndrewScheidecker/BrickGame/blob/master/Plugins/BrickGrid/Source/BrickGrid/Classes/BrickGridComponent.h#L317

So is there any alternative? Like the level streaming in the engine?

Thanks!

[=;206974]

But i wonder if there is a solution to asynchronously generate the mesh without blocking the render and the game thread?
Right now if i generate a bigger mesh, the editor freezes.

[/]

You can of course generate the mesh data (vertices, normals, etc) in a separate thread, but as far as I know, you can only push this data to the engine during the render thread. If the mesh you update is very big then the engine also has to do a lot of work (building geometry, collision data, apply materials, rendering, …), which can cause a noticable stutter.

You can decrease the size of your updated mesh by dividing it into chunks (which is also what AndrewScheidecker did) and only update individual chunks. If you have to update all of them you can still spread the update over several frames so it is not as noticable to the player.

Has anyone else run into issues with translucent materials not rendering in the right order? I know there is a bug with two-sided translucent materials, but I didn’t expect an issue with single-sided translucent materials as well. Is there some sort of post-creation call I need to ensure the object is rendered on the correct pass?

update: I ended up setting up a translucency sort priority and sorting the few objects I had away from the camera every tick, very horrible hack and it is a shame there is no forward renderer for transparency yet, not sure if the new work on the water transparency will be applicable more generally or not.

I’m still having problems with the vertex tangents. The current algorithm seems to use 2 edge lengths to generate the tangents which give opposite results for 2 triangles that share the same edge. Can someone point me in the right direction for calculating tangents for a quad (or preferably several quads)? Thanks.

Hi,
I have problems to decide where to create my procedural mesh within my actor lifecycle. I need some information about the world around my object before I can create its mesh. So I decided to use PostInitializeComponents where I create the mesh in.

For testing I use this code:



void ATest::PostInitializeComponents()
{
	Super::PostInitializeComponents();

	TArray<FProceduralMeshTriangle> triangles;
	FVector pos( 0, 0, 0 );	// generate cube triangles for every cube in this cunk
	GenerateCube( pos, 10.f, triangles );
	mesh->SetProceduralMeshTriangles( triangles );

}


This creates me a single cube, but I don’t get any collision detection. The cube does not collide with the scene.

I tested it using the same code in the constructor:



ATest::ATest( const class FObjectInitializer& PCIP ) : Super( PCIP )
{
	mesh = PCIP.CreateDefaultSubobject<UProceduralMeshComponent>( this, TEXT( "Test" ) );

	static ConstructorHelpers::FObjectFinder<UMaterialInterface> Material( TEXT( "Material'/Game/Materials/BaseColor.BaseColor'" ) );
	mesh->SetMaterial( 0, Material.Object );

	TArray<FProceduralMeshTriangle> triangles;
	FVector pos( 0, 0, 0 );	// generate cube triangles for every cube in this cunk
	GenerateCube( pos, 10.f, triangles );
	mesh->SetProceduralMeshTriangles( triangles );

	RootComponent = mesh;
}


Spawning the Actor this way gets me collision detection and I cannot walk trough this cube anymore.

But for more complex objects I cannot create the whole mesh in the constructor. It seems that the collision object is built after construction and before PostInitialization. So is there a way to recreate the collision-object after creating my mesh in the PostInitializeComponents method?

Sorry, I missed the previous post by mordentral. I had to move the physics state creation below my ModelBodySetup settings and now it seems to work.

I’m basing this off of http://www.terathon.com/code/tangent.html

I had to store an array of all triangles that are attached to each other and then calculate the tangents together. Vertex normals are stored in triangle tanX, tangents in tanY, and bitangents in tanZ. Attempting to orthogonalize and calculate the bitangents makes the lighting worse.


// UE4 Procedural Mesh Generation from the Epic Wiki (https://wiki.unrealengine.com/Procedural_Mesh_Generation)
//
// forked from "Engine/Plugins/Runtime/CustomMeshComponent/Source/CustomMeshComponent/Classes/CustomMeshComponent.h"

#pragma once

#include "ProceduralMeshComponent.generated.h"

USTRUCT(BlueprintType)
struct FProceduralMeshVertex
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, Category = Triangle)
	FVector Position;

	UPROPERTY(EditAnywhere, Category = Triangle)
		FColor Color;

	UPROPERTY(EditAnywhere, Category = Triangle)
		float U;

	UPROPERTY(EditAnywhere, Category = Triangle)
		float V;

	UPROPERTY(EditAnywhere, Category = Triangle)
		FVector tanX;

	UPROPERTY(EditAnywhere, Category = Triangle)
		FVector tanY;

	UPROPERTY(EditAnywhere, Category = Triangle)
		FVector tanZ;

	UPROPERTY(EditAnywhere, Category = Triangle)
		bool manualTangents = false;
};

USTRUCT(BlueprintType)
struct FProceduralMeshTriangle
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, Category = Triangle)
		FProceduralMeshVertex Vertex0;

	UPROPERTY(EditAnywhere, Category = Triangle)
		FProceduralMeshVertex Vertex1;

	UPROPERTY(EditAnywhere, Category = Triangle)
		FProceduralMeshVertex Vertex2;

	UPROPERTY(EditAnywhere, Category = Triangle)
		TArray<int8> attchedTriangles;

	UPROPERTY(EditAnywhere, Category = Triangle)
		bool normalsHaveBeenCalculated = false;

	//used to keep track of the triangles intended position in the array
	UPROPERTY(EditAnywhere, Category = Triangle)
		int8 triangleIndexNumber;
};

/** Component that allows you to specify custom triangle mesh geometry */
UCLASS(editinlinenew, meta = (BlueprintSpawnableComponent), ClassGroup = Rendering)
class UProceduralMeshComponent : public UMeshComponent, public IInterface_CollisionDataProvider
{
	GENERATED_UCLASS_BODY()

public:
	/** Set the geometry to use on this triangle mesh */
	UFUNCTION(BlueprintCallable, Category = "Components|ProceduralMesh")
		bool SetProceduralMeshTriangles(const TArray<FProceduralMeshTriangle>& Triangles);

	/** Description of collision */
	UPROPERTY(BlueprintReadOnly, Category = "Collision")
	class UBodySetup* ModelBodySetup;

	// Begin Interface_CollisionDataProvider Interface
	virtual bool GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData) override;
	virtual bool ContainsPhysicsTriMeshData(bool InUseAllTriData) const override;
	virtual bool WantsNegXTriMesh() override{ return false; }
	// End Interface_CollisionDataProvider Interface

	// Begin UPrimitiveComponent interface.
	virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
	virtual class UBodySetup* GetBodySetup() override;
	// End UPrimitiveComponent interface.

	// Begin UMeshComponent interface.
	virtual int32 GetNumMaterials() const override;
	// End UMeshComponent interface.

	void UpdateBodySetup();
	void UpdateCollision();

	//calcualte our normals, tangents, and bitangents
	FProceduralMeshTriangle GetNormals(TArray<FProceduralMeshTriangle>& Triangles);

private:
	// Begin USceneComponent interface.
	virtual FBoxSphereBounds CalcBounds(const FTransform & LocalToWorld) const override;
	// Begin USceneComponent interface.

	/** */
	TArray<FProceduralMeshTriangle> ProceduralMeshTris;

	friend class FProceduralMeshSceneProxy;
};


{
		//Store an array of identical triangles
		TArray<FProceduralMeshTriangle> storedTriangles;
		for (int TriIdx = 0; TriIdx < Component->ProceduralMeshTris.Num(); TriIdx++)
		{
			storedTriangles.Add(Component->ProceduralMeshTris[TriIdx]);
			storedTriangles[TriIdx].triangleIndexNumber = TriIdx;
		}
		
		//search predefined attached triangles for matching vertices and average tangents
		for (int i = 0; i < storedTriangles.Num() - 1; i++)
		{
			//store an array of all attached triangles so that they can have thier normals calculated
			TArray<FProceduralMeshTriangle> attachedTriangles;

			if (!storedTriangles*.normalsHaveBeenCalculated)
			{
				attachedTriangles.Add(storedTriangles*);
				storedTriangles*.normalsHaveBeenCalculated = true;

				//add all attached triangles to the array that gets bulk normals calculation
				//set normalsHaveBeenCalculated to true to prevent the values from getting overwritten
				for (int j = 0; j < storedTriangles*.attchedTriangles.Num(); j++)
				{
					attachedTriangles.Add(storedTriangles[storedTriangles*.attchedTriangles[j]]);
					storedTriangles[storedTriangles*.attchedTriangles[j]].normalsHaveBeenCalculated = true;
					//get a refernce to the the attached triange that we are currently searching
					//FProceduralMeshTriangle currentAttachedTriangle = storedTriangles[storedTriangles*.attchedTriangles[j]];

				}

				
	//Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html

	for (long a = 0; a < attachedTriangles.Num(); a++)
	{
		//determine which way the normals are facing
		int8 handedness;

		FVector v1 = attachedTriangles[a].Vertex0.Position;
		FVector v2 = attachedTriangles[a].Vertex1.Position;
		FVector v3 = attachedTriangles[a].Vertex2.Position;

		FVector2D w1 = FVector2D(attachedTriangles[a].Vertex0.U, attachedTriangles[a].Vertex0.V);
		FVector2D w2 = FVector2D(attachedTriangles[a].Vertex1.U, attachedTriangles[a].Vertex1.V);
		FVector2D w3 = FVector2D(attachedTriangles[a].Vertex2.U, attachedTriangles[a].Vertex2.V);

		float x1 = v2.X - v1.X;
		float x2 = v3.X - v1.X;
		float y1 = v2.Y - v1.Y;
		float y2 = v3.Y - v1.Y;
		float z1 = v2.Z - v1.Z;
		float z2 = v3.Z - v1.Z;

		float s1 = w2.X - w1.X;
		float s2 = w3.X - w1.X;
		float t1 = w2.Y - w1.Y;
		float t2 = w3.Y - w1.Y;

		float r = 1.0F / (s1 * t2 - s2 * t1);
		FVector sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
			(t2 * z1 - t1 * z2) * r);
		FVector tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
			(s1 * z2 - s2 * z1) * r);

		attachedTriangles[a].Vertex0.tanY += sdir;
		attachedTriangles[a].Vertex1.tanY += sdir;
		attachedTriangles[a].Vertex2.tanY += sdir;

		attachedTriangles[a].Vertex0.tanZ += tdir;
		attachedTriangles[a].Vertex1.tanZ += tdir;
		attachedTriangles[a].Vertex2.tanZ += tdir;


		//attachedTriangles[a].
		storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.tanY = attachedTriangles[a].Vertex0.tanY;
		storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.tanZ = attachedTriangles[a].Vertex0.tanZ;

		storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.tanY = attachedTriangles[a].Vertex1.tanY;
		storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.tanZ = attachedTriangles[a].Vertex1.tanZ;
	
		storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.tanY = attachedTriangles[a].Vertex2.tanY;
		storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.tanZ = attachedTriangles[a].Vertex2.tanZ;


			const FVector n = attachedTriangles[a].Vertex0.tanX;
			const FVector t = attachedTriangles[a].Vertex0.tanY;

			// Gram-Schmidt orthogonalize
			//storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.tanY = (t - n * (n | t)).SafeNormal();
	
			//calculateHandedness
			handedness = ((( n ^ t) | storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.tanZ) < 0.0F) ? -1.0F : 1.0F;

			//calculate Bitangent

			//storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.tanZ = (n ^ t) * handedness;

			


			const FVector nn = attachedTriangles[a].Vertex1.tanX;
			const FVector tt = attachedTriangles[a].Vertex1.tanY;

			// Gram-Schmidt orthogonalize
			//storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.tanY = (tt - nn * (nn | tt)).SafeNormal();

			//calculateHandedness
			handedness = (((nn ^ tt) | storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.tanZ) < 0.0F) ? -1.0F : 1.0F;

			//calculate Bitangent

			//storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.tanZ = (nn ^ tt) * handedness;



			const FVector nnn = attachedTriangles[a].Vertex2.tanX;
			const FVector ttt = attachedTriangles[a].Vertex2.tanY;

			// Gram-Schmidt orthogonalize
			//storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.tanY = (ttt - nnn * (nnn | ttt)).SafeNormal();


			//calculateHandedness
			handedness = (((nnn ^ ttt) | storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.tanZ) < 0.0F) ? -1.0F : 1.0F;

			//calculate Bitangent

			//storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.tanZ = (nnn ^ ttt) * handedness;
			
	}

Edit: got it - I outputting the normals as bitangents.

Has anyone else seen rendering issues with this on 4.7? I have terrain code that works in 4.5 and 4.6, but does not render in 4.7. The collision works fine, there is just no graphical representation on screen.

Does anyone has any hints for me to improve performance when generating a voxel cube world?
My current approach is that I have several lists in which I store chunks that should be loaded, destroyed and those that are currently loaded.
In my update method I mark e.g. 16x16x16 chunks to be loaded. Those chunks are packed to batches that should be executed by several FQueuedWork objects using a FQueuedThreadPool. The worker threads push the resulting triangle lists for every chunk (still batched in multiple chunk triangle list per packet) into an TQueue that is handled by the main thread to spawn the chunks.

To spawn the chunks I “unbatch” the chunk triangle lists and put them into an sperate std::queue (I do this to prevent slowing the system by many parallel FQueue calls and so there is only one push by one worker and those results are extracted in the main thread and then the chunks are spawned as ProceduralMeshComponent that are attached to the generator actor using this code:



UProceduralMeshComponent* mesh = NewObject<UProceduralMeshComponent>( this );
			mesh->RegisterComponentWithWorld( world );
			mesh->AttachTo( RootComponent );
			mesh->SetProceduralMeshTriangles( triangles );
			mesh->SetComponentTickEnabled( false );
			mesh->SetMaterial( 0, worldMaterial );
			mesh->SetRelativeLocation( FVector( pos.chunkPosX * chunkSize * blockSize, pos.chunkPosY * chunkSize * blockSize, pos.chunkPosZ * chunkSize * blockSize ) );
			AddOwnedComponent( mesh );


I don’t spawn all the chunks at once but try to share them over multiple frames (using a queue that has a maximum duration time).

But I still get massive frame drops and the terrain is mostly not spawned fast enough so I can walk faster from one end to the other than spawning new chunks. The problem is, that the main bottle neck seems to be the spawning itself - especially the method call mesh->SetProceduralMeshTriangles( triangles ) takes much time and for some chunks it needs about 60 to 80 ms to spawn one single chunk object.

I have tried to reduce the triangle memory that has to be copied by using only uint8 as vertex coordinates, but it is still too slow. I also do some basic optimization for creating the chunk triangles (only create cube faces that are visible and not hidden by neighboring cubes).

So in my eyes this should be pretty fast and I have no idea what I could to to speed up chunk spawning to get a smoothly generated terrain without frame drops and without me easily walking out of the world. So do you have any special hints to improve my performance even more? Or did I something wrong? Thanks, for your help.

@JohnnyBeans78: I don’t have any issues with 4.7 and I have a graphical representation. But I created a new 4.7 project so I cannot say if it would work using 4.5 or 4.6. But I didn’t use something special.

Hey ,

I noticed the same thing when I generate or update my voxel-world. I did some performance tracing and here are some rough numbers:
Generate mesh surface: 4ms
SetProceduralMeshTriangles: 0.2ms
Update Navmesh: 0.009ms

As you can see, the numbers show that an update of the mesh should not have that big of an impact on fps. In fact, it should not even be noticable. However, there is a very noticeable FPS drop and stutter in the game the moment after the update. Using the Stats tool in the engine provided little insight into what causes this. This max “Tick” time after the update is 100ms, but I have no idea where they come from. I suspect it to be some engine-internal stuff like adding the geometry to the world or the renderer.

Hello,
I can’t add CustomMeshComponent to my project. I add dependency in build.cs and when I try to #include “CustomMeshComponent.h” editor dont see it :frowning:

[=Xender;214057]
Hello,
I can’t add CustomMeshComponent to my project. I add dependency in build.cs and when I try to #include “CustomMeshComponent.h” editor dont see it :frowning:
[/]

Have you re-generated your visual project files?
What is the error message you get from the compiler?

[=;213053]
Does anyone has any hints for me to improve performance when generating a voxel cube world?

[/]

I spawn all of my “chunks” in my master actors initializer as components, then I can move them around, hide them, and refresh the mesh on them without actually re-creating any actors during gameplay. Then to save on draw calls I have collision chunks that do not render anything and I have render chunks that act as a collection of chunks and render multiple chunks at a time.

I get little to no frame drops updating large numbers of chunks at a time, the only drops I do get are if I spawn physics actors in as it suffers from the same actor registration slowdown that you have been noticing.

In 4.7 beta 5 I’m getting a LogPhysics:Warning: MoveFixedBody: Trying to move component’/Game/Maps/UEDPIE_0_Example_Map.Example_Map:PersistentLevel.Pro_CabinetDoor_C_0.StaticMeshComponent_735’ with a non-Movable Mobility.

How do I set my mesh to moveable?

[=mordentral;214973]
I spawn all of my “chunks” in my master actors initializer as components, then I can move them around, hide them, and refresh the mesh on them without actually re-creating any actors during gameplay.

[/]

Thank you. I have played around with my chunks for the last couple of days and now the chunk loading is much smoother. Spawning chunk components at the beginning of the game helped a lot (and fixing some stupid mistakes I made saved also a lot of computation time ;)). It could still be faster, but I should be able to do some more optimizations.

[=mordentral;214973]
Then to save on draw calls I have collision chunks that do not render anything and I have render chunks that act as a collection of chunks and render multiple chunks at a time.
[/]

I don’t know if I got you right. You have some chunk components, and additional collision and render chunks? And if you say you use theses render chunks to render multiple chunks at a time, why don’t you increase the chunk size for a single chunk? How do you update your chunks within those render chunks (you probably have to recreate all the triangles for alle the chunks within one render chunks, don’t you?).

[=;218116]

How do I set my mesh to moveable?
[/]

You could use


SetMobility( EComponentMobility::Movable );

within the constructor of your procedural mesh.

I tried that one with no luck. Does EcomponentMobility only affect lighting?

[=;221272]
I tried that one with no luck. Does EcomponentMobility only affect lighting?
[/]

It does affect lighting, but not only. Usually, the MoveFixedBody warning is a red flag that you are doing something wrong.
You posted that you tried to move Pro_CabinetDoor_C_0.StaticMeshComponent_735 from you persistent level - check that all parts of Pro_CabinetDoor_C_0 are set to moveable - or maybe you set the mobility of your CabinetDoor actor to static after you placed it in your map?

[=;220411]
I don’t know if I got you right. You have some chunk components, and additional collision and render chunks? And if you say you use theses render chunks to render multiple chunks at a time, why don’t you increase the chunk size for a single chunk? How do you update your chunks within those render chunks (you probably have to recreate all the triangles for alle the chunks within one render chunks, don’t you?).
[/]

No I just have collision and render chunks, the collision chunks store all of the information and the render chunks group the vertices of multiple collision chunks together and render them.

Larger chunk sizes = I have to extract the mesh, run greedy meshing on it, process lighting, and generally iterate over many more voxels at a time when something changes (or not use greedy meshing on the changed area). Compared to larger chunk sizes using render chunks that contain multiple chunks and resends the vertices and indices to the GPU is generally faster in all cases from my tests. I don’t extract the chunks that didn’t change, I store their actual vertices in the collision chunk and just merge them with the changed chunks in the render chunk before sending to the GPU.

It also has the upside of allowing me to set the collision chunks to static mobility so that physics objects don’t fall right through things when they hit a high enough velocity.

[=Cultrarius;221395]
It does affect lighting, but not only. Usually, the MoveFixedBody warning is a red flag that you are doing something wrong.
You posted that you tried to move Pro_CabinetDoor_C_0.StaticMeshComponent_735 from you persistent level - check that all parts of Pro_CabinetDoor_C_0 are set to moveable - or maybe you set the mobility of your CabinetDoor actor to static after you placed it in your map?
[/]

I still get the warning with all parts of the mesh set to moveable. It doesn’t seem to matter if I’m extending my procedural classes from aactor or astaticmeshactor.

Edit: it appears to be working properly in 4.7 beta 8

I am stumped. I cannot get this working in 4.7. As I have said the collision mesh exists but it does not render. I am going to start fresh and do the tutorial step by step to see if I can get it working then add in the other stuff and see where it breaks (U.V.'s, vertex colors, smooth meshing, …). I am digging the VR features of 4.7 too much to stay in 4.6 any longer. It is time to bite the bullet.