10 KiB
10 KiB
title, date, excerpt, tags, rating
title | date | excerpt | tags | rating |
---|---|---|---|---|
Untitled | 2025-01-05 11:32:25 | ⭐ |
Assimp
加载选项:
- aiProcess_JoinIdenticalVertices:
- aiProcess_RemoveComponent:
- aiProcess_OptimizeMeshes:推荐移除,会导致模型只有一个Material。
真正的数据都存储在Scene节点mMeshes[]
、mMaterials[]
中。其RootNode、ChildrenNode存储对应的数据Index。
- mMeshes[]
- mVertices[]
- mNormals[]
- mTextureCoords[]
- mFaces[]
- mIndices[]
- mMaterialIndex
Materials
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
可用组件
URuntimeMeshComponent
第三方插件实现。
UDynamicMeshComponent
- https://zhuanlan.zhihu.com/p/506779703
- https://www.bilibili.com/opus/798754326935764996
- https://zhuanlan.zhihu.com/p/649062059
UProceduralMeshComponent
使用现在StaticMesh构建PMC,PMC的MaterialSection是正常的。
- UKismetProceduralMeshLibrary::CopyProceduralMeshFromStaticMeshComponent()
void UKismetProceduralMeshLibrary::CopyProceduralMeshFromStaticMeshComponent(UStaticMeshComponent* StaticMeshComponent, int32 LODIndex, UProceduralMeshComponent* ProcMeshComponent, bool bCreateCollision)
{
if( StaticMeshComponent != nullptr &&
StaticMeshComponent->GetStaticMesh() != nullptr &&
ProcMeshComponent != nullptr )
{
UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh();
//// MESH DATA
int32 NumSections = StaticMesh->GetNumSections(LODIndex);
for (int32 SectionIndex = 0; SectionIndex < NumSections; SectionIndex++)
{
// Buffers for copying geom data
TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
TArray<FVector2D> UVs1;
TArray<FVector2D> UVs2;
TArray<FVector2D> UVs3;
TArray<FProcMeshTangent> Tangents;
// Get geom data from static mesh
GetSectionFromStaticMesh(StaticMesh, LODIndex, SectionIndex, Vertices, Triangles, Normals, UVs, Tangents);
// Create section using data
TArray<FLinearColor> DummyColors;
ProcMeshComponent->CreateMeshSection_LinearColor(SectionIndex, Vertices, Triangles, Normals, UVs, UVs1, UVs2, UVs3, DummyColors, Tangents, bCreateCollision);
}
//// SIMPLE COLLISION
// Clear any existing collision hulls
ProcMeshComponent->ClearCollisionConvexMeshes();
if (StaticMesh->GetBodySetup() != nullptr)
{
// Iterate over all convex hulls on static mesh..
const int32 NumConvex = StaticMesh->GetBodySetup()->AggGeom.ConvexElems.Num();
for (int ConvexIndex = 0; ConvexIndex < NumConvex; ConvexIndex++)
{
// Copy convex verts to ProcMesh
FKConvexElem& MeshConvex = StaticMesh->GetBodySetup()->AggGeom.ConvexElems[ConvexIndex];
ProcMeshComponent->AddCollisionConvexMesh(MeshConvex.VertexData);
}
}
//// MATERIALS
for (int32 MatIndex = 0; MatIndex < StaticMeshComponent->GetNumMaterials(); MatIndex++)
{
ProcMeshComponent->SetMaterial(MatIndex, StaticMeshComponent->GetMaterial(MatIndex));
}
}
}
void UKismetProceduralMeshLibrary::GetSectionFromStaticMesh(UStaticMesh* InMesh, int32 LODIndex, int32 SectionIndex, TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector>& Normals, TArray<FVector2D>& UVs, TArray<FProcMeshTangent>& Tangents)
{
if( InMesh != nullptr )
{
if (!InMesh->bAllowCPUAccess)
{
FMessageLog("PIE").Warning()
->AddToken(FTextToken::Create(LOCTEXT("GetSectionFromStaticMeshStart", "Calling GetSectionFromStaticMesh on")))
->AddToken(FUObjectToken::Create(InMesh))
->AddToken(FTextToken::Create(LOCTEXT("GetSectionFromStaticMeshEnd", "but 'Allow CPU Access' is not enabled. This is required for converting StaticMesh to ProceduralMeshComponent in cooked builds.")));
}
if (InMesh->GetRenderData() != nullptr && InMesh->GetRenderData()->LODResources.IsValidIndex(LODIndex))
{
const FStaticMeshLODResources& LOD = InMesh->GetRenderData()->LODResources[LODIndex];
if (LOD.Sections.IsValidIndex(SectionIndex))
{
// Empty output buffers
Vertices.Reset();
Triangles.Reset();
Normals.Reset();
UVs.Reset();
Tangents.Reset();
// Map from vert buffer for whole mesh to vert buffer for section of interest
TMap<int32, int32> MeshToSectionVertMap;
const FStaticMeshSection& Section = LOD.Sections[SectionIndex];//获取指定的MeshSection
const uint32 OnePastLastIndex = Section.FirstIndex + Section.NumTriangles * 3;//计算最后一个VertexIndex
FIndexArrayView Indices = LOD.IndexBuffer.GetArrayView();//获得IndexArray
//遍历所有IndexBuffer,并且复制顶点
for (uint32 i = Section.FirstIndex; i < OnePastLastIndex; i++)
{
uint32 MeshVertIndex = Indices[i];//取得VertexIndex
// See if we have this vert already in our section vert buffer, and copy vert in if not
// 从VertexBuffers.StaticMeshVertexBuffer中读取并且添加Vertex Position、Normal、UVs、Tangents;构建MeshToSectionVertMap作为缓存,如果能在Map找到则直接返回Index。
int32 SectionVertIndex = GetNewIndexForOldVertIndex(MeshVertIndex, MeshToSectionVertMap, LOD.VertexBuffers, Vertices, Normals, UVs, Tangents);
// Add to index buffer
Triangles.Add(SectionVertIndex);
}
}
}
}
}
static int32 GetNewIndexForOldVertIndex(int32 MeshVertIndex, TMap<int32, int32>& MeshToSectionVertMap, const FStaticMeshVertexBuffers& VertexBuffers, TArray<FVector>& Vertices, TArray<FVector>& Normals, TArray<FVector2D>& UVs, TArray<FProcMeshTangent>& Tangents)
{
int32* NewIndexPtr = MeshToSectionVertMap.Find(MeshVertIndex);
if (NewIndexPtr != nullptr)
{
return *NewIndexPtr;
}
else
{
// Copy position
int32 SectionVertIndex = Vertices.Add((FVector)VertexBuffers.PositionVertexBuffer.VertexPosition(MeshVertIndex));
// Copy normal
Normals.Add(FVector4(VertexBuffers.StaticMeshVertexBuffer.VertexTangentZ(MeshVertIndex)));
check(Normals.Num() == Vertices.Num());
// Copy UVs
UVs.Add(FVector2D(VertexBuffers.StaticMeshVertexBuffer.GetVertexUV(MeshVertIndex, 0)));
check(UVs.Num() == Vertices.Num());
// Copy tangents
FVector4 TangentX = (FVector4)VertexBuffers.StaticMeshVertexBuffer.VertexTangentX(MeshVertIndex);
FProcMeshTangent NewTangent(TangentX, TangentX.W < 0.f);
Tangents.Add(NewTangent);
check(Tangents.Num() == Vertices.Num());
MeshToSectionVertMap.Add(MeshVertIndex, SectionVertIndex);
return SectionVertIndex;
}
}
Editor转换成StaticMesh逻辑: FProceduralMeshComponentDetails::ClickedOnConvertToStaticMesh()
https://forums.unrealengine.com/t/procedural-mesh-not-saving-all-of-its-sections-to-static-mesh/382319/10 感觉链接中的这个代码是正确:
//Hallo from Unreal Forums
UStaticMesh* UWeedFarmerBFL::SaveProcmesh(UProceduralMeshComponent* ProcMesh, FString SavePath, FString Name)
{
UProceduralMeshComponent* ProcMeshComp = ProcMesh;
if (ProcMeshComp != nullptr)
{
FString PackageName = SavePath;
FRawMesh RawMesh;
TArray<UMaterialInterface*> MeshMaterials;
const int32 NumSections = ProcMeshComp->GetNumSections();
int32 VertexBase = 0;
for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
{
FProcMeshSection* ProcSection = ProcMeshComp->GetProcMeshSection(SectionIdx);
// Copy verts
for (FProcMeshVertex& Vert : ProcSection->ProcVertexBuffer)
{
RawMesh.VertexPositions.Add(FVector3f(Vert.Position));
}
// Copy 'wedge' info
int32 NumIndices = ProcSection->ProcIndexBuffer.Num();
for (int32 IndexIdx = 0; IndexIdx < NumIndices; IndexIdx++)
{
int32 Index = ProcSection->ProcIndexBuffer[IndexIdx];
RawMesh.WedgeIndices.Add(Index + VertexBase);
FProcMeshVertex& ProcVertex = ProcSection->ProcVertexBuffer[Index];
FVector3f TangentX = FVector3f(ProcVertex.Tangent.TangentX);
FVector3f TangentZ = FVector3f(ProcVertex.Normal);
FVector3f TangentY = FVector3f(
(TangentX ^ TangentZ).GetSafeNormal() * (ProcVertex.Tangent.bFlipTangentY ? -1.f : 1.f));
RawMesh.WedgeTangentX.Add(TangentX);
RawMesh.WedgeTangentY.Add(TangentY);
RawMesh.WedgeTangentZ.Add(TangentZ);
RawMesh.WedgeTexCoords[0].Add(FVector2f(ProcVertex.UV0));
RawMesh.WedgeColors.Add(ProcVertex.Color);
}
// copy face info
int32 NumTris = NumIndices / 3;
for (int32 TriIdx = 0; TriIdx < NumTris; TriIdx++)
{
RawMesh.FaceMaterialIndices.Add(SectionIdx);
RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
}
// Remember material
MeshMaterials.Add(ProcMeshComp->GetMaterial(SectionIdx));
// Update offset for creating one big index/vertex buffer
VertexBase += ProcSection->ProcVertexBuffer.Num();
}
// If we got some valid data.
if (RawMesh.VertexPositions.Num() > 3 && RawMesh.WedgeIndices.Num() > 3)
{
// Then find/create it.
UPackage* Package = CreatePackage(*PackageName);
check(Package);
// Create StaticMesh object
UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, FName(*Name), RF_Public | RF_Standalone);
StaticMesh->InitResources();
FGuid::NewGuid() = StaticMesh->GetLightingGuid();
//StaticMesh->GetLightingGuid() = FGuid::NewGuid();
// Create a Source Model then set it to variable
StaticMesh->AddSourceModel();
FStaticMeshSourceModel& SrcModel = StaticMesh->GetSourceModel(0);
// Add source to new StaticMesh
SrcModel.BuildSettings.bRecomputeNormals = false;
SrcModel.BuildSettings.bRecomputeTangents = false;
SrcModel.BuildSettings.bRemoveDegenerates = false;
SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false;
SrcModel.BuildSettings.bUseFullPrecisionUVs = false;
SrcModel.BuildSettings.bGenerateLightmapUVs = true;
SrcModel.BuildSettings.SrcLightmapIndex = 0;
SrcModel.BuildSettings.DstLightmapIndex = 1;
SrcModel.RawMeshBulkData->SaveRawMesh(RawMesh);
// Copy materials to new mesh
for (UMaterialInterface* Material : MeshMaterials)
{
StaticMesh->GetStaticMaterials().Add(FStaticMaterial(Material));
}
//Set the Imported version before calling the build
StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;
// Build mesh from source
StaticMesh->Build(false);
StaticMesh->PostEditChange();
// Notify asset registry of new asset
FAssetRegistryModule::AssetCreated(StaticMesh);
return StaticMesh;
}
}
return nullptr;
}