目标
通常,一个StaticMesh资源由一个模型文件(如fbx)导入得到。不过如果需要和某种程序化生成工具(如Houdini)交互,则需要用C++代码从特定数据中读取顶点数据然后生成StaticMesh。我想了解下这个过程。
我发现基础的步骤并不复杂,概括讲:
- 生成一个空的
UStaticMesh
- 创建一个
FRawMesh
- 向
UStaticMesh
中添加一个FStaticMeshSourceModel
,然后调用SaveRawMesh
接口来将FRawMesh
保存进去。 - 调用
UStaticMesh
的Build
接口。
本篇目标是创建一个插件,其中点击一个按钮可以生成一个最简单的StaticMesh。
0.插件
以Editor Toolbar Button
为模板创建一个插件。(其实这里对模板并没有什么严格的要求,选它是因为会在界面上生成一个按钮,比较方便)
插件名为TestCodeMeshPlg
接下来就可以在FTestCodeMeshPlgModule::PluginButtonClicked()
中指定这个按钮点击后的操作了。
1.创建StaticMesh资源
//设定模型名字
FString MeshName = "TestSM";
//设定包的路径
FString PackageName = "/Game/"+ MeshName;
//创建包
UPackage * MeshPackage = CreatePackage(nullptr, *PackageName);
//创建StaticMesh资源
UStaticMesh *StaticMesh = NewObject< UStaticMesh >(MeshPackage, FName(*MeshName), RF_Public | RF_Standalone);
2. 创建RawMesh
在创建之前,需要知道FRawMesh
有一个接口,负责检查当前的数据是否有效
/**
* Returns true if the mesh contains valid information.
* - Validates that stream sizes match.
* - Validates that there is at least one texture coordinate.
* - Validates that indices are valid positions in the vertex stream.
*/
RAWMESH_API bool IsValid() const;
他检查的具体内容如下:
bool FRawMesh::IsValid() const
{
int32 NumVertices = VertexPositions.Num();
int32 NumWedges = WedgeIndices.Num();
int32 NumFaces = NumWedges / 3;
bool bValid = NumVertices > 0
&& NumWedges > 0
&& NumFaces > 0
&& (NumWedges / 3) == NumFaces
&& ValidateArraySize(FaceMaterialIndices, NumFaces)
&& ValidateArraySize(FaceSmoothingMasks, NumFaces)
&& ValidateArraySize(WedgeTangentX, NumWedges)
&& ValidateArraySize(WedgeTangentY, NumWedges)
&& ValidateArraySize(WedgeTangentZ, NumWedges)
&& ValidateArraySize(WedgeColors, NumWedges)
// All meshes must have a valid texture coordinate.
&& WedgeTexCoords[0].Num() == NumWedges;
for (int32 TexCoordIndex = 1; TexCoordIndex < MAX_MESH_TEXTURE_COORDS; ++TexCoordIndex)
{
bValid = bValid && ValidateArraySize(WedgeTexCoords[TexCoordIndex], NumWedges);
}
int32 WedgeIndex = 0;
while (bValid && WedgeIndex < NumWedges)
{
bValid = bValid && ( WedgeIndices[WedgeIndex] < (uint32)NumVertices );
WedgeIndex++;
}
return bValid;
}
这样就需要注意,创建的FRawMesh
的数据要通过上面的检查。
下面是为了测试所创建的一个最简单的RawMesh,只是一个直角三角形。
//测试RawMesh
FRawMesh RawMesh;
//顶点位置数据,一个直角三角形
RawMesh.VertexPositions.Add(FVector(0, 0, 0));
RawMesh.VertexPositions.Add(FVector(1, 0, 0));
RawMesh.VertexPositions.Add(FVector(1, 1, 0));
//索引数据
RawMesh.WedgeIndices.Add(0);
RawMesh.WedgeIndices.Add(1);
RawMesh.WedgeIndices.Add(2);
//接下来的数据并不重要,只是需要满足Array的长度以能通过 FRawMesh::IsValid()
RawMesh.WedgeTexCoords->Add(FVector2D(0, 0));
RawMesh.WedgeTexCoords->Add(FVector2D(1, 0));
RawMesh.WedgeTexCoords->Add(FVector2D(1, 1));
RawMesh.FaceMaterialIndices.Add(0);
RawMesh.FaceSmoothingMasks.Add(0);
RawMesh.WedgeTangentX.Add(FVector(1,0,0));
RawMesh.WedgeTangentX.Add(FVector(1, 0, 0));
RawMesh.WedgeTangentX.Add(FVector(1, 0, 0));
RawMesh.WedgeTangentY.Add(FVector(0, 1, 0));
RawMesh.WedgeTangentY.Add(FVector(0, 1, 0));
RawMesh.WedgeTangentY.Add(FVector(0, 1, 0));
RawMesh.WedgeTangentZ.Add(FVector(0, 0, 1));
RawMesh.WedgeTangentZ.Add(FVector(0, 0, 1));
RawMesh.WedgeTangentZ.Add(FVector(0, 0, 1));
另外要注意的是,需要让插件的模块依赖RawMesh
这个模块:
3. 创建StaticMeshSourceModel
先标注需要开始编辑这个StaticMesh了:
StaticMesh->PreEditChange(nullptr);
添加一个FStaticMeshSourceModel
:
FStaticMeshSourceModel & SrcModel = StaticMesh->AddSourceModel();
保存RawMesh
SrcModel.SaveRawMesh(RawMesh);
4. 对StaticMesh进行Build
TArray< FText > BuildErrors;
StaticMesh->Build(true, &BuildErrors);
随后通知Asset创建
FAssetRegistryModule::AssetCreated(StaticMesh);
效果
点击按钮后可看到创建了一个StaticMesh
打开可看到小三角形: