UStaticMesh相关类图
UStaticMesh的数据构成
UStaticMesh的FStaticMeshSourceModel
UStaticMesh的Mesh几何元数据来自于FStaticMeshSourceModel, 一级Lod就存在一个FStaticMeshSourceModel. FStaticMeshSourceModel几何数据大致包含以下几类:
Vertex(点), VertexInstance(顶点), Edge(边), Triangle(三角形), Polygon(多边形)
总体上概念和Houdini的一些几何概念相似,每种几何元素都可以附带属性,比如点的“Position”, 顶点的“Normal,UV”等等。
下面介绍下如何获取Mesh元数据和相关属性
void AMyActor::TestMeshInfo() const
{
if(!Mesh)
return;
// SourceModel
const TArray<FStaticMeshSourceModel>& SourceModels = Mesh->GetSourceModels();
for(int32 Index = 0; Index < SourceModels.Num(); Index++)
{
FMeshDescription* MeshDesc = Mesh->GetMeshDescription(Index);
if(MeshDesc)
{
const FVertexArray& VertexArray = MeshDesc->Vertices();
// TVertexAttributesRef<FVector3f> VertexPositions = MeshDesc->VertexAttributes().GetAttributesRef<FVector3f>(MeshAttribute::Vertex::Position);
TVertexAttributesRef<FVector3f> VertexPositions = MeshDesc->GetVertexPositions();
//loop vertex postion
for(const FVertexID VertexID : VertexArray.GetElementIDs())
{
FVector3f& Pos = VertexPositions[VertexID];
}
UE_LOG(LogTemp, Warning, TEXT("Vertex num = %d"), VertexArray.Num());
//loop vertex instance attribute
const FVertexInstanceArray& VertexInstanceArray = MeshDesc->VertexInstances();
TVertexInstanceAttributesRef<FVector4f> VertexInstanceColors = MeshDesc->VertexInstanceAttributes().GetAttributesRef<FVector4f>(MeshAttribute::VertexInstance::Color);
for(const FVertexInstanceID VertxInstanceId : VertexInstanceArray.GetElementIDs())
{
}
UE_LOG(LogTemp, Warning, TEXT("VertexInstanceArray num = %d"), VertexInstanceArray.Num());
//loop vertex edge attribute
const FEdgeArray& EdgeArray = MeshDesc->Edges();
TEdgeAttributesRef<bool> EdgeHardness = MeshDesc->EdgeAttributes().GetAttributesRef<bool>(MeshAttribute::Edge::IsHard);
for (FEdgeID EdgeID : EdgeArray.GetElementIDs())
{
}
UE_LOG(LogTemp, Warning, TEXT("EdgeArray num = %d"), EdgeArray.Num());
// loop triangle attribute
const FTriangleArray& TriangleArray = MeshDesc->Triangles();
TTriangleAttributesRef<FVector3f> TriangleNormals = MeshDesc->TriangleAttributes().GetAttributesRef<FVector3f>(MeshAttribute::Triangle::Normal);
for (FTriangleID TriangleID : TriangleArray.GetElementIDs())
{
}
UE_LOG(LogTemp, Warning, TEXT("TriangleArray num = %d"), TriangleArray.Num());
// loop polygon
const FPolygonArray& PolygonArray = MeshDesc->Polygons();
TPolygonAttributesRef<int32> PatchGroups = MeshDesc->PolygonAttributes().GetAttributesRef<int32>(MeshAttribute::Polygon::PolygonGroupIndex);
UE_LOG(LogTemp, Warning, TEXT("PolygonArray num = %d"), PolygonArray.Num());
}
if(Mesh->GetRenderData()->LODResources.Num() > Index)
{
TArray<FVector3f> Vertexs;
auto& LodResource = Mesh->GetRenderData()->LODResources[Index];
int32 VertexNum = LodResource.GetNumVertices();
for(int32 I = 0; I < VertexNum; I++)
{
Vertexs.Add(LodResource.VertexBuffers.PositionVertexBuffer.VertexPosition(I));
}
int32 TriangleNum = LodResource.GetNumTriangles();
TArray<uint32> Indexs;
for(int32 I = 0; I < TriangleNum; I++)
{
Indexs.Add(LodResource.IndexBuffer.GetIndex(I * 3 + 0));
Indexs.Add(LodResource.IndexBuffer.GetIndex(I * 3 + 1));
Indexs.Add(LodResource.IndexBuffer.GetIndex(I * 3 + 2));
}
}
}
}
如何填充数据生成一个UStaticMesh
主要是通过填充FMeshDescription的各种图元数据(Vertex, VertexInstance, Polygon, PolygonGroup等等)
void AMyActor::TestCreateStaticMesh()
{
const FString ObjectName = "TestStaticMesh";
const FString GameFoldPath = "/Game";
const FString PackageName = FString::Printf(TEXT("%s/%s"), *GameFoldPath, *ObjectName);
UPackage* Package = CreatePackage(*PackageName);
UStaticMesh* MyMesh = NewObject<UStaticMesh>(Package, *ObjectName, RF_Standalone | RF_Public);
FMeshDescription MeshDescription;
FStaticMeshAttributes MeshAttribute(MeshDescription);
MeshAttribute.Register();
TArray<FDynamicMeshVertex> Vertices;
Vertices.Add(FDynamicMeshVertex(FVector3f(500.0f, 500.0f, 200)));
Vertices.Add(FDynamicMeshVertex(FVector3f(-500.0f, 500.0f, 200)));
Vertices.Add(FDynamicMeshVertex(FVector3f(-500.0f, -500.0f, 200)));
Vertices.Add(FDynamicMeshVertex(FVector3f(500.0f, -500.0f, 200)));
TArray<int32> Indexs;
Indexs.Add(0);
Indexs.Add(3);
Indexs.Add(1);
Indexs.Add(1);
Indexs.Add(3);
Indexs.Add(2);
MeshDescription.Empty();
MeshDescription.ReserveNewVertices(Vertices.Num());
MeshDescription.ReserveNewTriangles(Indexs.Num() / 3);
MeshDescription.ReserveNewVertexInstances(Indexs.Num());
TVertexAttributesRef<FVector3f> VertexPositions = MeshAttribute.GetVertexPositions();
TVertexInstanceAttributesRef<FVector2f> VertexInstanceUVs = MeshAttribute.GetVertexInstanceUVs();
TVertexInstanceAttributesRef<FVector3f> VertexInstanceNormals = MeshAttribute.GetVertexInstanceNormals();
TVertexInstanceAttributesRef<FVector3f> VertexInstanceTangents = MeshAttribute.GetVertexInstanceTangents();
TPolygonGroupAttributesRef<FName> MaterialSlotNames = MeshAttribute.GetPolygonGroupMaterialSlotNames();
TArray<UMaterialInterface*> MaterialInterfaces = {UMaterial::GetDefaultMaterial(MD_Surface)};
for(int32 Index = 0; Index < MaterialInterfaces.Num(); Index++)
{
FPolygonGroupID PolygonGroupID = MeshDescription.CreatePolygonGroup();
FStaticMaterial StaticMaterial = MaterialInterfaces[Index];
MaterialSlotNames[PolygonGroupID] = StaticMaterial.MaterialSlotName;
}
//create vertex
for(int32 Index = 0; Index < Vertices.Num(); Index++)
{
FVertexID VertexId = MeshDescription.CreateVertex();
VertexPositions.Set(VertexId, Vertices[Index].Position);
}
//create vertex instance
for(int32 Index = 0; Index < Indexs.Num(); Index++)
{
FVertexInstanceID VertexInstanceId = FVertexInstanceID(Index);
FVertexID VertexID = FVertexID(Indexs[Index]);
MeshDescription.CreateVertexInstanceWithID(VertexInstanceId, VertexID);
}
// set vertex instance normal/tangent/uv
FPolygonGroupID PolygonGroupID(0);
TArray<FVertexInstanceID> InstanceIds;
for(int32 Index = 0; Index < Indexs.Num(); Index++)
{
FVertexInstanceID VertexInstanceId = FVertexInstanceID(Index);
InstanceIds.Add(VertexInstanceId);
const FDynamicMeshVertex& DynamicVertex = Vertices[Indexs[Index]];
VertexInstanceUVs.Set(VertexInstanceId, DynamicVertex.TextureCoordinate[0]);
VertexInstanceNormals.Set(VertexInstanceId, DynamicVertex.TangentX.ToFVector3f());
VertexInstanceTangents.Set(VertexInstanceId, DynamicVertex.TangentZ.ToFVector3f());
if(InstanceIds.Num() == 3)
{
TArray<FEdgeID> EdgeIds;
MeshDescription.CreatePolygon(PolygonGroupID, InstanceIds, &EdgeIds);
InstanceIds.Empty();
}
}
// create source model lod0 and commit mesh desc
const int32 LodIndex = 0;
if(!MyMesh->IsSourceModelValid(LodIndex))
{
FStaticMeshSourceModel& SourceModel = MyMesh->AddSourceModel();
SourceModel.BuildSettings.bRecomputeNormals = false;
SourceModel.BuildSettings.bRecomputeTangents = false;
SourceModel.BuildSettings.bGenerateLightmapUVs = false;
}
MyMesh->CreateMeshDescription(LodIndex, MeshDescription);
MyMesh->CommitMeshDescription(LodIndex);
MyMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;
MyMesh->CreateBodySetup();
MyMesh->GetBodySetup()->CollisionReponse = EBodyCollisionResponse::BodyCollision_Disabled;
MyMesh->Build(true);
MyMesh->PostEditChange();
MyMesh->MarkPackageDirty();
FAssetRegistryModule::AssetCreated(MyMesh);
}