UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
InstancedSkinnedMeshComponentHelper.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
10
13{
14public:
15 template <class T, bool bSupportHitProxies = true>
17
18 template <class T>
19 static FBoxSphereBounds CalcBounds(const T& InComponent, const FTransform& LocalToWorld);
20
21 template <class T>
23
24 template <class T>
25 static bool IsEnabled(const T& InComponent);
26
27 template <class T>
29};
30
31template <class T, bool bSupportHitProxies>
33{
35
37
39 Flags.bHasPerInstanceRandom = ComponentDesc.PrimitiveMaterialDesc.bAnyMaterialHasPerInstanceRandom;
40 Flags.bHasPerInstanceCustomData = ComponentDesc.PrimitiveMaterialDesc.bAnyMaterialHasPerInstanceCustomData && InComponent.NumCustomDataFloats != 0;
41#if WITH_EDITOR
42 if constexpr (bSupportHitProxies)
43 {
44 Flags.bHasPerInstanceEditorData = GIsEditor != 0 && InComponent.bHasPerInstanceHitProxies;
45 }
46#endif
47
48 const USkinnedAsset* SkinnedAsset = InComponent.GetSkinnedAsset();
49 const UTransformProviderData* TransformProvider = InComponent.GetTransformProvider();
50 const bool bForceRefPose = UInstancedSkinnedMeshComponent::ShouldForceRefPose();
51 const bool bValidTransformProvider = !bForceRefPose && TransformProvider != nullptr && TransformProvider->IsEnabled();
52
53 Flags.bHasPerInstanceHierarchyOffset = false;
54 Flags.bHasPerInstanceLocalBounds = TransformProvider ? TransformProvider->HasAnimationBounds() : false;
55 Flags.bHasPerInstanceDynamicData = false;
56 Flags.bHasPerInstanceSkinningData = true;
57
58 Flags.bHasPerInstanceLMSMUVBias = false;//IsStaticLightingAllowed();
59
60 ComponentDesc.Flags = Flags;
61
62 // TODO: rename
63 ComponentDesc.MeshBounds = SkinnedAsset->GetBounds();
64 ComponentDesc.NumCustomDataFloats = InComponent.NumCustomDataFloats;
65 ComponentDesc.NumInstances = InComponent.InstanceData.Num();
66
67 ComponentDesc.PrimitiveLocalToWorld = InComponent.GetRenderMatrix();
68 ComponentDesc.ComponentMobility = InComponent.GetMobility();
69
70 const FTransform& ComponentTransform = InComponent.GetComponentTransform();
71
72 ComponentDesc.BuildChangeSet =
73 [
75 ComponentTransform,
76 TransformProvider = bValidTransformProvider ? TransformProvider : nullptr,
77 MeshBounds = ComponentDesc.MeshBounds,
78 bHasPerInstanceLocalBounds = Flags.bHasPerInstanceLocalBounds
80 {
81 // publish data
82 ChangeSet.GetTransformWriter().Gather([&InComponent](int32 InstanceIndex) -> FRenderTransform { return FRenderTransform(InComponent.InstanceData[InstanceIndex].Transform.ToMatrixWithScale()); });
83 ChangeSet.GetCustomDataWriter().Gather(MakeArrayView(InComponent.InstanceCustomData), InComponent.NumCustomDataFloats);
84
85 if (TransformProvider)
86 {
87 ChangeSet.GetSkinningDataWriter().Gather(
88 [&InComponent, TransformProvider, ComponentTransform](int32 InstanceIndex)->uint32
89 {
90 check(InComponent.InstanceData.IsValidIndex(InstanceIndex));
91 const FSkinnedMeshInstanceData& Instance = InComponent.InstanceData[InstanceIndex];
92 return TransformProvider->GetSkinningDataOffset(InstanceIndex, ComponentTransform, Instance);
93 });
94 }
95 else
96 {
97 ChangeSet.GetSkinningDataWriter().Gather(0u);
98 }
99
100 if (TransformProvider && bHasPerInstanceLocalBounds)
101 {
102 ChangeSet.GetLocalBoundsWriter().Gather(
103 [&InComponent, TransformProvider, MeshBounds](int32 InstanceIndex) -> FRenderBounds
104 {
105 const uint32 AnimationIndex = InComponent.InstanceData[InstanceIndex].AnimationIndex;
107 if (TransformProvider->GetAnimationBounds(AnimationIndex, AnimationBounds))
108 {
109 return AnimationBounds;
110 }
111 return MeshBounds;
112 });
113 }
114 else
115 {
116 ChangeSet.GetLocalBoundsWriter().Gather(MeshBounds);
117 }
118
119#if WITH_EDITOR
120 if constexpr (bSupportHitProxies)
121 {
122 if (ChangeSet.Flags.bHasPerInstanceEditorData)
123 {
124 // TODO: the way hit proxies are managed seems daft, why don't we just add them when needed and store them in an array alongside the instances?
125 // this will always force us to update all the hit proxy data for every instances.
127 InComponent.CreateHitProxyData(HitProxies);
128 ChangeSet.SetEditorData(HitProxies, InComponent.SelectedInstances);
129 }
130 }
131#endif
132
133
134 };
135
136 return ComponentDesc;
137}
138
139template <class T>
141{
142 const USkinnedAsset* SkinnedAssetPtr = InComponent.GetSkinnedAsset();
143 if (SkinnedAssetPtr && InComponent.InstanceData.Num() > 0)
144 {
145 const FMatrix BoundTransformMatrix = LocalToWorld.ToMatrixWithScale();
146
148
149 const bool bUseAnimationBounds = UInstancedSkinnedMeshComponent::ShouldUseAnimationBounds();
150 const UTransformProviderData* TransformProvider = InComponent.GetTransformProvider();
151 if (TransformProvider != nullptr && TransformProvider->IsEnabled() && TransformProvider->HasAnimationBounds() && !TransformProvider->IsCompiling())
152 {
153 const uint32 AnimationCount = TransformProvider->GetUniqueAnimationCount();
154
155 // Trade per sequence bounds (tighter fitting) for faster builds with high instance counts.
156 const bool bFastBuild = false;
157 if (bFastBuild)
158 {
159 FBox MergedBounds;
160
161 for (uint32 AnimationIndex = 0; AnimationIndex < AnimationCount; ++AnimationIndex)
162 {
164 if (TransformProvider->GetAnimationBounds(AnimationIndex, AnimationBounds))
165 {
166 MergedBounds += AnimationBounds.ToBox();
167 }
168 }
169
170 if (MergedBounds.IsValid)
171 {
172 for (int32 InstanceIndex = 0; InstanceIndex < InComponent.InstanceData.Num(); InstanceIndex++)
173 {
174 const FSkinnedMeshInstanceData& Instance = InComponent.InstanceData[InstanceIndex];
175 BoundsBuilder += MergedBounds.TransformBy(FTransform(Instance.Transform) * LocalToWorld);
176 }
177 }
178 }
179 else
180 {
181 for (int32 InstanceIndex = 0; InstanceIndex < InComponent.InstanceData.Num(); InstanceIndex++)
182 {
183 const FSkinnedMeshInstanceData& Instance = InComponent.InstanceData[InstanceIndex];
185 if (TransformProvider->GetAnimationBounds(Instance.AnimationIndex, AnimationBounds))
186 {
187 BoundsBuilder += AnimationBounds.ToBox().TransformBy(FTransform(Instance.Transform) * LocalToWorld);
188 }
189 }
190 }
191
192 // Only use bounds if valid, otherwise use the skinned asset bounds in ref pose.
193 if (BoundsBuilder.IsValid())
194 {
195 return BoundsBuilder;
196 }
197 }
198
199 const FBox InstanceBounds = SkinnedAssetPtr->GetBounds().GetBox();
200 if (InstanceBounds.IsValid)
201 {
202 for (int32 InstanceIndex = 0; InstanceIndex < InComponent.InstanceData.Num(); InstanceIndex++)
203 {
204 BoundsBuilder += InstanceBounds.TransformBy(FTransform(InComponent.InstanceData[InstanceIndex].Transform) * LocalToWorld);
205 }
206
207 return BoundsBuilder;
208 }
209 }
210
211 return InComponent.CalcMeshBound(FVector3f::ZeroVector, false, LocalToWorld);
212}
213
214template <class T>
219
220template <class T>
222{
224 if (SkeletalMeshPtr && SkeletalMeshPtr->GetResourceForRendering())
225 {
226 return InComponent.GetInstanceCount() > 0;
227 }
228
229 return false;
230}
231
232template <class T>
constexpr auto MakeArrayView(OtherRangeType &&Other)
Definition ArrayView.h:873
#define check(expr)
Definition AssertionMacros.h:314
#define GIsEditor
Definition CoreGlobals.h:233
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::Math::TTransform< double > FTransform
Definition MathFwd.h:53
EShaderPlatform
Definition RHIShaderPlatform.h:11
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition InstanceUpdateChangeSet.h:298
Definition InstancedSkinnedMeshComponentHelper.h:13
static FPrimitiveSceneProxy * CreateSceneProxy(const T &InComponent, const FInstancedSkinnedMeshSceneProxyDesc &Desc)
Definition InstancedSkinnedMeshComponentHelper.h:233
static FInstanceDataManagerSourceDataDesc GetComponentDesc(T &InComponent, EShaderPlatform ShaderPlatform)
Definition InstancedSkinnedMeshComponentHelper.h:32
static FSkeletalMeshObject * CreateMeshObject(const T &InComponent, const FInstancedSkinnedMeshSceneProxyDesc &InSceneProxyDesc)
Definition InstancedSkinnedMeshComponentHelper.h:215
static FBoxSphereBounds CalcBounds(const T &InComponent, const FTransform &LocalToWorld)
Definition InstancedSkinnedMeshComponentHelper.h:140
static bool IsEnabled(const T &InComponent)
Definition InstancedSkinnedMeshComponentHelper.h:221
static FPrimitiveMaterialPropertyDescriptor GetUsedMaterialPropertyDesc(const T &Component, EShaderPlatform InShaderPlatform)
Definition PrimitiveComponentHelper.h:33
Definition PrimitiveSceneProxy.h:296
Definition SkeletalRenderPublic.h:85
static bool ShouldNaniteSkin(const T &InComponent)
Definition SkinnedMeshComponentHelper.h:197
static int32 ComputeMinLOD(const T &InComponent)
Definition SkinnedMeshComponentHelper.h:83
static FSkeletalMeshRenderData * GetSkeletalMeshRenderData(const T &InComponent)
Definition SkinnedMeshComponentHelper.h:54
Definition Array.h:670
Definition SkeletalMesh.h:440
Definition SkinnedAsset.h:47
virtual FBoxSphereBounds GetBounds() const PURE_VIRTUAL(USkinnedAsset
Definition SkinnedAsset.h:143
Definition TransformProviderData.h:38
virtual bool IsCompiling() const
Definition TransformProviderData.h:87
virtual bool GetAnimationBounds(uint32 AnimationIndex, FRenderBounds &OutBounds) const
Definition TransformProviderData.h:68
virtual bool HasAnimationBounds() const
Definition TransformProviderData.h:63
virtual bool IsEnabled() const
Definition TransformProviderData.h:42
virtual const uint32 GetUniqueAnimationCount() const
Definition TransformProviderData.h:53
virtual uint32 GetSkinningDataOffset(int32 InstanceIndex, const FTransform &ComponentTransform, const FSkinnedMeshInstanceData &InstanceData) const
Definition TransformProviderData.h:73
Definition InstanceDataManager.h:20
FPrimitiveMaterialPropertyDescriptor PrimitiveMaterialDesc
Definition InstanceDataManager.h:26
Definition InstancedSkinnedMeshSceneProxyDesc.h:11
static ENGINE_API FSkeletalMeshObject * CreateMeshObject(const FInstancedSkinnedMeshSceneProxyDesc &Desc, FSkeletalMeshRenderData *InRenderData, ERHIFeatureLevel::Type InFeatureLevel)
Definition InstancedSkinnedMeshSceneProxyDesc.cpp:10
static ENGINE_API FPrimitiveSceneProxy * CreateSceneProxy(const FInstancedSkinnedMeshSceneProxyDesc &Desc, bool bHideSkin, bool bShouldNaniteSkin, bool bIsEnabled, int32 MinLODIndex)
Definition InstancedSkinnedMeshSceneProxyDesc.cpp:25
Definition RenderTransform.h:272
FBox ToBox() const
Definition RenderTransform.h:301
Definition RenderTransform.h:23
Definition InstancedSkinnedMeshComponent.h:24
Definition BoxSphereBounds.h:336
Definition BoxSphereBounds.h:25
TBox< T > TransformBy(const TMatrix< T > &M) const
Definition Box.h:871
uint8 IsValid
Definition Box.h:45
TMatrix< T > ToMatrixWithScale() const
Definition TransformNonVectorized.h:241
static CORE_API const TVector< float > ZeroVector
Definition Vector.h:79
Definition InstanceDataTypes.h:36
uint16 bHasPerInstanceRandom
Definition InstanceDataTypes.h:40