UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
TextureLayout3d.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3/*=============================================================================
4 TextureLayout3d.h: Texture space allocation.
5=============================================================================*/
6
7#pragma once
8
9#include "CoreMinimal.h"
10
16{
17public:
18
28 MinSizeX(InMinSizeX),
29 MinSizeY(InMinSizeY),
30 MinSizeZ(InMinSizeZ),
31 SizeX(InMinSizeX),
32 SizeY(InMinSizeY),
33 SizeZ(InMinSizeZ),
34 bPowerOfTwoSize(bInPowerOfTwoSize),
35 bAlignByFour(bInAlignByFour),
36 bAllowShrink(bInAllowShrink)
37 {
39 Nodes.Emplace(static_cast<uint16>(0), static_cast<uint16>(0), static_cast<uint16>(0), IntCastChecked<uint16>(MaxSizeX), IntCastChecked<uint16>(MaxSizeY), IntCastChecked<uint16>(MaxSizeZ), INDEX_NONE);
40 UnusedLeaves.Emplace(static_cast<uint16>(0), static_cast<uint16>(0), static_cast<uint16>(0), IntCastChecked<uint16>(MaxSizeX), IntCastChecked<uint16>(MaxSizeY), IntCastChecked<uint16>(MaxSizeZ), 0);
41 }
42
55 {
56 if (ElementSizeX == 0 || ElementSizeY == 0 || ElementSizeZ == 0)
57 {
58 OutBaseX = 0;
59 OutBaseY = 0;
60 OutBaseZ = 0;
61 return true;
62 }
63
64 if (bAlignByFour)
65 {
66 // Pad to 4 to ensure alignment
67 ElementSizeX = (ElementSizeX + 3) & ~3;
68 ElementSizeY = (ElementSizeY + 3) & ~3;
69 ElementSizeZ = (ElementSizeZ + 3) & ~3;
70 }
71
74 int32 RemoveIndex = INDEX_NONE;
75 for (int32 Index = 0; Index < UnusedLeaves.Num(); ++Index)
76 {
77 if (ElementSizeX <= UnusedLeaves[Index].SizeX && ElementSizeY <= UnusedLeaves[Index].SizeY && ElementSizeZ <= UnusedLeaves[Index].SizeZ)
78 {
80 {
81 ContainingNodeIndex = UnusedLeaves[Index].NodeIndex;
82 RemoveIndex = Index;
83 }
84
85 if (UnusedLeaves[Index].MinX + ElementSizeX <= SizeX && UnusedLeaves[Index].MinY + ElementSizeY <= SizeY && UnusedLeaves[Index].MinZ + ElementSizeZ <= SizeZ)
86 {
87 IdealNodeIndex = UnusedLeaves[Index].NodeIndex;
88 RemoveIndex = Index;
89 break;
90 }
91 }
92 }
93
94 if (RemoveIndex != INDEX_NONE)
95 {
96 UnusedLeaves.RemoveAtSwap(RemoveIndex);
97 }
98
99 // Try allocating space without enlarging the texture.
100 int32 NodeIndex = INDEX_NONE;
102 {
103 NodeIndex = AddSurfaceInner(IdealNodeIndex, ElementSizeX, ElementSizeY, ElementSizeZ, false);
104 check(NodeIndex != INDEX_NONE);
105 }
107 {
108 // Try allocating space which might enlarge the texture.
109 NodeIndex = AddSurfaceInner(ContainingNodeIndex, ElementSizeX, ElementSizeY, ElementSizeZ, true);
110 }
111
112 if (NodeIndex != INDEX_NONE)
113 {
114 FTextureLayoutNode3d& Node = Nodes[NodeIndex];
115 Node.bUsed = true;
116 OutBaseX = Node.MinX;
117 OutBaseY = Node.MinY;
118 OutBaseZ = Node.MinZ;
119
120 UsedLeaves.Add(FIntVector(OutBaseX, OutBaseY, OutBaseZ), NodeIndex);
121
123 return true;
124 }
125 else
126 {
127 return false;
128 }
129 }
130
137 {
139
140 // Find the element to remove
141 UsedLeaves.RemoveAndCopyValue(FIntVector(ElementBaseX, ElementBaseY, ElementBaseZ), FoundNodeIndex);
142
144 {
145 check(Nodes[FoundNodeIndex].SizeX == (bAlignByFour ? ((ElementSizeX + 3u) & ~3u) : ElementSizeX)
146 && Nodes[FoundNodeIndex].SizeY == (bAlignByFour ? ((ElementSizeY + 3u) & ~3u) : ElementSizeY)
147 && Nodes[FoundNodeIndex].SizeZ == (bAlignByFour ? ((ElementSizeZ + 3u) & ~3u) : ElementSizeZ));
148
149 // Mark the found node as not being used anymore
150 Nodes[FoundNodeIndex].bUsed = false;
151
152 // Walk up the tree to find the node closest to the root that doesn't have any used children
153 int32 ParentNodeIndex = FindParentNode(FoundNodeIndex);
155
156 while (ParentNodeIndex != INDEX_NONE
157 && !IsNodeUsed(Nodes[ParentNodeIndex].ChildA)
158 && !IsNodeUsed(Nodes[ParentNodeIndex].ChildB))
159 {
160 LastParentNodeIndex = ParentNodeIndex;
161 ParentNodeIndex = FindParentNode(ParentNodeIndex);
162 }
163
164 // Remove the children of the node closest to the root with only unused children,
165 // Which restores the tree to its state before this element was allocated,
166 // And allows allocations as large as LastParentNode in the future.
168 {
171 }
172
173 UnusedLeaves.Emplace(
174 Nodes[FoundNodeIndex].MinX,
175 Nodes[FoundNodeIndex].MinY,
176 Nodes[FoundNodeIndex].MinZ,
177 Nodes[FoundNodeIndex].SizeX,
178 Nodes[FoundNodeIndex].SizeY,
179 Nodes[FoundNodeIndex].SizeZ,
181
182 // Recalculate size
183 if (bAllowShrink)
184 {
185 SizeX = MinSizeX;
186 SizeY = MinSizeY;
187 SizeZ = MinSizeZ;
188
189 for (int32 NodeIndex = 0; NodeIndex < Nodes.Num(); NodeIndex++)
190 {
191 const FTextureLayoutNode3d& Node = Nodes[NodeIndex];
192
193 if (Node.bUsed)
194 {
195 UpdateSize(Node.MinX + Node.SizeX, Node.MinY + Node.SizeY, Node.MinZ + Node.SizeZ);
196 }
197 }
198 }
199
200 return true;
201 }
202
203 return false;
204 }
205
209 uint32 GetSizeX() const { return SizeX; }
210
214 uint32 GetSizeY() const { return SizeY; }
215
216 uint32 GetSizeZ() const { return SizeZ; }
217
218 FIntVector GetSize() const { return FIntVector(SizeX, SizeY, SizeZ); }
219
221 {
222 return Nodes[0].SizeX;
223 }
224
226 {
227 return Nodes[0].SizeY;
228 }
229
231 {
232 return Nodes[0].SizeZ;
233 }
234
235private:
236
237 struct FTextureLayoutNode3d
238 {
239 int32 ChildA,
240 ChildB,
241 Parent;
242 uint16 MinX,
243 MinY,
244 MinZ,
245 SizeX,
246 SizeY,
247 SizeZ;
248 bool bUsed;
249
251 ChildA(INDEX_NONE),
252 ChildB(INDEX_NONE),
254 MinX(InMinX),
255 MinY(InMinY),
256 MinZ(InMinZ),
257 SizeX(InSizeX),
258 SizeY(InSizeY),
259 SizeZ(InSizeZ),
260 bUsed(false)
261 {}
262 };
263
264 struct FUnusedLeaf
265 {
266 uint16 MinX, MinY, MinZ;
267 uint16 SizeX, SizeY, SizeZ;
268 int32 NodeIndex;
269
271 MinX(InMinX),
272 MinY(InMinY),
273 MinZ(InMinZ),
274 SizeX(InSizeX),
275 SizeY(InSizeY),
276 SizeZ(InSizeZ),
277 NodeIndex(InNodeIndex)
278 {}
279 };
280
281 uint32 MinSizeX;
282 uint32 MinSizeY;
283 uint32 MinSizeZ;
284 uint32 SizeX;
285 uint32 SizeY;
286 uint32 SizeZ;
287 bool bPowerOfTwoSize;
288 bool bAlignByFour;
289 bool bAllowShrink;
291 TArray<int32> FreeNodeIndices;
292 TArray<FUnusedLeaf> UnusedLeaves;
293 TMap<FIntVector, int32> UsedLeaves;
294
297 {
298 checkSlow(NodeIndex != INDEX_NONE);
299 // But do access this node via a pointer until the first recursive call. Prevents a ton of LHS.
300 const FTextureLayoutNode3d* CurrentNodePtr = &Nodes[NodeIndex];
301 if (CurrentNodePtr->ChildA != INDEX_NONE)
302 {
303 // Children are always allocated together
305
306 // Traverse the children
308
309 // The pointer is now invalid, be explicit!
310 CurrentNodePtr = 0;
311
312 if (Result != INDEX_NONE)
313 {
314 return Result;
315 }
316
317 return AddSurfaceInner(Nodes[NodeIndex].ChildB, ElementSizeX, ElementSizeY, ElementSizeZ, bAllowTextureEnlargement);
318 }
319 // Node has no children, it is a leaf
320 else
321 {
322 // Reject this node if it is already used
323 if (CurrentNodePtr->bUsed)
324 {
325 return INDEX_NONE;
326 }
327
328 // Reject this node if it is too small for the element being placed
330 {
331 return INDEX_NONE;
332 }
333
335 {
336 // Reject this node if this is an attempt to allocate space without enlarging the texture,
337 // And this node cannot hold the element without enlarging the texture.
338 if (CurrentNodePtr->MinX + ElementSizeX > SizeX || CurrentNodePtr->MinY + ElementSizeY > SizeY || CurrentNodePtr->MinZ + ElementSizeZ > SizeZ)
339 {
340 return INDEX_NONE;
341 }
342 }
343
344 // Use this node if the size matches the requested element size
346 {
347 return NodeIndex;
348 }
349
353
354 // The pointer to the current node may be invalidated below, be explicit!
355 CurrentNodePtr = 0;
356
357 // Store a copy of the current node on the stack for easier debugging.
358 // Can't store a pointer to the current node since Nodes may be reallocated in this function.
359 const FTextureLayoutNode3d CurrentNode = Nodes[NodeIndex];
360
361 // Update the child indices
362 int32 ChildANodeIndex = FreeNodeIndices.Num() ? FreeNodeIndices.Pop() : Nodes.AddUninitialized();
363 int32 ChildBNodeIndex = FreeNodeIndices.Num() ? FreeNodeIndices.Pop() : Nodes.AddUninitialized();
364
365 Nodes[NodeIndex].ChildA = ChildANodeIndex;
366 Nodes[NodeIndex].ChildB = ChildBNodeIndex;
367
368 // Add new nodes, and link them as children of the current node.
370 {
372 {
373 // Create a child with the same width as the element being placed.
374 // The height may not be the same as the element height yet, in that case another subdivision will occur when traversing this child node.
375 new(&Nodes[ChildANodeIndex]) FTextureLayoutNode3d(
376 CurrentNode.MinX,
377 CurrentNode.MinY,
378 CurrentNode.MinZ,
380 CurrentNode.SizeY,
381 CurrentNode.SizeZ,
382 NodeIndex);
383
384 // Create a second child to contain the leftover area in the X direction
385 new(&Nodes[ChildBNodeIndex]) FTextureLayoutNode3d(
386 IntCastChecked<uint16>(CurrentNode.MinX + ElementSizeX),
387 CurrentNode.MinY,
388 CurrentNode.MinZ,
389 IntCastChecked<uint16>(CurrentNode.SizeX - ElementSizeX),
390 CurrentNode.SizeY,
391 CurrentNode.SizeZ,
392 NodeIndex);
393 }
394 else
395 {
396 new(&Nodes[ChildANodeIndex]) FTextureLayoutNode3d(
397 CurrentNode.MinX,
398 CurrentNode.MinY,
399 CurrentNode.MinZ,
400 CurrentNode.SizeX,
401 CurrentNode.SizeY,
403 NodeIndex);
404
405 new(&Nodes[ChildBNodeIndex]) FTextureLayoutNode3d(
406 CurrentNode.MinX,
407 CurrentNode.MinY,
408 IntCastChecked<uint16>(CurrentNode.MinZ + ElementSizeZ),
409 CurrentNode.SizeX,
410 CurrentNode.SizeY,
411 IntCastChecked<uint16>(CurrentNode.SizeZ - ElementSizeZ),
412 NodeIndex);
413 }
414 }
415 else
416 {
418 {
419 new(&Nodes[ChildANodeIndex]) FTextureLayoutNode3d(
420 CurrentNode.MinX,
421 CurrentNode.MinY,
422 CurrentNode.MinZ,
423 CurrentNode.SizeX,
425 CurrentNode.SizeZ,
426 NodeIndex);
427
428 new(&Nodes[ChildBNodeIndex]) FTextureLayoutNode3d(
429 CurrentNode.MinX,
430 IntCastChecked<uint16>(CurrentNode.MinY + ElementSizeY),
431 CurrentNode.MinZ,
432 CurrentNode.SizeX,
433 IntCastChecked<uint16>(CurrentNode.SizeY - ElementSizeY),
434 CurrentNode.SizeZ,
435 NodeIndex);
436 }
437 else
438 {
439 new(&Nodes[ChildANodeIndex]) FTextureLayoutNode3d(
440 CurrentNode.MinX,
441 CurrentNode.MinY,
442 CurrentNode.MinZ,
443 CurrentNode.SizeX,
444 CurrentNode.SizeY,
446 NodeIndex);
447
448 new(&Nodes[ChildBNodeIndex]) FTextureLayoutNode3d(
449 CurrentNode.MinX,
450 CurrentNode.MinY,
451 IntCastChecked<uint16>(CurrentNode.MinZ + ElementSizeZ),
452 CurrentNode.SizeX,
453 CurrentNode.SizeY,
454 IntCastChecked<uint16>(CurrentNode.SizeZ - ElementSizeZ),
455 NodeIndex);
456 }
457 }
458
459 const FTextureLayoutNode3d& ChildBNode = Nodes[ChildBNodeIndex];
460
461 UnusedLeaves.Emplace(ChildBNode.MinX, ChildBNode.MinY, ChildBNode.MinZ, ChildBNode.SizeX, ChildBNode.SizeY, ChildBNode.SizeZ, ChildBNodeIndex);
462
463 // Only traversing ChildA, since ChildA is always the newly created node that matches the element size
465 }
466 }
467
469 {
470 if (bPowerOfTwoSize)
471 {
472 SizeX = FMath::Max<uint32>(SizeX, FMath::RoundUpToPowerOfTwo(ElementMaxX));
473 SizeY = FMath::Max<uint32>(SizeY, FMath::RoundUpToPowerOfTwo(ElementMaxY));
474 SizeZ = FMath::Max<uint32>(SizeZ, FMath::RoundUpToPowerOfTwo(ElementMaxZ));
475 }
476 else
477 {
478 SizeX = FMath::Max<uint32>(SizeX, ElementMaxX);
479 SizeY = FMath::Max<uint32>(SizeY, ElementMaxY);
480 SizeZ = FMath::Max<uint32>(SizeZ, ElementMaxZ);
481 }
482 }
483
485 int32 FindParentNode(int32 SearchNodeIndex)
486 {
487 return Nodes[SearchNodeIndex].Parent;
488 }
489
491 bool IsNodeUsed(int32 NodeIndex)
492 {
493 bool bChildrenUsed = false;
494 if (Nodes[NodeIndex].ChildA != INDEX_NONE)
495 {
496 checkSlow(Nodes[NodeIndex].ChildB != INDEX_NONE);
497 bChildrenUsed = IsNodeUsed(Nodes[NodeIndex].ChildA) || IsNodeUsed(Nodes[NodeIndex].ChildB);
498 }
499 return bChildrenUsed || Nodes[NodeIndex].bUsed;
500 }
501
503 void RemoveChildren(int32 NodeIndex)
504 {
505 // Traverse the children depth first
506 if (Nodes[NodeIndex].ChildA == INDEX_NONE)
507 {
508 check(Nodes[NodeIndex].ChildB == INDEX_NONE);
509 return;
510 }
511
512 check(Nodes[NodeIndex].ChildB != INDEX_NONE);
513
514 // Traverse the children depth first
515 RemoveChildren(Nodes[NodeIndex].ChildA);
516 RemoveChildren(Nodes[NodeIndex].ChildB);
517
518 {
519 const int32 ChildNodeIndex = Nodes[NodeIndex].ChildA;
520
521 // Remove the child from the Nodes array
522 FreeNodeIndices.Add(ChildNodeIndex);
523
524 for (int32 Index = 0; Index < UnusedLeaves.Num(); ++Index)
525 {
526 if (UnusedLeaves[Index].NodeIndex == ChildNodeIndex)
527 {
528 UnusedLeaves.RemoveAtSwap(Index);
529 break;
530 }
531 }
532 }
533
534 {
535 const int32 ChildNodeIndex = Nodes[NodeIndex].ChildB;
536 FreeNodeIndices.Add(ChildNodeIndex);
537
538 for (int32 Index = 0; Index < UnusedLeaves.Num(); ++Index)
539 {
540 if (UnusedLeaves[Index].NodeIndex == ChildNodeIndex)
541 {
542 UnusedLeaves.RemoveAtSwap(Index);
543 break;
544 }
545 }
546 }
547
548 Nodes[NodeIndex].ChildA = INDEX_NONE;
549 Nodes[NodeIndex].ChildB = INDEX_NONE;
550 }
551};
552
#define checkSlow(expr)
Definition AssertionMacros.h:332
#define check(expr)
Definition AssertionMacros.h:314
@ INDEX_NONE
Definition CoreMiscDefines.h:150
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
FInt32Vector3 FIntVector
Definition MathFwd.h:115
uint16_t uint16
Definition binka_ue_file_header.h:7
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition TextureLayout3d.h:16
FTextureLayout3d(uint32 InMinSizeX, uint32 InMinSizeY, uint32 InMinSizeZ, uint32 MaxSizeX, uint32 MaxSizeY, uint32 MaxSizeZ, bool bInPowerOfTwoSize=false, bool bInAlignByFour=true, bool bInAllowShrink=true)
Definition TextureLayout3d.h:27
bool RemoveElement(uint32 ElementBaseX, uint32 ElementBaseY, uint32 ElementBaseZ, uint32 ElementSizeX, uint32 ElementSizeY, uint32 ElementSizeZ)
Definition TextureLayout3d.h:136
uint32 GetSizeX() const
Definition TextureLayout3d.h:209
uint32 GetSizeY() const
Definition TextureLayout3d.h:214
uint32 GetMaxSizeY() const
Definition TextureLayout3d.h:225
FIntVector GetSize() const
Definition TextureLayout3d.h:218
uint32 GetMaxSizeX() const
Definition TextureLayout3d.h:220
uint32 GetMaxSizeZ() const
Definition TextureLayout3d.h:230
bool AddElement(uint32 &OutBaseX, uint32 &OutBaseY, uint32 &OutBaseZ, uint32 ElementSizeX, uint32 ElementSizeY, uint32 ElementSizeZ)
Definition TextureLayout3d.h:54
uint32 GetSizeZ() const
Definition TextureLayout3d.h:216
Definition Array.h:670
UE_FORCEINLINE_HINT SizeType AddUninitialized()
Definition Array.h:1664
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_FORCEINLINE_HINT void RemoveAtSwap(SizeType Index, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2185
UE_FORCEINLINE_HINT SizeType Emplace(ArgsType &&... Args)
Definition Array.h:2561
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
ElementType Pop(EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:1196
Definition UnrealString.h.inl:34
UE_STRING_CLASS Result(Forward< LhsType >(Lhs), RhsLen)
Definition String.cpp.inl:732
@ false
Definition radaudio_common.h:23
U16 Index
Definition radfft.cpp:71