Skip to content

Commit 20dce12

Browse files
committed
Improved SMC -> RMC conversion
1 parent 821d14d commit 20dce12

3 files changed

Lines changed: 144 additions & 76 deletions

File tree

Source/RuntimeMeshComponent/Private/RuntimeMeshStaticMeshConverter.cpp

Lines changed: 121 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
#include "RuntimeMeshStaticMeshConverter.h"
55
#include "RuntimeMeshComponentPlugin.h"
6-
#include "EngineGlobals.h"
76
#include "Engine/StaticMesh.h"
87
#include "PhysicsEngine/BodySetup.h"
98
#include "RuntimeMeshRenderable.h"
@@ -14,6 +13,27 @@
1413
#define RMC_LOG_VERBOSE(MeshId, Format, ...) \
1514
UE_LOG(RuntimeMeshLog, Verbose, TEXT("[SMC->RMC Mesh:%d Thread:%d]: " Format), MeshId, FPlatformTLS::GetCurrentThreadId(), ##__VA_ARGS__);
1615

16+
#define RMC_LOG_WARNING(MeshId, Format, ...) \
17+
UE_LOG(RuntimeMeshLog, Warning, TEXT("[SMC->RMC Mesh:%d Thread:%d]: " Format), MeshId, FPlatformTLS::GetCurrentThreadId(), ##__VA_ARGS__);
18+
19+
20+
bool URuntimeMeshStaticMeshConverter::CheckStaticMeshAccessible(UStaticMesh* StaticMesh)
21+
{
22+
if (!IsValid(StaticMesh))
23+
{
24+
RMC_LOG_WARNING(-1, "Tried to copy data from static mesh but static mesh is invalid! Stack trace:");
25+
FDebug::DumpStackTraceToLog(ELogVerbosity::Verbose);
26+
return false;
27+
}
28+
29+
// Check mesh data is accessible
30+
if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->GetRenderData() != nullptr))
31+
{
32+
RMC_LOG_WARNING(-1, "Tried to copy data from static mesh but static mesh \"%s\" is inaccessible!", *StaticMesh->GetFullName());
33+
return false;
34+
}
35+
return true;
36+
}
1737

