Modifying Static Meshes At Runtime

Hi all,

I am new to UE4 and this community. I made some brief searches beforehand and couldn’t find anything that answered my questions. I did spend more than a few hours searching online. Basically I have a static mesh (default UE4 cube) and I can successfully retrieve its vertices and indices. My goal is to be able to modify the mesh by removing/adding vertices at runtime. I know about the UProceduralMeshComponent but I saw that it is experimental and usually used to build a mesh from scratch, so I haven’t gone that way.

I have several issues:

  • One of the problems is that after calling Init() on the PositionVertexBuffer the number of vertices doubles (e.g. 24 -> 48), instead of just refreshing.
  • I also change the IndexBuffer with SetIndices() - in terms of index count it seems to work, but the change somehow persists between PIE instances until I decide to restart the editor.
  • Nothing changes visually - I don’t see anything extra from the double vertices and I don’t see a missing face on the cube (removing the last 3 indices that form a face).

What am I doing wrong?

Here’s the code:


/get the mesh component(s)
	_actor->GetComponents<UStaticMeshComponent>(Components);
	for (int32 i = 0; i<Components.Num(); i++)
	{
		UStaticMeshComponent* StaticMeshComponent = Components*;
		UStaticMesh* StaticMesh = StaticMeshComponent->StaticMesh;
	}

	//containers
	TArray<FVector> vertices = TArray<FVector>();
	TArray<uint32> indices = TArray<uint32>();

	FStaticMeshVertexBuffer* VertexBuffer = &Components[0]->StaticMesh->RenderData->LODResources[0].VertexBuffer;
	FPositionVertexBuffer* PositionVertexBuffer = &Components[0]->StaticMesh->RenderData->LODResources[0].PositionVertexBuffer;
	FRawStaticIndexBuffer* IndexBuffer = &Components[0]->StaticMesh->RenderData->LODResources[0].IndexBuffer;

	if (PositionVertexBuffer)
	{
		const int32 VertexCount = PositionVertexBuffer->GetNumVertices();
		for (int32 Index = 0; Index < VertexCount; Index++)
		{
			//const FVector WorldSpaceVertexLocation = _actor->GetActorLocation() + _actor->GetTransform().TransformVector(PositionVertexBuffer->VertexPosition(Index));
			//vertices.Add(WorldSpaceVertexLocation);
			//if I get the vertices in world space and then feed the array list back, in addition to doubling the vertices all the walls and the cube change their position
			vertices.Add(PositionVertexBuffer->VertexPosition(Index));
		}
	}
	if (IndexBuffer)
	{
		IndexBuffer->GetCopy(indices);
	}

	TArray<uint32> newIndices;
	count = indices.Num();
	for (int32 Index = 0; Index < count - 3; Index++)    //remove one face
	{
		newIndices.Add(indices[Index]);
	}
	Components[0]->StaticMesh->RenderData->LODResources[0].IndexBuffer.SetIndices(newIndices, EIndexBufferStride::AutoDetect);

	TArray<FStaticMeshBuildVertex> newVertexList;
	if (PositionVertexBuffer)
	{
		const int32 VertexCount = PositionVertexBuffer->GetNumVertices();
		for (int32 Index = 0; Index < VertexCount; Index++)
		{
			FStaticMeshBuildVertex tempVertex;
			tempVertex.Color = FColor::White;
			tempVertex.Position = vertices[Index];
			tempVertex.TangentX = VertexBuffer->VertexTangentX(Index);
			tempVertex.TangentY = VertexBuffer->VertexTangentY(Index);
			tempVertex.TangentZ = VertexBuffer->VertexTangentZ(Index);
			tempVertex.UVs[0] = VertexBuffer->GetVertexUV(Index, 0);
			newVertexList.Add(tempVertex);
		}
	}
	Components[0]->StaticMesh->RenderData->LODResources[0].PositionVertexBuffer.Init(newVertexList);
	RunAsyncTask(ENamedThreads::RenderThread, [this]()
	{
		Components[0]->StaticMesh->RenderData->LODResources[0].PositionVertexBuffer.InitRHI();
	});

