BlueRoseNote/03-UnrealEngine/Gameplay/Gameplay/Assimp & UProceduralMeshComponent实时载入StaticMesh.md

277 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Untitled
date: 2025-01-05 11:32:25
excerpt:
tags:
rating: ⭐
---
# Assimp
加载选项:
- aiProcess_JoinIdenticalVertices
- aiProcess_RemoveComponent
- aiProcess_OptimizeMeshes推荐移除会导致模型只有一个Material。
真正的数据都存储在Scene节点`mMeshes[]``mMaterials[]`中。其RootNode、ChildrenNode存储对应的数据Index。
- mMeshes[]
- mVertices[]
- mNormals[]
- mTextureCoords[]
- mFaces[]
- mIndices[]
- mMaterialIndex
## Materials
```c++
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
```
# 可用组件
## URuntimeMeshComponent
第三方插件实现。
## UDynamicMeshComponent
1. https://zhuanlan.zhihu.com/p/506779703
2. https://www.bilibili.com/opus/798754326935764996
3. https://zhuanlan.zhihu.com/p/649062059
## UProceduralMeshComponent
使用现在StaticMesh构建PMCPMC的MaterialSection是正常的。
1. UKismetProceduralMeshLibrary::CopyProceduralMeshFromStaticMeshComponent()
```c++
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));
}
}
}
```
```c++
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
感觉链接中的这个代码是正确:
```c++
//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;
}
```
# RuntimeFBXImport