UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SegmentTree3.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
7#include "SegmentTypes.h"
8#include "BoxTypes.h"
12
13
14namespace UE
15{
16namespace Geometry
17{
18
19
20
36{
37public:
39
40public:
41
50
56 template<typename SegmentIDEnumerable, typename GetSegmentFunc>
58 {
60
61 for (int32 SegmentID : Enumerable)
62 {
64 SegmentList.Add( FSegment{SegmentID, Segment} );
65 }
66
67 // Make explicit list of linear indices and centers for the build step, which
68 // will sort/swap these lists in-place.
69 // Centers could possibly be removed as centers can be fetched from the SegmentList...
74 for (int32 k = 0; k < SegmentList.Num(); ++k )
75 {
76 SegmentIndices.Add(k);
77 Centers.Add( SegmentList[k].Segment.Center );
78 }
80 }
81
83 {
85 for (int32 SegmentID = 0; SegmentID < Segments.Num(); ++SegmentID)
86 {
87 SegmentList[SegmentID].ID = SegmentID;
88 SegmentList[SegmentID].Segment = Segments[SegmentID];
89 }
90
91 // Make explicit list of linear indices and centers for the build step, which
92 // will sort/swap these lists in-place.
93 // Centers could possibly be removed as centers can be fetched from the SegmentList...
98 for (int32 k = 0; k < SegmentList.Num(); ++k)
99 {
100 SegmentIndices.Add(k);
101 Centers.Add(SegmentList[k].Segment.Center);
102 }
104 }
105
112 ) const
113 {
114 double NearestDistSqr = (Options.MaxDistance < TNumericLimits<double>::Max()) ?
115 Options.MaxDistance * Options.MaxDistance : TNumericLimits<double>::Max();
119 {
121 return true;
122 }
123 return false;
124 }
125
130 {
132 double RayParam;
138 double Metric;
139 };
140
153 const FRay3d& Ray,
158 {
159 // Note: using TNumericLimits<float>::Max() here because we need to use <= to compare Box hit
160 // to NearestT, and Box hit returns TNumericLimits<double>::Max() on no-hit. So, if we set
161 // nearestT to TNumericLimits<double>::Max(), then we will test all boxes (!)
163 NearestInfo.RayParam = (Options.MaxDistance < TNumericLimits<float>::Max()) ? Options.MaxDistance : TNumericLimits<float>::Max();
164 NearestInfo.SegmentParam = 0.5;
167
171 {
173 return true;
174 }
175 return false;
176 }
177
178
179
180protected:
181
182
184
186 {
187 return [](int Depth, const FAxisAlignedBox3d&)
188 {
189 return Depth % 3;
190 };
191 }
192
194
195 // list of segments
197
198 // storage for Box Nodes.
199 struct FTreeBox
200 {
201 int BoxToIndex; // index into IndexList
204 };
206
207 // list of indices for a given Box. There is *no* marker/sentinel between
208 // the per-box lists, you have to get the starting index from TreeBox.BoxToIndex
209 //
210 // There are two sections, first are the lists of triangle indices in the leaf boxes,
211 // and then the lists for the internal-node boxes that have one or two child boxes.
212 // So there are three kinds of records:
213 // - if i < SegmentsEnd, then the list is a number of Segments,
214 // stored as [N t1 t2 t3 ... tN]
215 // - if i > SegmentsEnd and IndexList[i] < 0, this is a single-child
216 // internal Box, with index (-IndexList[i])-1 (shift-by-one in case actual value is 0!)
217 // - if i > SegmentsEnd and IndexList[i] > 0, this is a two-child
218 // internal Box, with indices IndexList[i]-1 and IndexList[i+1]-1
220
221 // IndexList[i] for i < SegmentsEnd is a segment-index list, ie a leaf-box list
222 int SegmentsEnd = -1;
223
224 // RootBoxIndex is the index of the root box of the tree, index into TreeBoxes
225 int RootBoxIndex = -1;
226
227
228
229 // this is a temporary data structure used in tree building
242
244 {
246 FBoxesSet Nodes;
248 int SplitsRootNode =
250
251 SegmentsEnd = Tris.IIndicesCur;
252 int IndexShift = SegmentsEnd;
253 int BoxShift = Tris.IBoxCur;
254
255 // append internal node boxes & index ptrs
257 for (int32 i = 0; i < Nodes.IBoxCur; ++i)
258 {
259 FVector3d NodeBoxCenter = Nodes.Boxes[i].Center; // cannot pass as argument in case a resize happens
260 FVector3d NodeBoxExtents = Nodes.Boxes[i].Extents;
261 int NodeBoxIndex = Nodes.Boxes[i].BoxToIndex;
262
263 // internal node indices are shifted
264 UseBoxes.InsertAt(
266 }
267
268 // copy to final boxes list
269 int32 NumBoxes = UseBoxes.Num();
270 TreeBoxes.SetNum(NumBoxes);
271 for (int32 k = 0; k < NumBoxes; ++k)
272 {
273 TreeBoxes[k] = UseBoxes[k];
274 }
275
276 // now append index list
278 for (int32 i = 0; i < Nodes.IIndicesCur; ++i)
279 {
280 int ChildBox = Nodes.IndexList[i];
281 if (ChildBox < 0)
282 {
283 // this is a Segments Box
284 ChildBox = (-ChildBox) - 1;
285 }
286 else
287 {
289 }
290 ChildBox = ChildBox + 1;
291 UseIndexList.InsertAt(ChildBox, IndexShift + i);
292 }
293
294 int32 NumIndices = UseIndexList.Num();
295 IndexList.SetNum(NumIndices);
296 for (int32 k = 0; k < NumIndices; ++k)
297 {
298 IndexList[k] = UseIndexList[k];
299 }
300
302 }
303
304
305 // TODO: should not actually need SegmentCenters here, can get from Indices...
306
310 int StartIdx, int Count, int Depth, int MinSegmentCount,
312 {
313 Box = (SegmentIndices.Num() > 0) ?
315 int BoxIndex = -1;
316
317 if (Count <= MinSegmentCount)
318 {
319 // append new Segments Box
320 BoxIndex = SegmentBoxes.IBoxCur++;
321 int32 IndexCur = SegmentBoxes.IIndicesCur;
322
323 SegmentBoxes.IndexList.InsertAt(Count, SegmentBoxes.IIndicesCur++);
324 for (int i = 0; i < Count; ++i)
325 {
326 int32 SegmentIdx = SegmentIndices[StartIdx + i];
327 SegmentBoxes.IndexList.InsertAt(SegmentIdx, SegmentBoxes.IIndicesCur++);
329 Box.Contain(SegmentBounds);
330 }
331
332 SegmentBoxes.Boxes.InsertAt( FTreeBox{IndexCur, Box.Center(), Box.Extents()}, BoxIndex );
333
334 return -(BoxIndex + 1);
335 }
336
337 //compute interval along an axis and find midpoint
339 FInterval1d Interval = FInterval1d::Empty();
340 for (int i = 0; i < Count; ++i)
341 {
342 Interval.Contain(SegmentCenters[StartIdx + i][SplitAxis]);
343 }
344 double Midpoint = Interval.Center();
345
346 int Count0, Count1;
347 if (Interval.Length() > FMathd::ZeroTolerance)
348 {
349 // we have to re-sort the Centers & Segments lists so that Centers < midpoint
350 // are first, so that we can recurse on the two subsets. We walk in from each side,
351 // until we find two out-of-order locations, then we swap them.
352 int Left = 0;
353 int Right = Count - 1;
354 while (Left < Right)
355 {
356 // TODO: is <= right here? if V.axis == midpoint, then this loop
357 // can get stuck unless one of these has an equality test. But
358 // I did not think enough about if this is the right thing to do...
359 while (SegmentCenters[StartIdx + Left][SplitAxis] <= Midpoint)
360 {
361 Left++;
362 }
363 while (SegmentCenters[StartIdx + Right][SplitAxis] > Midpoint)
364 {
365 Right--;
366 }
367 if (Left >= Right)
368 {
369 break; //done!
370 //swap
371 }
372 Swap(SegmentCenters[StartIdx + Left], SegmentCenters[StartIdx + Right]);
373 Swap(SegmentIndices[StartIdx + Left], SegmentIndices[StartIdx + Right]);
374 }
375
376 Count0 = Left;
377 Count1 = Count - Count0;
378 checkSlow(Count0 >= 1 && Count1 >= 1);
379 }
380 else
381 {
382 // interval is near-empty, so no point trying to do sorting, just split half and half
383 Count0 = Count / 2;
384 Count1 = Count - Count0;
385 }
386
387 // create child boxes
391 Box.Contain(Child1Box);
392
393 // append new Box
394 BoxIndex = Nodes.IBoxCur++;
395 Nodes.Boxes.InsertAt( FTreeBox{Nodes.IIndicesCur, Box.Center(), Box.Extents()}, BoxIndex);
396
397 Nodes.IndexList.InsertAt(Child0, Nodes.IIndicesCur++);
398 Nodes.IndexList.InsertAt(Child1, Nodes.IIndicesCur++);
399
400 return BoxIndex;
401 }
402
403
404public:
409 void SetTolerance(double Tolerance)
410 {
411 BoxEps = Tolerance;
412 }
413
414protected:
415 // TODO: move BoxEps to IMeshSpatial::FQueryOptions
416 double BoxEps = FMathd::ZeroTolerance;
417
418 double GetBoxDistanceSqr(int BoxIndex, const FVector3d& V) const
419 {
420 const FTreeBox& Box = TreeBoxes[BoxIndex];
421
422 // This appears to be more accurate, but I think the distance computation is more expensive (branches)
423 // (if we preferred accuracy, we could store box in [Min,Max] instead of [Center,Extent]...)
424 //FAxisAlignedBox3d Temp(Box.Center - Box.Extents, Box.Center + Box.Extents);
425 //double DistSqr1 = Temp.DistanceSquared(V);
426
427 // per-axis delta is max(abs(P-c) - e, 0)... ?
428 double dx = FMath::Max(FMathd::Abs(V.X - Box.Center.X) - Box.Extents.X, 0.0);
429 double dy = FMath::Max(FMathd::Abs(V.Y - Box.Center.Y) - Box.Extents.Y, 0.0);
430 double dz = FMath::Max(FMathd::Abs(V.Z - Box.Center.Z) - Box.Extents.Z, 0.0);
431 return dx * dx + dy * dy + dz * dz;
432 }
433
434 double GetRayBoxIntersectionParam(int BoxIndex, const FRay3d& Ray, double Radius = 0) const
435 {
436 const FTreeBox& TreeBox = TreeBoxes[BoxIndex];
437 FVector3d Extents = TreeBox.Extents + (Radius + BoxEps);
438 FAxisAlignedBox3d Box(TreeBox.Center - Extents, TreeBox.Center + Extents);
439
440 double RayParameter = TNumericLimits<double>::Max();
441 if (FIntrRay3AxisAlignedBox3d::FindIntersection(Ray, Box, RayParameter))
442 {
443 return RayParameter;
444 }
445 else
446 {
448 }
449 }
450
451
452
453protected:
455 {
456 const FTreeBox& TreeBox = TreeBoxes[BoxIndex];
457 if (TreeBox.BoxToIndex < SegmentsEnd)
458 {
459 // segment-list case, array is [N t1 t2 ... tN]
460 int NumSegments = IndexList[TreeBox.BoxToIndex];
461 for (int i = 1; i <= NumSegments; ++i)
462 {
463 int SegmentIdx = IndexList[TreeBox.BoxToIndex + i];
464
465 if (Options.TriangleFilterF != nullptr && Options.TriangleFilterF(SegmentIdx) == false)
466 {
467 continue;
468 }
469
470 double SegmentDistSqr = SegmentList[SegmentIdx].Segment.DistanceSquared(P);
472 {
475 }
476 }
477 }
478 else
479 {
480 // internal node, either 1 or 2 child boxes
481 int iChild1 = IndexList[TreeBox.BoxToIndex];
482 if (iChild1 < 0)
483 {
484 // 1 child, descend if nearer than cur min-dist
485 iChild1 = (-iChild1) - 1;
488 {
490 }
491 }
492 else
493 {
494 // 2 children, descend closest first
495 iChild1 = iChild1 - 1;
496 int iChild2 = IndexList[TreeBox.BoxToIndex + 1] - 1;
497
501 {
503 {
506 {
508 }
509 }
510 }
511 else
512 {
514 {
517 {
519 }
520 }
521 }
522 }
523 }
524 }
525
526
527
528
529 // This function tries to find the segment "hit" by the ray, where the definition
530 // of "hit" tries to balance distance from the ray origin (ie "close to eye") and
531 // distance from the hit segment (ie "on the line"). There is no correct way to do
532 // this, currently the code uses a metric based on the opening angle between ray
533 // and segment points.
535 int BoxIndex, const FRay3d& Ray,
538 const IMeshSpatial::FQueryOptions& Options) const
539 {
540 const FTreeBox& TreeBox = TreeBoxes[BoxIndex];
541 if (TreeBox.BoxToIndex < SegmentsEnd)
542 {
543 // segment-list case, array is [N t1 t2 ... tN]
544 int NumSegments = IndexList[TreeBox.BoxToIndex];
545 for (int i = 1; i <= NumSegments; ++i)
546 {
547 int SegmentIdx = IndexList[TreeBox.BoxToIndex + i];
548
549 const FSegment3d& Segment = SegmentList[SegmentIdx].Segment;
550 double SegRayParam; double SegSegParam;
554 {
555 // try to balance preference for closer-to-ray-origin and closer-to-segment
557
558 if (DistanceMetric < NearestInfo.Metric)
559 {
561 NearestInfo.RayParam = SegRayParam;
562 NearestInfo.SegmentParam = SegSegParam;
563 NearestInfo.SegmentDist = FMathd::Sqrt(SegDistSqr);
565 }
566
567 }
568 }
569 }
570 else
571 {
572 // expand boxes by this amount to try to ensure that we hit sufficient radius...
573 double UseBoxRadius = FMathd::Min( 2*NearestInfo.SegmentDist, (double)TNumericLimits<float>::Max() );
574
575 // This code may need some work. Since we might prefer a "further" hit segment
576 // that is closer to the ray, have to descend any hit box. We need to grow the
577 // box by the maximum hit radius that would have any effect. However we don't
578 // easily know that, in particular because we don't know what WithinToleranceCheck()
579 // is based on. It could be a world-space distance but in some usage it is based
580 // on a projection to 2D, or a visual-angle. In this case it is quite hard to
581 // determine how much the bounding-box needs to be expanded. One option would be
582 // to do the WithinToleranceCheck() on the box corners, or alternate the caller
583 // could provide a function to compute the tolerance radius for any 3D point
584 // (which may be quite expensive too)...
585
586 // internal node, either 1 or 2 child boxes
587 int iChild1 = IndexList[TreeBox.BoxToIndex];
588 if (iChild1 < 0)
589 {
590 // 1 child, descend if nearer than cur min-dist
591 iChild1 = (-iChild1) - 1;
594 {
596 }
597 }
598 else
599 {
600 // 2 children. No sorting of descent here because we are only checking hit/no-hit
601 iChild1 = iChild1 - 1;
602 int iChild2 = IndexList[TreeBox.BoxToIndex + 1] - 1;
603
606 {
608 }
609
612 {
614 }
615 }
616 }
617 }
618
619
620};
621
622
623} // end namespace UE::Geometry
624} // end namespace UE
#define checkSlow(expr)
Definition AssertionMacros.h:332
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
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
void SetNumUninitialized(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2369
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition AssetRegistryState.h:50
static RealType Sqrt(const RealType Value)
Definition MathUtil.h:342
static RealType Abs(const RealType Value)
Definition MathUtil.h:215
static RealType Min(const RealType A, const RealType B)
Definition MathUtil.h:271
Definition SegmentTree3.h:36
void FindNearestVisibleSegmentHitByRayInternal(int BoxIndex, const FRay3d &Ray, TFunctionRef< bool(int32, const FVector3d &, const FVector3d &)> WithinToleranceCheck, int &NearSegmentIdx, FRayNearestSegmentInfo &NearestInfo, const IMeshSpatial::FQueryOptions &Options) const
Definition SegmentTree3.h:534
int TopDownLeafMaxSegmentCount
Definition SegmentTree3.h:183
bool FindNearestVisibleSegmentHitByRay(const FRay3d &Ray, TFunctionRef< bool(int32, const FVector3d &, const FVector3d &)> WithinToleranceCheck, FSegment &NearestHitSegmentOut, FRayNearestSegmentInfo &NearestInfo, const IMeshSpatial::FQueryOptions &Options=IMeshSpatial::FQueryOptions()) const
Definition SegmentTree3.h:152
int SplitSegmentSetMidpoints(TArray< int > &SegmentIndices, TArray< FVector3d > &SegmentCenters, int StartIdx, int Count, int Depth, int MinSegmentCount, FBoxesSet &SegmentBoxes, FBoxesSet &Nodes, FAxisAlignedBox3d &Box)
Definition SegmentTree3.h:307
double GetBoxDistanceSqr(int BoxIndex, const FVector3d &V) const
Definition SegmentTree3.h:418
void Build(const TArray< FSegment3d > &Segments)
Definition SegmentTree3.h:82
void Build(SegmentIDEnumerable Enumerable, GetSegmentFunc GetSegmentForID, int32 NumSegmentsHint=0)
Definition SegmentTree3.h:57
double GetRayBoxIntersectionParam(int BoxIndex, const FRay3d &Ray, double Radius=0) const
Definition SegmentTree3.h:434
double BoxEps
Definition SegmentTree3.h:416
void FindNearestSegmentInternal(int BoxIndex, const FVector3d &P, double &NearestDistSqr, int &NearSegmentIdx, const IMeshSpatial::FQueryOptions &Options) const
Definition SegmentTree3.h:454
TArray< FTreeBox > TreeBoxes
Definition SegmentTree3.h:205
bool FindNearestSegment(const FVector3d &P, FSegment &NearestSegmentOut, const IMeshSpatial::FQueryOptions &Options=IMeshSpatial::FQueryOptions()) const
Definition SegmentTree3.h:109
void SetTolerance(double Tolerance)
Definition SegmentTree3.h:409
int SegmentsEnd
Definition SegmentTree3.h:222
GetSplitAxisFunc GetSplitAxis
Definition SegmentTree3.h:193
static GetSplitAxisFunc MakeDefaultSplitAxisFunc()
Definition SegmentTree3.h:185
int RootBoxIndex
Definition SegmentTree3.h:225
TArray< int > IndexList
Definition SegmentTree3.h:219
void BuildTopDown(TArray< int > &SegmentIndices, TArray< FVector3d > &SegmentCenters, int32 NumSegments)
Definition SegmentTree3.h:243
TArray< FSegment > SegmentList
Definition SegmentTree3.h:196
static double SquaredDistance(const TRay< Real > &Ray, const TSegment3< Real > &Segment, Real &RayParam, Real &SegParam)
Definition DistRay3Segment3.h:225
Definition DynamicVector.h:27
void InsertAt(const Type &Data, unsigned int Index)
Definition DynamicVector.h:747
static bool FindIntersection(const TRay< RealType > &Ray, const TAxisAlignedBox3< RealType > &Box, RealType &RayParamOut)
Definition IntrRay3AxisAlignedBox3.h:110
constexpr int InvalidID
Definition IndexTypes.h:13
RealType Area(const TVector< RealType > &V0, const TVector< RealType > &V1, const TVector< RealType > &V2)
Definition VectorUtil.h:93
TAxisAlignedBox3< double > FAxisAlignedBox3d
Definition BoxTypes.h:885
Definition AdvancedWidgetsModule.cpp:13
Definition NumericLimits.h:41
Definition SegmentTree3.h:231
int IBoxCur
Definition SegmentTree3.h:234
TDynamicVector< int > IndexList
Definition SegmentTree3.h:233
TDynamicVector< FTreeBox > Boxes
Definition SegmentTree3.h:232
FBoxesSet()
Definition SegmentTree3.h:236
int IIndicesCur
Definition SegmentTree3.h:235
double Metric
Definition SegmentTree3.h:138
double SegmentParam
Definition SegmentTree3.h:134
double RayParam
Definition SegmentTree3.h:132
double SegmentDist
Definition SegmentTree3.h:136
Definition SegmentTree3.h:46
int32 ID
Definition SegmentTree3.h:47
FSegment3d Segment
Definition SegmentTree3.h:48
Definition SegmentTree3.h:200
int BoxToIndex
Definition SegmentTree3.h:201
FVector3d Extents
Definition SegmentTree3.h:203
FVector3d Center
Definition SegmentTree3.h:202
Definition SpatialInterfaces.h:57
TFunction< bool(int)> TriangleFilterF
Definition SpatialInterfaces.h:66
static TAxisAlignedBox3< double > Empty()
Definition BoxTypes.h:382
void Contain(const RealType &V)
Definition BoxTypes.h:68
RealType Length() const
Definition BoxTypes.h:58
RealType Center() const
Definition BoxTypes.h:49
static TInterval1< double > Empty()
Definition BoxTypes.h:34
TVector< T > Origin
Definition Ray.h:24
TVector< T > PointAt(T RayParameter) const
Definition Ray.h:105
T Z
Definition Vector.h:68
static TVector< double > Zero()
Definition Vector.h:112
T Y
Definition Vector.h:65
T X
Definition Vector.h:62