
기존에는 Static Mesh의 데이터를 읽어 DynamicMeshComponent로 변환하기 위해 MeshDescription을 이용하여 버텍스와 트라이앵글 정보를 복사하는 방식을 사용했다.
void ASlime::ConvertStaticMeshToDynamicMesh(const UStaticMesh* StaticMesh, UE::Geometry::FDynamicMesh3& OutMesh)
{
if (!StaticMesh) return;
// LOD0 사용
const FMeshDescription* MeshDesc = StaticMesh->GetMeshDescription(0);
if (!MeshDesc) return;
// MeshDescription에서 버텍스와 트라이앵글 정보를 읽어와 DynamicMesh에 복사
FStaticMeshAttributes Attributes(const_cast<FMeshDescription&>(*MeshDesc));
auto VertexPositions = Attributes.GetVertexPositions();
// VertexID → DynamicMesh VertexID 매핑
TMap<FVertexID, int> VertexMap;
// 버텍스 복사
for (const FVertexID VertexID : MeshDesc->Vertices().GetElementIDs())
{
FVector3f Pos = VertexPositions[VertexID];
int NewID = OutMesh.AppendVertex(static_cast<FVector3d>(Pos));
VertexMap.Add(VertexID, NewID);
}
// 트라이앵글 복사
for (const FTriangleID TriID : MeshDesc->Triangles().GetElementIDs())
{
TArrayView<const FVertexInstanceID> InstanceIDs =
MeshDesc->GetTriangleVertexInstances(TriID);
int V0 = VertexMap[
MeshDesc->GetVertexInstanceVertex(InstanceIDs[0])];
int V1 = VertexMap[
MeshDesc->GetVertexInstanceVertex(InstanceIDs[1])];
int V2 = VertexMap[
MeshDesc->GetVertexInstanceVertex(InstanceIDs[2])];
OutMesh.AppendTriangle(V0, V1, V2);
}
}
하지만 패키징된 빌드에서 오류가 발생했다.
문제는 Static Mesh의 데이터를 가져오는 MeshDescription 클래스에 있었다.
엔진 소스를 확인해 보면 다음과 같은 코드가 존재한다.
UStaticMeshDescription* UStaticMesh::GetStaticMeshDescription(int32 LODIndex)
{
#if WITH_EDITOR
if (LODIndex < GetNumSourceModels())
{
GetSourceModel(LODIndex).GetOrCacheMeshDescription();
return GetSourceModel(LODIndex).GetCachedStaticMeshDescription();
}
#endif
return nullptr;
}
엔진의 소스코드를 보니 Editor에서만 사용 가능하도록 매크로가 설정되어 있어 패키징 시에는 사용이 불가능했던 것이다.
즉, MeshDescription은 에디터에서만 사용할 수 있는 Source Mesh 데이터이며 패키징된 빌드에서는 제거되는 EditorOnly인 것이다.
따라서 런타임에서 MeshDescription을 이용해 Static Mesh 데이터를 읽는 방식은 패키징 빌드에서는 동작하지 않았다.
패키징 환경에서도 접근 가능하도록 Static Mesh의
를 통해 버텍스와 인덱스 정보를 읽어오도록 코드를 수정했다.
void ASlime::ConvertStaticMeshToDynamicMesh(const UStaticMesh* StaticMesh, UE::Geometry::FDynamicMesh3& OutMesh)
{
if (!StaticMesh || !StaticMesh->GetRenderData())
{
return;
}
const FStaticMeshLODResources& LOD = StaticMesh->GetRenderData()->LODResources[0];
OutMesh.EnableAttributes();
auto* UVOverlay = OutMesh.Attributes()->PrimaryUV();
const FPositionVertexBuffer& PositionBuffer = LOD.VertexBuffers.PositionVertexBuffer;
const FStaticMeshVertexBuffer& VertexBuffer = LOD.VertexBuffers.StaticMeshVertexBuffer;
const FIndexArrayView Indices = LOD.IndexBuffer.GetArrayView();
TArray<int32> VertexMap;
VertexMap.SetNum(PositionBuffer.GetNumVertices());
TArray<int32> UVMap;
UVMap.SetNum(PositionBuffer.GetNumVertices());
TMap<FVector3f, int32> WeldMap;
// Vertex 생성
for (uint32 i = 0; i < PositionBuffer.GetNumVertices(); ++i)
{
FVector3f Pos = PositionBuffer.VertexPosition(i);
int32 V;
if (int32* Existing = WeldMap.Find(Pos))
{
V = *Existing;
}
else
{
V = OutMesh.AppendVertex((FVector3d)Pos);
WeldMap.Add(Pos, V);
}
VertexMap[i] = V;
FVector2f UV = VertexBuffer.GetVertexUV(i, 0);
UVMap[i] = UVOverlay->AppendElement(UV);
}
for (int32 i = 0; i < Indices.Num(); i += 3)
{
int32 I0 = Indices[i];
int32 I1 = Indices[i + 1];
int32 I2 = Indices[i + 2];
int V0 = VertexMap[I0];
int V1 = VertexMap[I1];
int V2 = VertexMap[I2];
int TID = OutMesh.AppendTriangle(V0, V1, V2);
int U0 = UVMap[I0];
int U1 = UVMap[I1];
int U2 = UVMap[I2];
UVOverlay->SetTriangle(TID, UE::Geometry::FIndex3i(U0, U1, U2));
}
}
런타임에서 Static Mesh의 버텍스 데이터를 읽어오려면 Static Mesh Asset에서 Allow CPU Access 옵션을 활성화해야 한다.
이 옵션이 꺼져 있으면 VertexBuffer 데이터가 CPU에서 접근 불가능하기 때문에 런타임에서 메시 데이터를 읽을 수 없다.
