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;// SourceModelconst 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 postionfor(const FVertexID VertexID : VertexArray.GetElementIDs()){FVector3f& Pos = VertexPositions[VertexID];}UE_LOG(LogTemp, Warning, TEXT("Vertex num = %d"), VertexArray.Num());//loop vertex instance attributeconst 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 attributeconst 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 attributeconst 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 polygonconst 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 vertexfor(int32 Index = 0; Index < Vertices.Num(); Index++){FVertexID VertexId = MeshDescription.CreateVertex();VertexPositions.Set(VertexId, Vertices[Index].Position);}//create vertex instancefor(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/uvFPolygonGroupID 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 descconst 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);
}