Retrieve BBs of all the elements of the level (#3301)

* Added new function to calculate BBs on SKM and SM

* Added BB calculation for an array of actors

* Added BB folliage calculation

* Added a call on the GM to get all the BBs of the level

* Fixed Folliage BB calculation.

* GM::GetAllBBsOfLevel now returns the array of BBs

* Added call on the server side to retrieve the BBs

* Removed debug draw calls

* Added call to the PythonAPI

* Added check for empty folliage actors

* Added missing EOF

* Added new RotateVector function

* Added rotation to the BBs

* Update changelog

* Split BB calculation on more functions

* Add tag query and simplified Folliage actor BB calculation

* Look for ISM instead of HISM

* Cleaning comments

* Fixed BB duplication on BP_Procedural_Buildings

* Fixed BB duplication on vehicles BPs

* Added pedestrians case

* Added CityObjectLabel enum on PythonAPI

* Splitted logic in small functions

* Fixed uint8_t

* Merged BB of the lights in a TL

* Splitted work into more functions

* Added missing BB and rotation to TL

* Cleaned comments

* Added missing include

* Changed None to Any in PythonAPI

* Fixed Any enum

* Added check for empty folliage actors

* Added missing EOF

* Added new RotateVector function

* Added rotation to the BBs

* Update changelog

* Split BB calculation on more functions

* Add tag query and simplified Folliage actor BB calculation

* Look for ISM instead of HISM

* Cleaning comments

* Fixed BB duplication on BP_Procedural_Buildings

* Fixed BB duplication on vehicles BPs

* Added pedestrians case

* Added CityObjectLabel enum on PythonAPI

* Splitted logic in small functions

* Fixed uint8_t

* Merged BB of the lights in a TL

* Splitted work into more functions

* Added missing BB and rotation to TL

* Cleaned comments

* Changed None to Any in PythonAPI

* Fixed Any enum

* Fixed rebase
This commit is contained in:
doterop 2020-09-21 18:22:03 +02:00 committed by GitHub
parent 9586cd24d5
commit cc7b1ea910
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 582 additions and 208 deletions

View File

@ -60,6 +60,7 @@
* Fixed minor typo in the introduction section of documentation.
* Fixed a bug at the local planner when changing the route, causing it to maintain the first part of the previous one. This was only relevant when using very large buffer sizes.
* Added automatic calculation of vehicle's BB
* Retrieve BBs of all the elements of the level
## CARLA 0.9.9

View File

@ -158,8 +158,8 @@ namespace client {
_episode.Lock()->FreezeAllTrafficLights(frozen);
}
std::vector<geom::BoundingBox> World::GetLevelBBs() const {
return _episode.Lock()->GetLevelBBs();
std::vector<geom::BoundingBox> World::GetLevelBBs(uint8_t queried_tag) const {
return _episode.Lock()->GetLevelBBs(queried_tag);
}
} // namespace client

View File

@ -151,7 +151,7 @@ namespace client {
void FreezeAllTrafficLights(bool frozen);
/// Returns all the BBs of all the elements of the level
std::vector<geom::BoundingBox> GetLevelBBs() const;
std::vector<geom::BoundingBox> GetLevelBBs(uint8_t queried_tag) const;
private:

View File

@ -413,9 +413,9 @@ namespace detail {
_pimpl->AsyncCall("update_lights_state", _pimpl->endpoint, std::move(lights), discard_client);
}
std::vector<geom::BoundingBox> Client::GetLevelBBs() const {
std::vector<geom::BoundingBox> Client::GetLevelBBs(uint8_t queried_tag) const {
using return_t = std::vector<geom::BoundingBox>;
return _pimpl->CallAndWait<return_t>("get_all_level_BBs");
return _pimpl->CallAndWait<return_t>("get_all_level_BBs", queried_tag);
}
} // namespace detail

View File

@ -257,7 +257,7 @@ namespace detail {
bool discard_client = false) const;
/// Returns all the BBs of all the elements of the level
std::vector<geom::BoundingBox> GetLevelBBs() const;
std::vector<geom::BoundingBox> GetLevelBBs(uint8_t queried_tag) const;
private:

View File

@ -227,8 +227,8 @@ namespace detail {
}
/// Returns all the BBs of all the elements of the level
std::vector<geom::BoundingBox> GetLevelBBs() const {
return _client.GetLevelBBs();
std::vector<geom::BoundingBox> GetLevelBBs(uint8_t queried_tag) const {
return _client.GetLevelBBs(queried_tag);
}
/// @}

View File

@ -30,6 +30,11 @@ namespace geom {
// -- Constructors ---------------------------------------------------------
// =========================================================================
explicit BoundingBox(const Location &in_location, const Vector3D &in_extent, const Rotation &in_rotation)
: location(in_location),
extent(in_extent),
rotation(in_rotation) {}
explicit BoundingBox(const Location &in_location, const Vector3D &in_extent)
: location(in_location),
extent(in_extent) {}
@ -38,7 +43,8 @@ namespace geom {
: extent(in_extent) {}
Location location; ///< Center of the BoundingBox in local space
Vector3D extent; ///< Half the size of the BoundingBox in local space
Vector3D extent; ///< Half the size of the BoundingBox in local space
Rotation rotation; ///< Rotation of the BoundingBox in local space
// =========================================================================
// -- Other methods --------------------------------------------------------
@ -63,15 +69,16 @@ namespace geom {
* Returns the positions of the 8 vertices of this BoundingBox in local space.
*/
std::array<Location, 8> GetLocalVertices() const {
return {{
location + Location(-extent.x,-extent.y,-extent.z),
location + Location(-extent.x,-extent.y, extent.z),
location + Location(-extent.x, extent.y,-extent.z),
location + Location(-extent.x, extent.y, extent.z),
location + Location( extent.x,-extent.y,-extent.z),
location + Location( extent.x,-extent.y, extent.z),
location + Location( extent.x, extent.y,-extent.z),
location + Location( extent.x, extent.y, extent.z)
location + Location(rotation.RotateVector({-extent.x,-extent.y,-extent.z})),
location + Location(rotation.RotateVector({-extent.x,-extent.y, extent.z})),
location + Location(rotation.RotateVector({-extent.x, extent.y,-extent.z})),
location + Location(rotation.RotateVector({-extent.x, extent.y, extent.z})),
location + Location(rotation.RotateVector({ extent.x,-extent.y,-extent.z})),
location + Location(rotation.RotateVector({ extent.x,-extent.y, extent.z})),
location + Location(rotation.RotateVector({ extent.x, extent.y,-extent.z})),
location + Location(rotation.RotateVector({ extent.x, extent.y, extent.z}))
}};
}
@ -92,7 +99,7 @@ namespace geom {
// =========================================================================
bool operator==(const BoundingBox &rhs) const {
return (location == rhs.location) && (extent == rhs.extent);
return (location == rhs.location) && (extent == rhs.extent) && (rotation == rhs.rotation);
}
bool operator!=(const BoundingBox &rhs) const {
@ -107,11 +114,12 @@ namespace geom {
BoundingBox(const FBoundingBox &Box)
: location(Box.Origin),
extent(1e-2f * Box.Extent.X, 1e-2f * Box.Extent.Y, 1e-2f * Box.Extent.Z) {}
extent(1e-2f * Box.Extent.X, 1e-2f * Box.Extent.Y, 1e-2f * Box.Extent.Z),
rotation(Box.Rotation) {}
#endif // LIBCARLA_INCLUDED_FROM_UE4
MSGPACK_DEFINE_ARRAY(location, extent);
MSGPACK_DEFINE_ARRAY(location, extent, rotation);
};
} // namespace geom

View File

@ -87,6 +87,34 @@ namespace geom {
in_point = out_point;
}
Vector3D RotateVector(const Vector3D& in_point) const {
// Rotates Rz(yaw) * Ry(pitch) * Rx(roll) = first x, then y, then z.
const float cy = std::cos(Math::ToRadians(yaw));
const float sy = std::sin(Math::ToRadians(yaw));
const float cr = std::cos(Math::ToRadians(roll));
const float sr = std::sin(Math::ToRadians(roll));
const float cp = std::cos(Math::ToRadians(pitch));
const float sp = std::sin(Math::ToRadians(pitch));
Vector3D out_point;
out_point.x =
in_point.x * (cp * cy) +
in_point.y * (cy * sp * sr - sy * cr) +
in_point.z * (-cy * sp * cr - sy * sr);
out_point.y =
in_point.x * (cp * sy) +
in_point.y * (sy * sp * sr + cy * cr) +
in_point.z * (-sy * sp * cr + cy * sr);
out_point.z =
in_point.x * (sp) +
in_point.y * (-cp * sr) +
in_point.z * (cp * cr);
return out_point;
}
void InverseRotateVector(Vector3D &in_point) const {
// Applies the transposed of the matrix used in RotateVector function,
// which is the rotation inverse.

View File

@ -0,0 +1,45 @@
// Copyright (c) 2020 Computer Vision Center (CVC) at the Universitat Autonoma
// de Barcelona (UAB).
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
#pragma once
#include "carla/MsgPack.h"
#include <cstdint>
namespace carla {
namespace rpc {
enum class CityObjectLabel : uint8_t {
None = 0u,
Buildings = 1u,
Fences = 2u,
Other = 3u,
Pedestrians = 4u,
Poles = 5u,
RoadLines = 6u,
Roads = 7u,
Sidewalks = 8u,
TrafficSigns = 12u,
Vegetation = 9u,
Vehicles = 10u,
Walls = 11u,
Sky = 13u,
Ground = 14u,
Bridge = 15u,
RailTrack = 16u,
GuardRail = 17u,
TrafficLight = 18u,
Static = 19u,
Dynamic = 20u,
Water = 21u,
Terrain = 22u,
};
} // namespace rpc
} // namespace carla
MSGPACK_ADD_ENUM(carla::rpc::CityObjectLabel);

View File

@ -205,9 +205,10 @@ void export_geom() {
class_<cg::BoundingBox>("BoundingBox")
.def(init<cg::Location, cg::Vector3D>(
(arg("location")=cg::Location(), arg("extent")=cg::Vector3D())))
(arg("location")=cg::Location(), arg("extent")=cg::Vector3D(), arg("rotation")=cg::Rotation())))
.def_readwrite("location", &cg::BoundingBox::location)
.def_readwrite("extent", &cg::BoundingBox::extent)
.def_readwrite("rotation", &cg::BoundingBox::rotation)
.def("contains", &cg::BoundingBox::Contains, arg("point"), arg("bbox_transform"))
.def("get_local_vertices", CALL_RETURNING_LIST(cg::BoundingBox, GetLocalVertices))
.def("get_world_vertices", CALL_RETURNING_LIST_1(cg::BoundingBox, GetWorldVertices, const cg::Transform&), arg("bbox_transform"))

View File

@ -8,6 +8,7 @@
#include <carla/client/Actor.h>
#include <carla/client/ActorList.h>
#include <carla/client/World.h>
#include <carla/rpc/ObjectLabel.h>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
@ -70,10 +71,10 @@ static auto GetVehiclesLightStates(carla::client::World &self) {
return dict;
}
static auto GetLevelBBs(const carla::client::World &self) {
static auto GetLevelBBs(const carla::client::World &self, uint8_t queried_tag) {
carla::PythonUtil::ReleaseGIL unlock;
boost::python::list result;
for (const auto &bb : self.GetLevelBBs()) {
for (const auto &bb : self.GetLevelBBs(queried_tag)) {
result.append(bb);
}
return result;
@ -135,6 +136,32 @@ void export_world() {
.value("SpringArm", cr::AttachmentType::SpringArm)
;
enum_<cr::CityObjectLabel>("CityObjectLabel")
.value("Any", cr::CityObjectLabel::None)
.value("Buildings", cr::CityObjectLabel::Buildings)
.value("Fences", cr::CityObjectLabel::Fences)
.value("Other", cr::CityObjectLabel::Other)
.value("Pedestrians", cr::CityObjectLabel::Pedestrians)
.value("Poles", cr::CityObjectLabel::Poles)
.value("RoadLines", cr::CityObjectLabel::RoadLines)
.value("Roads", cr::CityObjectLabel::Roads)
.value("Sidewalks", cr::CityObjectLabel::Sidewalks)
.value("TrafficSigns", cr::CityObjectLabel::TrafficSigns)
.value("Vegetation", cr::CityObjectLabel::Vegetation)
.value("Vehicles", cr::CityObjectLabel::Vehicles)
.value("Walls", cr::CityObjectLabel::Walls)
.value("Sky", cr::CityObjectLabel::Sky)
.value("Ground", cr::CityObjectLabel::Ground)
.value("Bridge", cr::CityObjectLabel::Bridge)
.value("RailTrack", cr::CityObjectLabel::RailTrack)
.value("GuardRail", cr::CityObjectLabel::GuardRail)
.value("TrafficLight", cr::CityObjectLabel::TrafficLight)
.value("Static", cr::CityObjectLabel::Static)
.value("Dynamic", cr::CityObjectLabel::Dynamic)
.value("Water", cr::CityObjectLabel::Water)
.value("Terrain", cr::CityObjectLabel::Terrain)
;
#define SPAWN_ACTOR_WITHOUT_GIL(fn) +[]( \
cc::World &self, \
const cc::ActorBlueprint &blueprint, \
@ -177,7 +204,7 @@ void export_world() {
.def("get_traffic_light", CONST_CALL_WITHOUT_GIL_1(cc::World, GetTrafficLight, cc::Landmark), arg("landmark"))
.def("get_lightmanager", CONST_CALL_WITHOUT_GIL(cc::World, GetLightManager))
.def("freeze_all_traffic_lights", &cc::World::FreezeAllTrafficLights, (arg("frozen")))
.def("get_level_bbs", &GetLevelBBs)
.def("get_level_bbs", &GetLevelBBs, (arg("actor_type")=cr::CityObjectLabel::None))
.def(self_ns::str(self_ns::self))
;

View File

@ -11,8 +11,11 @@
#include <compiler/disable-ue4-macros.h>
#include <carla/rpc/Actor.h>
#include <carla/rpc/ObjectLabel.h>
#include <compiler/enable-ue4-macros.h>
namespace crp = carla::rpc;
/// A view over an actor and its properties.
struct FActorInfo
{
@ -20,11 +23,11 @@ public:
FActorDescription Description;
TSet<ECityObjectLabel> SemanticTags;
TSet<crp::CityObjectLabel> SemanticTags;
FBoundingBox BoundingBox;
carla::rpc::Actor SerializedData;
crp::Actor SerializedData;
/// @todo To be used solely by the FWorldObserver.
mutable FVector Velocity = {0.0f, 0.0f, 0.0f};

View File

@ -13,6 +13,8 @@
#include "Carla/Traffic/TrafficLightBase.h"
#include "Carla/Util/BoundingBoxCalculator.h"
namespace crp = carla::rpc;
static FActorView::ActorType FActorRegistry_GetActorType(const FActorView &View)
{
if (!View.IsValid())
@ -41,11 +43,11 @@ static FActorView::ActorType FActorRegistry_GetActorType(const FActorView &View)
}
}
static FString GetRelevantTagAsString(const TSet<ECityObjectLabel> &SemanticTags)
static FString GetRelevantTagAsString(const TSet<crp::CityObjectLabel> &SemanticTags)
{
for (auto &&Tag : SemanticTags)
{
if ((Tag != ECityObjectLabel::None) && (Tag != ECityObjectLabel::Other))
if ((Tag != crp::CityObjectLabel::None) && (Tag != crp::CityObjectLabel::Other))
{
auto Str = ATagger::GetTagAsString(Tag).ToLower();
return (Str.EndsWith(TEXT("s")) ? Str.LeftChop(1) : Str);

View File

@ -22,6 +22,8 @@
#include <sstream>
#endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
namespace crp = carla::rpc;
// =============================================================================
// -- Private types ------------------------------------------------------------
// =============================================================================
@ -248,7 +250,7 @@ static bool LineTrace(
if (Success) {
for (FHitResult &Item : OutHits) {
if (ATagger::MatchComponent(*Item.Component, ECityObjectLabel::Roads)) {
if (ATagger::MatchComponent(*Item.Component, crp::CityObjectLabel::Roads)) {
HitResult = Item;
return true;
}

View File

@ -332,7 +332,7 @@ void ACarlaGameModeBase::DebugShowSignals(bool enable)
}
TArray<FBoundingBox> ACarlaGameModeBase::GetAllBBsOfLevel()
TArray<FBoundingBox> ACarlaGameModeBase::GetAllBBsOfLevel(uint8 TagQueried)
{
UWorld* World = GetWorld();
@ -341,7 +341,7 @@ TArray<FBoundingBox> ACarlaGameModeBase::GetAllBBsOfLevel()
UGameplayStatics::GetAllActorsOfClass(World, AActor::StaticClass(), FoundActors);
TArray<FBoundingBox> BoundingBoxes;
BoundingBoxes = UBoundingBoxCalculator::GetBoundingBoxOfActors(FoundActors);
BoundingBoxes = UBoundingBoxCalculator::GetBoundingBoxOfActors(FoundActors, TagQueried);
return BoundingBoxes;
}

View File

@ -53,7 +53,7 @@ public:
ATrafficLightManager* GetTrafficLightManager();
UFUNCTION(Category = "Carla Game Mode", BlueprintCallable, CallInEditor, Exec)
TArray<FBoundingBox> GetAllBBsOfLevel();
TArray<FBoundingBox> GetAllBBsOfLevel(uint8 TagQueried = 0);
protected:

View File

@ -14,46 +14,48 @@
#include "EngineUtils.h"
#include "PhysicsEngine/PhysicsAsset.h"
namespace crp = carla::rpc;
template <typename T>
static auto CastEnum(T label)
{
return static_cast<typename std::underlying_type<T>::type>(label);
}
ECityObjectLabel ATagger::GetLabelByFolderName(const FString &String) {
if (String == "Building") return ECityObjectLabel::Buildings;
else if (String == "Fence") return ECityObjectLabel::Fences;
else if (String == "Pedestrian") return ECityObjectLabel::Pedestrians;
else if (String == "Pole") return ECityObjectLabel::Poles;
else if (String == "Other") return ECityObjectLabel::Other;
else if (String == "Road") return ECityObjectLabel::Roads;
else if (String == "RoadLine") return ECityObjectLabel::RoadLines;
else if (String == "SideWalk") return ECityObjectLabel::Sidewalks;
else if (String == "TrafficSign") return ECityObjectLabel::TrafficSigns;
else if (String == "Vegetation") return ECityObjectLabel::Vegetation;
else if (String == "Vehicles") return ECityObjectLabel::Vehicles;
else if (String == "Wall") return ECityObjectLabel::Walls;
else if (String == "Sky") return ECityObjectLabel::Sky;
else if (String == "Ground") return ECityObjectLabel::Ground;
else if (String == "Bridge") return ECityObjectLabel::Bridge;
else if (String == "RailTrack") return ECityObjectLabel::RailTrack;
else if (String == "GuardRail") return ECityObjectLabel::GuardRail;
else if (String == "TrafficLight") return ECityObjectLabel::TrafficLight;
else if (String == "Static") return ECityObjectLabel::Static;
else if (String == "Dynamic") return ECityObjectLabel::Dynamic;
else if (String == "Water") return ECityObjectLabel::Water;
else if (String == "Terrain") return ECityObjectLabel::Terrain;
else return ECityObjectLabel::None;
crp::CityObjectLabel ATagger::GetLabelByFolderName(const FString &String) {
if (String == "Building") return crp::CityObjectLabel::Buildings;
else if (String == "Fence") return crp::CityObjectLabel::Fences;
else if (String == "Pedestrian") return crp::CityObjectLabel::Pedestrians;
else if (String == "Pole") return crp::CityObjectLabel::Poles;
else if (String == "Other") return crp::CityObjectLabel::Other;
else if (String == "Road") return crp::CityObjectLabel::Roads;
else if (String == "RoadLine") return crp::CityObjectLabel::RoadLines;
else if (String == "SideWalk") return crp::CityObjectLabel::Sidewalks;
else if (String == "TrafficSign") return crp::CityObjectLabel::TrafficSigns;
else if (String == "Vegetation") return crp::CityObjectLabel::Vegetation;
else if (String == "Vehicles") return crp::CityObjectLabel::Vehicles;
else if (String == "Wall") return crp::CityObjectLabel::Walls;
else if (String == "Sky") return crp::CityObjectLabel::Sky;
else if (String == "Ground") return crp::CityObjectLabel::Ground;
else if (String == "Bridge") return crp::CityObjectLabel::Bridge;
else if (String == "RailTrack") return crp::CityObjectLabel::RailTrack;
else if (String == "GuardRail") return crp::CityObjectLabel::GuardRail;
else if (String == "TrafficLight") return crp::CityObjectLabel::TrafficLight;
else if (String == "Static") return crp::CityObjectLabel::Static;
else if (String == "Dynamic") return crp::CityObjectLabel::Dynamic;
else if (String == "Water") return crp::CityObjectLabel::Water;
else if (String == "Terrain") return crp::CityObjectLabel::Terrain;
else return crp::CityObjectLabel::None;
}
void ATagger::SetStencilValue(
UPrimitiveComponent &Component,
const ECityObjectLabel &Label,
const crp::CityObjectLabel &Label,
const bool bSetRenderCustomDepth) {
Component.SetCustomDepthStencilValue(CastEnum(Label));
Component.SetRenderCustomDepth(
bSetRenderCustomDepth &&
(Label != ECityObjectLabel::None));
(Label != crp::CityObjectLabel::None));
}
// =============================================================================
@ -98,24 +100,24 @@ void ATagger::TagActorsInLevel(UWorld &World, bool bTagForSemanticSegmentation)
}
}
void ATagger::GetTagsOfTaggedActor(const AActor &Actor, TSet<ECityObjectLabel> &Tags)
void ATagger::GetTagsOfTaggedActor(const AActor &Actor, TSet<crp::CityObjectLabel> &Tags)
{
TArray<UPrimitiveComponent *> Components;
Actor.GetComponents<UPrimitiveComponent>(Components);
for (auto *Component : Components) {
if (Component != nullptr) {
const auto Tag = GetTagOfTaggedComponent(*Component);
if (Tag != ECityObjectLabel::None) {
if (Tag != crp::CityObjectLabel::None) {
Tags.Add(Tag);
}
}
}
}
FString ATagger::GetTagAsString(const ECityObjectLabel Label)
FString ATagger::GetTagAsString(const crp::CityObjectLabel Label)
{
switch (Label) {
#define CARLA_GET_LABEL_STR(lbl) case ECityObjectLabel:: lbl : return TEXT(#lbl);
#define CARLA_GET_LABEL_STR(lbl) case crp::CityObjectLabel:: lbl : return TEXT(#lbl);
default:
CARLA_GET_LABEL_STR(None)
CARLA_GET_LABEL_STR(Buildings)

View File

@ -8,34 +8,14 @@
#include "GameFramework/Actor.h"
#include "Components/PrimitiveComponent.h"
#include <compiler/disable-ue4-macros.h>
#include <carla/rpc/ObjectLabel.h>
#include <compiler/enable-ue4-macros.h>
#include "Tagger.generated.h"
enum class ECityObjectLabel : uint8
{
None = 0u,
Buildings = 1u,
Fences = 2u,
Other = 3u,
Pedestrians = 4u,
Poles = 5u,
RoadLines = 6u,
Roads = 7u,
Sidewalks = 8u,
TrafficSigns = 12u,
Vegetation = 9u,
Vehicles = 10u,
Walls = 11u,
Sky = 13u,
Ground = 14u,
Bridge = 15u,
RailTrack = 16u,
GuardRail = 17u,
TrafficLight = 18u,
Static = 19u,
Dynamic = 20u,
Water = 21u,
Terrain = 22u,
};
namespace crp = carla::rpc;
/// Sets actors' custom depth stencil value for semantic segmentation according
/// to their meshes.
@ -67,40 +47,40 @@ public:
static void TagActorsInLevel(UWorld &World, bool bTagForSemanticSegmentation);
/// Retrieve the tag of an already tagged component.
static ECityObjectLabel GetTagOfTaggedComponent(const UPrimitiveComponent &Component)
static crp::CityObjectLabel GetTagOfTaggedComponent(const UPrimitiveComponent &Component)
{
return static_cast<ECityObjectLabel>(Component.CustomDepthStencilValue);
return static_cast<crp::CityObjectLabel>(Component.CustomDepthStencilValue);
}
/// Retrieve the tags of an already tagged actor. ECityObjectLabel::None is
/// Retrieve the tags of an already tagged actor. CityObjectLabel::None is
/// not added to the array.
static void GetTagsOfTaggedActor(const AActor &Actor, TSet<ECityObjectLabel> &Tags);
static void GetTagsOfTaggedActor(const AActor &Actor, TSet<crp::CityObjectLabel> &Tags);
/// Return true if @a Component has been tagged with the given @a Tag.
static bool MatchComponent(const UPrimitiveComponent &Component, ECityObjectLabel Tag)
static bool MatchComponent(const UPrimitiveComponent &Component, crp::CityObjectLabel Tag)
{
return (Tag == GetTagOfTaggedComponent(Component));
}
/// Retrieve the tags of an already tagged actor. ECityObjectLabel::None is
/// Retrieve the tags of an already tagged actor. CityObjectLabel::None is
/// not added to the array.
static FString GetTagAsString(ECityObjectLabel Tag);
static FString GetTagAsString(crp::CityObjectLabel Tag);
/// Method that computes the label corresponding to a folder path
static ECityObjectLabel GetLabelByFolderName(const FString &String);
static crp::CityObjectLabel GetLabelByFolderName(const FString &String);
/// Method that computes the label corresponding to an specific object
/// using the folder path in which it is stored
template <typename T>
static ECityObjectLabel GetLabelByPath(const T *Object) {
static crp::CityObjectLabel GetLabelByPath(const T *Object) {
const FString Path = Object->GetPathName();
TArray<FString> StringArray;
Path.ParseIntoArray(StringArray, TEXT("/"), false);
return (StringArray.Num() > 4 ? GetLabelByFolderName(StringArray[4]) : ECityObjectLabel::None);
return (StringArray.Num() > 4 ? GetLabelByFolderName(StringArray[4]) : crp::CityObjectLabel::None);
}
static void SetStencilValue(UPrimitiveComponent &Component,
const ECityObjectLabel &Label, const bool bSetRenderCustomDepth);
const crp::CityObjectLabel &Label, const bool bSetRenderCustomDepth);
ATagger();

View File

@ -19,6 +19,8 @@
#include "Runtime/Engine/Classes/Kismet/KismetMathLibrary.h"
#include "Runtime/Core/Public/Async/ParallelFor.h"
namespace crp = carla::rpc;
FActorDefinition ARayCastSemanticLidar::GetSensorDefinition()
{
return UActorBlueprintFunctionLibrary::MakeLidarDefinition(TEXT("ray_cast_semantic"));
@ -160,7 +162,7 @@ void ARayCastSemanticLidar::ComputeRawDetection(const FHitResult& HitInfo, const
AActor* actor = HitInfo.Actor.Get();
Detection.object_idx = 0;
Detection.object_tag = static_cast<uint32_t>(ECityObjectLabel::None);
Detection.object_tag = static_cast<uint32_t>(crp::CityObjectLabel::None);
if (actor != nullptr) {
FActorView view = Registry.Find(actor);

View File

@ -306,7 +306,7 @@ void FCarlaServer::FPimpl::BindActions()
return Episode->SerializeActor(ActorView);
};
BIND_SYNC(get_all_level_BBs) << [this]() -> R<std::vector<cg::BoundingBox>>
BIND_SYNC(get_all_level_BBs) << [this](uint8 QueriedTag) -> R<std::vector<cg::BoundingBox>>
{
REQUIRE_CARLA_EPISODE();
TArray<FBoundingBox> Result;
@ -315,7 +315,7 @@ void FCarlaServer::FPimpl::BindActions()
{
RESPOND_ERROR("unable to find CARLA game mode");
}
Result = GameMode->GetAllBBsOfLevel();
Result = GameMode->GetAllBBsOfLevel(QueriedTag);
return MakeVectorFromTArray<cg::BoundingBox>(Result);
};

View File

@ -15,7 +15,61 @@
#include "Rendering/SkeletalMeshRenderData.h"
FBoundingBox UBoundingBoxCalculator::GetActorBoundingBox(const AActor *Actor)
namespace crp = carla::rpc;
static FBoundingBox ApplyTransformToBB(
const FBoundingBox& InBoundingBox,
const FTransform& Transform)
{
const FRotator Rotation = Transform.GetRotation().Rotator();
const FVector Translation = Transform.GetLocation();
const FVector Scale = Transform.GetScale3D();
FBoundingBox BoundingBox = InBoundingBox;
BoundingBox.Origin *= Scale;
BoundingBox.Origin = Rotation.RotateVector(BoundingBox.Origin) + Translation;
BoundingBox.Extent *= Scale;
BoundingBox.Rotation = Rotation;
return BoundingBox;
}
static FBoundingBox CombineBBs(const TArray<FBoundingBox>& BBsToCombine)
{
FVector MaxVertex(TNumericLimits<float>::Lowest());
FVector MinVertex(TNumericLimits<float>::Max());
for(const FBoundingBox& BB : BBsToCombine) {
FVector MaxVertexOfBB = BB.Origin + BB.Extent;
FVector MinVertexOfBB = BB.Origin - BB.Extent;
MaxVertex.X = (MaxVertexOfBB.X > MaxVertex.X) ? MaxVertexOfBB.X : MaxVertex.X;
MaxVertex.Y = (MaxVertexOfBB.Y > MaxVertex.Y) ? MaxVertexOfBB.Y : MaxVertex.Y;
MaxVertex.Z = (MaxVertexOfBB.Z > MaxVertex.Z) ? MaxVertexOfBB.Z : MaxVertex.Z;
MinVertex.X = (MinVertexOfBB.X < MinVertex.X) ? MinVertexOfBB.X : MinVertex.X;
MinVertex.Y = (MinVertexOfBB.Y < MinVertex.Y) ? MinVertexOfBB.Y : MinVertex.Y;
MinVertex.Z = (MinVertexOfBB.Z < MinVertex.Z) ? MinVertexOfBB.Z : MinVertex.Z;
}
// Calculate box extent
FVector Extent (
(MaxVertex.X - MinVertex.X) * 0.5f,
(MaxVertex.Y - MinVertex.Y) * 0.5f,
(MaxVertex.Z - MinVertex.Z) * 0.5f
);
// Calculate middle point
FVector Origin (
(MinVertex.X + Extent.X),
(MinVertex.Y + Extent.Y),
(MinVertex.Z + Extent.Z)
);
return {Origin, Extent};
}
FBoundingBox UBoundingBoxCalculator::GetActorBoundingBox(const AActor *Actor, uint8 InTagQueried)
{
if (Actor != nullptr)
{
@ -59,10 +113,132 @@ FBoundingBox UBoundingBoxCalculator::GetActorBoundingBox(const AActor *Actor)
return {};
}
}
}
return {};
}
FBoundingBox UBoundingBoxCalculator::GetVehicleBoundingBox(
const ACarlaWheeledVehicle* Vehicle,
uint8 InTagQueried)
{
check(Vehicle);
crp::CityObjectLabel TagQueried = (crp::CityObjectLabel)InTagQueried;
bool FilterByTagEnabled = (TagQueried != crp::CityObjectLabel::None);
UActorComponent *ActorComp = Vehicle->GetComponentByClass(USkeletalMeshComponent::StaticClass());
USkeletalMeshComponent* Comp = Cast<USkeletalMeshComponent>(ActorComp);
// Filter by tag
crp::CityObjectLabel Tag = ATagger::GetTagOfTaggedComponent(*Comp);
if(FilterByTagEnabled && Tag != TagQueried) return {};
USkeletalMesh* SkeletalMesh = Comp->SkeletalMesh;
FBoundingBox BoundingBox = GetSkeletalMeshBoundingBox(SkeletalMesh);
if(BoundingBox.Extent.IsZero())
{
UE_LOG(LogCarla, Error, TEXT("%s has no SKM assigned"), *Vehicle->GetName());
return {};
}
// Component-to-world transform for this component
const FTransform& CompToWorldTransform = Comp->GetComponentTransform();
BoundingBox = ApplyTransformToBB(BoundingBox, CompToWorldTransform);
return BoundingBox;
}
FBoundingBox UBoundingBoxCalculator::GetCharacterBoundingBox(
const ACharacter* Character,
uint8 InTagQueried)
{
check(Character);
UCapsuleComponent* Capsule = Character->GetCapsuleComponent();
if (Capsule)
{
const float Radius = Capsule->GetScaledCapsuleRadius();
const float HalfHeight = Capsule->GetScaledCapsuleHalfHeight();
FBoundingBox BoundingBox;
// Characters have the pivot point centered.
BoundingBox.Origin = {0.0f, 0.0f, 0.0f};
BoundingBox.Extent = {Radius, Radius, HalfHeight};
// Component-to-world transform for this component
const FTransform& CompToWorldTransform = Capsule->GetComponentTransform();
BoundingBox = ApplyTransformToBB(BoundingBox, CompToWorldTransform);
return BoundingBox;
}
return {};
}
void UBoundingBoxCalculator::GetTrafficLightBoundingBox(
const ATrafficLightBase* TrafficLight,
TArray<FBoundingBox>& OutBB,
uint8 InTagQueried)
{
check(TrafficLight);
TArray<FBoundingBox> BBsOfTL;
TArray<UStaticMeshComponent*> StaticMeshComps;
TrafficLight->GetComponents<UStaticMeshComponent>(StaticMeshComps);
GetBBsOfStaticMeshComponents(StaticMeshComps, BBsOfTL, InTagQueried);
// This kind of a magic number relying that the lights of a TL are not bigger than 100.
// and we are gonna compare against a squared distance
const float DistanceThreshold = 100.0f * 100.0f;
// The BBs of the TL are calculated per light, so we need to merge the full-box
TSet<int> IndicesDiscarded;
for(int i = 0; i < BBsOfTL.Num(); i++)
{
// Check if the index was used to merge a previous BB
if(IndicesDiscarded.Contains(i)) continue;
TArray<FBoundingBox> BBsToCombine;
FBoundingBox& BB1 = BBsOfTL[i];
for(int j = i + 1; j < BBsOfTL.Num(); j++)
{
// Check if the index was used to merge a previous BB
if(IndicesDiscarded.Contains(j)) continue;
FBoundingBox& BB2 = BBsOfTL[j];
float Distance = FVector::DistSquared(BB1.Origin, BB2.Origin);
// If the lights are close enough, we merge it
if(Distance <= DistanceThreshold)
{
BBsToCombine.Emplace(BB2);
IndicesDiscarded.Emplace(j);
}
}
if(BBsToCombine.Num() > 0)
{
BBsToCombine.Emplace(BB1);
IndicesDiscarded.Emplace(i);
FBoundingBox MergedBB = CombineBBs(BBsToCombine);
MergedBB.Rotation = BB1.Rotation;
OutBB.Add(MergedBB);
}
}
// Add the BB of the meshes that didn't need to combine (ie: poles)
for(int i = 0; i < BBsOfTL.Num(); i++)
{
// Check if the index was used to merge a previous BB
if(IndicesDiscarded.Contains(i)) continue;
FBoundingBox& BB = BBsOfTL[i];
OutBB.Add(BB);
}
}
// TODO: update to calculate current animation pose
FBoundingBox UBoundingBoxCalculator::GetSkeletalMeshBoundingBox(const USkeletalMesh* SkeletalMesh)
{
@ -80,7 +256,7 @@ FBoundingBox UBoundingBoxCalculator::GetSkeletalMeshBoundingBox(const USkeletalM
uint32 NumVertices = FPositionVertexBuffer.GetNumVertices();
// Look for Skeletal Mesh bounds (vertex perfect)
FVector MaxVertex(TNumericLimits<float>::Min());
FVector MaxVertex(TNumericLimits<float>::Lowest());
FVector MinVertex(TNumericLimits<float>::Max());
for(uint32 i = 0; i < NumVertices; i++)
{
@ -93,13 +269,6 @@ FBoundingBox UBoundingBoxCalculator::GetSkeletalMeshBoundingBox(const USkeletalM
MinVertex.Z = (Pos.Z < MinVertex.Z) ? Pos.Z : MinVertex.Z;
}
// Calculate middle point
FVector Origin (
(MaxVertex.X + MinVertex.X) * 0.5f,
(MaxVertex.Y + MinVertex.Y) * 0.5f,
(MaxVertex.Z + MinVertex.Z) * 0.5f
);
// Calculate box extent
FVector Extent (
(MaxVertex.X - MinVertex.X) * 0.5f,
@ -107,6 +276,13 @@ FBoundingBox UBoundingBoxCalculator::GetSkeletalMeshBoundingBox(const USkeletalM
(MaxVertex.Z - MinVertex.Z) * 0.5f
);
// Calculate middle point
FVector Origin (
(MinVertex.X + Extent.X),
(MinVertex.Y + Extent.Y),
(MinVertex.Z + Extent.Z)
);
return {Origin, Extent};
}
@ -119,114 +295,174 @@ FBoundingBox UBoundingBoxCalculator::GetStaticMeshBoundingBox(const UStaticMesh*
}
FBox Box = StaticMesh->GetBoundingBox();
return {Box.GetCenter(), Box.GetExtent()};
}
// TODO: Dynamic vehicle, avoid SM of collision
TArray<FBoundingBox> UBoundingBoxCalculator::GetBoundingBoxOfActors(const TArray<AActor*>& Actors)
void UBoundingBoxCalculator::GetISMBoundingBox(
UInstancedStaticMeshComponent* ISMComp,
TArray<FBoundingBox>& OutBoundingBox)
{
TArray<FBoundingBox> Result;
int ActorIndex = 0;
for(AActor* Actor : Actors)
if(!ISMComp)
{
//UE_LOG(LogCarla, Warning, TEXT(" %d / %d"), ActorIndex, Actors.Num());
ActorIndex++;
UE_LOG(LogCarla, Error, TEXT("GetISMBoundingBox no ISMComp"));
return;
}
AInstancedFoliageActor* InstancedFolliageActor = Cast<AInstancedFoliageActor>(Actor);
if(InstancedFolliageActor != nullptr)
const UStaticMesh *Mesh = ISMComp->GetStaticMesh();
const FBoundingBox SMBoundingBox = GetStaticMeshBoundingBox(Mesh);
if(SMBoundingBox.Extent.IsZero())
{
UE_LOG(LogCarla, Error, TEXT("%s has no SM assigned to the ISM"), *ISMComp->GetOwner()->GetName());
return;
}
const TArray<FInstancedStaticMeshInstanceData>& PerInstanceSMData = ISMComp->PerInstanceSMData;
for(auto& InstSMIData : PerInstanceSMData)
{
const FTransform Transform = FTransform(InstSMIData.Transform);
FBoundingBox BoundingBox = ApplyTransformToBB(SMBoundingBox, Transform);
OutBoundingBox.Add(BoundingBox);
}
}
void UBoundingBoxCalculator::GetBBsOfStaticMeshComponents(
const TArray<UStaticMeshComponent*>& StaticMeshComps,
TArray<FBoundingBox>& OutBB,
uint8 InTagQueried)
{
crp::CityObjectLabel TagQueried = (crp::CityObjectLabel)InTagQueried;
bool FilterByTagEnabled = (TagQueried != crp::CityObjectLabel::None);
for(UStaticMeshComponent* Comp : StaticMeshComps)
{
// Filter by tag
crp::CityObjectLabel Tag = ATagger::GetTagOfTaggedComponent(*Comp);
if(FilterByTagEnabled && Tag != TagQueried) continue;
UStaticMesh* StaticMesh = Comp->GetStaticMesh();
FBoundingBox BoundingBox = GetStaticMeshBoundingBox(StaticMesh);
if(BoundingBox.Extent.IsZero())
{
TMap<UFoliageType*, TUniqueObj<FFoliageInfo>>& FoliageInstancesMap = InstancedFolliageActor->FoliageInfos;
UE_LOG(LogCarla, Warning, TEXT("FolliageActor with %d FoliageTypes"), FoliageInstancesMap.Num());
int FoliageIndex = 0;
for(auto& FoliagePair: FoliageInstancesMap)
{
const UFoliageType* FoliageType = FoliagePair.Key;
const FFoliageInfo& FoliageInfo = FoliagePair.Value.Get();
const UFoliageType_InstancedStaticMesh* FoliageType_ISM = Cast<UFoliageType_InstancedStaticMesh>(FoliageType);
UHierarchicalInstancedStaticMeshComponent* HISMComp = FoliageInfo.GetComponent();
UStaticMesh *Mesh = FoliageType_ISM->GetStaticMesh();
FBoundingBox SMBoundingBox = GetStaticMeshBoundingBox(Mesh);
int32 NumHISMInstances = HISMComp->GetNumRenderInstances();
UE_LOG(LogCarla, Warning, TEXT(" %d/%d : NumHISMInstances = %d"),
FoliageIndex, FoliageInstancesMap.Num(), NumHISMInstances);
FoliageIndex++;
const TArray<FInstancedStaticMeshInstanceData>& PerInstanceSMData = HISMComp->PerInstanceSMData;
for(auto& InstSMIData : PerInstanceSMData)
{
FTransform Transform = FTransform(InstSMIData.Transform);
FRotator Rotation = Transform.GetRotation().Rotator();
FVector Translation = Transform.GetLocation();
FVector Scale = Transform.GetScale3D();
FBoundingBox BoundingBox = SMBoundingBox;
BoundingBox.Origin *= Scale;
BoundingBox.Origin = Rotation.RotateVector(BoundingBox.Origin) + Translation;
BoundingBox.Extent *= Scale;
BoundingBox.Rotation = Rotation;
Result.Add(BoundingBox);
}
}
UE_LOG(LogCarla, Error, TEXT("%s has no SM assigned"), *Comp->GetOwner()->GetName());
}
else
{
TArray<UMeshComponent*> MeshComps;
Actor->GetComponents<UMeshComponent>(MeshComps);
// Component-to-world transform for this component
const FTransform& CompToWorldTransform = Comp->GetComponentTransform();
BoundingBox = ApplyTransformToBB(BoundingBox, CompToWorldTransform);
OutBB.Add(BoundingBox);
}
}
}
FVector WorldLocation = Actor->GetActorLocation();
FVector WorldScale = Actor->GetActorScale();
FRotator WorldRotation = Actor->GetActorRotation();
void UBoundingBoxCalculator::GetBBsOfSkeletalMeshComponents(
const TArray<USkeletalMeshComponent*>& SkeletalMeshComps,
TArray<FBoundingBox>& OutBB,
uint8 InTagQueried)
{
crp::CityObjectLabel TagQueried = (crp::CityObjectLabel)InTagQueried;
bool FilterByTagEnabled = (TagQueried != crp::CityObjectLabel::None);
// Find if there is some geometry component
TArray<UStaticMeshComponent*> StaticMeshComps;
TArray<USkeletalMeshComponent*> SkeletalMeshComps;
Actor->GetComponents<UStaticMeshComponent>(StaticMeshComps);
Actor->GetComponents<USkeletalMeshComponent>(SkeletalMeshComps);
for(USkeletalMeshComponent* Comp : SkeletalMeshComps)
{
// Filter by tag
crp::CityObjectLabel Tag = ATagger::GetTagOfTaggedComponent(*Comp);
if(FilterByTagEnabled && Tag != TagQueried) continue;
// Calculate FBoundingBox of SM
for(UStaticMeshComponent* StaticMeshComp : StaticMeshComps)
USkeletalMesh* SkeletalMesh = Comp->SkeletalMesh;
FBoundingBox BoundingBox = GetSkeletalMeshBoundingBox(SkeletalMesh);
if(BoundingBox.Extent.IsZero())
{
UE_LOG(LogCarla, Error, TEXT("%s has no SKM assigned"), *Comp->GetOwner()->GetName());
}
else
{
// Component-to-world transform for this component
const FTransform& CompToWorldTransform = Comp->GetComponentTransform();
BoundingBox = ApplyTransformToBB(BoundingBox, CompToWorldTransform);
OutBB.Add(BoundingBox);
}
}
}
TArray<FBoundingBox> UBoundingBoxCalculator::GetBoundingBoxOfActors(
const TArray<AActor*>& Actors,
uint8 InTagQueried)
{
TArray<FBoundingBox> Result;
crp::CityObjectLabel TagQueried = (crp::CityObjectLabel)InTagQueried;
bool FilterByTagEnabled = (TagQueried != crp::CityObjectLabel::None);
for(AActor* Actor : Actors)
{
FString ClassName = Actor->GetClass()->GetName();
// Avoid the BP_Procedural_Building to avoid duplication with their child actors
// When improved the BP_Procedural_Building this maybe should be removed
// Note: We don't use casting here because the base class is a BP and is easier to do it this way,
// than getting the UClass of the BP to cast the actor.
if(ClassName.Contains("BP_Procedural_Bulding")) continue;
// The vehicle's BP has a low-polystatic mesh for collisions, we should avoid it
ACarlaWheeledVehicle* Vehicle = Cast<ACarlaWheeledVehicle>(Actor);
if (Vehicle)
{
FBoundingBox BoundingBox = GetVehicleBoundingBox(Vehicle, InTagQueried);
if(!BoundingBox.Extent.IsZero())
{
UStaticMesh* StaticMesh = StaticMeshComp->GetStaticMesh();
FBoundingBox BoundingBox = GetStaticMeshBoundingBox(StaticMesh);
if(BoundingBox.Extent.IsZero())
{
UE_LOG(LogCarla, Error, TEXT("%s has no SM assigned"), *Actor->GetName());
}
else
{
BoundingBox.Origin *= WorldScale;
BoundingBox.Origin = WorldRotation.RotateVector(BoundingBox.Origin) + WorldLocation;
BoundingBox.Extent *= WorldScale;
BoundingBox.Rotation = WorldRotation;
Result.Add(BoundingBox);
}
}
// Calculate FBoundingBox of SK_M
for(USkeletalMeshComponent* SkeletalMeshComp : SkeletalMeshComps)
{
USkeletalMesh* SkeletalMesh = SkeletalMeshComp->SkeletalMesh;
FBoundingBox BoundingBox = GetSkeletalMeshBoundingBox(SkeletalMesh);
BoundingBox.Origin *= WorldScale;
BoundingBox.Origin = WorldRotation.RotateVector(BoundingBox.Origin) + WorldLocation;
BoundingBox.Extent *= WorldScale;
BoundingBox.Rotation = WorldRotation;
Result.Add(BoundingBox);
}
continue;
}
// Pedestrians, we just use the capsule component at the moment.
ACharacter* Character = Cast<ACharacter>(Actor);
if (Character)
{
FBoundingBox BoundingBox = GetCharacterBoundingBox(Character, InTagQueried);
if(!BoundingBox.Extent.IsZero())
{
Result.Add(BoundingBox);
}
continue;
}
// TrafficLight, we need to join all the BB of the lights in one
ATrafficLightBase* TrafficLight = Cast<ATrafficLightBase>(Actor);
if(TrafficLight)
{
GetTrafficLightBoundingBox(TrafficLight, Result, InTagQueried);
continue;
}
// Calculate FBoundingBox of SM
TArray<UStaticMeshComponent*> StaticMeshComps;
Actor->GetComponents<UStaticMeshComponent>(StaticMeshComps);
GetBBsOfStaticMeshComponents(StaticMeshComps, Result, InTagQueried);
// Calculate FBoundingBox of SK_M
TArray<USkeletalMeshComponent*> SkeletalMeshComps;
Actor->GetComponents<USkeletalMeshComponent>(SkeletalMeshComps);
GetBBsOfSkeletalMeshComponents(SkeletalMeshComps, Result, InTagQueried);
// Calculate FBoundingBox of ISM
TArray<UInstancedStaticMeshComponent *> ISMComps;
Actor->GetComponents<UInstancedStaticMeshComponent>(ISMComps);
for(UInstancedStaticMeshComponent* Comp: ISMComps)
{
// Filter by tag
crp::CityObjectLabel Tag = ATagger::GetTagOfTaggedComponent(*Comp);
if(FilterByTagEnabled && Tag != TagQueried) continue;
GetISMBoundingBox(Comp, Result);
}
}
return Result;

View File

@ -28,15 +28,52 @@ public:
///
/// @warning Traffic signs return its trigger box instead.
UFUNCTION(Category = "Carla Actor", BlueprintCallable)
static FBoundingBox GetActorBoundingBox(const AActor *Actor);
static FBoundingBox GetActorBoundingBox(
const AActor *Actor,
uint8 InTagQueried = 0);
UFUNCTION(Category = "Carla Util", BlueprintCallable, CallInEditor)
UFUNCTION(Category = "Carla Actor", BlueprintCallable)
static FBoundingBox GetVehicleBoundingBox(
const ACarlaWheeledVehicle* Vehicle,
uint8 InTagQueried = 0);
UFUNCTION(Category = "Carla Actor", BlueprintCallable)
static FBoundingBox GetCharacterBoundingBox(
const ACharacter* Character,
uint8 InTagQueried = 0);
UFUNCTION(Category = "Carla Actor", BlueprintCallable)
static void GetTrafficLightBoundingBox(
const ATrafficLightBase* TrafficLight,
TArray<FBoundingBox>& OutBB,
uint8 InTagQueried = 0);
UFUNCTION(Category = "Carla Util", BlueprintCallable)
static FBoundingBox GetSkeletalMeshBoundingBox(const USkeletalMesh* SkeletalMesh);
UFUNCTION(Category = "Carla Util", BlueprintCallable, CallInEditor)
UFUNCTION(Category = "Carla Util", BlueprintCallable)
static FBoundingBox GetStaticMeshBoundingBox(const UStaticMesh* StaticMesh);
UFUNCTION(Category = "Carla Util", BlueprintCallable, CallInEditor)
static TArray<FBoundingBox> GetBoundingBoxOfActors(const TArray<AActor*>& Actors);
UFUNCTION(Category = "Carla Util", BlueprintCallable)
static void GetISMBoundingBox(
UInstancedStaticMeshComponent* HISMComp,
TArray<FBoundingBox>& OutBoundingBox);
UFUNCTION(Category = "Carla Util", BlueprintCallable)
static void GetBBsOfStaticMeshComponents(
const TArray<UStaticMeshComponent*>& StaticMeshComps,
TArray<FBoundingBox>& OutBB,
uint8 InTagQueried = 0);
UFUNCTION(Category = "Carla Util", BlueprintCallable)
static void GetBBsOfSkeletalMeshComponents(
const TArray<USkeletalMeshComponent*>& SkeletalMeshComps,
TArray<FBoundingBox>& OutBB,
uint8 InTagQueried = 0);
UFUNCTION(Category = "Carla Util", BlueprintCallable)
static TArray<FBoundingBox> GetBoundingBoxOfActors(
const TArray<AActor*>& Actors,
uint8 InTagQueried = 0);
};