Luis/enchance foliage (#5620)

* improved performance, reducing the elements to check

* clamp rotation and fix set max rotation angle

* Fixed bug causing editor to crash.
Reestructured VegetationSpawner.
50+ FPS now but without update UInstancedStaticMeshComponent.

* Vehicle now is added to the vegetation manager in the begin play and removed in the endplay

* renamed VegetationSpawner for VegetationManager and changed the interal architecture.

Now looks for all tiles in the world and processes only the ones in use.
Also the hidding of the foliage has been moved away to the material.

* Added Reset for SpringBasedVegetationComponent

* Fixed bug with coordinate when spawning for the same static mesh.
This commit is contained in:
LuisPoveda 2022-07-27 16:57:25 +02:00 committed by GitHub
parent 078cf0f824
commit b4b1a27572
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 924 additions and 444 deletions

View File

@ -17,10 +17,10 @@
#define SPRINGVEGETATIONLOGS 0 #define SPRINGVEGETATIONLOGS 0
#define SOLVERLOGS 0 #define SOLVERLOGS 0
#define COLLISIONLOGS 1 #define COLLISIONLOGS 0
#define ACCUMULATIONLOGS 0 #define ACCUMULATIONLOGS 0
#define FICTITIOUSFORCELOGS 0 #define FICTITIOUSFORCELOGS 0
#define OTHERLOGS 1 #define OTHERLOGS 0
#if SOLVERLOGS && SPRINGVEGETATIONLOGS #if SOLVERLOGS && SPRINGVEGETATIONLOGS
#define SOLVER_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__) #define SOLVER_LOG(Level, Msg, ...) UE_LOG(LogCarla, Level, TEXT(Msg), ##__VA_ARGS__)
@ -48,6 +48,24 @@
#define OTHER_LOG(...) #define OTHER_LOG(...)
#endif #endif
static float ClampToPositiveDegrees(float d)
{
if (d < 0.0f)
{
while (d < 0.0f)
{
d += 360.0f;
}
}
else
{
while (d >= 360.0f)
{
d -= 360.0f;
}
}
return d;
}
template <class T> template <class T>
static T GetSign(T n) static T GetSign(T n)
{ {
@ -115,6 +133,7 @@ void FSkeletonHierarchy::Clear()
} }
void FSkeletonHierarchy::ComputeChildrenJointsAndBones() void FSkeletonHierarchy::ComputeChildrenJointsAndBones()
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(FSkeletonHierarchy::ComputeChildrenJointsAndBones);
for (int i = 1; i < Joints.Num(); i++) for (int i = 1; i < Joints.Num(); i++)
{ {
FSkeletonJoint& Joint = Joints[i]; FSkeletonJoint& Joint = Joints[i];
@ -137,6 +156,7 @@ void FSkeletonHierarchy::ComputeChildrenJointsAndBones()
void FSkeletonHierarchy::ComputeEndJoints() void FSkeletonHierarchy::ComputeEndJoints()
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(FSkeletonHierarchy::ComputeEndJoints);
EndJoints.Empty(); EndJoints.Empty();
for (int i = 0; i < Joints.Num(); i++) for (int i = 0; i < Joints.Num(); i++)
{ {
@ -183,16 +203,18 @@ void FSkeletonHierarchy::ComputeEndJoints()
void FSkeletonHierarchy::AddForce(const FString& BoneName, const FVector& Force) void FSkeletonHierarchy::AddForce(const FString& BoneName, const FVector& Force)
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(FSkeletonHierarchy::AddForce);
for (FSkeletonJoint& Joint : Joints) for (FSkeletonJoint& Joint : Joints)
{ {
if(Joint.JointName == BoneName) if(Joint.JointName == BoneName)
{ {
Joint.ExternalForces += Force; Joint.ExternalForces += Force * 0.001f;
} }
} }
} }
void FSkeletonHierarchy::ClearExternalForces() void FSkeletonHierarchy::ClearExternalForces()
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(FSkeletonHierarchy::ClearExternalForces);
for (FSkeletonJoint& Joint : Joints) for (FSkeletonJoint& Joint : Joints)
{ {
Joint.ExternalForces = FVector(0,0,0); Joint.ExternalForces = FVector(0,0,0);
@ -208,6 +230,7 @@ USpringBasedVegetationComponent::USpringBasedVegetationComponent(const FObjectIn
void USpringBasedVegetationComponent::GenerateSkeletonHierarchy() void USpringBasedVegetationComponent::GenerateSkeletonHierarchy()
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::GenerateSkeletonHierarchy);
OTHER_LOG(Warning, "Get skeleton hierarchy"); OTHER_LOG(Warning, "Get skeleton hierarchy");
// Get skeleton hierarchy // Get skeleton hierarchy
if (!SkeletalMesh) if (!SkeletalMesh)
@ -275,6 +298,7 @@ void USpringBasedVegetationComponent::GenerateSkeletonHierarchy()
void USpringBasedVegetationComponent::BeginPlay() void USpringBasedVegetationComponent::BeginPlay()
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::BeginPlay);
Super::BeginPlay(); Super::BeginPlay();
OTHER_LOG(Warning, "USpringBasedVegetationComponent::BeginPlay"); OTHER_LOG(Warning, "USpringBasedVegetationComponent::BeginPlay");
OTHER_LOG(Warning, "Params: BaseSpringStrength %f, Num joints: %d, CollisionForceParameter %f", BaseSpringStrength, Skeleton.Joints.Num(), CollisionForceParameter); OTHER_LOG(Warning, "Params: BaseSpringStrength %f, Num joints: %d, CollisionForceParameter %f", BaseSpringStrength, Skeleton.Joints.Num(), CollisionForceParameter);
@ -347,6 +371,33 @@ void USpringBasedVegetationComponent::BeginPlay()
} }
} }
void USpringBasedVegetationComponent::ResetComponent()
{
Skeleton.ClearExternalForces();
// Get resting pose for bones
auto *AnimInst = SkeletalMesh->GetAnimInstance();
if (!AnimInst)
{
OTHER_LOG(Error, "Could not get animation instance.");
return;
}
UWalkerAnim *WalkerAnim = Cast<UWalkerAnim>(AnimInst);
if (!WalkerAnim)
{
OTHER_LOG(Error, "Could not get UWalkerAnim.");
return;
}
// get current pose
FPoseSnapshot TempSnapshot;
SkeletalMesh->SnapshotPose(TempSnapshot);
// copy pose
WalkerAnim->Snap = TempSnapshot;
UpdateGlobalTransform();
}
void USpringBasedVegetationComponent::GenerateCollisionCapsules() void USpringBasedVegetationComponent::GenerateCollisionCapsules()
{ {
for (FSkeletonJoint& Joint : Skeleton.Joints) for (FSkeletonJoint& Joint : Skeleton.Joints)
@ -376,6 +427,7 @@ void USpringBasedVegetationComponent::GenerateCollisionCapsules()
void USpringBasedVegetationComponent::ComputeSpringStrengthForBranches() void USpringBasedVegetationComponent::ComputeSpringStrengthForBranches()
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeSpringStrengthForBranches);
FTransform RootTransform = Skeleton.GetRootJoint().GlobalTransform; FTransform RootTransform = Skeleton.GetRootJoint().GlobalTransform;
FVector RootLocation = RootTransform.GetLocation(); FVector RootLocation = RootTransform.GetLocation();
// FVector TreeAxis = RootTransform.GetRotation().GetUpVector(); // FVector TreeAxis = RootTransform.GetRotation().GetUpVector();
@ -410,6 +462,7 @@ void USpringBasedVegetationComponent::ComputePerJointProperties(
std::vector<FJointProperties>& JointLocalPropertiesList, std::vector<FJointProperties>& JointLocalPropertiesList,
std::vector<FJointProperties>& JointPropertiesList) std::vector<FJointProperties>& JointPropertiesList)
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputePerJointProperties);
for(FSkeletonJoint& Joint : Skeleton.Joints) for(FSkeletonJoint& Joint : Skeleton.Joints)
{ {
FJointProperties& Properties = JointLocalPropertiesList[Joint.JointId]; FJointProperties& Properties = JointLocalPropertiesList[Joint.JointId];
@ -472,6 +525,7 @@ void USpringBasedVegetationComponent::ComputeCompositeBodyContribution(
std::vector<FJointProperties>& JointLocalPropertiesList, std::vector<FJointProperties>& JointLocalPropertiesList,
std::vector<FJointProperties>& JointPropertiesList) std::vector<FJointProperties>& JointPropertiesList)
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeCompositeBodyContribution);
for (int Id : Skeleton.EndToRootOrder) for (int Id : Skeleton.EndToRootOrder)
{ {
FSkeletonJoint& Joint = Skeleton.Joints[Id]; FSkeletonJoint& Joint = Skeleton.Joints[Id];
@ -533,6 +587,7 @@ void USpringBasedVegetationComponent::ComputeFictitiousForces(
std::vector<FJointProperties>& JointLocalPropertiesList, std::vector<FJointProperties>& JointLocalPropertiesList,
std::vector<FJointProperties>& JointPropertiesList) std::vector<FJointProperties>& JointPropertiesList)
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ComputeFictitiousForces);
// fictitious forces // fictitious forces
FSkeletonJoint& RootJoint = Skeleton.Joints[0]; FSkeletonJoint& RootJoint = Skeleton.Joints[0];
FJointProperties& RootProperties = JointPropertiesList[0]; FJointProperties& RootProperties = JointPropertiesList[0];
@ -581,6 +636,7 @@ void USpringBasedVegetationComponent::ResolveContactsAndCollisions(
std::vector<FJointProperties>& JointLocalPropertiesList, std::vector<FJointProperties>& JointLocalPropertiesList,
std::vector<FJointProperties>& JointPropertiesList) std::vector<FJointProperties>& JointPropertiesList)
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::ResolveContactsAndCollisions);
float MinDistance = INFINITY; float MinDistance = INFINITY;
FVector ClosestSurfacePoint; FVector ClosestSurfacePoint;
for (auto& ActorCapsules : OverlappingActors) for (auto& ActorCapsules : OverlappingActors)
@ -627,17 +683,15 @@ void USpringBasedVegetationComponent::ResolveContactsAndCollisions(
CurrRotator.Pitch - RestRotator.Pitch, CurrRotator.Pitch - RestRotator.Pitch,
CurrRotator.Yaw - RestRotator.Yaw, CurrRotator.Yaw - RestRotator.Yaw,
CurrRotator.Roll - RestRotator.Roll); CurrRotator.Roll - RestRotator.Roll);
Eigen::Vector3d SpringTorque = SpringStrength*RotatorToEigenVector(DeltaRotator); const Eigen::Vector3d SpringTorque = SpringStrength*RotatorToEigenVector(DeltaRotator);
Eigen::Vector3d JointCapsuleVector = JointGlobalPosition - CapsulePosition; const Eigen::Vector3d JointCapsuleVector = JointGlobalPosition - CapsulePosition;
Eigen::Vector3d SpringForce = SpringTorque.cross(JointCapsuleVector)*JointCapsuleVector.squaredNorm(); const Eigen::Vector3d SpringForce = SpringTorque.cross(JointCapsuleVector)*JointCapsuleVector.squaredNorm();
const Eigen::Vector3d RepulsionForce = SpringForce;
const Eigen::Vector3d Multiplier {XMultiplier, YMultiplier, -ZMultiplier};
Eigen::Vector3d RepulsionForce = SpringForce.cwiseProduct(Multiplier);
Primitive->AddForceAtLocation(-ToUnrealVector(RepulsionForce)*100.f, Capsule->GetComponentLocation()); Primitive->AddForceAtLocation(-ToUnrealVector(RepulsionForce)*100.f, Capsule->GetComponentLocation());
// force to repel geometry overlapping // force to repel geometry overlapping
Eigen::Vector3d OverlappingForces = (CapsulePosition - ColliderPosition).normalized()*CollisionForceParameter; Eigen::Vector3d OverlappingForces = (CapsulePosition - ColliderPosition).normalized()*CollisionForceParameter;
OverlappingForces = OverlappingForces.cwiseProduct(Multiplier);
Primitive->AddForceAtLocation(-ToUnrealVector(OverlappingForces)*100.f, Capsule->GetComponentLocation()); Primitive->AddForceAtLocation(-ToUnrealVector(OverlappingForces)*100.f, Capsule->GetComponentLocation());
CollisionTorque += (JointProperties.CenterOfMass - JointGlobalPosition).cross(RepulsionForce + CollisionImpulse + OverlappingForces); CollisionTorque += (JointProperties.CenterOfMass - JointGlobalPosition).cross(RepulsionForce + CollisionImpulse + OverlappingForces);
JointProperties.Torque += CollisionTorque; JointProperties.Torque += CollisionTorque;
@ -656,6 +710,7 @@ void USpringBasedVegetationComponent::SolveEquationOfMotion(
std::vector<FJointProperties>& JointPropertiesList, std::vector<FJointProperties>& JointPropertiesList,
float DeltaTime) float DeltaTime)
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::SolveEquationOfMotion);
// solver // solver
for (FSkeletonJoint& Joint : Skeleton.Joints) for (FSkeletonJoint& Joint : Skeleton.Joints)
{ {
@ -791,22 +846,26 @@ void USpringBasedVegetationComponent::SolveEquationOfMotion(
FRotator NewAngularVelocity = EigenVectorToRotator(FinalNewThetaVelocity); FRotator NewAngularVelocity = EigenVectorToRotator(FinalNewThetaVelocity);
FRotator NewAngularAccel = EigenVectorToRotator(FinalNewThetaAccel); FRotator NewAngularAccel = EigenVectorToRotator(FinalNewThetaAccel);
if (abs(NewPitch) > MaxPitch){ const float ClampedNewPitch = ClampToPositiveDegrees(NewPitch);
NewPitch = GetSign(NewPitch) * MaxPitch; const float ClampedNewYaw = ClampToPositiveDegrees(MaxYaw);
const float ClampedNewRoll = ClampToPositiveDegrees(MaxRoll);
if (ClampedNewPitch > MaxPitch){
NewPitch = ClampedNewPitch;
NewAngularVelocity.Pitch = 0.0f; NewAngularVelocity.Pitch = 0.0f;
NewAngularAccel.Pitch = 0.0f; NewAngularAccel.Pitch = 0.0f;
} }
if (abs(NewYaw) > MaxYaw){ if (ClampedNewYaw > MaxYaw){
NewYaw = GetSign(NewYaw) * MaxYaw; NewYaw = ClampedNewYaw;
NewAngularVelocity.Yaw = 0.0f; NewAngularVelocity.Yaw = 0.0f;
NewAngularAccel.Yaw = 0.0f; NewAngularAccel.Yaw = 0.0f;
} }
if (abs(NewRoll) > MaxRoll){ if (ClampedNewRoll > MaxRoll){
NewRoll = GetSign(NewRoll) * MaxRoll; NewRoll = ClampedNewRoll;
NewAngularVelocity.Roll = 0.0f; NewAngularVelocity.Roll = 0.0f;
NewAngularAccel.Roll = 0.0f; NewAngularAccel.Roll = 0.0f;
} }
@ -830,6 +889,7 @@ void USpringBasedVegetationComponent::TickComponent(
enum ELevelTick TickType, enum ELevelTick TickType,
FActorComponentTickFunction * ThisTickFunction) FActorComponentTickFunction * ThisTickFunction)
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::TickComponent);
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
float DeltaTimeFinal = DeltaTime; float DeltaTimeFinal = DeltaTime;
@ -915,6 +975,7 @@ void USpringBasedVegetationComponent::OnEndOverlapEvent(
void USpringBasedVegetationComponent::UpdateSkeletalMesh() void USpringBasedVegetationComponent::UpdateSkeletalMesh()
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::UpdateSkeletalMesh);
// get the walker animation class // get the walker animation class
auto *AnimInst = SkeletalMesh->GetAnimInstance(); auto *AnimInst = SkeletalMesh->GetAnimInstance();
if (!AnimInst) return; if (!AnimInst) return;
@ -949,7 +1010,7 @@ void USpringBasedVegetationComponent::UpdateSkeletalMesh()
void USpringBasedVegetationComponent::UpdateGlobalTransform() void USpringBasedVegetationComponent::UpdateGlobalTransform()
{ {
TRACE_CPUPROFILER_EVENT_SCOPE(USpringBasedVegetationComponent::UpdateGlobalTransform);
FTransform InitialTransform = SkeletalMesh->GetOwner()->GetActorTransform(); FTransform InitialTransform = SkeletalMesh->GetOwner()->GetActorTransform();
FSkeletonJoint& RootJoint = Skeleton.Joints[0]; FSkeletonJoint& RootJoint = Skeleton.Joints[0];
RootJoint.GlobalTransform = RootJoint.Transform * InitialTransform; RootJoint.GlobalTransform = RootJoint.Transform * InitialTransform;

View File

@ -138,6 +138,8 @@ public:
void GenerateCollisionCapsules(); void GenerateCollisionCapsules();
void ResetComponent();
UFUNCTION(CallInEditor, Category = "Spring Based Vegetation Component") UFUNCTION(CallInEditor, Category = "Spring Based Vegetation Component")
void ComputeSpringStrengthForBranches(); void ComputeSpringStrengthForBranches();
@ -249,15 +251,5 @@ private:
UPROPERTY(EditAnywhere, Category = "Spring Based Vegetation Component") UPROPERTY(EditAnywhere, Category = "Spring Based Vegetation Component")
float MaxRoll = 180.0f; float MaxRoll = 180.0f;
UPROPERTY(EditAnywhere, Category = "Spring Based Vegetation Component")
float XMultiplier = 1.0f;
UPROPERTY(EditAnywhere, Category = "Spring Based Vegetation Component")
float YMultiplier = 1.0f;
UPROPERTY(EditAnywhere, Category = "Spring Based Vegetation Component")
float ZMultiplier = 1.0f;
}; };

View File

@ -0,0 +1,643 @@
// Copyright (c) 2022 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>.
#include "ProceduralFoliageVolume.h"
#include "ProceduralFoliageComponent.h"
#include "Carla/Vegetation/VegetationManager.h"
static FString GetVersionFromFString(const FString& String)
{
TRACE_CPUPROFILER_EVENT_SCOPE(GetVersionFromFString);
auto IsDigit = [](TCHAR charToCheck) {
if (charToCheck == TCHAR('0')) return true;
if (charToCheck == TCHAR('1')) return true;
if (charToCheck == TCHAR('2')) return true;
if (charToCheck == TCHAR('3')) return true;
if (charToCheck == TCHAR('4')) return true;
if (charToCheck == TCHAR('5')) return true;
if (charToCheck == TCHAR('6')) return true;
if (charToCheck == TCHAR('7')) return true;
if (charToCheck == TCHAR('8')) return true;
if (charToCheck == TCHAR('9')) return true;
return false;
};
int index = String.Find(TEXT("_v"));
if (index != -1)
{
index += 2;
FString Version = "_v";
while(IsDigit(String[index]))
{
Version += String[index];
++index;
if (index == String.Len())
return Version;
}
return Version;
}
return FString();
}
/********************************************************************************/
/********** POOLED ACTOR STRUCT *************************************************/
/********************************************************************************/
void FPooledActor::EnableActor()
{
TRACE_CPUPROFILER_EVENT_SCOPE(FPooledActor::EnableActor);
InUse = true;
Actor->SetActorHiddenInGame(false);
Actor->SetActorEnableCollision(true);
Actor->SetActorTickEnabled(true);
USpringBasedVegetationComponent* Component = Actor->FindComponentByClass<USpringBasedVegetationComponent>();
if (Component)
{
Component->ResetComponent();
Component->SetComponentTickEnabled(true);
}
}
void FPooledActor::DisableActor()
{
TRACE_CPUPROFILER_EVENT_SCOPE(FPooledActor::DisableActor);
InUse = false;
Actor->SetActorTransform(FTransform());
Actor->SetActorHiddenInGame(true);
Actor->SetActorEnableCollision(false);
Actor->SetActorTickEnabled(false);
USpringBasedVegetationComponent* Component = Actor->FindComponentByClass<USpringBasedVegetationComponent>();
if (Component)
Component->SetComponentTickEnabled(false);
}
/********************************************************************************/
/********** FOLIAGE BLUEPRINT STRUCT ********************************************/
/********************************************************************************/
bool FFoliageBlueprint::IsValid() const
{
return !BPFullClassName.IsEmpty() && SpawnedClass;
}
bool FFoliageBlueprint::SetBPClassName(const FString& Path)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FoliageBlueprintCache::SetBPClassName);
if (Path.IsEmpty())
return false;
TArray< FString > ParsedString;
Path.ParseIntoArray(ParsedString, TEXT("/"), false);
int Position = ParsedString.Num() - 1;
const FString FullVersion = GetVersionFromFString(ParsedString[Position]);
const FString Folder = ParsedString[--Position];
BPClassName = "BP_" + Folder + FullVersion;
BPFullClassName = "Blueprint'";
for (int i = 0; i <= Position; ++i)
{
BPFullClassName += ParsedString[i];
BPFullClassName += '/';
}
BPFullClassName += BPClassName;
BPFullClassName += ".";
BPFullClassName += BPClassName;
BPFullClassName += "'";
return true;
}
bool FFoliageBlueprint::SetSpawnedClass()
{
TRACE_CPUPROFILER_EVENT_SCOPE(FoliageBlueprintCache::SetSpawnedClass);
UObject* LoadedObject = StaticLoadObject(UObject::StaticClass(), nullptr, *BPFullClassName);
UBlueprint* CastedBlueprint = Cast<UBlueprint>(LoadedObject);
if (CastedBlueprint && CastedBlueprint->GeneratedClass->IsChildOf(AActor::StaticClass()))
{
SpawnedClass = *CastedBlueprint->GeneratedClass;
return true;
}
SpawnedClass = nullptr;
return false;
}
/********************************************************************************/
/********** TILE DATA STRUCT ****************************************************/
/********************************************************************************/
void FTileData::UpdateMaterialCache(const FLinearColor& Value)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FTileData::UpdateMaterialCache);
for (UMaterialInstanceDynamic* Material : MaterialInstanceDynamicCache)
{
Material->SetScalarParameterValue("ActivateDebug", 0);
Material->SetScalarParameterValue("ActivateOpacity", 1);
Material->SetVectorParameterValue("VehiclePosition", Value);
}
}
/********************************************************************************/
/********** OVERRIDE FROM ACTOR *************************************************/
/********************************************************************************/
void AVegetationManager::BeginPlay()
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::BeginPlay);
Super::BeginPlay();
}
void AVegetationManager::Tick(float DeltaTime)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::Tick);
{
TRACE_CPUPROFILER_EVENT_SCOPE(Parent Tick);
Super::Tick(DeltaTime);
}
bool FoundVehicles = CheckIfAnyVehicleInLevel();
if (!FoundVehicles)
return;
UpdateVehiclesDetectionBoxes();
bool NewTilesFound = CheckForNewTiles();
if (NewTilesFound)
{
UpdateFoliageBlueprintCache();
UpdateTileDataCache();
GenerateTileDataInternals();
}
TArray<FString> TilesInUse = GetTilesInUse();
UpdateMaterials(TilesInUse);
TArray<TPair<FFoliageBlueprint, TArray<FTransform>>> ElementsToSpawn = GetElementsToSpawn(TilesInUse);
SpawnSkeletalFoliages(ElementsToSpawn);
DestroySkeletalFoliages();
}
/********************************************************************************/
/********** VEHICLE *************************************************************/
/********************************************************************************/
void AVegetationManager::AddVehicle(ACarlaWheeledVehicle* Vehicle)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::AddVehicle);
if (!IsValid(Vehicle))
return;
if (VehiclesInLevel.Contains(Vehicle))
return;
VehiclesInLevel.Emplace(Vehicle);
UE_LOG(LogCarla, Display, TEXT("Vehicle added."));
}
void AVegetationManager::RemoveVehicle(ACarlaWheeledVehicle* Vehicle)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::RemoveVehicle);
if (!IsValid(Vehicle))
return;
if (!VehiclesInLevel.Contains(Vehicle))
return;
VehiclesInLevel.RemoveSingle(Vehicle);
UE_LOG(LogCarla, Display, TEXT("Vehicle removed."));
}
/********************************************************************************/
/********** CACHES **************************************************************/
/********************************************************************************/
void AVegetationManager::UpdateTileDataCache()
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::UpdateTileDataCache);
const UObject* World = GetWorld();
TArray<FString> NewKeys;
TArray<AActor*> ActorsInLevel;
UGameplayStatics::GetAllActorsOfClass(World, AInstancedFoliageActor::StaticClass(), ActorsInLevel);
for (AActor* Actor : ActorsInLevel)
{
AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
if (!IsValid(InstancedFoliageActor))
continue;
const FString TileName = InstancedFoliageActor->GetLevel()->GetOuter()->GetName();
if (TileDataCache.Contains(TileName))
{
UE_LOG(LogCarla, Warning, TEXT("Tile: %s is already in the cache."), *TileName);
continue;
}
FTileData TileData {};
TileData.InstancedFoliageActor = InstancedFoliageActor;
TileDataCache.Emplace(TileName, TileData);
NewKeys.Emplace(TileName);
}
ActorsInLevel.Reset();
UGameplayStatics::GetAllActorsOfClass(World, AProceduralFoliageVolume::StaticClass(), ActorsInLevel);
for (AActor* Actor : ActorsInLevel)
{
AProceduralFoliageVolume* Procedural = Cast<AProceduralFoliageVolume>(Actor);
if (!IsValid(Procedural))
continue;
const FString TileName = Procedural->GetLevel()->GetOuter()->GetName();
if (NewKeys.Contains(TileName))
{
FTileData* TileData = TileDataCache.Find(TileName);
if (TileData)
{
TileData->ProceduralFoliageVolume = Procedural;
UE_LOG(LogCarla, Display, TEXT("Tile: %s added to the cache."), *TileName);
}
}
}
}
void AVegetationManager::UpdateFoliageBlueprintCache()
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::UpdateFoliageBlueprintCache);
const UObject* World = GetWorld();
TArray<AActor*> ActorsInLevel;
UGameplayStatics::GetAllActorsOfClass(World, AInstancedFoliageActor::StaticClass(), ActorsInLevel);
for (AActor* Actor : ActorsInLevel)
{
AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
if (!IsValid(InstancedFoliageActor))
continue;
const TSet<UActorComponent*>& ActorComponents = InstancedFoliageActor->GetComponents();
for (UActorComponent* Component : ActorComponents)
{
UInstancedStaticMeshComponent* Mesh = Cast<UInstancedStaticMeshComponent>(Component);
if (!IsValid(Mesh))
continue;
const FString Path = Mesh->GetStaticMesh()->GetPathName();
if (!IsFoliageTypeEnabled(Path))
continue;
if (FoliageBlueprintCache.Contains(Path))
continue;
FFoliageBlueprint NewFoliageBlueprint;
NewFoliageBlueprint.SetBPClassName(Path);
NewFoliageBlueprint.SetSpawnedClass();
FoliageBlueprintCache.Emplace(Path, NewFoliageBlueprint);
if (!NewFoliageBlueprint.IsValid())
{
UE_LOG(LogCarla, Warning, TEXT("Blueprint %s was invalid."), *NewFoliageBlueprint.BPClassName);
}
else
{
UE_LOG(LogCarla, Display, TEXT("Blueprint %s created."), *NewFoliageBlueprint.BPClassName);
CreatePoolForBPClass(NewFoliageBlueprint);
}
}
}
}
void AVegetationManager::GenerateTileDataInternals()
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::GenerateTileDataInternals);
for (TPair<FString, FTileData>& Element : TileDataCache)
{
if (Element.Value.TileMeshesCache.Num() > 0)
continue;
InitializeInstancedStaticMeshComponentCache(Element.Value);
InitializeMaterialCache(Element.Value);
}
}
void AVegetationManager::InitializeInstancedStaticMeshComponentCache(FTileData& TileData)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::InitializeInstancedStaticMeshComponentCache);
const TSet<UActorComponent*>& ActorComponents = TileData.InstancedFoliageActor->GetComponents();
for (UActorComponent* Component : ActorComponents)
{
UInstancedStaticMeshComponent* Mesh = Cast<UInstancedStaticMeshComponent>(Component);
if (!IsValid(Mesh))
continue;
const FString Path = Mesh->GetStaticMesh()->GetPathName();
const FFoliageBlueprint* BPCache = FoliageBlueprintCache.Find(Path);
if (!BPCache)
continue;
if (!BPCache->IsValid())
continue;
bool Found = false;
for (auto& Element : TileData.TileMeshesCache)
{
if (Element.InstancedStaticMeshComponent == Mesh)
{
Found = true;
break;
}
}
if (Found)
continue;
FTileMeshComponent Aux;
Aux.InstancedStaticMeshComponent = Mesh;
TileData.TileMeshesCache.Emplace(Aux);
}
}
void AVegetationManager::InitializeMaterialCache(FTileData& TileData)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::InitializeMaterialCache);
const float Distance = VehiclesInLevel[0]->DetectionSize * 2.0f;
for (FTileMeshComponent& Element : TileData.TileMeshesCache)
{
UInstancedStaticMeshComponent* Mesh = Element.InstancedStaticMeshComponent;
UMaterialInterface* Material {nullptr};
int32 Index {0};
do
{
Material = Mesh->GetMaterial(Index);
if (!Material)
continue;
UMaterialInstanceDynamic* MaterialInstanceDynamic = UMaterialInstanceDynamic::Create(Material, this);
if (!MaterialInstanceDynamic)
continue;
if (TileData.MaterialInstanceDynamicCache.Contains(MaterialInstanceDynamic))
continue;
MaterialInstanceDynamic->SetScalarParameterValue("ActivateOpacity", 0);
MaterialInstanceDynamic->SetScalarParameterValue("ActivateDebug", 0);
MaterialInstanceDynamic->SetScalarParameterValue("Distance", Distance);
Mesh->SetMaterial(Index, MaterialInstanceDynamic);
TileData.MaterialInstanceDynamicCache.Emplace(MaterialInstanceDynamic);
++Index;
} while (Material);
}
}
/********************************************************************************/
/********** TICK ****************************************************************/
/********************************************************************************/
void AVegetationManager::UpdateVehiclesDetectionBoxes()
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::UpdateVehiclesDetectionBoxes);
for (ACarlaWheeledVehicle* Vehicle : VehiclesInLevel)
Vehicle->UpdateDetectionBox();
}
void AVegetationManager::UpdateMaterials(TArray<FString>& Tiles)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::UpdateMaterials);
FLinearColor Position = FLinearColor(VehiclesInLevel[0]->GetActorLocation());
for (const FString& TileName : Tiles)
{
FTileData* TileData = TileDataCache.Find(TileName);
if (!TileData)
continue;
TileData->UpdateMaterialCache(Position);
}
}
TArray<TPair<FFoliageBlueprint, TArray<FTransform>>> AVegetationManager::GetElementsToSpawn(const TArray<FString>& Tiles)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::GetElementsToSpawn);
TArray<TPair<FFoliageBlueprint, TArray<FTransform>>> Results;
ALargeMapManager* LargeMap =
UCarlaStatics::GetLargeMapManager(GetWorld());
check(LargeMap);
for (const FString& TileKey : Tiles)
{
FTileData* Tile = TileDataCache.Find(TileKey);
if (!Tile)
continue;
for (FTileMeshComponent& Element : Tile->TileMeshesCache)
{
TRACE_CPUPROFILER_EVENT_SCOPE(Update Foliage Usage);
UInstancedStaticMeshComponent* InstancedStaticMeshComponent = Element.InstancedStaticMeshComponent;
FString Path = InstancedStaticMeshComponent->GetStaticMesh()->GetPathName();
FFoliageBlueprint* BP = FoliageBlueprintCache.Find(Path);
if (!BP)
continue;
if (!BP->IsValid())
continue;
TArray<int32> Indices = VehiclesInLevel[0]->GetFoliageInstancesCloseToVehicle(InstancedStaticMeshComponent);
if (Indices.Num() == 0)
continue;
for (int32 i = 0; i < Element.IndicesInUse.Num(); ++i)
{
if (Element.IndicesInUse[i] == -1)
continue;
if (Indices.Contains(Element.IndicesInUse[i]))
continue;
Element.IndicesInUse[i] = -1;
}
TPair<FFoliageBlueprint, TArray<FTransform>> NewElement {};
NewElement.Key = *BP;
TArray<FTransform> Transforms;
for (int32 Index : Indices)
{
if (Element.IndicesInUse.Contains(Index))
continue;
FTransform Transform;
InstancedStaticMeshComponent->GetInstanceTransform(Index, Transform, true);
FTransform GlobalTransform = LargeMap->LocalToGlobalTransform(Transform);
Transforms.Emplace(GlobalTransform);
bool Found = false;
//Add Index to Array
for (int32 i = 0; i < Element.IndicesInUse.Num(); ++i)
{
if (Element.IndicesInUse[i] == -1)
{
Element.IndicesInUse[i] = Index;
Found = true;
break;
}
}
if (!Found)
Element.IndicesInUse.Emplace(Index);
//----------------------------------------------------
}
if (Transforms.Num() > 0)
{
NewElement.Value = Transforms;
Results.Emplace(NewElement);
}
}
}
return Results;
}
void AVegetationManager::SpawnSkeletalFoliages(TArray<TPair<FFoliageBlueprint, TArray<FTransform>>>& ElementsToSpawn)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::SpawnSkeletalFoliages);
ALargeMapManager* LargeMap =
UCarlaStatics::GetLargeMapManager(GetWorld());
check(LargeMap);
for (TPair<FFoliageBlueprint, TArray<FTransform>>& Element : ElementsToSpawn)
{
FFoliageBlueprint BP = Element.Key;
if (!BP.IsValid())
continue;
TArray<FPooledActor>* Pool = ActorPool.Find(BP.BPClassName);
if (!Pool)
continue;
for (const FTransform& Transform : Element.Value)
{
bool Ok = EnableActorFromPool(Transform, *Pool);
if (Ok)
{
UE_LOG(LogCarla, Display, TEXT("Pooled actor from pool: %s"), *BP.BPClassName);
}
else
{
FPooledActor NewElement;
NewElement.GlobalTransform = Transform;
FTransform LocalTransform = LargeMap->GlobalToLocalTransform(Transform);
NewElement.Actor = CreateFoliage(BP, LocalTransform);
if (IsValid(NewElement.Actor))
{
NewElement.EnableActor();
Pool->Emplace(NewElement);
UE_LOG(LogCarla, Display, TEXT("Created actor for pool: %s"), *BP.BPClassName);
}
}
}
}
}
void AVegetationManager::DestroySkeletalFoliages()
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::DestroySkeletalFoliages);
ALargeMapManager* LargeMap =
UCarlaStatics::GetLargeMapManager(GetWorld());
check(LargeMap);
for (TPair<FString, TArray<FPooledActor>>& Element : ActorPool)
{
TArray<FPooledActor>& Pool = Element.Value;
for (FPooledActor& Actor : Pool)
{
if (!Actor.InUse)
continue;
const FVector Location = Actor.GlobalTransform.GetLocation();
if (!VehiclesInLevel[0]->IsInVehicleRange(Location))
{
Actor.DisableActor();
UE_LOG(LogCarla, Display, TEXT("Disabled Actor from pool"));
}
}
}
}
bool AVegetationManager::EnableActorFromPool(const FTransform& Transform, TArray<FPooledActor>& Pool)
{
ALargeMapManager* LargeMap =
UCarlaStatics::GetLargeMapManager(GetWorld());
check(LargeMap);
for (FPooledActor& PooledActor : Pool)
{
if (PooledActor.InUse)
continue;
PooledActor.GlobalTransform = Transform;
FTransform LocalTransform = LargeMap->GlobalToLocalTransform(Transform);
PooledActor.EnableActor();
PooledActor.Actor->SetActorLocationAndRotation(LocalTransform.GetLocation(), LocalTransform.Rotator(), true, nullptr, ETeleportType::ResetPhysics);
if (SpawnScale <= 1.01f && SpawnScale >= 0.99f)
PooledActor.Actor->SetActorScale3D(LocalTransform.GetScale3D());
else
PooledActor.Actor->SetActorScale3D({SpawnScale, SpawnScale, SpawnScale});
return true;
}
return false;
}
/********************************************************************************/
/********** POOLS ***************************************************************/
/********************************************************************************/
void AVegetationManager::CreatePoolForBPClass(const FFoliageBlueprint& BP)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::CreatePoolForBPClass);
TArray<FPooledActor> AuxPool;
const FTransform Transform {};
for (int32 i = 0; i < InitialPoolSize; ++i)
{
FPooledActor NewElement;
NewElement.Actor = CreateFoliage(BP, Transform);
if (IsValid(NewElement.Actor))
{
UE_LOG(LogCarla, Display, TEXT("Created actor for pool: %s"), *BP.BPClassName);
NewElement.DisableActor();
AuxPool.Emplace(NewElement);
}
else
{
UE_LOG(LogCarla, Error, TEXT("Failed to create actor for pool: %s"), *BP.BPClassName);
}
}
ActorPool.Emplace(BP.BPClassName, AuxPool);
}
AActor* AVegetationManager::CreateFoliage(const FFoliageBlueprint& BP, const FTransform& Transform) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::CreateFoliage);
AActor* Actor = GetWorld()->SpawnActor<AActor>(BP.SpawnedClass,
Transform.GetLocation(), Transform.Rotator());
if (SpawnScale <= 1.01f && SpawnScale >= 0.99f)
Actor->SetActorScale3D(Transform.GetScale3D());
else
Actor->SetActorScale3D({SpawnScale, SpawnScale, SpawnScale});
Actor->SetTickableWhenPaused(false);
return Actor;
}
/********************************************************************************/
/********** OTHER ***************************************************************/
/********************************************************************************/
bool AVegetationManager::CheckIfAnyVehicleInLevel() const
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::CheckIfAnyVehicleInLevel);
return VehiclesInLevel.Num() > 0;
}
bool AVegetationManager::IsFoliageTypeEnabled(const FString& Path) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::IsFoliageTypeEnabled);
if (!SpawnRocks)
if (Path.Contains("Rock"))
return false;
if (!SpawnTrees)
if (Path.Contains("Tree"))
return false;
if (!SpawnBushes)
if (Path.Contains("Bush"))
return false;
if (!SpawnPlants)
if (Path.Contains("Plant"))
return false;
return true;
}
bool AVegetationManager::CheckForNewTiles() const
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::CheckForNewTiles);
const UObject* World = GetWorld();
TArray<AActor*> ActorsInLevel;
UGameplayStatics::GetAllActorsOfClass(World, AInstancedFoliageActor::StaticClass(), ActorsInLevel);
for (AActor* Actor : ActorsInLevel)
{
AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
if (!IsValid(InstancedFoliageActor))
continue;
const FString TileName = InstancedFoliageActor->GetLevel()->GetOuter()->GetName();
if (!TileDataCache.Contains(TileName))
return true;
}
return false;
}
TArray<FString> AVegetationManager::GetTilesInUse() const
{
TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::GetTilesInUse);
TArray<FString> Results;
for (const TPair<FString, FTileData>& Element : TileDataCache)
{
const FTileData& TileData = Element.Value;
const AProceduralFoliageVolume* Procedural = TileData.ProceduralFoliageVolume;
const FString TileName = Procedural->GetLevel()->GetOuter()->GetName();
const FBox Box = Procedural->ProceduralComponent->GetBounds();
if (Results.Contains(Element.Key))
continue;
for (ACarlaWheeledVehicle* Vehicle : VehiclesInLevel)
{
if (Box.IsInside(Vehicle->GetActorLocation()))
{
Results.Emplace(Element.Key);
break;
}
}
}
return Results;
}

