最新的UE5已经提供了很多几何建模接口,而且这些接口都可以在Runtime下调用,避免了为生成UStaticMesh
而改源码所带来的麻烦。
运行时创建UStaticMesh
需要用到MeshDescription
和StaticMeshDescription
这两个模块。
MeshDescription
对基础模型的数据结构进行了定义。
StaticMeshDescription
则针对静态模型进行了更详细的定义,同时还提供了很多有用的几何运算函数,如计算法向量、计算切向量、计算UV、计算光照UV等。
加载模型的过程是这样的,首先加载和解析模型文件(.obj、.fbx、gltf等),然后通过所读取的三角面、顶点、法线等数据来构建FMeshDescription
,最后调用UStaticMesh::BuildFromMeshDescriptions
将模型显示出来。
那么在这个过程中需要注意以下几点:
单模型多材质的处理
我们经常会碰到单个模型上有多个材质,这是通过三角面材质索引列表和材质列表来实现的。三角面材质索引列表上记录了每个面所对应的材质索引,通过材质列表[三角面材质索引]
就能访问到材质。
在构建这样的模型时首先要通过UStaticMesh::AddMaterial
将材质添加到静态模型上,同时将返回的FName
保存到材质名队列中,然后将这个队列赋值给MeshAttribute::PolygonGroup::ImportedMaterialSlotName
属性。这里要注意材质名队列中的值必须是静态模型UStaticMesh::AddMaterial
的返回值,并不是材质UMaterialInterface::GetFName
的返回值。
/// create the static mesh
UStaticMesh* StaticMesh = NewObject<UStaticMesh>(/* owner */);
TArray<UMaterialInterface*> Materials;
/// TODO: build materials from the model
/// record the material names
TArray<FName> MaterialNames;
for (int32 i = 0, ic = Materials.Num(); i < ic; ++i)
{
MaterialNames.Add(StaticMesh->AddMaterial(Materials[i]));
}
FMeshDescription MeshDescription;
/// TODO: build your MeshDescription from a model file
/// set the material by the polygon group attribute
TPolygonGroupAttributesRef<FName> ImportedMaterialSlotName = MeshDescription.PolygonGroupAttributes().GetAttributesRef<FName>(MeshAttribute::PolygonGroup::ImportedMaterialSlotName);
for (int32 i = 0, ic = MaterialNames.Num(); i < ic; ++i)
{
ImportedMaterialSlotName.Set(i, MaterialNames[i]);
}
对静态模型的属性进行注册
如果你想用StaticMeshDescription
模块里的函数,那么必须要通过FStaticMeshAttributes::Register
注册静态模型属性之后才能调用。这样做的原因是:基础模型与静态模型的属性是有区别的,通过这个注册将静态模型属性赋予FMeshDescription
中,StaticMeshDescription
的函数就可以从FMeshDescription
中访问到所需的静态模型属性来进行运算,否则会出现因访问错误而引起的崩溃。
当然你也可以使用
UStaticMeshDescription
类来进行编辑,同时也需要调用UStaticMeshDescription::RegisterAttributes
来完成注册。
FMeshDescription MeshDescription;
/// TODO: set the triangle index, vertex, normal, texcoord
FStaticMeshAttributes(MeshDescription).Register();
/// then you can call functions from `FStaticMeshOperations`
FStaticMeshOperations::ComputeTriangleTangentsAndNormals(MeshDescription);
// or
UStaticMeshDescription* StaticMeshDescription = /* NewObject */;
/// TODO: set the triangle index, vertex, normal, texcoord
StaticMeshDescription->RegisterAttributes();
/// then you can call functions from `FStaticMeshOperations`
/// example: compute the tangent and normal
FStaticMeshOperations::ComputeTriangleTangentsAndNormals(StaticMeshDescription->GetMeshDescription());
题外话
过去很期待的一个功能是在Runtime下生成UStaticMesh
,可是当时只能通过FRawMesh
在Editor下生成,唯一的办法是改动引擎源码。后来慢慢地出现了很多新的模块,其中许多都可以在Runtime下处理模型。
其中一个很重要的改动是允许FRawMesh
运行在Runtime下,这样做的好处是FRawMesh
也可以在Runtime下构建UStaticMesh
了(通过FStaticMeshOperations::ConvertFromRawMesh
将FRawMesh
转换为FMeshDescription
,再调用UStaticMesh::BuildFromMeshDescriptions
显示出来),而且过去的旧代码依然能用。