277 lines
10 KiB
Markdown
277 lines
10 KiB
Markdown
---
|
||
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构建PMC,PMC的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
|