View File

@ -0,0 +1,132 @@
// Copyright (c) 2022 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 "GameFramework/Actor.h"
#include "ProceduralFoliageVolume.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Containers/Array.h"
#include "Containers/Map.h"
#include "Carla/Vehicle/CarlaWheeledVehicle.h"
#include "VegetationManager.generated.h"
USTRUCT()
struct FTileMeshComponent
{
GENERATED_BODY()
UInstancedStaticMeshComponent* InstancedStaticMeshComponent {nullptr};
TArray<int32> IndicesInUse {};
};
USTRUCT()
struct FTileData
{
GENERATED_BODY()
AInstancedFoliageActor* InstancedFoliageActor {nullptr};
AProceduralFoliageVolume* ProceduralFoliageVolume {nullptr};
TArray<FTileMeshComponent> TileMeshesCache {};
TArray<UMaterialInstanceDynamic*> MaterialInstanceDynamicCache {};
void UpdateMaterialCache(const FLinearColor& Value);
};
USTRUCT()
struct FFoliageBlueprint
{
GENERATED_BODY()
FString BPClassName {};
FString BPFullClassName {};
TSubclassOf<AActor> SpawnedClass { nullptr };
bool IsValid() const;
bool SetBPClassName(const FString& Path);
bool SetSpawnedClass();
};
USTRUCT()
struct FPooledActor
{
GENERATED_BODY()
bool InUse { false };
AActor* Actor { nullptr };
FTransform GlobalTransform {FTransform()};
void EnableActor();
void DisableActor();
};
UCLASS()
class CARLA_API AVegetationManager : public AActor
{
GENERATED_BODY()
public:
void AddVehicle(ACarlaWheeledVehicle* Vehicle);
void RemoveVehicle(ACarlaWheeledVehicle* Vehicle);
public:
//Cuando se carge un level engancharme al broadcast del delate del collision
//Filters for debug
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
bool SpawnBushes {true};
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
bool SpawnTrees {true};
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
bool SpawnRocks {true};
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
bool SpawnPlants {true};
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
float SpawnScale {1.0f};
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
int32 InitialPoolSize {5};
/// @}
// ===========================================================================
/// @name Overriden from AActor
// ===========================================================================
/// @{
protected:
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
private:
bool IsFoliageTypeEnabled(const FString& Path) const;
bool CheckIfAnyVehicleInLevel() const;
bool CheckForNewTiles() const;
TArray<FString> GetTilesInUse() const;
void UpdateVehiclesDetectionBoxes();
void UpdateMaterials(TArray<FString>& Tiles);
TArray<TPair<FFoliageBlueprint, TArray<FTransform>>> GetElementsToSpawn(const TArray<FString>& Tiles);
void SpawnSkeletalFoliages(TArray<TPair<FFoliageBlueprint, TArray<FTransform>>>& ElementsToSpawn);
void DestroySkeletalFoliages();
bool EnableActorFromPool(const FTransform& Transform, TArray<FPooledActor>& Pool);
void UpdateTileDataCache();
void UpdateFoliageBlueprintCache();
void GenerateTileDataInternals();
void InitializeInstancedStaticMeshComponentCache(FTileData& TileData);
void InitializeMaterialCache(FTileData& TileData);
void CreatePoolForBPClass(const FFoliageBlueprint& BP);
AActor* CreateFoliage(const FFoliageBlueprint& BP, const FTransform& Transform) const;
private:
TArray<ACarlaWheeledVehicle*> VehiclesInLevel {};
TMap<FString, FFoliageBlueprint> FoliageBlueprintCache {};
TMap<FString, TArray<FPooledActor>> ActorPool {};
TMap<FString, FTileData> TileDataCache {};
};

