目标
之前的博客《UE4尝试用C++生成一个最简单的StaticMesh》是创建一个新的StaticMesh,本篇则是对一个已有的StaticMesh的顶点数据进行修改。思路上是获取StaticMesh的RawMesh,然后对其进行操作。
本篇内容具体包括:
- 创建一个插件,其中包含能修改StaticMesh的顶点数据的功能。
- 总结出通用的框架代码。
实验插件
以Blank为模板创建插件
在插件模块的.Build.cs
文件中添加RawMesh
到PrivateDependencyModuleNames
列表中
接下来,我选择定义一个Actor来实现功能。因为Actor可以很方便被创建,且其UPROPERTY
可以在Detail面板中有对应界面,这样我能更方便指定StaticMesh并提供按钮界面。
StaticMeshModifier.h
:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include"StaticMeshModifier.generated.h"
UCLASS()
class AStaticMeshModifier : public AActor
{
GENERATED_UCLASS_BODY()
public:
UPROPERTY(Category = "StaticMeshModifier", EditAnywhere)
UStaticMesh* StaticMesh;
UFUNCTION(Category = "StaticMeshModifier", CallInEditor)
void Process();
};
StaticMeshModifier.cpp
:
当前所做的操作只是简单地将模型的最低点对齐到水平位置
#include "StaticMeshModifier.h"
#include "Engine/StaticMesh.h"
#include "RawMesh.h"
AStaticMeshModifier::AStaticMeshModifier(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
}
void AStaticMeshModifier::Process()
{
//表明将要更改StaticMesh
StaticMesh->PreEditChange(nullptr);
//遍历所有LOD
for (int LOD = 0; LOD < StaticMesh->SourceModels.Num(); LOD++)
{
//获得此LOD的SourceModel
FStaticMeshSourceModel& SourceModel = StaticMesh->SourceModels[LOD];
//获得RawMesh
FRawMesh RawMesh;
SourceModel.RawMeshBulkData->LoadRawMesh(RawMesh);
//处理顶点数据,这里就是将模型的最低点对齐到水平位置
{
//最低点
float MinZ = RawMesh.VertexPositions[0].Z;
//找到最低点:
for (int VertexId = 1; VertexId < RawMesh.VertexPositions.Num(); VertexId++)
{
if (RawMesh.VertexPositions[VertexId].Z < MinZ)
MinZ = RawMesh.VertexPositions[VertexId].Z;
}
//对所有顶点进行偏移:
for (int VertexId = 0; VertexId < RawMesh.VertexPositions.Num(); VertexId++)
RawMesh.VertexPositions[VertexId].Z -= MinZ;
}
//保存RawMesh
SourceModel.SaveRawMesh(RawMesh);
}
TArray< FText > BuildErrors;
StaticMesh->Build(true, &BuildErrors);
//表明修改结束StaticMesh
StaticMesh->PostEditChange();
}
修改前:
使用时:
- 将StaticMeshModifier这个Actor拖到场景中
- 将待处理模型放到StaticMeshModifier细节面板中
- 点击Process按钮
修改后:
总结
代码框架:
//表明将要更改StaticMesh
StaticMesh->PreEditChange(nullptr);
//遍历所有LOD
for (int LOD = 0; LOD < StaticMesh->SourceModels.Num(); LOD++)
{
//获得此LOD的SourceModel
FStaticMeshSourceModel& SourceModel = StaticMesh->SourceModels[LOD];
//获得RawMesh
FRawMesh RawMesh;
SourceModel.RawMeshBulkData->LoadRawMesh(RawMesh);
//处理顶点数据
{
//在这里写修改的逻辑
}
//保存RawMesh
SourceModel.SaveRawMesh(RawMesh);
}
TArray< FText > BuildErrors;
StaticMesh->Build(true, &BuildErrors);
//表明修改结束StaticMesh
StaticMesh->PostEditChange();
另外记得要在.Build.cs
文件中将RawMesh
到PrivateDependencyModuleNames
中。
对于FRawMesh
的数据结构,可以参考《UE4实验观察FRawMesh的数据结构》
问题记录
我这里有时候会发现FRawMesh
的数据是空的:
不过如果重新导入,则就能得到
关于“RawMesh数据是否能保证得到”的问题,待深入研究。