Download

Dynamic Mesh Construction

I would like to create code that adds static meshes and sets the material on the fly.

The calling code knows what it wants. It knows location, scale, rotation, mesh and material.
I envision having an array of mesh paths and an array of material paths. I could then create anything I want at any time.

This is my current calling code:
ABlock* MyActor = World->SpawnActorDeferred<ABlock>(ABlock::StaticClass(), SpawnTransform);
// Good luck trying to set mesh and material here. Any attempt seems to cause the engine to blow up.
MyActor->FinishSpawning(SpawnTransform);

In the constructor I have this:
ABlock::ABlock(const class FObjectInitializer& PCIP) : Super(PCIP) {
MyBlock = PCIP.CreateAbstractDefaultSubobject<UStaticMeshComponent>(this, TEXT(“SolidBlock”));
static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMesh(TEXT(“StaticMesh’/Game/cube_1_texture.cube_1_texture’”));
MyBlock->SetStaticMesh(StaticMesh.Object);
static ConstructorHelpers::FObjectFinder<UMaterial> Material(TEXT(“Material’/Game/Material.Material’”));
MyBlock->SetMaterial(0, Material.Object);
}

This is not what I want but if I try to use FObjectFinder outside the constructor, the engine blows up. I understand that method is limited to object construction.
The constructor doesn’t know the provided location, rotation or scale either. I can’t even use that as hacked communication channel and then change it before calling FinishSpawning.
I’ve read that an alternative is a static loader. I have not been able to make that work.

Is there no way to communicate to this constructor from the outside to tell it what I want for mesh and material?
If I could just pass in a variable through some side channel to tell it what I want. I may resort to a singleton that the caller can set and the constructor can pull but that could run into issues if this constructor is loaded multi-threaded so that between the call and construction, the value could change.

Another alternative is to create a subclass for each mesh. I don’t want to go down that road. I could create subclasses via code generation but that would be nasty.

If anyone has a way to talk to that constructor from the caller, that would be awesome.

I was able to communicate to the constructor via a singleton.

Even with that, I had to play some games to get this to work.

This method is very finicky:
static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMesh

If you try to pass an *FString into that, it does not work. It wants that text hardwired right there. I have added an if/else statement with all possible meshes and textures. The singleton then communicates which mesh and material to use. I believe this is caching everything at once. I will have to watch my memory footprint to see if I need a different solution. Even if I use a different solution, the code outside of this method will be ignorant of the implementation change.

For reference, here is example code.


This is the calling class requesting an object with rotation, location, scale, mesh and material:

SpawnBlock(FString MeshName, FString MaterialName, FVector Location, FRotator Rotation, FVector Scale) {

// singleton
GlobalClass* globalClass = GlobalClass::instance();
globalClass->setMeshName(MeshName);
globalClass->setMaterialName(MaterialName);

// Location, Rotation and Scale
const FTransform SpawnTransform = FTransform(Rotation, Location, Scale);

ABlock* MyActor = World->SpawnActorDeferred<ABlock>(ABlock::StaticClass(), SpawnTransform);
MyActor->FinishSpawning(SpawnTransform);

}


The ABlock “constructor”. I have 1 mesh and 2 materials for testing.

ABlock::ABlock(const class FObjectInitializer& PCIP) : Super(PCIP) {

// Singleton “side channel” constructor communication
GlobalClass* globalClass = GlobalClass::instance();
FString MeshName = globalClass->getMeshName();
FString MaterialName = globalClass->getMaterialName();

// All possible meshes
if (MeshName == “BLOCK”) {
static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMesh(TEXT(“StaticMesh’/Game/cube_1_texture.cube_1_texture’”));
MyBlock->SetStaticMesh(StaticMesh.Object);
}

// All possible materials
if (MaterialName == “STONE”) {
static ConstructorHelpers::FObjectFinder<UMaterial> Material(TEXT(“Material’/Game/Material.Material’”));
MyBlock->SetMaterial(0, Material.Object);
}
else if (MaterialName == “BLUE”) {
static ConstructorHelpers::FObjectFinder<UMaterial> Material(TEXT(“Material’/Game/Material2.Material2’”));
MyBlock->SetMaterial(0, Material.Object);
}


Now it is super simple for my environment generation logic. Simply call this:
SpawnBlock(“BLOCK”,“STONE”,FVector(XPos, YPos, ZPos), FRotator(0.0f, 0.0f, 0.0f), FVector(scale, scale, scale))

As I add new meshes and materials, I just add them to the ABlock constructor.

I will need to clean up this code but it is working. I did not see complete examples of this on the web. I hope this helps someone down the road…

You could use LoadObject<UStaticMesh>(…) to load them at runtime instead of in the constructor.

But if you want to spawn many meshes in a row, it might be more efficient to preload all of the meshes/materials first.