View File

@ -1,315 +0,0 @@
// Copyright (c) 2022 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>.
#include "Carla/Vegetation/VegetationSpawner.h"
#include "Carla/Vehicle/CarlaWheeledVehicle.h"
#include "ProceduralFoliageVolume.h"
#include "Kismet/GameplayStatics.h"
void FFoliageBlueprintCache::GetBPName()
{
FString Left;
FString Right;
FString Aux = Path;
Aux.Split(".BP", &Left, &Right, ESearchCase::CaseSensitive, ESearchDir::FromEnd);
if (Right.IsEmpty())
{
BPClassName = Path;
return;
}
Right = "BP" + Right;
Right.RemoveFromEnd("'", ESearchCase::IgnoreCase);
BPClassName = Right;
}
void AVegetationSpawner::BeginPlay()
{
Super::BeginPlay();
CheckForProcedurals();
}
void AVegetationSpawner::CheckForProcedurals()
{
const UObject* World = GetWorld();
TArray<AActor*> Spawners;
TArray<FSpawnedFoliage> FrameFound;
UGameplayStatics::GetAllActorsOfClass(World, AActor::StaticClass(), Spawners);
for (const AActor* Spawner : Spawners)
{
const TSet<UActorComponent*>& Components = Spawner->GetComponents();
for (UActorComponent* Component : Components)
{
UInstancedStaticMeshComponent* Mesh = Cast<UInstancedStaticMeshComponent>(Component);
if (Mesh == nullptr)
continue;
if (IsInstancedStaticMeshComponentLoaded(Mesh))
continue;
const int32 NumOfInstances = Mesh->GetInstanceCount();
TArray<int32> SpawnerOverlappingInstances;
TArray<FTransform> SpawnerOverlappingTransforms;
TArray<AActor*> SpawnedActors;
SpawnerOverlappingInstances.Init(0, NumOfInstances);
SpawnerOverlappingTransforms.SetNum(NumOfInstances);
SpawnedActors.Init(nullptr, NumOfInstances);
for (int32 i = 0; i < NumOfInstances; ++i)
{
FTransform Transform;
Mesh->GetInstanceTransform(i, Transform, true);
SpawnerOverlappingTransforms[i] = Transform;
}
FSpawnedFoliage Aux;
Aux.InUse = true;
Aux.Mesh = Mesh;
Aux.Indices = SpawnerOverlappingInstances;
Aux.Transforms = SpawnerOverlappingTransforms;
Aux.SpawnedActors = SpawnedActors;
ProceduralInstances.Add(Aux);
FrameFound.Add(Aux);
}
}
for (auto& Element : ProceduralInstances)
{
if (Element.InUse == false)
continue;
bool Found = false;
for (const FSpawnedFoliage& NewElement : FrameFound)
{
if (Element.Mesh == NewElement.Mesh)
{
Found = true;
break;
}
}
Element.InUse = Found;
}
}
void AVegetationSpawner::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
UpdateVehiclesInLevel();
if (VehiclesInLevel.Num() == 0)
{
if (Defaulted == false)
Default();
Defaulted = true;
return;
}
CheckForProcedurals();
Defaulted = false;
UpdateProceduralInstanceCount();
}
void AVegetationSpawner::UpdateVehiclesInLevel()
{
TArray<AActor*> FoundVehicles;
const UObject* World = GetWorld();
UGameplayStatics::GetAllActorsOfClass(World, ACarlaWheeledVehicle::StaticClass(), FoundVehicles);
VehiclesInLevel = FoundVehicles;
}
void AVegetationSpawner::UpdateProceduralInstanceCount()
{
TArray<int32> ProceduralInstanceCount;
for (FSpawnedFoliage& Foliage : ProceduralInstances)
{
if (!Foliage.InUse)
continue;
const int32 NumOfInstances = Foliage.Mesh->GetInstanceCount();
TArray<int32> SpawnerOverlappingInstances;
SpawnerOverlappingInstances.Init(0, NumOfInstances);
for (int32 i = 0; i < NumOfInstances; ++i)
{
const FVector Location = Foliage.Transforms[i].GetLocation();
for (const AActor* Vehicle : VehiclesInLevel)
{
const ACarlaWheeledVehicle* CarlaVehicle = Cast<ACarlaWheeledVehicle>(Vehicle);
if (CarlaVehicle == nullptr)
continue;
if (CarlaVehicle->IsInVehicleRange(Location))
{
SpawnerOverlappingInstances[i]++;
}
}
}
UpdateFoliage(Foliage, SpawnerOverlappingInstances);
}
}
void AVegetationSpawner::UpdateFoliage(FSpawnedFoliage& Foliage, TArray<int32>& VehiclesDetection)
{
auto FoliageIterator = Foliage.Indices.CreateIterator();
auto VehicleDetectionIterator = VehiclesDetection.CreateConstIterator();
for (int32 i = 0; FoliageIterator && VehicleDetectionIterator;
++i, ++FoliageIterator, ++VehicleDetectionIterator)
{
if (*FoliageIterator > 0 && *VehicleDetectionIterator == 0)
{
*FoliageIterator = 0;
if (Foliage.SpawnedActors[i])
{
Foliage.SpawnedActors[i]->Destroy();
Foliage.SpawnedActors[i] = nullptr;
}
Foliage.Mesh->UpdateInstanceTransform(i, Foliage.Transforms[i], true, true, true);
}
else if (*FoliageIterator == 0 && *VehicleDetectionIterator > 0)
{
const FString Path = Foliage.Mesh->GetStaticMesh()->GetPathName();
if (!IsFoliageTypeEnabled(Path))
continue;
const FFoliageBlueprintCache FullClassName = GetClassFromPath(Path);
if (FullClassName.SpawnedClass == nullptr)
continue;
*FoliageIterator = *VehicleDetectionIterator;
Foliage.Mesh->UpdateInstanceTransform(i, {}, true, true, true);
Foliage.SpawnedActors[i] = SpawnFoliage(FullClassName, Foliage.Transforms[i]);
}
}
}
FFoliageBlueprintCache AVegetationSpawner::GetClassFromPath(const FString& Path)
{
FFoliageBlueprintCache Result = FindInCache(Path);
if (!Result.Path.IsEmpty())
return Result;
Result.Path = Path;
TArray< FString > ParsedString;
Path.ParseIntoArray(ParsedString, TEXT("/"), false);
int Position = ParsedString.Num() - 1;
const FString Version = ParsedString[Position];
--Position;
const FString Folder = ParsedString[Position];
++Position;
const FString FullVersion = GetVersionFromFString(Version);
if (FullVersion.IsEmpty())
{
Result.BPClassName = Path;
FoliageCache.Add(Path, Result);
return Result;
}
FString ClassPath = "BP_" + Folder + FullVersion;
FString FullClassPath = "Blueprint'";
for (int i = 0; i < Position; ++i)
{
FullClassPath += ParsedString[i];
FullClassPath += '/';
}
FullClassPath += ClassPath;
FullClassPath += ".";
FullClassPath += ClassPath;
FullClassPath += "'";
UObject* LoadedObject = StaticLoadObject(UObject::StaticClass(), nullptr, *FullClassPath);
UBlueprint* CastedBlueprint = Cast<UBlueprint>(LoadedObject);
Result.GetBPName();
if (CastedBlueprint && CastedBlueprint->GeneratedClass->IsChildOf(AActor::StaticClass()))
{
Result.SpawnedClass = *CastedBlueprint->GeneratedClass;
}
FoliageCache.Add(Path, Result);
return Result;
}
AActor* AVegetationSpawner::SpawnFoliage(const FFoliageBlueprintCache& CacheBPClass, const FTransform& FoliageTransform)
{
AActor* Actor = GetWorld()->SpawnActor<AActor>(CacheBPClass.SpawnedClass,
FoliageTransform.GetLocation(), FoliageTransform.Rotator());
Actor->SetActorScale3D(FoliageTransform.GetScale3D());
return Actor;
}
void AVegetationSpawner::Default()
{
for (FSpawnedFoliage& Foliage : ProceduralInstances)
{
const int32 NumOfInstances = Foliage.Mesh->GetInstanceCount();
Foliage.InUse = false;
Foliage.Indices.Init(0, NumOfInstances);
Foliage.SpawnedActors.Init(nullptr, NumOfInstances);
}
}
FFoliageBlueprintCache AVegetationSpawner::FindInCache(const FString& Path)
{
const FFoliageBlueprintCache* Element = FoliageCache.Find(Path);
if(Element)
return *Element;
return FFoliageBlueprintCache();
}
bool AVegetationSpawner::IsFoliageTypeEnabled(const FString& Path)
{
if (!SpawnRocks)
if (Path.Contains("Rock"))
return false;
if (!SpawnTrees)
if (Path.Contains("Tree"))
return false;
if (!SpawnBushes)
if (Path.Contains("Bush"))
return false;
if (!SpawnPlants)
if (Path.Contains("Plant"))
return false;
return true;
}
FString AVegetationSpawner::GetVersionFromFString(const FString& string)
{
auto IsDigit = [](TCHAR charToTest) {
if (charToTest == TCHAR('0')) return true;
if (charToTest == TCHAR('1')) return true;
if (charToTest == TCHAR('2')) return true;
if (charToTest == TCHAR('3')) return true;
if (charToTest == TCHAR('4')) return true;
if (charToTest == TCHAR('5')) return true;
if (charToTest == TCHAR('6')) return true;
if (charToTest == TCHAR('7')) return true;
if (charToTest == TCHAR('8')) return true;
if (charToTest == TCHAR('9')) return true;
return false;
};
int index = string.Find(TEXT("_v"));
if (index != -1)
{
index += 2;
FString Version = "_v";
while(IsDigit(string[index]))
{
Version += string[index];
++index;
if (index == string.Len())
return Version;
}
return Version;
}
return FString();
}
bool AVegetationSpawner::IsInstancedStaticMeshComponentLoaded(const UInstancedStaticMeshComponent* Mesh)
{
for(const FSpawnedFoliage& Instance : ProceduralInstances)
{
if (Instance.Mesh == Mesh && Instance.InUse == true)
{
return true;
}
}
return false;
}

