效果
如下所示,通过“ProceduralMeshComponent”创建了一个自定义形状的Mesh,并且该Mesh包含碰撞信息,然后2s后更新Mesh形状。
步骤
1. 在“xxx.Build.cs”中引入“ProceduralMeshComponent”模块
2. 新建一个Actor类,这里命名为“DrawMeshActor”
在“DrawMeshActor.h”中引入“ProceduralMeshComponent.h”
定义“程序化网格组件”和创建自定义Mesh的方法“GenerateMesh”。其中函数“GenerateMesh”需要传入材质、三角形网格数组“Triangles”和一个布尔值“bCreateCollision”,“bCreateCollision”表示是否要生成碰撞。
三角形网格数组“Triangles”中的元素是结构体“FTriangleData”,该结构体定义如下,主要由三个点的位置构成
3. 在“DrawMeshActor.cpp”中,先引入"Materials/MaterialInterface.h",然后在“DrawMeshActor”构造时添加“ProceduralMeshComponent”组件
最后实现函数“GenerateMesh”如下
void ADrawMeshActor::GenerateMesh(UMaterialInterface* InMaterial, const TArray<FTriangleData>& Triangles, bool bCreateCollision)
{
if (!ProceduralMesh)
{
UE_LOG(LogTemp, Error, TEXT("GenerateMeshFromTriangles: ProceduralMesh component is null."));
return;
}
if (Triangles.Num() == 0)
{
UE_LOG(LogTemp, Warning, TEXT("GenerateMeshFromTriangles: Input Triangles array is empty. Clearing mesh."));
ProceduralMesh->ClearMeshSection(0);
return;
}
//清除之前的网格
ProceduralMesh->ClearMeshSection(0);
//根据三角形数量计算顶点个数
int32 NumTriangles = Triangles.Num();
int32 NumVertices = NumTriangles * 3;
TArray<FVector> Vertices;
TArray<int32> TriangleIndices;
TArray<FVector> Normals;
TArray<FVector2D> UV0;
TArray<FProcMeshTangent> Tangents;
TArray<FLinearColor> VertexColors;
//预留空间
Vertices.Reserve(NumVertices);
TriangleIndices.Reserve(NumVertices);
Normals.Reserve(NumVertices);
UV0.Reserve(NumVertices);
//循环添加每一个三角形网格数据
for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex)
{
const FTriangleData& CurrentTriangle = Triangles[TriIndex];
int32 BaseVertexIndex = TriIndex * 3;
//为三角形添加顶点
Vertices.Add(CurrentTriangle.Point1);
Vertices.Add(CurrentTriangle.Point2);
Vertices.Add(CurrentTriangle.Point3);
//添加三角形索引
TriangleIndices.Add(BaseVertexIndex + 0);
TriangleIndices.Add(BaseVertexIndex + 1);
TriangleIndices.Add(BaseVertexIndex + 2);
// 计算三角形法线
FVector Normal = FVector::CrossProduct(CurrentTriangle.Point3 - CurrentTriangle.Point1, CurrentTriangle.Point2 - CurrentTriangle.Point1);
Normal.Normalize();
//为三角形的所有3个顶点添加相同的法线
Normals.Add(Normal);
Normals.Add(Normal);
Normals.Add(Normal);
//添加UV
UV0.Add(FVector2D(0.0f, 1.0f)); // UV for Point1
UV0.Add(FVector2D(1.0f, 1.0f)); // UV for Point2
UV0.Add(FVector2D(0.0f, 0.0f)); // UV for Point3
}
//创建网格
ProceduralMesh->CreateMeshSection_LinearColor(
0,
Vertices,
TriangleIndices,
Normals,
UV0,
VertexColors,
Tangents,
bCreateCollision
);
//设置材质
if (InMaterial)
{
ProceduralMesh->SetMaterial(0, InMaterial);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("GenerateMeshFromTriangles: Input Material (InMaterial) is null. Mesh section 0 might have default material."));
// if (DefaultMaterial) ProceduralMesh->SetMaterial(0, DefaultMaterial);
}
}
源码:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "DrawMeshActor.generated.h"
USTRUCT(BlueprintType)
struct FTriangleData
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Triangle Data")
FVector Point1 = FVector::ZeroVector;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Triangle Data")
FVector Point2 = FVector::ZeroVector;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Triangle Data")
FVector Point3 = FVector::ZeroVector;
FTriangleData() {}
FTriangleData(const FVector& InP1, const FVector& InP2, const FVector& InP3)
: Point1(InP1), Point2(InP2), Point3(InP3) {}
};
UCLASS()
class TESTPROJECT2_API ADrawMeshActor : public AActor
{
GENERATED_BODY()
public:
ADrawMeshActor();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
public:
// 程序化网格组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UProceduralMeshComponent* ProceduralMesh;
UFUNCTION(BlueprintCallable)
void GenerateMesh(UMaterialInterface* InMaterial, const TArray<FTriangleData>& Triangles, bool bCreateCollision);
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "DrawMeshActor.h"
#include "Materials/MaterialInterface.h"
ADrawMeshActor::ADrawMeshActor()
{
PrimaryActorTick.bCanEverTick = false;
// 创建程序化网格组件并设为根组件
ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("ProceduralMesh"));
SetRootComponent(ProceduralMesh);
ProceduralMesh->bUseAsyncCooking = true; // 可选:在单独线程准备物理数据
}
// Called when the game starts or when spawned
void ADrawMeshActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ADrawMeshActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ADrawMeshActor::GenerateMesh(UMaterialInterface* InMaterial, const TArray<FTriangleData>& Triangles, bool bCreateCollision)
{
if (!ProceduralMesh)
{
UE_LOG(LogTemp, Error, TEXT("GenerateMeshFromTriangles: ProceduralMesh component is null."));
return;
}
if (Triangles.Num() == 0)
{
UE_LOG(LogTemp, Warning, TEXT("GenerateMeshFromTriangles: Input Triangles array is empty. Clearing mesh."));
ProceduralMesh->ClearMeshSection(0);
return;
}
//清除之前的网格
ProceduralMesh->ClearMeshSection(0);
//根据三角形数量计算顶点个数
int32 NumTriangles = Triangles.Num();
int32 NumVertices = NumTriangles * 3;
TArray<FVector> Vertices;
TArray<int32> TriangleIndices;
TArray<FVector> Normals;
TArray<FVector2D> UV0;
TArray<FProcMeshTangent> Tangents;
TArray<FLinearColor> VertexColors;
//预留空间
Vertices.Reserve(NumVertices);
TriangleIndices.Reserve(NumVertices);
Normals.Reserve(NumVertices);
UV0.Reserve(NumVertices);
//循环添加每一个三角形网格数据
for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex)
{
const FTriangleData& CurrentTriangle = Triangles[TriIndex];
int32 BaseVertexIndex = TriIndex * 3;
//为三角形添加顶点
Vertices.Add(CurrentTriangle.Point1);
Vertices.Add(CurrentTriangle.Point2);
Vertices.Add(CurrentTriangle.Point3);
//添加三角形索引
TriangleIndices.Add(BaseVertexIndex + 0);
TriangleIndices.Add(BaseVertexIndex + 1);
TriangleIndices.Add(BaseVertexIndex + 2);
// 计算三角形法线
FVector Normal = FVector::CrossProduct(CurrentTriangle.Point3 - CurrentTriangle.Point1, CurrentTriangle.Point2 - CurrentTriangle.Point1);
Normal.Normalize();
//为三角形的所有3个顶点添加相同的法线
Normals.Add(Normal);
Normals.Add(Normal);
Normals.Add(Normal);
//添加UV
UV0.Add(FVector2D(0.0f, 1.0f)); // UV for Point1
UV0.Add(FVector2D(1.0f, 1.0f)); // UV for Point2
UV0.Add(FVector2D(0.0f, 0.0f)); // UV for Point3
}
//创建网格
ProceduralMesh->CreateMeshSection_LinearColor(
0,
Vertices,
TriangleIndices,
Normals,
UV0,
VertexColors,
Tangents,
bCreateCollision
);
//设置材质
if (InMaterial)
{
ProceduralMesh->SetMaterial(0, InMaterial);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("GenerateMeshFromTriangles: Input Material (InMaterial) is null. Mesh section 0 might have default material."));
// if (DefaultMaterial) ProceduralMesh->SetMaterial(0, DefaultMaterial);
}
}
4. 在UE编辑器中创建基于“DrawMeshActor”的蓝图派生类“BP_DrawMeshActor”,再创建一个材质
在“BP_DrawMeshActor”事件图表中调用函数“GenerateMesh”来生成自定义网格体
最终效果如文章开头所示。