UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SkeletalMeshLODModel.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#if WITH_EDITOR
6
9#include "Async/Mutex.h"
10#include "Async/UniqueLock.h"
11#include "BoneIndices.h"
12#include "Components.h"
13#include "GPUSkinPublicDefs.h"
14#include "HAL/CriticalSection.h"
17#include "SkeletalMeshTypes.h"
18
23{
25 uint16 MaterialIndex;
26
28 uint32 BaseIndex;
29
31 uint32 NumTriangles;
32
34 uint8 bSelected : 1;
35
37 bool bRecomputeTangent;
38
40 ESkinVertexColorChannel RecomputeTangentsVertexMaskChannel;
41
43 bool bCastShadow;
44
46 bool bVisibleInRayTracing;
47
50
56
58 uint32 BaseVertexIndex;
59
62
72 TArray<TArray<FMeshToMeshVertData>> ClothMappingDataLODs;
73
76
78 int32 NumVertices;
79
81 int32 MaxBoneInfluences;
82
84 bool bUse16BitBoneIndex;
85
86 // INDEX_NONE if not set
87 int16 CorrespondClothAssetIndex;
88
90 FClothingSectionData ClothingData;
91
94
96 bool bDisabled;
97
98 /*
99 * The LOD index at which any generated lower quality LODs will include this section.
100 * A value of -1 mean the section will always be include when generating a LOD
101 */
103
104 /*
105 * This represent the original section index in the imported data. The original data is chunk per material,
106 * we use this index to store user section modification. The user cannot change a BONE chunked section data,
107 * since the BONE chunk can be per-platform. Do not use this value to index the Sections array, only the user
108 * section data should be index by this value.
109 */
111
112 /*
113 * If this section was produce because of BONE chunking, the parent section index will be valid.
114 * If the section is not the result of skin vertex chunking, this value will be INDEX_NONE.
115 * Use this value to know if the section was BONE chunked:
116 * if(ChunkedParentSectionIndex != INDEX_NONE) will be true if the section is BONE chunked
117 */
119
120
121
123 : MaterialIndex(0)
124 , BaseIndex(0)
125 , NumTriangles(0)
126 , bSelected(false)
127 , bRecomputeTangent(false)
128 , RecomputeTangentsVertexMaskChannel(ESkinVertexColorChannel::None)
129 , bCastShadow(true)
130 , bVisibleInRayTracing(true)
133 , BaseVertexIndex(0)
134 , NumVertices(0)
135 , MaxBoneInfluences(4)
136 , bUse16BitBoneIndex(false)
137 , CorrespondClothAssetIndex(INDEX_NONE)
138 , bDisabled(false)
142 {}
143
144
148 inline int32 GetNumVertices() const
149 {
150 // Either SoftVertices should be empty, or size should match NumVertices
151 check(SoftVertices.Num() == 0 || SoftVertices.Num() == NumVertices);
152 return NumVertices;
153 }
154
158 inline int32 GetVertexBufferIndex() const
159 {
160 return BaseVertexIndex;
161 }
162
166 inline bool HasClothingData() const
167 {
168 constexpr int32 ClothLODBias = 0; // Must at least have the mapping for the matching cloth LOD
169 return ClothMappingDataLODs.Num() && ClothMappingDataLODs[ClothLODBias].Num();
170 }
171
176
177 inline int32 GetMaxBoneInfluences() const
178 {
179 return MaxBoneInfluences;
180 }
181
186
187 inline bool Use16BitBoneIndex() const
188 {
189 return bUse16BitBoneIndex;
190 }
191
192 // Serialization.
194 static void DeclareCustomVersions(FArchive& Ar);
195};
196
197class FSkelMeshSectionArray : public TArray<FSkelMeshSection, TInlineAllocator<1>>
198{
200public:
201 using Super::Super;
202};
203
211{
213 bool bRecomputeTangent;
214
216 ESkinVertexColorChannel RecomputeTangentsVertexMaskChannel;
217
219 bool bCastShadow;
220
222 bool bVisibleInRayTracing;
223
224 // INDEX_NONE if not set
225 int16 CorrespondClothAssetIndex;
226
228 FClothingSectionData ClothingData;
229
230
232 // Skeletal mesh DDC key members, Add sections member that impact generated skel mesh here
233
235 bool bDisabled;
236
237 /*
238 * The LOD index at which any generated lower quality LODs will include this section.
239 * A value of -1 mean the section will always be include when generating a LOD
240 */
242
243 // End DDC members
245
246
247
249 : bRecomputeTangent(false)
250 , RecomputeTangentsVertexMaskChannel(ESkinVertexColorChannel::None)
251 , bCastShadow(true)
252 , bVisibleInRayTracing(true)
253 , CorrespondClothAssetIndex(INDEX_NONE)
254 , bDisabled(false)
256 {}
257
261 inline bool HasClothingData() const
262 {
263 return (ClothingData.AssetGuid.IsValid());
264 }
265
267 {
268 FSkelMeshSourceSectionUserData* UserSectionData = UserSectionsData.Find(Section.OriginalDataSectionIndex);
269 if (!UserSectionData)
270 {
271 //If the UserSectionData do not exist add it and copy from the section data
272 UserSectionData = &UserSectionsData.Add(Section.OriginalDataSectionIndex);
273 UserSectionData->bCastShadow = Section.bCastShadow;
274 UserSectionData->bVisibleInRayTracing = Section.bVisibleInRayTracing;
275 UserSectionData->bDisabled = Section.bDisabled;
276 UserSectionData->bRecomputeTangent = Section.bRecomputeTangent;
277 UserSectionData->RecomputeTangentsVertexMaskChannel = Section.RecomputeTangentsVertexMaskChannel;
278 UserSectionData->GenerateUpToLodIndex = Section.GenerateUpToLodIndex;
279 UserSectionData->CorrespondClothAssetIndex = Section.CorrespondClothAssetIndex;
280 UserSectionData->ClothingData.AssetGuid = Section.ClothingData.AssetGuid;
281 UserSectionData->ClothingData.AssetLodIndex = Section.ClothingData.AssetLodIndex;
282 }
284 return *UserSectionData;
285 }
286 // Serialization.
288};
289
294{
295public:
298
299 /*
300 * When user change section data in the UI, we store it here to be able to regenerate the changes
301 * Note: the key (int32) is the original imported section data, because of BONE chunk the size of
302 * this array is not the same as the Sections array. Use the section's OriginalDataSectionIndex to
303 * index it.
304 */
306
307 uint32 NumVertices;
309 uint32 NumTexCoords;
310
313
318 TArray<FBoneIndexType> ActiveBoneIndices;
319
325 TArray<FBoneIndexType> RequiredBones;
326
329
332
337
340
343 {
344 return RawPointIndices2;
345 }
346
349 {
350 return RawPointIndices2;
351 }
352
353 UE_DEPRECATED(5.0, "Please do not access this function anymore. This data is not use anymore.")
355 {
357 }
358
363
366 : NumVertices(0)
367 , NumTexCoords(0)
368 , MaxImportVertex(-1)
372 , BuildStringID(TEXT(""))
373 {
374 //Sice this ID is part of the DDC Key, we have to set it to an empty GUID not an empty string
376 //Allocate the private mutex
378 }
379
381 {
382 //Release the allocate resources
383 if(BulkDataReadMutex != nullptr)
384 {
385 delete BulkDataReadMutex;
386 BulkDataReadMutex = nullptr;
387 }
388 }
389
391 {
392 Sections = MoveTemp(InOther.Sections);
393 UserSectionsData = MoveTemp(InOther.UserSectionsData);
394 NumVertices = InOther.NumVertices;
395 NumTexCoords = InOther.NumTexCoords;
396 IndexBuffer = MoveTemp(InOther.IndexBuffer);
397 ActiveBoneIndices = MoveTemp(InOther.ActiveBoneIndices);
398 RequiredBones = MoveTemp(InOther.RequiredBones);
399 SkinWeightProfiles = MoveTemp(InOther.SkinWeightProfiles);
400 VertexAttributes = MoveTemp(InOther.VertexAttributes);
401 MeshToImportVertexMap = MoveTemp(InOther.MeshToImportVertexMap);
402 MaxImportVertex = InOther.MaxImportVertex;
403 RawSkeletalMeshBulkDataID = MoveTemp(InOther.RawSkeletalMeshBulkDataID);
404 bIsBuildDataAvailable = InOther.bIsBuildDataAvailable;
405 bIsRawSkeletalMeshBulkDataEmpty = InOther.bIsRawSkeletalMeshBulkDataEmpty;
406 ImportedMeshInfos = MoveTemp(InOther.ImportedMeshInfos);
407 RawPointIndices2 = MoveTemp(InOther.RawPointIndices2);
408 BulkDataReadMutex = InOther.BulkDataReadMutex;
409 InOther.BulkDataReadMutex = nullptr;
410 BuildStringID = InOther.BuildStringID;
411 }
412
413 /*Empty the skeletal mesh LOD model. Empty copy a default constructed FSkeletalMeshLODModel but will not copy the BulkDataReadMutex which will be the same after*/
414 void Empty()
415 {
417 *this = FSkeletalMeshLODModel();
419 }
420
421private:
422 friend struct FSkeletalMeshSourceModel;
423
425 {
426 FName Name; // The name of the mesh.
427 int32 NumVertices; // The number of imported (dcc) vertices that are part of this mesh. This is a value of 8 for a cube. So NOT the number of render vertices.
428 int32 StartImportedVertex; // The first index of imported (dcc) vertices in the mesh. So this NOT an index into the render vertex buffer. In range of 0..7 for a cube.
429
430 void Serialize(FArchive& Ar)
431 {
432 Ar << Name;
433 Ar << NumVertices;
435 }
436 };
438 {
439 Info.Serialize(Ar);
440 return Ar;
441 }
442
444
450
454
455 //Mutex use by the CopyStructure function. It's a pointer because FCriticalSection privatize the operator= function, which will prevent this class operator= to use the default.
456 //We want to avoid having a custom equal operator that will get deprecated if dev forget to add the new member in this class
457 //The CopyStructure function will copy everything but make sure the destination mutex is set to a new mutex pointer.
459
460 //Use the static FSkeletalMeshLODModel::CopyStructure function to copy from one instance to another
461 //The reason is we want the copy to be multithread safe and use the BulkDataReadMutex.
462 FSkeletalMeshLODModel& operator=(const FSkeletalMeshLODModel& Other) = default;
463
464 //Use the static FSkeletalMeshLODModel::CreateCopy function to copy from one instance to another
465 //The reason is we want the copy to be multithread safe and use the BulkDataReadMutex.
467
468public:
477 void Serialize(FArchive& Ar, UObject* Owner, int32 Idx);
478 static void DeclareCustomVersions(FArchive& Ar);
479
485 ENGINE_API void GetVertices(TArray<FSoftSkinVertex>& Vertices) const;
486
493
494
495
497 ENGINE_API void GetSectionFromVertexIndex(int32 InVertIndex, int32& OutSectionIndex, int32& OutVertIndex) const;
498
502 ENGINE_API bool HasClothData() const;
503
504 ENGINE_API int32 NumNonClothingSections() const;
505
507
515
516 ENGINE_API int32 GetMaxBoneInfluences() const;
518
519 void GetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize) const;
520
525
526 // Offers protected access while supporting copy.
528 {
529 private:
530 FString BuildStringID;
531 mutable UE::FMutex Mutex;
532 public:
533 FThreadSafeBuildStringID() = default;
536 {
537 }
538
540 {
542 UE::TUniqueLock Lock(Mutex);
543 BuildStringID = Other.BuildStringID;
544 }
545
547 {
548 if (&Other != this)
549 {
551 UE::TUniqueLock Lock(Mutex);
552 BuildStringID = Other.BuildStringID;
553 }
554 return *this;
555 }
556
557 bool operator==(const FString& Other) const
558 {
559 UE::TUniqueLock Lock(Mutex);
560 return BuildStringID == Other;
561 }
562
563 bool operator==(const FThreadSafeBuildStringID& Other) const
564 {
565 if (&Other == this)
566 {
567 return true;
568 }
569
571 UE::TUniqueLock Lock(Mutex);
572 return BuildStringID == Other.BuildStringID;
573 }
574
575 void operator=(const FString& Other)
576 {
577 UE::TUniqueLock Lock(Mutex);
579 }
580
581 operator FString() const
582 {
583 UE::TUniqueLock Lock(Mutex);
584 return BuildStringID;
585 }
586
589 };
590
591 //Temporary build String ID
592 //We use this string to store the LOD model data so we can know if the LOD need to be rebuild
593 //This GUID is set when we Cache the render data (build function)
595
599 ENGINE_API FString GetLODModelDeriveDataKey() const;
600
605 ENGINE_API void UpdateChunkedSectionInfo(const FString& SkeletalMeshName);
606
612 static ENGINE_API void CopyStructure(FSkeletalMeshLODModel* Destination, const FSkeletalMeshLODModel* Source);
613
618 static FSkeletalMeshLODModel* CreateCopy(const FSkeletalMeshLODModel* Other)
619 {
620 FSkeletalMeshLODModel* Destination = new FSkeletalMeshLODModel();
621 FSkeletalMeshLODModel::CopyStructure(Destination, Other);
622 return Destination;
623 }
624
629 void ENGINE_API GetMeshDescription(const USkeletalMesh *InSkeletalMesh, const int32 InLODIndex, FMeshDescription& OutMeshDescription) const;
630};
631
632#endif // WITH_EDITOR
#define check(expr)
Definition AssertionMacros.h:314
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define UE_DEPRECATED(Version, Message)
Definition CoreMiscDefines.h:302
FPlatformTypes::int16 int16
A 16-bit signed integer.
Definition Platform.h:1123
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::TCHAR TCHAR
Either ANSICHAR or WIDECHAR, depending on whether the platform supports wide characters or the requir...
Definition Platform.h:1135
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
UE::FPlatformRecursiveMutex FCriticalSection
Definition CriticalSection.h:53
FArchive & operator<<(FArchive &Ar, FEnvQueryDebugProfileData::FStep &Data)
Definition EnvQueryTypes.cpp:489
return true
Definition ExternalRpcRegistry.cpp:601
ESkinVertexColorChannel
Definition SkeletalMeshTypes.h:38
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
FRWLock Lock
Definition UnversionedPropertySerialization.cpp:921
uint8_t uint8
Definition binka_ue_file_header.h:8
uint16_t uint16
Definition binka_ue_file_header.h:7
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Archive.h:1208
virtual void Serialize(void *V, int64 Length)
Definition Archive.h:1689
Definition NameTypes.h:617
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
Definition BulkData.h:1036
Definition UnrealString.h.inl:34
Definition Mutex.h:18
Definition UniqueLock.h:20
Definition Object.h:95
Definition SkeletalMesh.h:440
UE::FRecursiveMutex Mutex
Definition MeshPaintVirtualTexture.cpp:164
bool operator==(const FCachedAssetKey &A, const FCachedAssetKey &B)
Definition AssetDataMap.h:501
void GetVertices(const TArray< FTopologicalEdge * > &InEdges, TArray< FTopologicalVertex * > &OutVertices)
Definition Topomaker.cpp:531
@ false
Definition radaudio_common.h:23
Definition SkeletalMeshTypes.h:141
FGuid AssetGuid
Definition SkeletalMeshTypes.h:153
Definition Guid.h:109
FString ToString(EGuidFormats Format=EGuidFormats::Digits) const
Definition Guid.h:329
bool IsValid() const
Definition Guid.h:318
Definition MeshDescription.h:94
Definition ResourceSize.h:31
Definition SkeletalMeshSourceModel.h:30