UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
InstancedPlacementHash.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6
7#if WITH_EDITORONLY_DATA
8#include "Misc/HashBuilder.h"
9
15{
16private:
17 static constexpr int32 MaxHashCellBits = 9; // 512x512x512 grid
18 struct FKey
19 {
20 int64 X;
21 int64 Y;
22 int64 Z;
23
24 FKey() = default;
25
27 : X(InX), Y(InY), Z(InZ) {}
28
29 // returns the minimum distance from the given position to the cell described by this key, squared
31 {
32 // compute distance to the cell cube
33 int32 CellSize = (1 << LocalHashCellBits);
34 int32 HalfCellSize = CellSize >> 1;
35 FVector CellCenter(static_cast<FVector::FReal>(X * CellSize + HalfCellSize), static_cast<FVector::FReal>(Y * CellSize + HalfCellSize), static_cast<FVector::FReal>(Z * CellSize + HalfCellSize));
36 FVector AbsRelativePosition = (Position - CellCenter).GetAbs();
39 }
40
41 bool operator==(const FKey& Other) const
42 {
43 return (X == Other.X) && (Y == Other.Y) && (Z == Other.Z);
44 }
45
46 friend uint32 GetTypeHash(const FKey& Key)
47 {
48 FHashBuilder HashBuilder;
49 HashBuilder << Key.X << Key.Y << Key.Z;
50 return HashBuilder.GetHash();
51 }
52
53 friend FArchive& operator<<(FArchive& Ar, FKey& Key)
54 {
55 Ar << Key.X;
56 Ar << Key.Y;
57 Ar << Key.Z;
58 return Ar;
59 }
60 };
61
62 const int32 HashCellBits;
64
65 FKey MakeKey(const FVector& Location) const
66 {
67 return FKey(FMath::FloorToInt64(Location.X) >> HashCellBits, FMath::FloorToInt64(Location.Y) >> HashCellBits, FMath::FloorToInt64(Location.Z) >> HashCellBits);
68 }
69
71 {
72 return FVector(static_cast<FVector::FReal>(CellKey.X << HashCellBits),
73 static_cast<FVector::FReal>(CellKey.Y << HashCellBits),
74 static_cast<FVector::FReal>(CellKey.Z << HashCellBits));
75 }
76
77public:
80 {}
81
82 ~FInstancedPlacementHash() = default;
83
84 void InsertInstance(const FVector& InstanceLocation, int32 InstanceIndex)
85 {
86 FKey Key = MakeKey(InstanceLocation);
87
88 CellMap.FindOrAdd(Key).Add(InstanceIndex);
89 }
90
91 void RemoveInstance(const FVector& InstanceLocation, int32 InstanceIndex, bool bChecked = true)
92 {
93 FKey Key = MakeKey(InstanceLocation);
94
95 if (bChecked)
96 {
97 int32 RemoveCount = CellMap.FindChecked(Key).Remove(InstanceIndex);
98 check(RemoveCount == 1);
99 }
100 else if (TSet<int32>* Value = CellMap.Find(Key))
101 {
102 Value->Remove(InstanceIndex);
103 }
104 }
105
106 void SwapInstance(const FVector& InstanceLocation, int32 OldIndex, int32 NewIndex, bool bCheckedRemoval = true)
107 {
108 RemoveInstance(InstanceLocation, OldIndex, bCheckedRemoval);
110 }
111
112 template<typename FunctionType>
113 bool IsAnyInstanceInSphere(FunctionType InstanceLocationGetter, const FVector& SphereCenter, double SphereRadius)
114 {
116
117 double SphereRadiusSquared = SphereRadius * SphereRadius;
118
119 // if there are more potential cells within range than the number of actually populated cells...
120 int32 CellSize = (1 << HashCellBits);
121 double SphereRadiusCells = SphereRadius / CellSize;
122 constexpr double SphereVolumeConstant = 4.0 / 3.0 * UE_PI;
124 if (ApproxCellsToCheck > CellMap.Num())
125 {
126 // then it's probably faster to just check all populated cells and be done
127 for (auto& Pair : CellMap)
128 {
129 if (Pair.Key.GetMinDistSquared(SphereCenter, HashCellBits) <= SphereRadiusSquared)
130 {
131 for (int32 InstanceIndex : Pair.Value)
132 {
133 double DistSquared = (InstanceLocationGetter(InstanceIndex) - SphereCenter).SizeSquared();
134 if (DistSquared < SphereRadiusSquared)
135 {
136 return true;
137 }
138 }
139 }
140 }
141 return false;
142 }
143
144 // otherwise we check all potential cells within range
146 FKey MinKey = MakeKey(SphereBox.Min);
147 FKey MaxKey = MakeKey(SphereBox.Max);
148 for (int64 z = MinKey.Z; z <= MaxKey.Z; ++z)
149 {
150 for (int64 y = MinKey.Y; y <= MaxKey.Y; y++)
151 {
152 for (int64 x = MinKey.X; x <= MaxKey.X; x++)
153 {
154 FKey Key(x, y, z);
155 auto* SetPtr = CellMap.Find(Key);
156 if (SetPtr)
157 {
158 for (int32 InstanceIndex : *SetPtr)
159 {
160 float DistSquared = (InstanceLocationGetter(InstanceIndex) - SphereCenter).SizeSquared();
161 if (DistSquared < SphereRadiusSquared)
162 {
163 return true;
164 }
165 }
166 }
167 }
168 }
169 }
170
171 return false;
172 }
173
174 void GetInstancesOverlappingBox(const FBox& InBox, TArray<int32>& OutInstanceIndices) const
175 {
176 FKey MinKey = MakeKey(InBox.Min);
177 FKey MaxKey = MakeKey(InBox.Max);
178
179 // compute in doubles to avoid overflow issues (queries using WORLD_MAX bounds can produce very large numbers...)
180 double CellsX = static_cast<double>((MaxKey.X - MinKey.X + 1));
181 double CellsY = static_cast<double>((MaxKey.Y - MinKey.Y + 1));
182 double CellsZ = static_cast<double>((MaxKey.Z - MinKey.Z + 1));
183 double CellCount = CellsX * CellsY * CellsZ;
184
185 // The idea here is to decide when it is faster to just check every populated cell.
186 // The actual ideal threshold will depend on the exact state of the cells, but we just pick
187 // some arbitrary ratio where we switch to checking populated cells over potential cells within range.
188 // In practice the exact threshold is not that important; we just want to know if there are different orders of magnitude involved.
189 // (i.e. it is faster to check 32 populated cells than 3,000,000,000 potential cells...)
190 constexpr double RelativeCostOfCheckingPopulatedCells = 2.0;
191 double Threshold = RelativeCostOfCheckingPopulatedCells * CellMap.Num();
192 if (CellCount > Threshold)
193 {
194 // check every populated cell
195 for (auto& Pair : CellMap)
196 {
197 if ((Pair.Key.X >= MinKey.X) &&
198 (Pair.Key.X <= MaxKey.X) &&
199 (Pair.Key.Y >= MinKey.Y) &&
200 (Pair.Key.Y <= MaxKey.Y) &&
201 (Pair.Key.Z >= MinKey.Z) &&
202 (Pair.Key.Z <= MaxKey.Z))
203 {
204 OutInstanceIndices.Reserve(OutInstanceIndices.Num() + Pair.Value.Num());
205 for (int32 InstanceIndex : Pair.Value)
206 {
207 OutInstanceIndices.Add(InstanceIndex);
208 }
209 }
210 }
211 }
212 else
213 {
214 // check all potential cells within range
215 for (int64 z = MinKey.Z; z <= MaxKey.Z; ++z)
216 {
217 for (int64 y = MinKey.Y; y <= MaxKey.Y; y++)
218 {
219 for (int64 x = MinKey.X; x <= MaxKey.X; x++)
220 {
221 auto* SetPtr = CellMap.Find(FKey(x, y, z));
222 if (SetPtr)
223 {
224 OutInstanceIndices.Reserve(OutInstanceIndices.Num() + SetPtr->Num());
225 for (int32 InstanceIndex : *SetPtr)
226 {
227 OutInstanceIndices.Add(InstanceIndex);
228 }
229 }
230 }
231 }
232 }
233 }
234 }
235
236 TArray<int32> GetInstancesOverlappingBox(const FBox& InBox) const
237 {
239 GetInstancesOverlappingBox(InBox, Result);
240 return Result;
241 }
242
244 {
245 int32 HashCount = 0;
246 for (const auto& Pair : CellMap)
247 {
248 HashCount += Pair.Value.Num();
249 }
250
251 check(HashCount == InCount);
252 }
253
254 FBox GetBounds() const
255 {
257 for (const auto& Pair : CellMap)
258 {
259 HashBounds += MakeLocation(Pair.Key);
260 }
261
262 return HashBounds;
263 }
264
265 void Empty()
266 {
267 CellMap.Empty();
268 }
269
271 {
272 Ar << Hash.CellMap;
273 return Ar;
274 }
275};
276#endif
#define check(expr)
Definition AssertionMacros.h:314
@ ForceInit
Definition CoreMiscDefines.h:155
FPlatformTypes::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
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
#define TRACE_CPUPROFILER_EVENT_SCOPE(Name)
Definition CpuProfilerTrace.h:528
FArchive & operator<<(FArchive &Ar, FEnvQueryDebugProfileData::FStep &Data)
Definition EnvQueryTypes.cpp:489
#define X(Name, Desc)
Definition FormatStringSan.h:47
#define FVector
Definition IOSSystemIncludes.h:8
UE_FORCEINLINE_HINT void SetPtr(uint32 To)
Definition LockFreeList.h:20
#define UE_PI
Definition UnrealMathUtility.h:129
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Archive.h:1208
Definition HashBuilder.h:18
UE_FORCEINLINE_HINT uint32 GetHash() const
Definition HashBuilder.h:87
Definition Array.h:670
Definition UnrealString.h.inl:34
uint32 GetTypeHash(const FKey &Key)
Definition BlackboardKey.h:35
bool operator==(const FCachedAssetKey &A, const FCachedAssetKey &B)
Definition AssetDataMap.h:501
double SizeSquared(const T &Value)
Definition SplineMath.h:172
UE_STRING_CLASS Result(Forward< LhsType >(Lhs), RhsLen)
Definition String.cpp.inl:732
Definition InputCoreTypes.h:50
static TBox< double > BuildAABB(const TVector< double > &Origin, const TVector< double > &Extent)
Definition Box.h:651
static CORE_API const TVector< double > ZeroVector
Definition Vector.h:79
double FReal
Definition Vector.h:55
T SizeSquared() const
Definition Vector.h:1728