1838
int32 URuntimeMeshStaticMeshConverter::CopyVertexOrGetIndex(const FStaticMeshLODResources& LOD, const FStaticMeshSection& Section, TMap<int32, int32>& MeshToSectionVertexMap, int32 VertexIndex, FRuntimeMeshRenderableMeshData& NewMeshData)
1939
{
@@ -92,6 +112,13 @@ int32 URuntimeMeshStaticMeshConverter::CopyVertexOrGetIndex(const FStaticMeshLOD
92112
}
93113

94114
bool URuntimeMeshStaticMeshConverter::CopyStaticMeshSectionToRenderableMeshData(UStaticMesh* StaticMesh, int32 LODIndex, int32 SectionId, FRuntimeMeshRenderableMeshData& OutMeshData)
115+
{
116+
FRuntimeMeshSectionProperties Props;
117+
return CopyStaticMeshSectionToRenderableMeshData(StaticMesh, LODIndex, SectionId, OutMeshData, Props);
118+
}
119+
120+
bool URuntimeMeshStaticMeshConverter::CopyStaticMeshSectionToRenderableMeshData(UStaticMesh* StaticMesh, int32 LODIndex,
121+
int32 SectionId, FRuntimeMeshRenderableMeshData& OutMeshData, FRuntimeMeshSectionProperties& OutProperties)
95122
{
96123
// Check valid static mesh
97124
if (!IsValid(StaticMesh))
@@ -144,42 +171,44 @@ bool URuntimeMeshStaticMeshConverter::CopyStaticMeshSectionToRenderableMeshData(
144171
OutMeshData.Triangles.Add(SectionVertIndex);
145172
}
146173

174+
FRuntimeMeshSectionProperties& SectionProperties = OutProperties;
175+
SectionProperties.MaterialSlot = Section.MaterialIndex;
176+
SectionProperties.bCastsShadow = Section.bCastShadow;
177+
SectionProperties.bForceOpaque = Section.bForceOpaque;
178+
SectionProperties.bIsVisible = true;
179+
147180
// Lets copy the adjacency information too for tessellation
148181
// At this point all vertices should be copied so it should work to just copy/convert the indices.
182+
//Tesselation is only supported for UE4
183+
#if ENGINE_MAJOR_VERSION == 4
184+
#if ENGINE_MINOR_VERSION <= 22
185+
const auto& LODAdjacencyIndexBuffer = LOD.AdjacencyIndexBuffer;
186+
#else
187+
const auto& LODAdjacencyIndexBuffer = LOD.AdditionalIndexBuffers->AdjacencyIndexBuffer;
188+
#endif
189+
190+
if (LOD.bHasAdjacencyInfo && LODAdjacencyIndexBuffer.GetNumIndices() > 0)
191+
{
192+
FIndexArrayView AdjacencyIndices = LODAdjacencyIndexBuffer.GetArrayView();
149193

150-
//#if ENGINE_MAJOR_VERSION <= 4 && ENGINE_MINOR_VERSION <= 22
151-
// const auto& LODAdjacencyIndexBuffer = LOD.AdjacencyIndexBuffer;
152-
//#else
153-
// const auto& LODAdjacencyIndexBuffer = LOD.AdditionalIndexBuffers->AdjacencyIndexBuffer;
154-
//#endif
155-
//
156-
// if (LOD.bHasAdjacencyInfo && LODAdjacencyIndexBuffer.GetNumIndices() > 0)
157-
// {
158-
// FIndexArrayView AdjacencyIndices = LODAdjacencyIndexBuffer.GetArrayView();
159-
//
160-
// // We multiply these by 4 as the adjacency data is 12 indices per triangle instead of the normal 3
161-
// uint32 StartIndex = Section.FirstIndex * 4;
162-
// uint32 NumIndices = Section.NumTriangles * 3 * 4;
163-
//
164-
// for (uint32 Index = 0; Index < NumIndices; Index++)
165-
// {
166-
// OutMeshData.AdjacencyTriangles.Add(MeshToSectionVertMap[AdjacencyIndices[StartIndex + Index]]);
167-
// }
168-
// }
194+
// We multiply these by 4 as the adjacency data is 12 indices per triangle instead of the normal 3
195+
uint32 StartIndex = Section.FirstIndex * 4;
196+
uint32 NumIndices = Section.NumTriangles * 3 * 4;
169197

198+
for (uint32 Index = 0; Index < NumIndices; Index++)
199+
{
200+
OutMeshData.AdjacencyTriangles.Add(MeshToSectionVertMap[AdjacencyIndices[StartIndex + Index]]);
201+
}
202+
}
203+
#endif
170204
return true;
171205
}
172206

173207
bool URuntimeMeshStaticMeshConverter::CopyStaticMeshCollisionToCollisionSettings(UStaticMesh* StaticMesh, FRuntimeMeshCollisionSettings& OutCollisionSettings)
174208
{
209+
OutCollisionSettings = FRuntimeMeshCollisionSettings();
175210
// Check valid static mesh
176-
if (!IsValid(StaticMesh))
177-
{
178-
return false;
179-
}
180-
181-
// Check mesh data is accessible
182-
if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->GetRenderData() != nullptr))
211+
if (!CheckStaticMeshAccessible(StaticMesh))
183212
{
184213
return false;
185214
}
@@ -248,13 +277,7 @@ bool URuntimeMeshStaticMeshConverter::CopyStaticMeshCollisionToCollisionSettings
248277
bool URuntimeMeshStaticMeshConverter::CopyStaticMeshLODToCollisionData(UStaticMesh* StaticMesh, int32 LODIndex, FRuntimeMeshCollisionData& OutCollisionData)
249278
{
250279
// Check valid static mesh
251-
if (!IsValid(StaticMesh))
252-
{
253-
return false;
254-
}
255-
256-
// Check mesh data is accessible
257-
if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->GetRenderData() != nullptr))
280+
if (!CheckStaticMeshAccessible(StaticMesh))
258281
{
259282
return false;
260283
}
@@ -296,7 +319,7 @@ bool URuntimeMeshStaticMeshConverter::CopyStaticMeshLODToCollisionData(UStaticMe
296319
uint32 MeshVertIndex = Indices[TriIndex * 3 + Index];
297320

298321
// See if we have this vert already in our section vert buffer, and copy vert in if not
299-
int32 SectionVertIndex = CopyVertexOrGetIndex(LOD, Section, MeshToSectionVertMap, MeshVertIndex, NumUVChannels, OutCollisionData);
322+
CopyVertexOrGetIndex(LOD, Section, MeshToSectionVertMap, MeshVertIndex, NumUVChannels, OutCollisionData);
300323

301324
TempIndices[Index] = MeshVertIndex;
302325
}
@@ -324,83 +347,111 @@ bool URuntimeMeshStaticMeshConverter::CopyStaticMeshToRuntimeMesh(UStaticMesh* S
324347
// Not able to convert to RMC without a static provider
325348
if (StaticProvider == nullptr)
326349
{
327-
RMC_LOG_VERBOSE(RuntimeMeshComponent->GetRuntimeMeshId(), "Unable to convert StaticMesh to RuntimeMesh. No StaticProvider present.");
328-
return false;
329-
}
330-
331-
332-
// Check valid static mesh
333-
if (!IsValid(StaticMesh))
334-
{
335-
RMC_LOG_VERBOSE(RuntimeMeshComponent->GetRuntimeMeshId(), "Unable to convert StaticMesh to RuntimeMesh. Invalid source StaticMesh.");
336-
StaticProvider->ConfigureLODs({ FRuntimeMeshLODProperties() });
337-
StaticProvider->SetCollisionMesh(FRuntimeMeshCollisionData());
338-
StaticProvider->SetCollisionSettings(FRuntimeMeshCollisionSettings());
350+
RMC_LOG_WARNING(RuntimeMeshComponent->GetRuntimeMeshId(), "Unable to convert StaticMesh to RuntimeMesh. No StaticProvider present.");
339351
return false;
340352
}
353+
TArray<TArray<FRuntimeMeshSectionData>> MeshData;
354+
FRuntimeMeshCollisionSettings CollisionSettings;
355+
FRuntimeMeshCollisionData CollisionData;
356+
TArray<FRuntimeMeshLODProperties> LODProperties;
357+
TArray<FStaticMaterial> Materials;
341358

342-
// Check mesh data is accessible
343-
if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->GetRenderData() != nullptr))
359+
if (!CopyStaticMeshData(StaticMesh, MeshData, LODProperties,
360+
Materials, CollisionData, CollisionSettings, MaxLODToCopy))
344361
{
345-
RMC_LOG_VERBOSE(RuntimeMeshComponent->GetRuntimeMeshId(), "Unable to convert StaticMesh to RuntimeMesh. Invalid source StaticMesh.");
346362
StaticProvider->ConfigureLODs({ FRuntimeMeshLODProperties() });
347363
StaticProvider->SetCollisionMesh(FRuntimeMeshCollisionData());
348364
StaticProvider->SetCollisionSettings(FRuntimeMeshCollisionSettings());
349365
return false;
350366
}
351367

