[Unreal Engine] StaticMesh → DynamicMeshComponent 변환 시 발생한 패키징 이슈

이매·2026년 3월 11일

Unreal Physics

목록 보기
5/5
post-thumbnail

기존 StaticMesh → DynamicMeshComponent 이슈

기존에는 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);
	}
}

하지만 패키징된 빌드에서 오류가 발생했다.

원인: MeshDescription은 Editor 전용 클래스

문제는 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 데이터를 읽는 방식은 패키징 빌드에서는 동작하지 않았다.

수정된 StaticMesh → DynamicMeshComponent

패키징 환경에서도 접근 가능하도록 Static Mesh의

  • RenderData → LODResources → VertexBuffer / IndexBuffer

를 통해 버텍스와 인덱스 정보를 읽어오도록 코드를 수정했다.

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));
	}
}

StaticMesh CPU Access Allow

런타임에서 Static Mesh의 버텍스 데이터를 읽어오려면 Static Mesh Asset에서 Allow CPU Access 옵션을 활성화해야 한다.

이 옵션이 꺼져 있으면 VertexBuffer 데이터가 CPU에서 접근 불가능하기 때문에 런타임에서 메시 데이터를 읽을 수 없다.

profile
언리얼 엔진 주니어(신입) 개발자 | 소설 쓰는 취준 개발자

0개의 댓글