View File

@ -1,89 +0,0 @@
// Copyright (c) 2022 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 "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "VegetationSpawner.generated.h"
USTRUCT()
struct FSpawnedFoliage
{
GENERATED_BODY()
bool InUse {false};
UInstancedStaticMeshComponent* Mesh {nullptr};
TArray<int32> Indices {};
TArray<FTransform> Transforms {};
TArray<AActor*> SpawnedActors {};
};
USTRUCT()
struct FFoliageBlueprintCache
{
GENERATED_BODY()
FString Path {};
FString BPClassName {};
TSubclassOf<AActor> SpawnedClass { nullptr };
void GetBPName();
};
UCLASS()
class CARLA_API AVegetationSpawner : public AActor
{
GENERATED_BODY()
public:
//Filters for debug, improving performance
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
bool SpawnBushes {true};
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
bool SpawnTrees {true};
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
bool SpawnRocks {true};
UPROPERTY(Category = "CARLA Vegetation Spwaner", EditDefaultsOnly)
bool SpawnPlants {true};
/// @}
// ===========================================================================
/// @name Overriden from AActor
// ===========================================================================
/// @{
protected:
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
private:
void CheckForProcedurals();
void UpdateVehiclesInLevel();
void UpdateProceduralInstanceCount();
void UpdateFoliage(FSpawnedFoliage& Foliage, TArray<int32>& VehiclesDetection);
void Default();
FFoliageBlueprintCache FindInCache(const FString& Path);
AActor* SpawnFoliage(const FFoliageBlueprintCache& CacheBPClass, const FTransform& FoliageTransform);
FFoliageBlueprintCache GetClassFromPath(const FString& Path);
bool IsFoliageTypeEnabled(const FString& Path);
FString GetVersionFromFString(const FString& string);
bool IsInstancedStaticMeshComponentLoaded(const UInstancedStaticMeshComponent* Mesh);
private:
TArray<AActor*> VehiclesInLevel;
TArray<FSpawnedFoliage> ProceduralInstances;
TMap<FString, FFoliageBlueprintCache> FoliageCache;
bool Defaulted {false};
};

