8 #include "PhysicsEngine/BodySetup.h" 9 #include "PhysicsEngine/PhysicsSettings.h" 13 DECLARE_CYCLE_STAT(TEXT(
"RM - Validation - Create"), STAT_RuntimeMesh_CheckCreate, STATGROUP_RuntimeMesh);
14 DECLARE_CYCLE_STAT(TEXT(
"RM - Validation - Update"), STAT_RuntimeMesh_CheckUpdate, STATGROUP_RuntimeMesh);
15 DECLARE_CYCLE_STAT(TEXT(
"RM - Validation - BoundingBox"), STAT_RuntimeMesh_CheckBoundingBox, STATGROUP_RuntimeMesh);
17 DECLARE_CYCLE_STAT(TEXT(
"RM - Create Mesh Section - MeshBuilder"), STAT_RuntimeMesh_CreateMeshSection_MeshData, STATGROUP_RuntimeMesh);
18 DECLARE_CYCLE_STAT(TEXT(
"RM - Create Mesh Section - MeshBuilder - Move"), STAT_RuntimeMesh_CreateMeshSection_MeshData_Move, STATGROUP_RuntimeMesh);
19 DECLARE_CYCLE_STAT(TEXT(
"RM - Update Mesh Section - MeshBuilder"), STAT_RuntimeMesh_UpdateMeshSection_MeshData, STATGROUP_RuntimeMesh);
20 DECLARE_CYCLE_STAT(TEXT(
"RM - Update Mesh Section - MeshBuilder - Move"), STAT_RuntimeMesh_UpdateMeshSection_MeshData_Move, STATGROUP_RuntimeMesh);
22 DECLARE_CYCLE_STAT(TEXT(
"RM - Create Mesh Section - Component Buffers"), STAT_RuntimeMesh_CreateMeshSectionFromComponents, STATGROUP_RuntimeMesh);
23 DECLARE_CYCLE_STAT(TEXT(
"RM - Update Mesh Section - Component Buffers"), STAT_RuntimeMesh_UpdateMeshSectionFromComponents, STATGROUP_RuntimeMesh);
25 DECLARE_CYCLE_STAT(TEXT(
"RM - Create Mesh Section - Blueprint Packed Buffer"), STAT_RuntimeMesh_CreateMeshSectionPacked_Blueprint, STATGROUP_RuntimeMesh);
26 DECLARE_CYCLE_STAT(TEXT(
"RM - Update Mesh Section - Blueprint Packed Buffer"), STAT_RuntimeMesh_UpdateMeshSectionPacked_Blueprint, STATGROUP_RuntimeMesh);
28 DECLARE_CYCLE_STAT(TEXT(
"RM - Get Readonly Section Accessor"), STAT_RuntimeMesh_GetReadonlyMeshAccessor, STATGROUP_RuntimeMesh);
30 DECLARE_CYCLE_STAT(TEXT(
"RM - Clear Mesh Section"), STAT_RuntimeMesh_ClearMeshSection, STATGROUP_RuntimeMesh);
31 DECLARE_CYCLE_STAT(TEXT(
"RM - Clear All Mesh Sections"), STAT_RuntimeMesh_ClearAllMeshSections, STATGROUP_RuntimeMesh);
32 DECLARE_CYCLE_STAT(TEXT(
"RM - Get Section Bounding Box"), STAT_RuntimeMesh_GetSectionBoundingBox, STATGROUP_RuntimeMesh);
34 DECLARE_CYCLE_STAT(TEXT(
"RM - Set Mesh Section Visible"), STAT_RuntimeMesh_SetMeshSectionVisible, STATGROUP_RuntimeMesh);
35 DECLARE_CYCLE_STAT(TEXT(
"RM - Is Mesh Section Visible"), STAT_RuntimeMesh_IsMeshSectionVisible, STATGROUP_RuntimeMesh);
37 DECLARE_CYCLE_STAT(TEXT(
"RM - Set Mesh Section Casts Shadow"), STAT_RuntimeMesh_SetMeshSectionCastsShadow, STATGROUP_RuntimeMesh);
38 DECLARE_CYCLE_STAT(TEXT(
"RM - Is Mesh Section Casting Shadows"), STAT_RuntimeMesh_IsMeshSectionCastingShadows, STATGROUP_RuntimeMesh);
40 DECLARE_CYCLE_STAT(TEXT(
"RM - Set Mesh Section Collision Enabled"), STAT_RuntimeMesh_SetMeshSectionCollisionEnabled, STATGROUP_RuntimeMesh);
41 DECLARE_CYCLE_STAT(TEXT(
"RM - Is Mesh Section Collision Enabled"), STAT_RuntimeMesh_IsMeshSectionCollisionEnabled, STATGROUP_RuntimeMesh);
43 DECLARE_CYCLE_STAT(TEXT(
"RM - Get Available Section Index"), STAT_RuntimeMesh_GetAvailableSectionIndex, STATGROUP_RuntimeMesh);
44 DECLARE_CYCLE_STAT(TEXT(
"RM - Get Section Ids"), STAT_RuntimeMesh_GetSectionIds, STATGROUP_RuntimeMesh);
46 DECLARE_CYCLE_STAT(TEXT(
"RM - Set Mesh Collision Section"), STAT_RuntimeMesh_SetMeshCollisionSection, STATGROUP_RuntimeMesh);
47 DECLARE_CYCLE_STAT(TEXT(
"RM - Clear Mesh Collision Section"), STAT_RuntimeMesh_ClearMeshCollisionSection, STATGROUP_RuntimeMesh);
48 DECLARE_CYCLE_STAT(TEXT(
"RM - Clear All Mesh Collision Sections"), STAT_RuntimeMesh_ClearAllMeshCollisionSections, STATGROUP_RuntimeMesh);
50 DECLARE_CYCLE_STAT(TEXT(
"RM - Add Convex Collision Section"), STAT_RuntimeMesh_AddConvexCollisionSection, STATGROUP_RuntimeMesh);
51 DECLARE_CYCLE_STAT(TEXT(
"RM - Set Convex Collision Section"), STAT_RuntimeMesh_SetConvexCollisionSection, STATGROUP_RuntimeMesh);
52 DECLARE_CYCLE_STAT(TEXT(
"RM - Clear Convex Collision Section"), STAT_RuntimeMesh_ClearConvexCollisionSection, STATGROUP_RuntimeMesh);
53 DECLARE_CYCLE_STAT(TEXT(
"RM - Clear All Convex Collision Sections"), STAT_RuntimeMesh_ClearAllConvexCollisionSections, STATGROUP_RuntimeMesh);
55 DECLARE_CYCLE_STAT(TEXT(
"RM - Create Mesh Section - Internal"), STAT_RuntimeMesh_CreateSectionInternal, STATGROUP_RuntimeMesh);
56 DECLARE_CYCLE_STAT(TEXT(
"RM - Update Mesh Section - Internal"), STAT_RuntimeMesh_UpdateSectionInternal, STATGROUP_RuntimeMesh);
57 DECLARE_CYCLE_STAT(TEXT(
"RM - Handle Common Section Update Flags"), STAT_RuntimeMesh_HandleCommonSectionUpdateFlags, STATGROUP_RuntimeMesh);
58 DECLARE_CYCLE_STAT(TEXT(
"RM - Handle Common Section Update Flags - Calculate Tangents"), STAT_RuntimeMesh_HandleCommonSectionUpdateFlags_CalculateTangents, STATGROUP_RuntimeMesh);
59 DECLARE_CYCLE_STAT(TEXT(
"RM - Handle Common Section Update Flags - Calculate Tessellation Indices"), STAT_RuntimeMesh_HandleCommonSectionUpdateFlags_CalculateTessellationIndices, STATGROUP_RuntimeMesh);
60 DECLARE_CYCLE_STAT(TEXT(
"RM - Update Section Properties Internal"), STAT_RuntimeMesh_UpdateSectionPropertiesInternal, STATGROUP_RuntimeMesh);
61 DECLARE_CYCLE_STAT(TEXT(
"RM - Update Local Bounds"), STAT_RuntimeMesh_UpdateLocalBounds, STATGROUP_RuntimeMesh);
62 DECLARE_CYCLE_STAT(TEXT(
"RM - Initialize"), STAT_RuntimeMesh_Initialize, STATGROUP_RuntimeMesh);
64 DECLARE_CYCLE_STAT(TEXT(
"RM - Contains Physics Triangle Mesh Data"), STAT_RuntimeMesh_ContainsPhysicsTriMeshData, STATGROUP_RuntimeMesh);
65 DECLARE_CYCLE_STAT(TEXT(
"RM - Get Physics Triangle Mesh Data"), STAT_RuntimeMesh_GetPhysicsTriMeshData, STATGROUP_RuntimeMesh);
66 DECLARE_CYCLE_STAT(TEXT(
"RM - Copy Collision Elements to Body Setup"), STAT_RuntimeMesh_CopyCollisionElementsToBodySetup, STATGROUP_RuntimeMesh);
67 DECLARE_CYCLE_STAT(TEXT(
"RM - Get Section From Collision Face Index"), STAT_RuntimeMesh_GetSectionFromCollisionFaceIndex, STATGROUP_RuntimeMesh);
85 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CheckCreate);
88 if (NumUVs < 1 || NumUVs > 8)
90 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"UV Channel Count must be between 1 and 8 inclusive"));
96 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"Indices can only be of type uint16, int32, or uint32."));
109 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"Position element must always be in stream 0."));
115 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"Streams cannot have overlapping elements, and all elements must be present."));
121 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"Indices can only be of type uint16, int32, or uint32."));
126 void FRuntimeMeshData::CheckUpdate(
bool bUseHighPrecisionTangents,
bool bUseHighPrecisionUVs, int32 NumUVs,
bool b32BitIndices, int32 SectionIndex,
bool bShouldCheckIndexType,
127 bool bCheckTangentVertexStream,
bool bCheckUVVertexStream)
const 129 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CheckUpdate);
133 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"Mesh Section %d does not exist in RMC."), SectionIndex);
138 if (bCheckTangentVertexStream && !Section->CheckTangentBuffer(bUseHighPrecisionTangents))
140 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"Supplied vertex type does not match stream 1 for mesh section %d."), SectionIndex);
143 if (bCheckUVVertexStream && !Section->CheckUVBuffer(bUseHighPrecisionUVs, NumUVs))
145 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"Supplied vertex type does not match stream 2 for mesh section %d."), SectionIndex);
148 if (bShouldCheckIndexType && !Section->CheckIndexBufferSize(b32BitIndices))
150 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"Supplied index type do not match mesh section %d."), SectionIndex);
157 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CheckBoundingBox);
159 if (!Box.IsValid || Box.GetVolume() <= 0)
161 UE_LOG(RuntimeMeshLog, Fatal, TEXT(
"Supplied bounding invalid was invalid."));
172 SyncRoot = MakeUnique<FRuntimeMeshMutexLockProvider>();
178 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSection_NoData);
184 auto NewSection =
CreateOrResetSection(SectionIndex, bWantsHighPrecisionTangents, bWantsHighPrecisionUVs, NumUVs, bWants32BitIndices, UpdateFrequency);
187 NewSection->SetCollisionEnabled(bCreateCollision);
195 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSection_MeshData);
202 auto NewSection =
CreateOrResetSection(SectionId, MeshData->IsUsingHighPrecisionTangents(), MeshData->IsUsingHighPrecisionUVs(), MeshData->NumUVChannels(), MeshData->IsUsing32BitIndices(), UpdateFrequency);
204 NewSection->UpdatePositionBuffer(MeshData->GetPositionStream(),
false);
205 NewSection->UpdateTangentsBuffer(MeshData->GetTangentStream(),
false);
206 NewSection->UpdateUVsBuffer(MeshData->GetUVStream(),
false);
207 NewSection->UpdateColorBuffer(MeshData->GetColorStream(),
false);
208 NewSection->UpdateIndexBuffer(MeshData->GetIndexStream(),
false);
211 NewSection->SetCollisionEnabled(bCreateCollision);
219 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSection_MeshData_Move);
225 auto NewSection =
CreateOrResetSection(SectionId, MeshData->IsUsingHighPrecisionTangents(), MeshData->IsUsingHighPrecisionUVs(), MeshData->NumUVChannels(), MeshData->IsUsing32BitIndices(), UpdateFrequency);
227 NewSection->UpdatePositionBuffer(MeshData->GetPositionStream(),
true);
228 NewSection->UpdateTangentsBuffer(MeshData->GetTangentStream(),
true);
229 NewSection->UpdateUVsBuffer(MeshData->GetUVStream(),
true);
230 NewSection->UpdateColorBuffer(MeshData->GetColorStream(),
true);
231 NewSection->UpdateIndexBuffer(MeshData->GetIndexStream(),
true);
234 NewSection->SetCollisionEnabled(bCreateCollision);
242 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_MeshData);
246 CheckUpdate(MeshData->IsUsingHighPrecisionTangents(), MeshData->IsUsingHighPrecisionUVs(), MeshData->NumUVChannels(), MeshData->IsUsing32BitIndices(), SectionId,
true,
true,
true);
252 Section->UpdatePositionBuffer(MeshData->GetPositionStream(),
false);
253 Section->UpdateTangentsBuffer(MeshData->GetTangentStream(),
false);
254 Section->UpdateUVsBuffer(MeshData->GetUVStream(),
false);
255 Section->UpdateColorBuffer(MeshData->GetColorStream(),
false);
256 Section->UpdateIndexBuffer(MeshData->GetIndexStream(),
false);
263 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSection_MeshData_Move);
267 CheckUpdate(MeshData->IsUsingHighPrecisionTangents(), MeshData->IsUsingHighPrecisionUVs(), MeshData->NumUVChannels(), MeshData->IsUsing32BitIndices(), SectionId,
true,
true,
true);
273 Section->UpdatePositionBuffer(MeshData->GetPositionStream(),
true);
274 Section->UpdateTangentsBuffer(MeshData->GetTangentStream(),
true);
275 Section->UpdateUVsBuffer(MeshData->GetUVStream(),
true);
276 Section->UpdateColorBuffer(MeshData->GetColorStream(),
true);
277 Section->UpdateIndexBuffer(MeshData->GetIndexStream(),
true);
294 return Section->GetSectionMeshUpdater(this->AsShared(), SectionId, UpdateFlags,
SyncRoot.Get(),
false);
317 Section->SetBoundingBox(*BoundingBox);
321 Section->UpdateBoundingBox();
328 const TArray<FVector2D>& UV0,
const TArray<FVector2D>& UV1, TFunction<FColor(int32
Index)> ColorAccessor, int32 NumColors,
330 bool bUseHighPrecisionTangents,
bool bUseHighPrecisionUVs,
bool bWantsSecondUV)
332 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSectionFromComponents);
339 TSharedPtr<FRuntimeMeshAccessor> MeshData = NewSection->GetSectionMeshAccessor();
342 MeshData->SetNumVertices(Vertices.Num());
344 for (int32 Index = 0; Index < Vertices.Num(); Index++)
346 MeshData->SetPosition(Index, Vertices[Index]);
347 MeshData->SetNormalTangent(Index,
348 Normals.Num() > Index ? Normals[
Index] : FVector(0.0
f, 0.0
f, 1.0
f),
350 MeshData->SetColor(Index, NumColors > Index ? ColorAccessor(Index) : FColor::White);
351 MeshData->SetUV(Index, 0, UV0.Num() > Index ? UV0[
Index] : FVector2D::ZeroVector);
354 MeshData->SetUV(Index, 1, UV1.Num() > Index ? UV1[
Index] : FVector2D::ZeroVector);
358 NewSection->UpdateIndexBuffer(Triangles);
360 NewSection->UpdateBoundingBox();
363 NewSection->SetCollisionEnabled(bCreateCollision);
370 const TArray<FVector2D>& UV0,
const TArray<FVector2D>& UV1, TFunction<FColor(int32
Index)> ColorAccessor, int32 NumColors,
const TArray<FRuntimeMeshTangent>& Tangents,
ESectionUpdateFlags UpdateFlags)
372 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSectionFromComponents);
382 if (Vertices.Num() > 0)
386 if (Normals.Num() > 0 || Tangents.Num() > 0)
390 if (UV0.Num() > 0 || UV1.Num() > 0)
401 TSharedPtr<FRuntimeMeshAccessor> MeshData = Section->GetSectionMeshAccessor();
403 int32 OldVertexCount = FMath::Min(MeshData->NumVertices(), Vertices.Num());
406 MeshData->SetNumVertices(Vertices.Num());
408 bool bHasSecondUV = MeshData->NumUVChannels() > 1;
411 for (int32 Index = 0; Index < OldVertexCount; Index++)
413 if (Vertices.Num() >
Index) MeshData->SetPosition(Index, Vertices[Index]);
414 if (Normals.Num() >
Index) MeshData->SetNormal(Index, Normals[Index]);
415 if (Tangents.Num() >
Index) MeshData->SetTangent(Index, FVector4(Tangents[Index].TangentX, Tangents[Index].bFlipTangentY ? -1.0
f : 1.0
f));
416 if (NumColors > Index) MeshData->SetColor(Index, ColorAccessor(Index));
417 if (UV0.Num() >
Index) MeshData->SetUV(Index, 0, UV0[Index]);
418 if (bHasSecondUV && UV1.Num() >
Index) MeshData->SetUV(Index, 1, UV1[Index]);
422 for (int32 Index = OldVertexCount; Index < Vertices.Num(); Index++)
424 MeshData->SetPosition(Index, Vertices[Index]);
425 MeshData->SetNormalTangent(Index,
426 Normals.Num() > Index ? Normals[
Index] : FVector(0.0
f, 0.0
f, 1.0
f),
428 MeshData->SetColor(Index, NumColors > Index ? ColorAccessor(Index) : FColor::White);
429 MeshData->SetUV(Index, 0, UV0.Num() > Index ? UV0[
Index] : FVector2D::ZeroVector);
431 MeshData->SetUV(Index, 1, UV1.Num() > Index ? UV1[
Index] : FVector2D::ZeroVector);
435 if (Vertices.Num() > 0)
437 Section->UpdateBoundingBox();
440 if (Triangles.Num() > 0)
442 Section->UpdateIndexBuffer(Triangles);
452 if (Colors.Num() == 0)
460 TSharedPtr<FRuntimeMeshAccessor> MeshData = Section->GetSectionMeshAccessor();
462 for (
int i = 0;
i < Colors.Num();
i++)
463 MeshData->SetColor(
i, Colors[
i]);
466 RenderProxy->UpdateSection_GameThread(SectionIndex, Section->GetSectionUpdateData(BuffersToUpdate));
468 const bool bRequireProxyRecreate = Section->GetUpdateFrequency() == EUpdateFrequency::Infrequent;
469 if (bRequireProxyRecreate)
476 const TArray<FVector2D>& UV0,
const TArray<FColor>& Colors,
const TArray<FRuntimeMeshTangent>& Tangents,
bool bCreateCollision,
EUpdateFrequency UpdateFrequency,
477 ESectionUpdateFlags UpdateFlags,
bool bUseHighPrecisionTangents,
bool bUseHighPrecisionUVs)
480 Colors.Num(), Tangents, bCreateCollision, UpdateFrequency, UpdateFlags, bUseHighPrecisionTangents, bUseHighPrecisionUVs,
false);
484 const TArray<FVector2D>& UV0,
const TArray<FVector2D>& UV1,
const TArray<FColor>& Colors,
const TArray<FRuntimeMeshTangent>& Tangents,
488 Colors.Num(), Tangents, bCreateCollision, UpdateFrequency, UpdateFlags, bUseHighPrecisionTangents, bUseHighPrecisionUVs,
true);
492 const TArray<FColor>& Colors,
const TArray<FRuntimeMeshTangent>& Tangents,
ESectionUpdateFlags UpdateFlags)
495 [&Colors](int32
Index) -> FColor {
return Colors[
Index]; }, Colors.Num(), Tangents, UpdateFlags);
499 const TArray<FVector2D>& UV1,
const TArray<FColor>& Colors,
const TArray<FRuntimeMeshTangent>& Tangents,
ESectionUpdateFlags UpdateFlags)
502 [&Colors](int32
Index) -> FColor {
return Colors[
Index]; }, Colors.Num(), Tangents, UpdateFlags);
506 const TArray<FVector2D>& UV0,
const TArray<FColor>& Colors,
const TArray<FRuntimeMeshTangent>& Tangents,
ESectionUpdateFlags UpdateFlags)
509 [&Colors](int32
Index) -> FColor {
return Colors[
Index]; }, Colors.Num(), Tangents, UpdateFlags);
513 const TArray<FVector2D>& UV0,
const TArray<FVector2D>& UV1,
const TArray<FColor>& Colors,
const TArray<FRuntimeMeshTangent>& Tangents,
ESectionUpdateFlags UpdateFlags)
516 [&Colors](int32
Index) -> FColor {
return Colors[
Index]; }, Colors.Num(), Tangents, UpdateFlags);
520 const TArray<FRuntimeMeshTangent>& Tangents,
const TArray<FVector2D>& UV0,
const TArray<FVector2D>& UV1,
const TArray<FLinearColor>& VertexColors,
bool bCreateCollision,
521 bool bCalculateNormalTangent,
bool bShouldCreateHardTangents,
bool bGenerateTessellationTriangles,
EUpdateFrequency UpdateFrequency,
bool bUseHighPrecisionTangents,
bool bUseHighPrecisionUVs)
529 VertexColors.Num(), Tangents, bCreateCollision, UpdateFrequency, UpdateFlags, bUseHighPrecisionTangents, bUseHighPrecisionUVs, UV1.Num() > 0);
533 const TArray<FVector2D>& UV0,
const TArray<FVector2D>& UV1,
const TArray<FLinearColor>& VertexColors,
bool bCalculateNormalTangent,
bool bShouldCreateHardTangents,
bool bGenerateTessellationTriangles)
541 [&VertexColors](int32
Index) -> FColor {
return VertexColors[
Index].ToFColor(
false); }, VertexColors.Num(), Tangents, UpdateFlags);
547 bool bCreateCollision,
bool bCalculateNormalTangent,
bool bShouldCreateHardTangents,
bool bGenerateTessellationTriangles,
EUpdateFrequency UpdateFrequency,
bool bUseHighPrecisionTangents,
bool bUseHighPrecisionUVs)
549 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateMeshSectionPacked_Blueprint);
556 TSharedPtr<FRuntimeMeshAccessor> MeshData = NewSection->GetSectionMeshAccessor();
559 MeshData->SetNumVertices(Vertices.Num());
563 MeshData->SetPosition(
Index, Vertices[
Index].Position);
564 MeshData->SetNormal(
Index, Vertices[
Index].Normal);
565 MeshData->SetTangent(
Index, FVector4(Vertices[
Index].Tangent.TangentX, Vertices[Index].Tangent.bFlipTangentY ? -1.0f : 1.0f));
566 MeshData->SetColor(Index, Vertices[Index].
Color.ToFColor(
false));
567 MeshData->SetUV(Index, 0, Vertices[Index].UV0);
570 NewSection->UpdateIndexBuffer(Triangles);
572 NewSection->UpdateBoundingBox();
575 NewSection->SetCollisionEnabled(bCreateCollision);
587 bool bCalculateNormalTangent,
bool bShouldCreateHardTangents,
bool bGenerateTessellationTriangles)
589 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateMeshSectionPacked_Blueprint);
599 if (Vertices.Num() > 0)
603 TSharedPtr<FRuntimeMeshAccessor> MeshData = Section->GetSectionMeshAccessor();
606 MeshData->SetNumVertices(Vertices.Num());
611 MeshData->SetPosition(
Index, Vertices[
Index].Position);
612 MeshData->SetNormal(
Index, Vertices[
Index].Normal);
613 MeshData->SetTangent(
Index, FVector4(Vertices[
Index].Tangent.TangentX, Vertices[Index].Tangent.bFlipTangentY ? -1.0f : 1.0f));
614 MeshData->SetColor(Index, Vertices[Index].
Color.ToFColor(
false));
615 MeshData->SetUV(Index, 0, Vertices[Index].UV0);
618 Section->UpdateBoundingBox();
622 if (Triangles.Num() > 0)
624 Section->UpdateIndexBuffer(Triangles);
641 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_GetReadonlyMeshAccessor);
648 return ConstCastSharedPtr<const FRuntimeMeshAccessor, FRuntimeMeshAccessor>(Section->GetSectionMeshAccessor());
653 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearMeshSection);
661 bool bHadCollision = Section->IsCollisionEnabled();
662 bool bWasStaticSection = Section->GetUpdateFrequency() == EUpdateFrequency::Infrequent;
687 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearAllMeshSections);
705 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_GetSectionBoundingBox);
719 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_SetMeshSectionVisible);
734 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_IsMeshSectionVisible);
749 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_SetMeshSectionCastsShadow);
755 MeshSections[SectionIndex]->SetCastsShadow(bNewCastsShadow);
764 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_IsMeshSectionCastingShadows);
779 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_SetMeshSectionCollisionEnabled);
785 bool bWasCollisionEnabled =
MeshSections[SectionIndex]->IsCollisionEnabled();
787 if (bWasCollisionEnabled != bNewCollisionEnabled)
789 MeshSections[SectionIndex]->SetCollisionEnabled(bNewCollisionEnabled);
798 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_IsMeshSectionCollisionEnabled);
804 return MeshSections[SectionIndex]->IsCollisionEnabled();
827 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_GetAvailableSectionIndex);
857 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_GetSectionIds);
861 TArray<int32> Sections;
862 for (int32 SectionId = 0; SectionId <
MeshSections.Num(); SectionId++)
866 Sections.Add(SectionId);
876 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_SetMeshCollisionSection);
882 Section.VertexBuffer = Vertices;
883 Section.IndexBuffer = Triangles;
890 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearMeshCollisionSection);
901 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearAllMeshCollisionSections);
913 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_AddConvexCollisionSection);
925 Section.VertexBuffer = ConvexVerts;
926 Section.BoundingBox = FBox(ConvexVerts);
935 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_SetConvexCollisionSection);
941 Section.VertexBuffer = ConvexVerts;
942 Section.BoundingBox = FBox(ConvexVerts);
949 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearConvexCollisionSection);
961 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearAllConvexCollisionSections);
972 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ClearAllConvexCollisionSections);
979 for (
const auto& Convex : ConvexMeshes)
983 Section.VertexBuffer = Convex;
984 Section.BoundingBox = FBox(Convex);
1090 FRuntimeMeshSectionPtr NewSection = MakeShared<FRuntimeMeshSection, ESPMode::ThreadSafe>(bInUseHighPrecisionTangents, bInUseHighPrecisionUVs, InNumUVs, b32BitIndices, UpdateFrequency);
1104 return CreateOrResetSection(SectionId, bHighPrecisionTangents, bHighPrecisionUVs, bWantsSecondUV? 2 : 1,
true, UpdateFrequency);
1112 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CreateSectionInternal);
1120 RenderProxy->CreateSection_GameThread(SectionId, Section->GetSectionCreationParams());
1139 if (Section->IsCollisionEnabled())
1149 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateSectionInternal);
1157 RenderProxy->UpdateSection_GameThread(SectionId, Section->GetSectionUpdateData(BuffersToUpdate));
1166 bool bRequireProxyRecreate = Section->GetUpdateFrequency() == EUpdateFrequency::Infrequent;
1167 if (bRequireProxyRecreate)
1175 if (Section->IsCollisionEnabled())
1185 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_HandleCommonSectionUpdateFlags);
1193 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_HandleCommonSectionUpdateFlags_CalculateTangents);
1200 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_HandleCommonSectionUpdateFlags_CalculateTessellationIndices);
1208 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateSectionPropertiesInternal);
1215 RenderProxy->UpdateSectionProperties_GameThread(SectionIndex, Section->GetSectionPropertyUpdateData());
1218 bool bRequiresRecreate = bUpdateRequiresProxyRecreateIfStatic &&
1219 Section->GetUpdateFrequency() == EUpdateFrequency::Infrequent;
1221 if (bRequiresRecreate)
1235 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_UpdateLocalBounds);
1237 FBox LocalBox(EForceInit::ForceInitToZero);
1239 for (int32 SectionId = 0; SectionId <
MeshSections.Num(); SectionId++)
1242 if (Section.IsValid() && Section->ShouldRender())
1244 LocalBox += Section->GetBoundingBox();
1248 LocalBounds = LocalBox.IsValid ? FBoxSphereBounds(LocalBox) :
1249 FBoxSphereBounds(FVector(0, 0, 0), FVector(0, 0, 0), 0);
1277 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_Initialize);
1280 for (int32 SectionId = 0; SectionId <
MeshSections.Num(); SectionId++)
1291 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_ContainsPhysicsTriMeshData);
1295 for (int32 SectionId = 0; SectionId <
MeshSections.Num(); SectionId++)
1298 if (Section.IsValid() && Section->HasValidMeshData() && Section->IsCollisionEnabled())
1306 if (Section.Value.VertexBuffer.Num() > 0 && Section.Value.IndexBuffer.Num() > 0)
1317 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_GetPhysicsTriMeshData);
1323 bool bHadCollision =
false;
1328 for (int32 SectionId = 0; SectionId <
MeshSections.Num(); SectionId++)
1332 TArray<FVector2D> UVs;
1333 int32 NumTriangles =
MeshSections[SectionId]->GetCollisionData(CollisionData->Vertices, CollisionData->Indices, UVs);
1337 CollisionData->UVs.Add(MoveTemp(UVs));
1342 CollisionData->MaterialIndices.Add(SectionId);
1346 bHadCollision =
true;
1350 int32 VertexBase = CollisionData->Vertices.Num();
1354 const auto& Section = SectionEntry.Value;
1355 if (Section.VertexBuffer.Num() > 0 && Section.IndexBuffer.Num() > 0)
1357 CollisionData->Vertices.Append(Section.VertexBuffer);
1359 const int32 NumTriangles = Section.IndexBuffer.Num() / 3;
1360 for (int32 TriIdx = 0; TriIdx < NumTriangles; TriIdx++)
1363 FTriIndices& Triangle = *
new (CollisionData->Indices) FTriIndices;
1364 Triangle.v0 = Section.IndexBuffer[(TriIdx * 3) + 0] + VertexBase;
1365 Triangle.v1 = Section.IndexBuffer[(TriIdx * 3) + 1] + VertexBase;
1366 Triangle.v2 = Section.IndexBuffer[(TriIdx * 3) + 2] + VertexBase;
1369 CollisionData->MaterialIndices.Add(SectionEntry.Key);
1373 VertexBase = CollisionData->Vertices.Num();
1374 bHadCollision =
true;
1378 CollisionData->bFlipNormals =
true;
1380 return bHadCollision;
1385 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_CopyCollisionElementsToBodySetup);
1389 check(IsInGameThread());
1391 auto& ConvexElems = Setup->AggGeom.ConvexElems;
1392 ConvexElems.Empty();
1395 FKConvexElem& NewConvexElem = *
new(ConvexElems) FKConvexElem();
1396 NewConvexElem.VertexData = Section.Value.VertexBuffer;
1398 NewConvexElem.ElemBox = Section.Value.BoundingBox;
1401 auto& BoxElems = Setup->AggGeom.BoxElems;
1405 FKBoxElem& NewBox = *
new(BoxElems) FKBoxElem();
1406 NewBox.Center = Box.Center;
1407 NewBox.Rotation = Box.Rotation;
1408 NewBox.X = Box.Extents.X;
1409 NewBox.Y = Box.Extents.Y;
1410 NewBox.Z = Box.Extents.Z;
1413 auto& SphereElems = Setup->AggGeom.SphereElems;
1414 SphereElems.Empty();
1417 FKSphereElem& NewSphere = *
new(SphereElems)FKSphereElem();
1418 NewSphere.Center = Sphere.Center;
1419 NewSphere.Radius = Sphere.Radius;
1422 auto& SphylElems = Setup->AggGeom.SphylElems;
1426 FKSphylElem& NewSphyl = *
new(SphylElems)FKSphylElem();
1427 NewSphyl.Center = Capsule.Center;
1428 NewSphyl.Rotation = Capsule.Rotation;
1429 NewSphyl.Radius = Capsule.Radius;
1430 NewSphyl.Length = Capsule.Length;
1436 if (bSkipChangedFlag)
1476 int32 FaceIdx = FaceIndex;
1486 SCOPE_CYCLE_COUNTER(STAT_RuntimeMesh_GetSectionFromCollisionFaceIndex);
1490 int32 SectionIndex = 0;
1493 int32 TotalFaceCount = 0;
1495 for (int32 SectionIdx = 0; SectionIdx <
MeshSections.Num(); SectionIdx++)
1499 if (Section.IsValid() && Section->IsCollisionEnabled())
1502 int32 NumFaces = Section->GetNumIndices() / 3;
1504 if (FaceIndex < TotalFaceCount + NumFaces)
1507 SectionIndex = SectionIdx;
1508 FaceIndex -= TotalFaceCount;
1511 TotalFaceCount += NumFaces;
1514 return SectionIndex;
1524 : RuntimeMesh(InRuntimeMesh), Delegate(InDelegate)
1535 return ENamedThreads::GameThread;
1540 return ESubsequentsMode::FireAndForget;
1547 Delegate.Execute(Comp);
1555 if (IsInGameThread())
1568 TGraphTask<FRuntimeMeshGameThreadTask>::CreateTask().ConstructAndDispatchWhenReady(
ParentMeshObject, Func);
void EndSectionUpdate(FRuntimeMeshScopedUpdater *Updater, ERuntimeMeshBuffersToUpdate BuffersToUpdate, const FBox *BoundingBox=nullptr)
FRuntimeMeshSectionPtr CreateOrResetSection(int32 SectionId, EUpdateFrequency UpdateFrequency)
void CreateMeshSection_Blueprint(int32 SectionIndex, const TArray< FVector > &Vertices, const TArray< int32 > &Triangles, const TArray< FVector > &Normals, const TArray< FRuntimeMeshTangent > &Tangents, const TArray< FVector2D > &UV0, const TArray< FVector2D > &UV1, const TArray< FLinearColor > &Colors, bool bCreateCollision=false, bool bCalculateNormalTangent=false, bool bShouldCreateHardTangents=false, bool bGenerateTessellationTriangles=false, EUpdateFrequency UpdateFrequency=EUpdateFrequency::Average, bool bUseHighPrecisionTangents=false, bool bUseHighPrecisionUVs=true)
void RemoveCollisionSphere(int32 Index)
static ESubsequentsMode::Type GetSubsequentsMode()
void CreateMeshSection(int32 SectionIndex, bool bWantsHighPrecisionTangents, bool bWantsHighPrecisionUVs, int32 NumUVs, bool bWants32BitIndices, bool bCreateCollision, EUpdateFrequency UpdateFrequency=EUpdateFrequency::Average)
TArray< FRuntimeMeshCollisionBox > CollisionBoxes
void CopyCollisionElementsToBodySetup(UBodySetup *Setup)
bool GetPhysicsTriMeshData(struct FTriMeshCollisionData *CollisionData, bool InUseAllTriData)
void UpdateMeshSectionPacked_Blueprint(int32 SectionIndex, const TArray< FRuntimeMeshBlueprintVertexSimple > &Vertices, const TArray< int32 > &Triangles, bool bCalculateNormalTangent=false, bool bShouldCreateHardTangents=false, bool bGenerateTessellationTriangles=false)
void MarkRenderStateDirty()
void UpdateMeshSection_Blueprint(int32 SectionIndex, const TArray< FVector > &Vertices, const TArray< int32 > &Triangles, const TArray< FVector > &Normals, const TArray< FRuntimeMeshTangent > &Tangents, const TArray< FVector2D > &UV0, const TArray< FVector2D > &UV1, const TArray< FLinearColor > &Colors, bool bCalculateNormalTangent=false, bool bShouldCreateHardTangents=false, bool bGenerateTessellationTriangles=false)
TSharedPtr< const FRuntimeMeshAccessor > GetReadonlyMeshAccessor(int32 SectionId)
void ClearConvexCollisionSections()
void ClearCollisionSpheres()
void UpdateMeshSectionFromComponents(int32 SectionIndex, const TArray< FVector > &Vertices, const TArray< int32 > &Triangles, const TArray< FVector > &Normals, const TArray< FVector2D > &UV0, const TArray< FVector2D > &UV1, TFunction< FColor(int32 Index)> ColorAccessor, int32 NumColors, const TArray< FRuntimeMeshTangent > &Tangents, ESectionUpdateFlags UpdateFlags)
void CheckCreate(int32 NumUVs, bool bIndexIsValid) const
FBox GetSectionBoundingBox(int32 SectionIndex)
void DoOnGameThread(FRuntimeMeshGameThreadTaskDelegate Func)
ESectionUpdateFlags UpdateFlags
void MarkCollisionDirty()
int32 GetSectionAndFaceFromCollisionFaceIndex(int32 &FaceIndex) const
void SendSectionCreation(int32 SectionIndex)
void ClearMeshCollisionSection(int32 CollisionSectionIndex)
void HandleCommonSectionUpdateFlags(int32 SectionIndex, ESectionUpdateFlags UpdateFlags, ERuntimeMeshBuffersToUpdate &BuffersToUpdate)
FRuntimeMeshVertexStreamStructureElement Position
void ClearCollisionCapsules()
void MarkCollisionDirty(bool bSkipChangedFlag=false)
void UpdateSectionPropertiesInternal(int32 SectionIndex, bool bUpdateRequiresProxyRecreateIfStatic)
bool IsMeshSectionCollisionEnabled(int32 SectionIndex)
void SetMeshSectionVisible(int32 SectionIndex, bool bNewVisibility)
TUniquePtr< FRuntimeMeshScopedUpdater > BeginSectionUpdate(int32 SectionId, ESectionUpdateFlags UpdateFlags=ESectionUpdateFlags::None)
TArray< FRuntimeMeshSectionPtr > MeshSections
void UpdateMeshSectionByMove(int32 SectionId, const TSharedPtr< FRuntimeMeshBuilder > &MeshData, ESectionUpdateFlags UpdateFlags=ESectionUpdateFlags::None)
void EnterSerializedMode()
TWeakObjectPtr< URuntimeMesh > RuntimeMesh
FRuntimeMeshProxyPtr RenderProxy
void SetCollisionSpheres(const TArray< FRuntimeMeshCollisionSphere > &NewSpheres)
bool DoesSectionExist(int32 SectionIndex) const
TUniquePtr< FRuntimeMeshScopedUpdater > GetSectionReadonly(int32 SectionId)
int32 GetAvailableSectionIndex() const
TWeakObjectPtr< URuntimeMesh > ParentMeshObject
int32 AddCollisionCapsule(const FRuntimeMeshCollisionCapsule &NewCapsule)
int32 AddConvexCollisionSection(TArray< FVector > ConvexVerts)
void SetMeshSectionCollisionEnabled(int32 SectionIndex, bool bNewCollisionEnabled)
FRuntimeMeshGameThreadTaskDelegate Delegate
int32 AddCollisionBox(const FRuntimeMeshCollisionBox &NewBox)
void RemoveCollisionBox(int32 Index)
static bool ValidTripleStream(const FRuntimeMeshVertexStreamStructure &Stream1, const FRuntimeMeshVertexStreamStructure &Stream2, const FRuntimeMeshVertexStreamStructure &Stream3)
bool IsMeshSectionVisible(int32 SectionIndex) const
void SetMeshCollisionSection(int32 CollisionSectionIndex, const TArray< FVector > &Vertices, const TArray< int32 > &Triangles)
int32 GetNumSections() const
int32 GetSectionFromCollisionFaceIndex(int32 FaceIndex) const
TArray< FRuntimeMeshCollisionCapsule > CollisionCapsules
static void CalculateTangentsForMesh(const TArray< FVector > &Vertices, const TArray< int32 > &Triangles, TArray< FVector > &Normals, const TArray< FVector2D > &UVs, TArray< FRuntimeMeshTangent > &Tangents, bool bCreateSmoothNormals=true)
void UpdateSectionInternal(int32 SectionIndex, ERuntimeMeshBuffersToUpdate BuffersToUpdate, ESectionUpdateFlags UpdateFlags)
UTexture2D * Get(TUniquePtr< T > &Dtex)
static const textual_icon check
bool ContainsPhysicsTriMeshData(bool InUseAllTriData) const
void CheckCreateLegacyInternal(const FRuntimeMeshVertexStreamStructure &Stream0Structure, const FRuntimeMeshVertexStreamStructure &Stream1Structure, const FRuntimeMeshVertexStreamStructure &Stream2Structure, bool bIsIndexValid) const
bool IsMeshSectionCastingShadows(int32 SectionIndex) const
FRuntimeMeshGameThreadTask(TWeakObjectPtr< URuntimeMesh > InRuntimeMesh, FRuntimeMeshGameThreadTaskDelegate InDelegate)
void CheckUpdate(bool bUseHighPrecisionTangents, bool bUseHighPrecisionUVs, int32 NumUVs, bool b32BitIndices, int32 SectionIndex, bool bShouldCheckIndexType, bool bCheckTangentVertexStream, bool bCheckUVVertexStream) const
void UpdateMeshSection(int32 SectionId, TArray< VertexType0 > &InVertices0, ESectionUpdateFlags UpdateFlags=ESectionUpdateFlags::None)
TArray< int32 > GetSectionIds() const
void ClearAllMeshSections()
void ClearCollisionBoxes()
TArray< FRuntimeMeshCollisionSphere > CollisionSpheres
TMap< int32, FRuntimeMeshCollisionConvexMesh > ConvexCollisionSections
void CreateMeshSectionByMove(int32 SectionId, const TSharedPtr< FRuntimeMeshBuilder > &MeshData, bool bCreateCollision=false, EUpdateFrequency UpdateFrequency=EUpdateFrequency::Average, ESectionUpdateFlags UpdateFlags=ESectionUpdateFlags::None)
TSharedPtr< FRuntimeMeshProxy, ESPMode::ThreadSafe > FRuntimeMeshProxyPtr
void CreateSectionInternal(int32 SectionIndex, ESectionUpdateFlags UpdateFlags)
void ClearMeshSection(int32 SectionIndex)
void CreateMeshSectionPacked_Blueprint(int32 SectionIndex, const TArray< FRuntimeMeshBlueprintVertexSimple > &Vertices, const TArray< int32 > &Triangles, bool bCreateCollision=false, bool bCalculateNormalTangent=false, bool bShouldCreateHardTangents=false, bool bGenerateTessellationTriangles=false, EUpdateFrequency UpdateFrequency=EUpdateFrequency::Average, bool bUseHighPrecisionTangents=false, bool bUseHighPrecisionUVs=true)
void SetConvexCollisionSection(int32 ConvexSectionIndex, TArray< FVector > ConvexVerts)
void RemoveConvexCollisionSection(int32 ConvexSectionIndex)
FRuntimeMeshProxyPtr EnsureProxyCreated(ERHIFeatureLevel::Type InFeatureLevel)
DECLARE_CYCLE_STAT(TEXT("RM - Validation - Create"), STAT_RuntimeMesh_CheckCreate, STATGROUP_RuntimeMesh)
void CheckBoundingBox(const FBox &Box) const
void SetCollisionConvexMeshes(const TArray< TArray< FVector >> &ConvexMeshes)
TMap< int32, FRuntimeMeshCollisionSection > MeshCollisionSections
int32 GetLastSectionIndex() const
ERuntimeMeshBuffersToUpdate
void Setup(TWeakObjectPtr< URuntimeMesh > InParentMeshObject)
int32 AddCollisionSphere(const FRuntimeMeshCollisionSphere &NewSphere)
void SetCollisionCapsules(const TArray< FRuntimeMeshCollisionCapsule > &NewCapsules)
void ClearAllMeshCollisionSections()
FORCEINLINE TStatId GetStatId() const
void CreateMeshSectionFromComponents(int32 SectionIndex, const TArray< FVector > &Vertices, const TArray< int32 > &Triangles, const TArray< FVector > &Normals, const TArray< FVector2D > &UV0, const TArray< FVector2D > &UV1, TFunction< FColor(int32 Index)> ColorAccessor, int32 NumColors, const TArray< FRuntimeMeshTangent > &Tangents, bool bCreateCollision, EUpdateFrequency UpdateFrequency, ESectionUpdateFlags UpdateFlags, bool bUseHighPrecisionTangents, bool bUseHighPrecisionUVs, bool bWantsSecondUV)
void ForceProxyRecreate()
virtual void MarkChanged()
void SendSectionPropertiesUpdate(int32 SectionIndex)
void UpdateMeshSectionColors(int32 SectionIndex, TArray< FColor > &Colors, ESectionUpdateFlags UpdateFlags=ESectionUpdateFlags::None)
static ENamedThreads::Type GetDesiredThread()
TSharedPtr< FRuntimeMeshSection, ESPMode::ThreadSafe > FRuntimeMeshSectionPtr
FBoxSphereBounds GetLocalBounds() const
void SendSectionPropertiesUpdate(int32 SectionIndex)
void SetMeshSectionCastsShadow(int32 SectionIndex, bool bNewCastsShadow)
FRuntimeMeshSectionPtr CreateOrResetSectionForBlueprint(int32 SectionId, bool bWantsSecondUV, bool bHighPrecisionTangents, bool bHighPrecisionUVs, EUpdateFrequency UpdateFrequency)
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef &MyCompletionGraphEvent)
void RemoveCollisionCapsule(int32 Index)
void SetCollisionBoxes(const TArray< FRuntimeMeshCollisionBox > &NewBoxes)
TUniquePtr< FRuntimeMeshLockProvider > SyncRoot
static TArray< int32 > GenerateTessellationIndexBuffer(const TArray< FVector > &Vertices, const TArray< int32 > &Triangles, TArray< FVector > &Normals, const TArray< FVector2D > &UVs, TArray< FRuntimeMeshTangent > &Tangents)
FBoxSphereBounds LocalBounds