CARLA
Loading...
Searching...
No Matches
BoundingBoxCalculator.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
8#include "Carla.h"
11#include "Carla/Game/Tagger.h"
16#include "Components/CapsuleComponent.h"
17#include "GameFramework/Character.h"
18#include "Components/BoxComponent.h"
19#include "Rendering/SkeletalMeshRenderData.h"
20#include "Engine/SkeletalMeshSocket.h"
21
25
26namespace crp = carla::rpc;
27
29 FBoundingBox InBB,
30 const FTransform& Transform)
31{
32 auto Scale = Transform.GetScale3D();
33 InBB.Origin *= Scale;
34 InBB.Rotation = Transform.GetRotation().Rotator();
35 InBB.Origin = InBB.Rotation.RotateVector(InBB.Origin) + Transform.GetLocation();
36 InBB.Extent *= Scale;
37 return InBB;
38}
39
41{
42 if (Actor != nullptr)
43 {
44 // Vehicle.
45 auto Vehicle = Cast<ACarlaWheeledVehicle>(Actor);
46 if (Vehicle != nullptr)
47 {
48 FVector Origin = Vehicle->GetVehicleBoundingBoxTransform().GetTranslation();
49 FVector Extent = Vehicle->GetVehicleBoundingBoxExtent();
50 return {Origin, Extent};
51 }
52 // Walker.
53 auto Character = Cast<ACharacter>(Actor);
54 if (Character != nullptr)
55 {
56 UActorComponent *ActorComp = Character->GetComponentByClass(USkeletalMeshComponent::StaticClass());
57 USkeletalMeshComponent* ParentComp = Cast<USkeletalMeshComponent>(ActorComp);
58
59 if (ParentComp != nullptr)
60 {
62
63 // Lift the mesh-component-space bounds into actor-local space. The
64 // skeletal mesh component on ACharacter is offset from the actor root
65 // (typically -Z by the capsule half-height so the feet line up with
66 // the capsule bottom) and rotated relative to the actor frame, so a
67 // full transform on origin + rotation + scale is required for the box
68 // to remain consistent with GetCharacterBoundingBox.
69 const FTransform &MeshRelative = ParentComp->GetRelativeTransform();
70 Box = ApplyTransformToBB(Box, MeshRelative);
71
72 if (Character->GetName().Contains("_AB001_G3")
73 || Character->GetName().Contains("_AG001_G3"))
74 {
75 // Hack to center the bbox of Gen3 kids
76 Box.Origin.Z -= Box.Extent.Z * (1.0f / 0.65f - 1.0f);
77 }
78
79 // Wheelchair users: the wheelchair mesh is attached by the walker
80 // blueprint and is not part of the skeletal mesh bounds, so grow
81 // and shift the box to cover it (same offsets as ue4-dev).
82 auto WalkerBase = Cast<AWalkerBase>(Character);
83 if (WalkerBase && WalkerBase->bUsesWheelChair)
84 {
85 Box.Origin.X += 15.0f;
86 Box.Origin.Z -= 15.0f;
87
88 Box.Extent.X += 5.0f;
89 Box.Extent.Z += 5.0f;
90 }
91
92 return Box;
93 }
94 }
95 // Traffic sign.
96 auto TrafficSign = Cast<ATrafficSignBase>(Actor);
97 if (TrafficSign != nullptr)
98 {
99 // Return the first generated trigger box (OpenDRIVE-based signals)
100 // Newer traffic signs may have multiple trigger volumes (EffectBox, CheckBox, etc.)
101 // but for the Python API we return only the first one to preserve its rotation
102 auto TriggerVolumes = TrafficSign->GetTriggerVolumes();
103 // GetTriggerVolumes() may return null entries (e.g. when the
104 // Blueprint GetTriggerVolume() event is not implemented), so pick
105 // the first valid one and fall back to GetTriggerVolume() otherwise.
106 UBoxComponent* FirstTriggerVolume = nullptr;
107 for (UBoxComponent* Volume : TriggerVolumes)
108 {
109 if (Volume != nullptr)
110 {
111 FirstTriggerVolume = Volume;
112 break;
113 }
114 }
115 if (FirstTriggerVolume != nullptr)
116 {
117 FTransform Transform = Actor->GetActorTransform();
118
119 FBoundingBox TVWorld;
120 TVWorld.Origin = FirstTriggerVolume->GetComponentLocation();
121 TVWorld.Extent = FirstTriggerVolume->GetScaledBoxExtent();
122 TVWorld.Rotation = FirstTriggerVolume->GetComponentRotation();
123
124 FBoundingBox TVLocal;
125 TVLocal.Origin = Transform.InverseTransformPosition(TVWorld.Origin);
126 TVLocal.Extent = TVWorld.Extent;
127 TVLocal.Rotation = Transform.InverseTransformRotation(TVWorld.Rotation.Quaternion()).Rotator();
128
129 return TVLocal;
130 }
131 // try to return the original bounding box
132 auto TriggerVolume = TrafficSign->GetTriggerVolume();
133 if (TriggerVolume != nullptr)
134 {
135 auto Transform = TriggerVolume->GetRelativeTransform();
136 return
137 {
138 Transform.GetTranslation(),
139 TriggerVolume->GetScaledBoxExtent(),
140 Transform.GetRotation().Rotator()
141 };
142 }
143 else
144 {
145 UE_LOG(LogCarla, Warning, TEXT("Traffic sign missing trigger volume: %s"), *Actor->GetName());
146 return {};
147 }
148 }
149 // Other, by default BB
150 TArray<FBoundingBox> BBs = GetBBsOfActor(Actor);
151 FBoundingBox BB = CombineBBs(BBs);
152 // Conver to local space; GetBBsOfActor return BBs in world space
153 FTransform Transform = Actor->GetActorTransform();
154 BB.Origin = Transform.InverseTransformPosition(BB.Origin);
155 BB.Rotation = Transform.InverseTransformRotation(BB.Rotation.Quaternion()).Rotator();
156 BB.Rotation = Transform.GetRotation().Rotator();
157 return BB;
158
159 }
160 return {};
161}
162
165 uint8 InTagQueried)
166{
167 check(Vehicle);
168
169 crp::CityObjectLabel TagQueried = (crp::CityObjectLabel)InTagQueried;
170 bool FilterByTagEnabled = (TagQueried != crp::CityObjectLabel::Any);
171
172 UActorComponent *ActorComp = Vehicle->GetComponentByClass(USkeletalMeshComponent::StaticClass());
173 USkeletalMeshComponent* ParentComp = Cast<USkeletalMeshComponent>(ActorComp);
174
175 // Filter by tag
177 if(FilterByTagEnabled && Tag != TagQueried) return {};
178
179 USkeletalMesh* SkeletalMesh = ParentComp->GetSkeletalMeshAsset();
180 FBoundingBox BB = GetSkeletalMeshBoundingBox(SkeletalMesh);
181
182 if(BB.Extent.IsZero())
183 {
184 UE_LOG(LogCarla, Error, TEXT("%s has no SKM assigned"), *Vehicle->GetName());
185 return {};
186 }
187
188 // Two wheel vehicle have more than one SKM
189 TArray<USkeletalMeshComponent*> SkeletalMeshComps;
190 Vehicle->GetComponents<USkeletalMeshComponent>(SkeletalMeshComps);
191
192 if(SkeletalMeshComps.Num() > 1)
193 {
194 FVector VehicleLocation = Vehicle->GetActorLocation();
195 FRotator VehicleRotation = Vehicle->GetActorRotation();
196 float MaxHeadLocation = TNumericLimits<float>::Lowest();
197 float MaxKneeLocation = TNumericLimits<float>::Lowest();
198 bool SocketFound = false;
199 for(USkeletalMeshComponent* Comp : SkeletalMeshComps)
200 {
201 if(Comp == ParentComp) continue; // ignore vehicle skeleton
202
203 // Location of sockets are in world space
204 FVector HeadLocation = Comp->GetSocketLocation("crl_Head__C") - VehicleLocation;
205 FVector KneeLocation = Comp->GetSocketLocation("crl_leg__L") - VehicleLocation;
206
207 HeadLocation = VehicleRotation.UnrotateVector(HeadLocation);
208 KneeLocation = VehicleRotation.UnrotateVector(KneeLocation);
209
210 MaxHeadLocation = (HeadLocation.Z > MaxHeadLocation) ? HeadLocation.Z : MaxHeadLocation;
211 MaxKneeLocation = (KneeLocation.Y > MaxKneeLocation) ? KneeLocation.Y : MaxKneeLocation;
212
213 SocketFound = true;
214 }
215
216 if(SocketFound)
217 {
218 auto Min = BB.Origin - BB.Rotation.RotateVector(BB.Extent);
219 auto Max = BB.Origin + BB.Rotation.RotateVector(BB.Extent);
220 auto Tmp = Min;
221 Min = Min.ComponentMin(Max);
222 Max = Max.ComponentMin(Tmp);
223
224 MaxHeadLocation += 20.0f; // hack, the bone of the head is at he bottom
225
226 Max.Y = (MaxKneeLocation > Max.Y) ? MaxKneeLocation : Max.Y;
227 Min.Y = (-MaxKneeLocation < Min.Y) ? MaxKneeLocation : Min.Y;
228 Max.Z = (MaxHeadLocation > Max.Z) ? MaxHeadLocation : Max.Z;
229
230 BB.Extent = (Max - Min) * 0.5f;
231 BB.Origin = Min + BB.Extent;
232 }
233 }
234
235 // Component-to-world transform for this component
236 auto& CompToWorldTransform = ParentComp->GetComponentTransform();
237 BB = ApplyTransformToBB(BB, CompToWorldTransform);
238 return BB;
239}
240
242 const ACharacter* Character,
243 uint8 InTagQueried)
244{
245 check(Character);
246
247 crp::CityObjectLabel TagQueried = (crp::CityObjectLabel)InTagQueried;
248 bool FilterByTag = TagQueried == crp::CityObjectLabel::Any ||
249 TagQueried == crp::CityObjectLabel::Pedestrians;
250
251 UActorComponent *ActorComp = Character->GetComponentByClass(USkeletalMeshComponent::StaticClass());
252 USkeletalMeshComponent* ParentComp = Cast<USkeletalMeshComponent>(ActorComp);
253
254 if (ParentComp && FilterByTag)
255 {
257
258 auto& CompToWorldTransform = ParentComp->GetComponentTransform();
259
260 BoundingBox = ApplyTransformToBB(BoundingBox, CompToWorldTransform);
261
262 return BoundingBox;
263 }
264
265 return {};
266}
267
270 TArray<FBoundingBox>& OutBB,
271 TArray<uint8>& OutTag,
272 uint8 InTagQueried)
273{
274 check(TrafficLight);
275
276 TArray<FBoundingBox> BBsOfTL;
277 TArray<uint8> TagsOfTL;
278
279 // This kind of a magic number relying that the lights of a TL are not bigger than 100.
280 // and we are gonna compare against a squared distance
281 const float DistanceThreshold = 100.0f * 100.0f;
282
283 // The BBs of the TL are calculated per light, so we need to merge the full-box
284 uint8 TLTag = static_cast<uint8>(crp::CityObjectLabel::TrafficLight);
285
286 CombineBBsOfActor(TrafficLight, BBsOfTL, TagsOfTL, DistanceThreshold, TLTag);
287 check(BBsOfTL.Num() == TagsOfTL.Num());
288
289 bool FilterByTagEnabled = (InTagQueried != static_cast<uint8>(crp::CityObjectLabel::Any));
290
291 for(int i = 0; i < BBsOfTL.Num(); i++)
292 {
293 FBoundingBox& BB = BBsOfTL[i];
294 uint8 Tag = TagsOfTL[i];
295
296 if(FilterByTagEnabled && Tag != InTagQueried) continue;
297
298 OutBB.Emplace(BB);
299 OutTag.Emplace(Tag);
300 }
301}
302
304 const USkeletalMeshComponent* SkeletalMeshComp
305)
306{
307 if (!SkeletalMeshComp || !SkeletalMeshComp->GetSkeletalMeshAsset())
308 {
309 UE_LOG(
310 LogCarla,
311 Error,
312 TEXT("GetSkeletalMeshBoundingBoxFromComponent: missing SkeletalMeshComponent or SkeletalMesh"));
313 return {};
314 }
315
316 // Bounds in mesh-component space: passing an identity transform yields a
317 // local AABB that already reflects the current animation pose, so the
318 // returned Origin is the actual mesh centre relative to its component (not
319 // the component's world position).
320 const FBoxSphereBounds LocalBounds =
321 SkeletalMeshComp->CalcBounds(FTransform::Identity);
322
323 return {LocalBounds.Origin, LocalBounds.BoxExtent};
324}
325
326// TODO: update to calculate current animation pose
328{
329 if(!SkeletalMesh)
330 {
331 UE_LOG(LogCarla, Error, TEXT("GetSkeletalMeshBoundingBox no SkeletalMesh"));
332 return {};
333 }
334
335 // Get Vertex postion information from LOD 0 of the Skeletal Mesh
336 FSkeletalMeshRenderData* SkeletalMeshRenderData = SkeletalMesh->GetResourceForRendering();
337 FSkeletalMeshLODRenderData& LODRenderData = SkeletalMeshRenderData->LODRenderData[0];
338 FStaticMeshVertexBuffers& StaticMeshVertexBuffers = LODRenderData.StaticVertexBuffers;
339 FPositionVertexBuffer& FPositionVertexBuffer = StaticMeshVertexBuffers.PositionVertexBuffer;
340 uint32 NumVertices = FPositionVertexBuffer.GetNumVertices();
341
342 // Look for Skeletal Mesh bounds (vertex perfect)
343 FVector Max(TNumericLimits<float>::Lowest());
344 FVector Min(TNumericLimits<float>::Max());
345
346 for(uint32 i = 0; i < NumVertices; i++)
347 {
348 auto Pos = FVector(FPositionVertexBuffer.VertexPosition(i));
349 Max = Max.ComponentMax(Pos);
350 Min = Min.ComponentMin(Pos);
351 }
352
353 auto Extent = (Max - Min) * 0.5f;
354 auto Origin = Min + Extent;
355
356 return {Origin, Extent};
357}
358
360{
361 if(!StaticMesh)
362 {
363 // UE_LOG(LogCarla, Error, TEXT("GetStaticMeshBoundingBox no StaticMesh"));
364 return {};
365 }
366
367 FBox Box = StaticMesh->GetBoundingBox();
368 return {Box.GetCenter(), Box.GetExtent()};
369
370}
371
373 UInstancedStaticMeshComponent* ISMComp,
374 TArray<FBoundingBox>& OutBoundingBox)
375{
376 if(!ISMComp)
377 {
378 UE_LOG(LogCarla, Error, TEXT("GetISMBoundingBox no ISMComp"));
379 return;
380 }
381
382 const UStaticMesh *Mesh = ISMComp->GetStaticMesh();
383 const FBoundingBox SMBoundingBox = GetStaticMeshBoundingBox(Mesh);
384
385 if(!Mesh)
386 {
387 #if WITH_EDITOR
388 UE_LOG(LogCarla, Error, TEXT("Actor Name: %s Actor Labe: %s has no SM assigned to the ISM"), *ISMComp->GetOwner()->GetName(), *ISMComp->GetOwner()->GetActorLabel());
389 #else
390 UE_LOG(LogCarla, Error, TEXT("Actor Name: %s has no SM assigned to the ISM"), *ISMComp->GetOwner()->GetName());
391 #endif
392 return;
393 }
394
395 const TArray<FInstancedStaticMeshInstanceData>& PerInstanceSMData = ISMComp->PerInstanceSMData;
396
397 const FTransform ParentTransform = ISMComp->GetComponentTransform();
398
399 for(auto& InstSMIData : PerInstanceSMData)
400 {
401 const FTransform Transform = FTransform(InstSMIData.Transform) * ParentTransform;
402 FBoundingBox BoundingBox = ApplyTransformToBB(SMBoundingBox, Transform);
403
404 OutBoundingBox.Add(BoundingBox);
405 }
406
407}
408
410 const TArray<UStaticMeshComponent*>& StaticMeshComps,
411 TArray<FBoundingBox>& OutBB,
412 TArray<uint8>& OutTag,
413 uint8 InTagQueried)
414{
415 crp::CityObjectLabel TagQueried = (crp::CityObjectLabel)InTagQueried;
416 bool FilterByTagEnabled = (TagQueried != crp::CityObjectLabel::Any);
417
418 for(UStaticMeshComponent* Comp : StaticMeshComps)
419 {
420
421 bool isCrosswalk = Comp->GetOwner()->GetName().Contains("crosswalk");
422
423 // Avoid duplication with SMComp and not visible meshes
424 if( (!Comp->IsVisible() && !isCrosswalk) || Cast<UInstancedStaticMeshComponent>(Comp))
425 {
426 continue;
427 }
428
429 // Filter by tag
431 if(FilterByTagEnabled && Tag != TagQueried) continue;
432
433 UStaticMesh* StaticMesh = Comp->GetStaticMesh();
435
436 if(StaticMesh)
437 {
438 // Component-to-world transform for this component
439 const FTransform& CompToWorldTransform = Comp->GetComponentTransform();
440 BoundingBox = ApplyTransformToBB(BoundingBox, CompToWorldTransform);
441 OutBB.Emplace(BoundingBox);
442 OutTag.Emplace(static_cast<uint8>(Tag));
443 }
444 }
445}
446
448 const TArray<USkeletalMeshComponent*>& SkeletalMeshComps,
449 TArray<FBoundingBox>& OutBB,
450 TArray<uint8>& OutTag,
451 uint8 InTagQueried)
452{
453 crp::CityObjectLabel TagQueried = (crp::CityObjectLabel)InTagQueried;
454 bool FilterByTagEnabled = (TagQueried != crp::CityObjectLabel::Any);
455
456 for(USkeletalMeshComponent* Comp : SkeletalMeshComps)
457 {
458 // Filter by tag
460
461 if(!Comp->IsVisible() || (FilterByTagEnabled && Tag != TagQueried)) continue;
462
463 USkeletalMesh* SkeletalMesh = Comp->GetSkeletalMeshAsset();
465 if(BoundingBox.Extent.IsZero())
466 {
467 UE_LOG(LogCarla, Error, TEXT("%s has no SKM assigned"), *Comp->GetOwner()->GetName());
468 }
469 else
470 {
471 // Component-to-world transform for this component
472 const FTransform& CompToWorldTransform = Comp->GetComponentTransform();
473 BoundingBox = ApplyTransformToBB(BoundingBox, CompToWorldTransform);
474 OutBB.Emplace(BoundingBox);
475 OutTag.Emplace(static_cast<uint8>(Tag));
476 }
477 }
478}
479
481 const TArray<AActor*>& Actors,
482 uint8 InTagQueried)
483{
484 TArray<FBoundingBox> Result;
485 for(AActor* Actor : Actors)
486 {
487 TArray<FBoundingBox> BBs = GetBBsOfActor(Actor, InTagQueried);
488 Result.Append(BBs.GetData(), BBs.Num());
489 }
490
491 return Result;
492}
493
495 const AActor* Actor,
496 uint8 InTagQueried)
497{
498 TArray<FBoundingBox> Result;
499 TArray<uint8> Tags;
500 crp::CityObjectLabel TagQueried = (crp::CityObjectLabel)InTagQueried;
501 bool FilterByTagEnabled = (TagQueried != crp::CityObjectLabel::Any);
502
503 FString ClassName = Actor->GetClass()->GetName();
504
505 // Avoid the BP_Procedural_Building to avoid duplication with their child actors
506 // When improved the BP_Procedural_Building this maybe should be removed
507 // Note: We don't use casting here because the base class is a BP and is easier to do it this way,
508 // than getting the UClass of the BP to cast the actor.
509 if( ClassName.Contains("Procedural_Bulding") ) return Result;
510
511 // The vehicle's BP has a low-polystatic mesh for collisions, we should avoid it
512 const ACarlaWheeledVehicle* Vehicle = Cast<ACarlaWheeledVehicle>(Actor);
513 if (Vehicle)
514 {
516 if(!BoundingBox.Extent.IsZero())
517 {
518 Result.Add(BoundingBox);
519 }
520 return Result;;
521 }
522
523 // Pedestrians, we just use the capsule component at the moment.
524 const ACharacter* Character = Cast<ACharacter>(Actor);
525 if (Character)
526 {
528 if(!BoundingBox.Extent.IsZero())
529 {
530 Result.Add(BoundingBox);
531 }
532 return Result;
533 }
534
535 // TrafficLight, we need to join all the BB of the lights in one
536 const ATrafficLightBase* TrafficLight = Cast<ATrafficLightBase>(Actor);
537 if(TrafficLight)
538 {
539 GetTrafficLightBoundingBox(TrafficLight, Result, Tags, InTagQueried);
540 return Result;
541 }
542
543 // Calculate FBoundingBox of ISM
544 TArray<UInstancedStaticMeshComponent *> ISMComps;
545 Actor->GetComponents<UInstancedStaticMeshComponent>(ISMComps);
546 for(UInstancedStaticMeshComponent* Comp: ISMComps)
547 {
548 // Filter by tag
550 if(FilterByTagEnabled && Tag != TagQueried) continue;
551
552 GetISMBoundingBox(Comp, Result);
553 }
554
555 // Calculate FBoundingBox of SM
556 TArray<UStaticMeshComponent*> StaticMeshComps;
557 Tags.Reset();
558 Actor->GetComponents<UStaticMeshComponent>(StaticMeshComps);
559 GetBBsOfStaticMeshComponents(StaticMeshComps, Result, Tags, InTagQueried);
560
561 // Calculate FBoundingBox of SK_M
562 TArray<USkeletalMeshComponent*> SkeletalMeshComps;
563 Tags.Reset();
564 Actor->GetComponents<USkeletalMeshComponent>(SkeletalMeshComps);
565 GetBBsOfSkeletalMeshComponents(SkeletalMeshComps, Result, Tags, InTagQueried);
566
567 return Result;
568}
569
571 const AActor* Actor,
572 TArray<FBoundingBox>& OutBB,
573 TArray<uint8>& OutTag,
574 const float DistanceThreshold,
575 uint8 TagToCombine)
576{
577 check(Actor);
578
579 TArray<FBoundingBox> BBsOfTL;
580 TArray<uint8> InternalOutTags;
581 TArray<UStaticMeshComponent*> StaticMeshComps;
582 Actor->GetComponents<UStaticMeshComponent>(StaticMeshComps);
583 GetBBsOfStaticMeshComponents(StaticMeshComps, BBsOfTL, InternalOutTags);
584
585 bool IgnoreDistanceFilter = (DistanceThreshold <= 0.0f);
586 bool IgnoreTag = (TagToCombine == 0);
587
588 TSet<int> IndicesDiscarded;
589 for(int i = 0; i < BBsOfTL.Num(); i++)
590 {
591 // Check if the index was used to merge a previous BB
592 if(IndicesDiscarded.Contains(i)) continue;
593
594 TArray<FBoundingBox> BBsToCombine;
595 FBoundingBox& BB1 = BBsOfTL[i];
596 uint8 Tag1 = InternalOutTags[i];
597
598 for(int j = i + 1; j < BBsOfTL.Num(); j++)
599 {
600 // Check if the index was used to merge a previous BB
601 if(IndicesDiscarded.Contains(j)) continue;
602
603 FBoundingBox& BB2 = BBsOfTL[j];
604 uint8 Tag2 = InternalOutTags[j];
605
606 float Distance = FVector::DistSquared(BB1.Origin, BB2.Origin);
607
608 // If the BBs are close enough and are from the same type, we merge it
609 if( ((Distance <= DistanceThreshold) || IgnoreDistanceFilter) &&
610 ((Tag1 == Tag2) || IgnoreTag) )
611 {
612 BBsToCombine.Emplace(BB2);
613 IndicesDiscarded.Emplace(j);
614 }
615 }
616 if(BBsToCombine.Num() > 0)
617 {
618 BBsToCombine.Emplace(BB1);
619 IndicesDiscarded.Emplace(i);
620 FBoundingBox MergedBB = CombineBBs(BBsToCombine);
621 MergedBB.Rotation = BB1.Rotation;
622 OutBB.Emplace(MergedBB);
623 OutTag.Emplace(InternalOutTags[i]);
624 }
625 }
626
627 // Add the BB of the meshes that didn't need to combine
628 for(int i = 0; i < BBsOfTL.Num(); i++)
629 {
630 // Check if the index was used to merge a previous BB
631 if(IndicesDiscarded.Contains(i)) continue;
632 FBoundingBox& BB = BBsOfTL[i];
633 OutBB.Emplace(BB);
634 OutTag.Emplace(InternalOutTags[i]);
635 }
636}
637
638template <typename Collection, typename ExtractAABB>
640 Collection&& collection,
641 ExtractAABB&& extract_aabb)
642{
643 FVector Max(std::numeric_limits<float>::lowest());
644 FVector Min(std::numeric_limits<float>::max());
645
646 for (auto& e : collection)
647 {
648 auto BB = extract_aabb(e);
649 auto LocalMax = BB.Origin + BB.Rotation.RotateVector(BB.Extent);
650 auto LocalMin = BB.Origin - BB.Rotation.RotateVector(BB.Extent);
651 auto Tmp = LocalMin;
652 LocalMin = LocalMin.ComponentMin(LocalMax);
653 LocalMax = LocalMax.ComponentMax(Tmp);
654 Max = Max.ComponentMax(LocalMax);
655 Min = Min.ComponentMin(LocalMin);
656 }
657
658 auto Extent = (Max - Min) * 0.5f;
659 auto Origin = Min + Extent;
660
661 return { Origin, Extent };
662}
663
664template <typename Collection>
665static auto CombineBoundingBoxesGeneric(Collection&& collection)
666{
667 using T = std::remove_reference_t<decltype(collection[0])>;
668 return CombineBoundingBoxesGeneric(std::forward<Collection>(collection), [](T& e) { return e; });
669}
670
671FBoundingBox UBoundingBoxCalculator::CombineBBs(const TArray<FBoundingBox>& BBsToCombine)
672{
673 return CombineBoundingBoxesGeneric(BBsToCombine);
674}
675
676FBoundingBox UBoundingBoxCalculator::CombineBoxes(const TArray<UBoxComponent *>& BBsToCombine)
677{
678 return CombineBoundingBoxesGeneric(BBsToCombine, [](UBoxComponent* e)
679 {
680 return FBoundingBox
681 {
682 e->GetComponentLocation(),
683 e->GetScaledBoxExtent(),
684 e->GetComponentRotation()
685 };
686 });
687}
688
690 const AActor* Actor,
691 const FBoundingBox& InBB,
692 TArray<UStaticMeshComponent*>& OutStaticMeshComps)
693{
694 check(Actor);
695
696
697 const float DistanceThreshold = 100.0f * 100.0f;
698
699 TArray<UStaticMeshComponent*> SMComps;
700 Actor->GetComponents<UStaticMeshComponent>(SMComps);
701
702 for(UStaticMeshComponent* Comp : SMComps)
703 {
704 if(!Comp->IsVisible()) continue;
705
706 TArray<FBoundingBox> BBs;
707 TArray<uint8> InternalOutTags;
708 GetBBsOfStaticMeshComponents({Comp}, BBs, InternalOutTags);
709
710 if(BBs.Num() == 0) continue;
711
712 const FBoundingBox& BB = BBs[0];
713 float Distance = FVector::DistSquared(InBB.Origin, BB.Origin);
714 if(Distance <= DistanceThreshold)
715 {
716 OutStaticMeshComps.Emplace(Comp);
717 }
718 }
719}
static FBoundingBox ApplyTransformToBB(FBoundingBox InBB, const FTransform &Transform)
static FBoundingBox CombineBoundingBoxesGeneric(Collection &&collection, ExtractAABB &&extract_aabb)
Base class for CARLA wheeled vehicles.
static crp::CityObjectLabel GetTagOfTaggedComponent(const UPrimitiveComponent &Component)
Retrieve the tag of an already tagged component.
Definition Tagger.cpp:213
static void GetBBsOfSkeletalMeshComponents(const TArray< USkeletalMeshComponent * > &SkeletalMeshComps, TArray< FBoundingBox > &OutBB, TArray< uint8 > &OutTag, uint8 InTagQueried=0xFF)
static TArray< FBoundingBox > GetBBsOfActor(const AActor *Actor, uint8 InTagQueried=0xFF)
static FBoundingBox GetCharacterBoundingBox(const ACharacter *Character, uint8 InTagQueried=0xFF)
static void GetISMBoundingBox(UInstancedStaticMeshComponent *ISMComp, TArray< FBoundingBox > &OutBoundingBox)
static FBoundingBox GetSkeletalMeshBoundingBoxFromComponent(const USkeletalMeshComponent *SkeletalMeshComp)
static FBoundingBox GetSkeletalMeshBoundingBox(const USkeletalMesh *SkeletalMesh)
static FBoundingBox GetStaticMeshBoundingBox(const UStaticMesh *StaticMesh)
static void GetTrafficLightBoundingBox(const ATrafficLightBase *TrafficLight, TArray< FBoundingBox > &OutBB, TArray< uint8 > &OutTag, uint8 InTagQueried=0xFF)
static FBoundingBox CombineBBs(const TArray< FBoundingBox > &BBsToCombine)
static void GetBBsOfStaticMeshComponents(const TArray< UStaticMeshComponent * > &StaticMeshComps, TArray< FBoundingBox > &OutBB, TArray< uint8 > &OutTag, uint8 InTagQueried=0xFF)
static FBoundingBox GetVehicleBoundingBox(const ACarlaWheeledVehicle *Vehicle, uint8 InTagQueried=0xFF)
static void CombineBBsOfActor(const AActor *Actor, TArray< FBoundingBox > &OutBB, TArray< uint8 > &OutTag, const float DistanceThreshold=0.0f, uint8 TagToCombine=0xFF)
static FBoundingBox GetActorBoundingBox(const AActor *Actor, uint8 InTagQueried=0xFF)
Compute the bounding box of the given Carla actor.
static void GetMeshCompsFromActorBoundingBox(const AActor *Actor, const FBoundingBox &InBB, TArray< UStaticMeshComponent * > &OutStaticMeshComps)
static FBoundingBox CombineBoxes(const TArray< UBoxComponent * > &BBsToCombine)
static TArray< FBoundingBox > GetBoundingBoxOfActors(const TArray< AActor * > &Actors, uint8 InTagQueried=0xFF)
FVector Origin
Origin of the bounding box relative to its owner.
FVector Extent
Radii extent of the bounding box.