View File

@ -25,6 +25,7 @@
#include "Carla/Util/ActorAttacher.h" #include "Carla/Util/ActorAttacher.h"
#include "Carla/Util/EmptyActor.h" #include "Carla/Util/EmptyActor.h"
#include "Carla/Util/BoundingBoxCalculator.h" #include "Carla/Util/BoundingBoxCalculator.h"
#include "Carla/Vegetation/VegetationManager.h"
#include "Carla/Vehicle/CarlaWheeledVehicle.h" #include "Carla/Vehicle/CarlaWheeledVehicle.h"
// ============================================================================= // =============================================================================
@ -197,22 +198,31 @@ void ACarlaWheeledVehicle::BeginPlay()
// Update physics in the Ackermann Controller // Update physics in the Ackermann Controller
AckermannController.UpdateVehiclePhysics(this); AckermannController.UpdateVehiclePhysics(this);
} }
AddReferenceToManager();
} }
UFUNCTION()
bool ACarlaWheeledVehicle::IsInVehicleRange(const FVector& Location) const bool ACarlaWheeledVehicle::IsInVehicleRange(const FVector& Location) const
{ {
const FVector Vec { DetectionSize, DetectionSize, DetectionSize}; TRACE_CPUPROFILER_EVENT_SCOPE(ACarlaWheeledVehicle::IsInVehicleRange);
FBox Box = FBox(-Vec, Vec); ALargeMapManager* LargeMap =
FoliageBoundingBox = Box.TransformBy(GetActorTransform()); UCarlaStatics::GetLargeMapManager(GetWorld());
return FoliageBoundingBox.IsInsideOrOn(Location); check(LargeMap);
FTransform GlobalTransform = LargeMap->LocalToGlobalTransform(GetActorTransform());
float Distance = Distance = FVector::Distance(Location, GlobalTransform.GetLocation());
return Distance < DetectionSize * 10.0f;
} }
TArray<int32> ACarlaWheeledVehicle::GetFoliageInstancesCloseToVehicle(const UInstancedStaticMeshComponent* Component) const void ACarlaWheeledVehicle::UpdateDetectionBox()
{ {
const FVector Vec { DetectionSize, DetectionSize, DetectionSize}; const FVector Vec { DetectionSize, DetectionSize, DetectionSize};
FBox Box = FBox(-Vec, Vec); FBox Box = FBox(-Vec, Vec);
FoliageBoundingBox = Box.TransformBy(GetActorTransform()); FoliageBoundingBox = Box.TransformBy(GetActorTransform());
}
const TArray<int32> ACarlaWheeledVehicle::GetFoliageInstancesCloseToVehicle(const UInstancedStaticMeshComponent* Component) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(ACarlaWheeledVehicle::GetFoliageInstancesCloseToVehicle);
return Component->GetInstancesOverlappingBox(FoliageBoundingBox); return Component->GetInstancesOverlappingBox(FoliageBoundingBox);
} }
@ -224,6 +234,11 @@ void ACarlaWheeledVehicle::DrawFoliageBoundingBox() const
DrawDebugBox(GetWorld(), Center, Extent, Rotation, FColor::Magenta, false, 0.0f, 0, 5.0f); DrawDebugBox(GetWorld(), Center, Extent, Rotation, FColor::Magenta, false, 0.0f, 0, 5.0f);
} }
FBoxSphereBounds ACarlaWheeledVehicle::GetBoxSphereBounds() const
{
return VehicleBounds->CalcBounds(GetActorTransform());
}
void ACarlaWheeledVehicle::AdjustVehicleBounds() void ACarlaWheeledVehicle::AdjustVehicleBounds()
{ {
FBoundingBox BoundingBox = UBoundingBoxCalculator::GetVehicleBoundingBox(this); FBoundingBox BoundingBox = UBoundingBoxCalculator::GetVehicleBoundingBox(this);
@ -911,6 +926,7 @@ FVector ACarlaWheeledVehicle::GetVelocity() const
void ACarlaWheeledVehicle::EndPlay(const EEndPlayReason::Type EndPlayReason) void ACarlaWheeledVehicle::EndPlay(const EEndPlayReason::Type EndPlayReason)
{ {
ShowDebugTelemetry(false); ShowDebugTelemetry(false);
RemoveReferenceToManager();
Super::EndPlay(EndPlayReason); Super::EndPlay(EndPlayReason);
} }
@ -1038,3 +1054,33 @@ void ACarlaWheeledVehicle::SetRolloverFlag(){
carla::rpc::VehicleFailureState ACarlaWheeledVehicle::GetFailureState() const{ carla::rpc::VehicleFailureState ACarlaWheeledVehicle::GetFailureState() const{
return FailureState; return FailureState;
} }
void ACarlaWheeledVehicle::AddReferenceToManager()
{
const UObject* World = GetWorld();
TArray<AActor*> ActorsInLevel;
UGameplayStatics::GetAllActorsOfClass(World, AActor::StaticClass(), ActorsInLevel);
for (AActor* Actor : ActorsInLevel)
{
AVegetationManager* Manager = Cast<AVegetationManager>(Actor);
if (!IsValid(Manager))
continue;
Manager->AddVehicle(this);
return;
}
}
void ACarlaWheeledVehicle::RemoveReferenceToManager()
{
const UObject* World = GetWorld();
TArray<AActor*> ActorsInLevel;
UGameplayStatics::GetAllActorsOfClass(World, AActor::StaticClass(), ActorsInLevel);
for (AActor* Actor : ActorsInLevel)
{
AVegetationManager* Manager = Cast<AVegetationManager>(Actor);
if (!IsValid(Manager))
continue;
Manager->RemoveVehicle(this);
return;
}
}

View File

@ -328,9 +328,6 @@ private:
UPROPERTY(Category = "AI Controller", VisibleAnywhere) UPROPERTY(Category = "AI Controller", VisibleAnywhere)
ECarlaWheeledVehicleState State = ECarlaWheeledVehicleState::UNKNOWN; ECarlaWheeledVehicleState State = ECarlaWheeledVehicleState::UNKNOWN;
UPROPERTY(Category = "CARLA Wheeled Vehicle", EditAnywhere)
UBoxComponent *VehicleBounds;
UPROPERTY(Category = "CARLA Wheeled Vehicle", EditAnywhere) UPROPERTY(Category = "CARLA Wheeled Vehicle", EditAnywhere)
UVehicleVelocityControl* VelocityControl; UVehicleVelocityControl* VelocityControl;
@ -360,14 +357,23 @@ public:
float DetectionSize {200.0f}; float DetectionSize {200.0f};
UPROPERTY(Category = "CARLA Wheeled Vehicle", VisibleAnywhere, BlueprintReadOnly) UPROPERTY(Category = "CARLA Wheeled Vehicle", VisibleAnywhere, BlueprintReadOnly)
mutable FBox FoliageBoundingBox; FBox FoliageBoundingBox;
UPROPERTY(Category = "CARLA Wheeled Vehicle", EditAnywhere)
UBoxComponent *VehicleBounds;
UFUNCTION() UFUNCTION()
TArray<int32> GetFoliageInstancesCloseToVehicle(const UInstancedStaticMeshComponent* Component) const; void UpdateDetectionBox();
UFUNCTION()
const TArray<int32> GetFoliageInstancesCloseToVehicle(const UInstancedStaticMeshComponent* Component) const;
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void DrawFoliageBoundingBox() const; void DrawFoliageBoundingBox() const;
UFUNCTION()
FBoxSphereBounds GetBoxSphereBounds() const;
UFUNCTION() UFUNCTION()
bool IsInVehicleRange(const FVector& Location) const; bool IsInVehicleRange(const FVector& Location) const;
@ -435,5 +441,9 @@ private:
void CheckRollover(const float roll, const std::pair<float, float> threshold_roll); void CheckRollover(const float roll, const std::pair<float, float> threshold_roll);
void AddReferenceToManager();
void RemoveReferenceToManager();
FTimerHandle TimerHandler; FTimerHandle TimerHandler;
}; };