Here’s the RunAsyncTask function (got it from https://github.com/Temaran/UE4RenderDocPlugin/blob/master/RenderDocPlugin/Source/RenderDocPlugin/Private/RenderDocPluginModule.cpp):

Header File:


static void RunAsyncTask(ENamedThreads::Type Where, TFunction<void()> What);

Cpp File:


void UModelCutting::RunAsyncTask(ENamedThreads::Type Where, TFunction<void()> What)
{
	struct FAsyncGraphTask : public FAsyncGraphTaskBase
	{
		ENamedThreads::Type TargetThread;
		TFunction<void()> TheTask;

		FAsyncGraphTask(ENamedThreads::Type Thread, TFunction<void()>&& Task) : TargetThread(Thread), TheTask(MoveTemp(Task)) { }
		void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) { TheTask(); }
		ENamedThreads::Type GetDesiredThread() { return(TargetThread); }
	};

	TGraphTask<FAsyncGraphTask>::CreateTask().ConstructAndDispatchWhenReady(Where, MoveTemp(What));
}

Thank you in advance for your help.

Hello,
Sorry for bringing up an old topic.
I need to know is there any way to scale an existing static mesh inside the level from a certain vertex or height?
Thanks

Yes, and you can do this without modifying the mesh:


Hello,
Thanks for your reply,
But I don’t think your solution works for me because It scales the entire mesh, I have a doorway as you can see in the screenshot, and I just need to scale it up in Z-axis from the vertex on the red line, so this doorway would work on rooms with different height without scaling the actual door mesh it self.

Thanks again.

Oh, I see what you mean. If you don’t need collision, you can do this in the material with world position offset, or you can use a morph target, or you can use a skeletal mesh and skin only the top vertices to a bone.

If you do need collision, you can convert the mesh to a procedural mesh and move those vertices that way. You could also get away with just using two separate meshes (one for the bottom part, and one for the top part), and only scale the top part (which would be like using modular pieces).

Moving/scaling vertices will cause the texture to stretch, though, so keep that in mind.

Thank you.
I’m in a very limited situation, Yesterday I changed codes to support two pieces of the meshes for the top and bottom to keep the project going until I found a solution…and it seems I have to stick to that for a while because skeletal mesh or material position offset is not good for me, as I agree with you they can do the trick.

1 Like

What you are trying to do is not supported - you cannot modify the render data for a cooked static mesh. As previous posts said, Procedural Mesh Component is one option. Another is to use the “runtime build” for a StaticMesh - in this case you do not modify the editor StaticMesh, you create a new UStaticMesh at runtime and call a different Build() function.

I have written about these options, as well as DynamicMeshComponent, here:

it might be helpful

4 Likes

Am I correct to say, that this might actually be possible now with the DynamicMeshActor and GeometryScript?

I am currently working on a irregular tile based terrain system. My issue: I cannot use static meshes without deforming them to fit the irregularity of the grid. Hence, I can use a large procedural mesh. Or I try using a static mesh in natural coordinates and deform it match the boundary conditions. So I wonder, can I convert my static mesh to a DynamicMeshActor and make it fit to the deform criteria? Finally, I would then re-bake them to static meshes and merch them.

The theoretical benefits of this approach would be that I can design the tile sets in blender. Allowing me to get a higher quality tile, than I would have gotten with procedural mesh. Though I am uncertain about the performance.

Could u confirm, whether or not, this approach is viable?

So I think, I can confirm it is possible now?!

I used this video https://www.youtube.com/watch?v=6adlrBQbcd8&t=815s from rmsEG to make my own deform blueprint. I can now specify the vertices, and the triangle will form to it:

Now, I just need to figure out how to fix the materials.