352368
// Copy materials
353-
const TArray<FStaticMaterial>& MaterialSlots = StaticMesh->GetStaticMaterials();
369+
TArray<FStaticMaterial>& MaterialSlots = Materials;
354370
for (int32 SlotIndex = 0; SlotIndex < MaterialSlots.Num(); SlotIndex++)
355371
{
356372
StaticProvider->SetupMaterialSlot(SlotIndex, MaterialSlots[SlotIndex].MaterialSlotName, MaterialSlots[SlotIndex].MaterialInterface);
357373
}
358374

359-
const auto& LODResources = StaticMesh->GetRenderData()->LODResources;
375+
// Setup LODs
376+
TArray<FRuntimeMeshLODProperties>& LODs = LODProperties;
377+
StaticProvider->ConfigureLODs(LODs);
378+
360379

380+
// Create all sections for all LODs
381+
for (int32 LODIndex = 0; LODIndex < MeshData.Num(); LODIndex++)
382+
{
383+
384+
for (int32 SectionId = 0; SectionId < MeshData[LODIndex].Num(); SectionId++)
385+
{
386+
StaticProvider->CreateSection(LODIndex, SectionId, MeshData[LODIndex][SectionId].Properties, MeshData[LODIndex][SectionId].MeshData);
387+
}
388+
}
389+
StaticProvider->SetCollisionSettings(CollisionSettings);
390+
391+
if (CollisionData.HasValidMeshData())
392+
{
393+
StaticProvider->SetCollisionMesh(CollisionData);
394+
}
395+
396+
return true;
397+
}
398+
399+
bool URuntimeMeshStaticMeshConverter::CopyStaticMeshData(UStaticMesh* StaticMesh,
400+
TArray<TArray<FRuntimeMeshSectionData>>& OutSectionData, TArray<FRuntimeMeshLODProperties>& OutLODProperties,
401+
TArray<FStaticMaterial>& OutMaterials, FRuntimeMeshCollisionData& OutCollisionData,
402+
FRuntimeMeshCollisionSettings& OutCollisionSettings, int32 MaxLODToCopy)
403+
{
404+
if (!CheckStaticMeshAccessible(StaticMesh))
405+
{
406+
return false;
407+
}
408+
409+
// Copy materials
410+
OutMaterials = StaticMesh->GetStaticMaterials();
411+
412+
FStaticMeshRenderData* RenderData = StaticMesh->GetRenderData();
413+
const auto& LODResources = RenderData->LODResources;
414+
415+
int32 NumLODs = FMath::Min(LODResources.Num(), MaxLODToCopy+1);
361416
// Setup LODs
362-
TArray<FRuntimeMeshLODProperties> LODs;
363-
for (int32 LODIndex = 0; LODIndex < LODResources.Num() && LODIndex <= MaxLODToCopy; LODIndex++)
417+
TArray<FRuntimeMeshLODProperties>& LODs = OutLODProperties;
418+
419+
for (int32 LODIndex = 0; LODIndex < NumLODs; LODIndex++)
364420
{
365421
FRuntimeMeshLODProperties LODProperties;
366-
LODProperties.ScreenSize = StaticMesh->GetRenderData()->ScreenSize[LODIndex].Default;
367-
422+
LODProperties.ScreenSize = RenderData->ScreenSize[LODIndex].Default;
368423
LODs.Add(LODProperties);
369424
}
370-
StaticProvider->ConfigureLODs(LODs);
371425

372426

373427
// Create all sections for all LODs
374-
for (int32 LODIndex = 0; LODIndex < LODResources.Num() && LODIndex <= MaxLODToCopy; LODIndex++)
428+
429+
OutSectionData.SetNum(NumLODs);
430+
for (int32 LODIndex = 0; LODIndex < NumLODs; LODIndex++)
375431
{
376432
const auto& LOD = LODResources[LODIndex];
377-
433+
OutSectionData[LODIndex].SetNum(LOD.Sections.Num());
378434
for (int32 SectionId = 0; SectionId < LOD.Sections.Num(); SectionId++)
379435
{
380-
const auto& Section = LOD.Sections[SectionId];
381-
382-
FRuntimeMeshSectionProperties SectionProperties;
383436

384437
FRuntimeMeshRenderableMeshData MeshData;
385-
CopyStaticMeshSectionToRenderableMeshData(StaticMesh, LODIndex, SectionId, MeshData);
386-
387-
StaticProvider->CreateSection(LODIndex, SectionId, SectionProperties, MeshData);
438+
CopyStaticMeshSectionToRenderableMeshData(StaticMesh, LODIndex, SectionId,
439+
OutSectionData[LODIndex][SectionId].MeshData, OutSectionData[LODIndex][SectionId].Properties);
388440
}
389441
}
390442

391-
FRuntimeMeshCollisionSettings CollisionSettings;
443+
FRuntimeMeshCollisionSettings CollisionSettings = OutCollisionSettings;
392444
if (CopyStaticMeshCollisionToCollisionSettings(StaticMesh, CollisionSettings))
393445
{
394-
StaticProvider->SetCollisionSettings(CollisionSettings);
395446
}
396447

448+
int CollisionLODIndex = StaticMesh->LODForCollision;
449+
397450
FRuntimeMeshCollisionData CollisionData;
398-
if (CollisionLODIndex != INDEX_NONE && CopyStaticMeshLODToCollisionData(StaticMesh, CollisionLODIndex, CollisionData))
451+
if (CollisionLODIndex >=0 && CollisionLODIndex < LODResources.Num() && CopyStaticMeshLODToCollisionData(StaticMesh, CollisionLODIndex, OutCollisionData))
399452
{
400-
StaticProvider->SetCollisionMesh(CollisionData);
401453
}
402454

403-
404455
return true;
405456
}
406457

