11#include <util/ue-header-guard-begin.h>
12#include "ProceduralFoliageVolume.h"
13#include "ProceduralFoliageComponent.h"
14#include "Engine/Level.h"
15#include <util/ue-header-guard-end.h>
22 auto IsDigit = [](TCHAR charToCheck) {
23 if (charToCheck == TCHAR(
'0'))
return true;
24 if (charToCheck == TCHAR(
'1'))
return true;
25 if (charToCheck == TCHAR(
'2'))
return true;
26 if (charToCheck == TCHAR(
'3'))
return true;
27 if (charToCheck == TCHAR(
'4'))
return true;
28 if (charToCheck == TCHAR(
'5'))
return true;
29 if (charToCheck == TCHAR(
'6'))
return true;
30 if (charToCheck == TCHAR(
'7'))
return true;
31 if (charToCheck == TCHAR(
'8'))
return true;
32 if (charToCheck == TCHAR(
'9'))
return true;
35 int index =
String.Find(TEXT(
"_v"));
39 FString Version =
"_v";
40 while(IsDigit(
String[index]))
59void FPooledActor::EnableActor(
const FTransform& Transform, int32 NewIndex, std::shared_ptr<FTileMeshComponent>& NewTileMeshComponent)
69 Actor->SetActorHiddenInGame(
false);
70 Actor->SetActorTickEnabled(
true);
72 USpringBasedVegetationComponent* Component =
Actor->FindComponentByClass<USpringBasedVegetationComponent>();
75 Component->ResetComponent();
84 Actor->SetActorEnableCollision(
true);
86 USpringBasedVegetationComponent* Component =
Actor->FindComponentByClass<USpringBasedVegetationComponent>();
89 Component->SetComponentTickEnabled(
true);
92 USkeletalMeshComponent* SkeletalMesh =
Actor->FindComponentByClass<USkeletalMeshComponent>();
95 SkeletalMesh->bNoSkeletonUpdate =
false;
119 Actor->SetActorEnableCollision(
false);
120 Actor->SetActorHiddenInGame(
true);
121 Actor->SetActorTickEnabled(
false);
123 USpringBasedVegetationComponent* Component =
Actor->FindComponentByClass<USpringBasedVegetationComponent>();
126 Component->SetComponentTickEnabled(
false);
129 USkeletalMeshComponent* SkeletalMesh =
Actor->FindComponentByClass<USkeletalMeshComponent>();
132 SkeletalMesh->bNoSkeletonUpdate =
true;
148 TRACE_CPUPROFILER_EVENT_SCOPE(FoliageBlueprintCache::SetBPClassName);
151 TArray< FString > ParsedString;
152 Path.ParseIntoArray(ParsedString, TEXT(
"/"),
false);
153 int Position = ParsedString.Num() - 1;
155 const FString Folder = ParsedString[--
Position];
156 const FString BPClassName =
"BP_" + Folder + FullVersion;
172 TRACE_CPUPROFILER_EVENT_SCOPE(FoliageBlueprintCache::SetSpawnedClass);
173 UClass* CastedBlueprint = LoadObject< UClass >(
nullptr, *
BPFullClassName);
188 UInstancedStaticMeshComponent* Aux {
nullptr };
189 for (std::shared_ptr<FTileMeshComponent>& ElementPtr
196 int32 NewCount = NewInstancedStaticMeshComponent->GetInstanceCount();
198 if (NewCount > CurrentCount)
209 for (
const std::shared_ptr<FTileMeshComponent>& Element
212 if (Element->InstancedStaticMeshComponent == Mesh)
224 Material->SetScalarParameterValue(
"ActivateDebug", 1);
226 Material->SetScalarParameterValue(
"ActivateDebug", 0);
227 Material->SetScalarParameterValue(
"ActivateOpacity", 1);
228 Material->SetVectorParameterValue(
"VehiclePosition", Value);
249 TRACE_CPUPROFILER_EVENT_SCOPE(Parent
Tick);
250 Super::Tick(DeltaTime);
260 if (TilesInUse.Num() == 0)
262 UE_LOG(LogCarla, Warning, TEXT(
"No tiles detected."));
266 for (
const FString& TileName : TilesInUse)
289 UE_LOG(LogCarla, Display, TEXT(
"Vehicle added."));
298 UE_LOG(LogCarla, Display, TEXT(
"Vehicle removed."));
308 for (
AActor* Actor : InLevel->Actors)
310 AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
311 if (!
IsValid(InstancedFoliageActor))
313 TileData.InstancedFoliageActor = InstancedFoliageActor;
316 if (!
IsValid(TileData.InstancedFoliageActor))
319 for (
AActor* Actor : InLevel->Actors)
321 AProceduralFoliageVolume* ProceduralFoliageVolume = Cast<AProceduralFoliageVolume>(Actor);
322 if (!
IsValid(ProceduralFoliageVolume))
324 TileData.ProceduralFoliageVolume = ProceduralFoliageVolume;
327 if (!
IsValid(TileData.ProceduralFoliageVolume))
330 const FString TileName = TileData.InstancedFoliageActor->GetLevel()->GetOuter()->GetName();
332 if (ExistingTileData)
358 for (UActorComponent* Component : ActorComponents)
360 UInstancedStaticMeshComponent* Mesh = Cast<UInstancedStaticMeshComponent>(Component);
363 const FString Path = Mesh->GetStaticMesh()->GetPathName();
374 std::shared_ptr<FTileMeshComponent> Aux =
375 std::make_shared<FTileMeshComponent>();
376 Aux->InstancedStaticMeshComponent = Mesh;
377 Aux->bIsAlive =
true;
389 for (std::shared_ptr<FTileMeshComponent>& ElementPtr
397 for (UMaterialInterface* Material : Mesh->GetMaterials())
402 UMaterialInstanceDynamic* MaterialInstanceDynamic = UMaterialInstanceDynamic::Create(Material,
this);
403 if (!MaterialInstanceDynamic)
407 MaterialInstanceDynamic->SetScalarParameterValue(
"ActivateOpacity", 0);
408 MaterialInstanceDynamic->SetScalarParameterValue(
"ActivateDebug", 0);
410 Mesh->SetMaterial(Index, MaterialInstanceDynamic);
419 for (
AActor* Actor : InLevel->Actors)
421 AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
422 if (!
IsValid(InstancedFoliageActor))
424 const FString TileName = InstancedFoliageActor->GetLevel()->GetOuter()->GetName();
425 const TSet<UActorComponent*>& ActorComponents = InstancedFoliageActor->GetComponents();
426 for (UActorComponent* Component : ActorComponents)
428 UInstancedStaticMeshComponent* Mesh = Cast<UInstancedStaticMeshComponent>(Component);
431 const FString Path = Mesh->GetStaticMesh()->GetPathName();
440 if (!NewFoliageBlueprint.
IsValid())
442 UE_LOG(LogCarla, Warning, TEXT(
"Blueprint %s was invalid."), *NewFoliageBlueprint.
BPFullClassName);
446 UE_LOG(LogCarla, Display, TEXT(
"Blueprint %s created."), *NewFoliageBlueprint.
BPFullClassName);
458 AInstancedFoliageActor* TileInstancedFoliageActor =
nullptr;
459 for (
AActor* Actor : InLevel->Actors)
463 AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
464 if (!
IsValid(InstancedFoliageActor))
466 TileInstancedFoliageActor = InstancedFoliageActor;
469 if (!
IsValid(TileInstancedFoliageActor))
471 const FString TileName = TileInstancedFoliageActor->GetLevel()->GetOuter()->GetName();
473 if (ExistingTileData)
477 for (std::shared_ptr<FTileMeshComponent>& Element
480 Element->IndicesInUse.Empty();
481 Element->bIsAlive =
false;
497 auto GlobalTransform =
HeroVehicle->GetActorTransform();
498 auto Position = GlobalTransform.GetLocation();
505 TArray<FElementsToSpawn> Results;
508 for (std::shared_ptr<FTileMeshComponent>& ElementPtr
512 TRACE_CPUPROFILER_EVENT_SCOPE(Update Foliage Usage);
515 const FString Path = InstancedStaticMeshComponent->GetStaticMesh()->GetPathName();
520 if (Indices.Num() == 0)
522 TArray<int32> NewIndices;
523 for (int32 Index : Indices)
529 NewIndices.Emplace(Index);
535 for (int32 Index : NewIndices)
537 FTransform Transform;
538 InstancedStaticMeshComponent->GetInstanceTransform(Index, Transform,
true);
539 NewElement.TransformIndex.Emplace(TPair<FTransform, int32>(Transform, Index));
541 if (NewElement.TransformIndex.Num() > 0)
542 Results.Emplace(NewElement);
550 const FTransform HeroTransform =
HeroVehicle->GetActorTransform();
551 const FVector HeroLocation = HeroTransform.GetLocation();
556 TArray<FPooledActor>* Pool =
ActorPool.Find(Element.BP.BPFullClassName);
560 UE_LOG(LogCarla, Error, TEXT(
"Pool not valid"));
563 for (
const TPair<FTransform, int32>& TransformIndex : Element.TransformIndex)
565 const FTransform& Transform = TransformIndex.Key;
566 int32 Index = TransformIndex.Value;
567 if (Element.TileMeshComponent->IndicesInUse.Contains(Index))
571 const float Distance = FMath::Abs(FVector::DistSquared(Transform.GetLocation(), HeroLocation));
572 if (Distance > HeroDetectionSizeSquared)
586 NewElement.
Actor->SetTickableWhenPaused(
false);
587 NewElement.
EnableActor(Transform, Index, Element.TileMeshComponent);
588 Pool->Emplace(NewElement);
598 const FTransform HeroTransform =
HeroVehicle->GetActorTransform();
599 const FVector HeroLocation = HeroTransform.GetLocation();
602 for (TPair<FString, TArray<FPooledActor>>& Element :
ActorPool)
604 TArray<FPooledActor>& Pool = Element.Value;
611 const FVector Location = Actor.GlobalTransform.GetLocation();
612 const float Distance = FMath::Abs(FVector::DistSquared(Location, HeroLocation));
613 if (Distance < SquaredActiveActorDistance)
624 const FTransform HeroTransform =
HeroVehicle->GetActorTransform();
625 const FVector HeroLocation = HeroTransform.GetLocation();
628 for (TPair<FString, TArray<FPooledActor>>& Element :
ActorPool)
630 TArray<FPooledActor>& Pool = Element.Value;
635 const FVector Location = Actor.GlobalTransform.GetLocation();
636 const float Distance = FMath::Abs(FVector::DistSquared(Location, HeroLocation));
637 if (Distance > HeroDetectionSizeSquared)
639 Actor.DisableActor();
646 const FTransform& Transform,
648 std::shared_ptr<FTileMeshComponent>& TileMeshComponent,
649 TArray<FPooledActor>& Pool)
654 if (PooledActor.InUse)
656 PooledActor.EnableActor(Transform, Index, TileMeshComponent);
657 PooledActor.Actor->SetActorLocationAndRotation(Transform.GetLocation(), Transform.Rotator(),
true,
nullptr, ETeleportType::ResetPhysics);
658 if (SpawnScale <= 1.01f && SpawnScale >= 0.99f)
659 PooledActor.Actor->SetActorScale3D(Transform.GetScale3D());
673 TArray<FPooledActor> AuxPool;
674 const FTransform Transform {};
681 UE_LOG(LogCarla, Display, TEXT(
"Created actor for pool"));
682 NewElement.
Actor->SetTickableWhenPaused(
false);
684 AuxPool.Emplace(NewElement);
688 UE_LOG(LogCarla, Error, TEXT(
"Failed to create actor for pool"));
692 UE_LOG(LogCarla, Display, TEXT(
"CreatePoolForBPClass: %s"), *BP.
BPFullClassName);
700 Transform.GetLocation(), Transform.Rotator());
702 if (SpawnScale <= 1.01f && SpawnScale >= 0.99f)
703 Actor->SetActorScale3D(Transform.GetScale3D());
712 for (TPair<FString, TArray<FPooledActor>>& Element :
ActorPool)
714 TArray<FPooledActor>& Pool = Element.Value;
717 if (PooledActor.InUse)
719 PooledActor.Actor->SetActorTransform(
InactivePoolTransform,
true,
nullptr, ETeleportType::ResetPhysics);
754 if (Path.Contains(
"/Rock"))
757 if (Path.Contains(
"/Tree"))
760 if (Path.Contains(
"/Bush"))
763 if (Path.Contains(
"/Plant"))
771 const UObject* World = GetWorld();
772 TArray<AActor*> ActorsInLevel;
773 UGameplayStatics::GetAllActorsOfClass(World, AInstancedFoliageActor::StaticClass(), ActorsInLevel);
774 for (
AActor* Actor : ActorsInLevel)
776 AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
777 if (!
IsValid(InstancedFoliageActor))
779 const FString TileName = InstancedFoliageActor->GetLevel()->GetOuter()->GetName();
789 TArray<FString> Results;
791 for (
const TPair<FString, FTileData>& Element :
TileCache)
793 const FTileData& TileData = Element.Value;
799 if (Results.Contains(Element.Key))
804 if (!
IsValid(Procedural->ProceduralComponent))
806 const FBox Box = Procedural->ProceduralComponent->GetBounds();
809 if (Box.IsInside(
HeroVehicle->GetActorTransform().GetLocation()))
810 Results.Emplace(Element.Key);
static bool IsValid(const ACarlaWheeledVehicle *Vehicle)
static FString GetVersionFromFString(const FString &String)
Base class for CARLA wheeled vehicles.
const TArray< int32 > GetFoliageInstancesCloseToVehicle(const UInstancedStaticMeshComponent *Component) const
void UpdateDetectionBox()
float GetDetectionSize() const
ACarlaWheeledVehicle * GetHeroVehicle()
TMap< FString, FTileData > TileCache
FTransform InactivePoolTransform
AActor * CreateFoliage(const FFoliageBlueprint &BP, const FTransform &Transform) const
void SpawnSkeletalFoliages(TArray< FElementsToSpawn > &ElementsToSpawn)
ACarlaWheeledVehicle * HeroVehicle
bool CheckForNewTiles() const
void UpdatePoolBasePosition()
float ActiveActorDistance
void SetInstancedStaticMeshComponentCache(FTileData &TileData)
void UpdateFoliageBlueprintCache(ULevel *InLevel)
TArray< FElementsToSpawn > GetElementsToSpawn(FTileData *Tile)
void SetMaterialCache(FTileData &TileData)
bool EnableActorFromPool(const FTransform &Transform, int32 Index, std::shared_ptr< FTileMeshComponent > &TileMeshComponent, TArray< FPooledActor > &Pool)
void AddVehicle(ACarlaWheeledVehicle *Vehicle)
ALargeMapManager * LargeMap
void FreeTileCache(ULevel *InLevel)
bool IsFoliageTypeEnabled(const FString &Path) const
void ActivePooledActors()
void DestroySkeletalFoliages()
void RemoveVehicle(ACarlaWheeledVehicle *Vehicle)
void CreateOrUpdateTileCache(ULevel *InLevel)
void UpdateMaterials(FTileData *Tile)
void CreatePoolForBPClass(const FFoliageBlueprint &BP)
TMap< FString, FFoliageBlueprint > FoliageBlueprintCache
void PostWorldOriginOffset(UWorld *, FIntVector, FIntVector InDstOrigin)
void OnLevelAddedToWorld(ULevel *InLevel, UWorld *InWorld)
void OnLevelRemovedFromWorld(ULevel *InLevel, UWorld *InWorld)
virtual void BeginPlay() override
virtual void Tick(float DeltaTime) override
TMap< FString, TArray< FPooledActor > > ActorPool
TArray< FString > GetTilesInUse()
void SetTileDataInternals(FTileData &TileData)
float HideMaterialDistance
static ALargeMapManager * GetLargeMapManager(const UObject *WorldContextObject)
std::shared_ptr< FTileMeshComponent > TileMeshComponent
bool SetBPClassName(const FString &Path)
TSubclassOf< AActor > SpawnedClass
FTransform GlobalTransform
void EnableActor(const FTransform &Transform, int32 NewIndex, std::shared_ptr< FTileMeshComponent > &NewTileMeshComponent)
std::shared_ptr< FTileMeshComponent > TileMeshComponent
bool ContainsMesh(const UInstancedStaticMeshComponent *) const
TArray< std::shared_ptr< FTileMeshComponent > > TileMeshesCache
AProceduralFoliageVolume * ProceduralFoliageVolume
void UpdateTileMeshComponent(UInstancedStaticMeshComponent *NewInstancedStaticMeshComponent)
void UpdateMaterialCache(const FLinearColor &Value, bool DebugMaterials)
AInstancedFoliageActor * InstancedFoliageActor
TArray< UMaterialInstanceDynamic * > MaterialInstanceDynamicCache
UInstancedStaticMeshComponent * InstancedStaticMeshComponent
TArray< int32 > IndicesInUse