Assign a material to UProceduralMesh in C++

Hi, I’m trying to render a generated mesh, and I don’t understand how to create a material and assign it to my mesh component, using C++.
Here’s my header:

#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "MyActor.generated.h"


UCLASS()
class PROCEDURALMESH_API AMyActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AMyActor();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = General) UProceduralMeshComponent* mesh;
	USphereComponent* SphereComponent;

	TArray<FVector> vertices;
	TArray<int32> triangles;
	TArray<FVector> normals;
	TArray<FVector2D> UV0;
	TArray<FColor> vertexColors;
	TArray<FProcMeshTangent> tangents;
	const int segs = 10;
	const int divs = 24;
	const float segHeight = 250;
	const float baseRadius = 500;
	
};

Here’s my class:

#include "ProceduralMesh.h"
#include "MyActor.h"


// Sets default values
AMyActor::AMyActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	//UPROPERTY(EditAnywhere);
	SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
	RootComponent = SphereComponent;

	
	mesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh"));
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
	int a = 0;
	int d = 0;
	float radius = baseRadius;

	//generate cylinder vertices
	for (int i = 0; i <= divs*segs; i++)
	{
		//random chance to alter the radius
		int chance = FMath::FRandRange(0, 3);
		if (chance == 1)
		{
			radius += baseRadius / 100;
		}
		else if (chance == 2)
		{
			radius -= baseRadius / 100;
		}
		else if (chance == 3)
		{
			//increment back to baseRadius
			radius += (baseRadius - radius) / 3;
		}
		//generate points in a circle
		float angle = (2 * PI) / divs * a;
		float z = (radius*sin(angle));
		float y = (radius*cos(angle));
		float x = d;
		vertices.Add(FVector(x, y, z));
		a += 1;
		if (a == divs)
		{
			a = 0;
			d += segHeight;
		}
	}

	//create triangles from vertices
	for (int i = 0; i < vertices.Num(); i++)
	{
		if (i > divs && i%divs == 0)
		{
			triangles.Add(i - divs);
			triangles.Add(i - 1);
			triangles.Add(i - (divs + 1));

			triangles.Add(i - divs * 2);
			triangles.Add(i - divs);
			triangles.Add(i - (divs + 1));
		}
		else if (i > divs)
		{
			triangles.Add(i);
			triangles.Add(i - 1);
			triangles.Add(i - (divs + 1));

			triangles.Add(i);
			triangles.Add(i - (divs + 1));
			triangles.Add(i - divs);
		}
	}

    //displace verts to resemble cave
	for (int i = 0; i < vertices.Num(); i++)
	{
		if (vertices[i].Z < 0 - baseRadius / 2)
		{
			vertices[i].Z = FMath::FRandRange(0 - (baseRadius + baseRadius / 4), 0 - baseRadius / 3);
		}
		vertices[i].Y += FMath::FRandRange(0 - baseRadius / 20, baseRadius / 20);

	}
	mesh->CreateMeshSection(1, vertices, triangles, TArray<FVector>(), TArray<FVector2D>(), TArray<FColor>(), TArray<FProcMeshTangent>(), true);

	mesh->AttachTo(RootComponent);
	Super::BeginPlay();
}

The mesh generates and renders in-game, but it’s just this default gray, and I was hoping to use a custom material for it. If someone could teach me how to add a custom material into this code, it’d be much appreciated!
Thanks!

You have a few options:
First, you’re going to need to assign or in your material, use the Absolute World Position node.
You’ll also need a way to assign your material to your mesh. In your header file add:

UPROPERTY(EditAnywhere)
UMaterialInterface* Material;

then in your cpp after you have created the mesh section:

mesh->SetMaterial(1, Material);

The “1” parameter is your mesh section index. At this point, you’ll probably need to wrap your AMyActor with a blueprint. When you do that, you get access to your Material variable that you created.

67576-capture.png

Now all you need to do is create your material, and set it.

Hi, thanks for the swift reply! That’s helped a lot. After I applied the default M_Rock_Basalt material the colour changed, but there’s no detail to the texture. I’m thinking this is to do with UVs, though I don’t know how to add UVs. Any ideas on how to continue?

You are correct about needing UVs. I had the same results with my procedural mesh before adding them - my grassy cube was just all the same shade of green.

Each vertex needs a matching UV0 element. These map a point on a texture to the vertex. UV0 X and Y between 0 and 1 are for one tile. A larger UV0 would be a new tile. e.g. (1.5, 0.5) would be the center of the texture, but on the second tile on the X axis.

In your code above I would add a new line after line 47 of your .cpp:

UV0.Add(FVector2D(x/(2.0f * segHeight), a/8.0f));

I think this would make it so you had a tile covering every two sections and three tiles around the circle (24 div / 8).