How can I add/remove vertices and indices of a static mesh?

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[i];
		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/UE4Render...uginModule.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));
}