Source/RuntimeMeshComponent/Public/RuntimeMeshCollision.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ struct RUNTIMEMESHCOMPONENT_API FRuntimeMeshCollisionData
734734

735735
bool HasValidMeshData()
736736
{
737-
return Vertices.Num() >= 3 && Triangles.Num() >= 3;
737+
return Vertices.Num() >= 3 && Triangles.Num() >= 1;
738738
}
739739

740740
void ReserveVertices(int32 Number, int32 NumTexCoordChannels = 1)

Source/RuntimeMeshComponent/Public/RuntimeMeshStaticMeshConverter.h

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "StaticMeshResources.h"
1010
#include "RuntimeMeshStaticMeshConverter.generated.h"
1111

12+
class URuntimeMeshComponent;
1213
/**
1314
*
1415
*/
@@ -18,23 +19,39 @@ class RUNTIMEMESHCOMPONENT_API URuntimeMeshStaticMeshConverter : public UBluepri
1819
GENERATED_BODY()
1920

2021
private:
22+
static bool CheckStaticMeshAccessible(UStaticMesh* StaticMesh);
23+
2124
static int32 CopyVertexOrGetIndex(const FStaticMeshLODResources& LOD, const FStaticMeshSection& Section, TMap<int32, int32>& MeshToSectionVertexMap, int32 VertexIndex, FRuntimeMeshRenderableMeshData& NewMeshData);
2225

2326
static int32 CopyVertexOrGetIndex(const FStaticMeshLODResources& LOD, const FStaticMeshSection& Section, TMap<int32, int32>& MeshToSectionVertexMap, int32 VertexIndex, int32 NumUVChannels, FRuntimeMeshCollisionData& NewMeshData);
2427

2528
public:
29+
static bool CopyStaticMeshSectionToRenderableMeshData(UStaticMesh* StaticMesh, int32 LODIndex, int32 SectionId,
30+
FRuntimeMeshRenderableMeshData& OutMeshData);
31+
2632
UFUNCTION(BlueprintCallable, Category = "RuntimeMesh|StaticMeshConversion")
27-
static bool CopyStaticMeshSectionToRenderableMeshData(UStaticMesh* StaticMesh, int32 LODIndex, int32 SectionId, FRuntimeMeshRenderableMeshData& OutMeshData);
33+
static bool CopyStaticMeshSectionToRenderableMeshData(UStaticMesh* StaticMesh, int32 LODIndex, int32 SectionId,
34+
FRuntimeMeshRenderableMeshData& OutMeshData, FRuntimeMeshSectionProperties& OutProperties);
2835

2936
UFUNCTION(BlueprintCallable, Category = "RuntimeMesh|StaticMeshConversion")
30-
static bool CopyStaticMeshCollisionToCollisionSettings(UStaticMesh* StaticMesh, FRuntimeMeshCollisionSettings& OutCollisionSettings);
37+
static bool CopyStaticMeshCollisionToCollisionSettings(UStaticMesh* StaticMesh,
38+
FRuntimeMeshCollisionSettings& OutCollisionSettings);
3139

3240
UFUNCTION(BlueprintCallable, Category = "RuntimeMesh|StaticMeshConversion")
33-
static bool CopyStaticMeshLODToCollisionData(UStaticMesh* StaticMesh, int32 LODIndex, FRuntimeMeshCollisionData& OutCollisionData);
41+
static bool CopyStaticMeshLODToCollisionData(UStaticMesh* StaticMesh, int32 LODIndex,
42+
FRuntimeMeshCollisionData& OutCollisionData);
3443

3544
UFUNCTION(BlueprintCallable, Category = "RuntimeMesh|StaticMeshConversion")
36-
static bool CopyStaticMeshToRuntimeMesh(UStaticMesh* StaticMesh, URuntimeMeshComponent* RuntimeMeshComponent, int32 CollisionLODIndex = -1, int32 MaxLODToCopy = 8);
45+
static bool CopyStaticMeshToRuntimeMesh(UStaticMesh* StaticMesh, URuntimeMeshComponent* RuntimeMeshComponent,
46+
int32 CollisionLODIndex = -1, int32 MaxLODToCopy = 8);
47+
48+
static bool CopyStaticMeshData(UStaticMesh* StaticMesh,
49+
TArray<TArray<FRuntimeMeshSectionData>>& OutSectionData, TArray<FRuntimeMeshLODProperties>& OutLODProperties,
50+
TArray<FStaticMaterial>& OutMaterials, FRuntimeMeshCollisionData& OutCollisionData,
51+
FRuntimeMeshCollisionSettings& OutCollisionSettings,
52+
int32 MaxLODToCopy = 8);
3753

3854
UFUNCTION(BlueprintCallable, Category = "RuntimeMesh|StaticMeshConversion")
39-
static bool CopyStaticMeshComponentToRuntimeMesh(UStaticMeshComponent* StaticMeshComponent, URuntimeMeshComponent* RuntimeMeshComponent, int32 CollisionLODIndex = -1, int32 MaxLODToCopy = 8);
55+
static bool CopyStaticMeshComponentToRuntimeMesh(UStaticMeshComponent* StaticMeshComponent,
56+
URuntimeMeshComponent* RuntimeMeshComponent, int32 CollisionLODIndex = -1, int32 MaxLODToCopy = 8);
4057
};

0 commit comments

Comments
 (0)