CARLA
Loading...
Searching...
No Matches
VegetationManager.cpp
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
10
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>
16
17
18
19static FString GetVersionFromFString(const FString& String)
20{
21 TRACE_CPUPROFILER_EVENT_SCOPE(GetVersionFromFString);
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;
33 return false;
34 };
35 int index = String.Find(TEXT("_v"));
36 if (index != -1)
37 {
38 index += 2;
39 FString Version = "_v";
40 while(IsDigit(String[index]))
41 {
42 Version += String[index];
43 ++index;
44 if (index == String.Len())
45 return Version;
46 }
47 return Version;
48 }
49 return FString();
50}
51
55
56/********************************************************************************/
57/********** POOLED ACTOR STRUCT *************************************************/
58/********************************************************************************/
59void FPooledActor::EnableActor(const FTransform& Transform, int32 NewIndex, std::shared_ptr<FTileMeshComponent>& NewTileMeshComponent)
60{
61 TRACE_CPUPROFILER_EVENT_SCOPE(FPooledActor::EnableActor);
62 InUse = true;
63 IsActive = false;
64 GlobalTransform = Transform;
65 Index = NewIndex;
66 TileMeshComponent = NewTileMeshComponent;
67 TileMeshComponent->IndicesInUse.Emplace(Index);
68
69 Actor->SetActorHiddenInGame(false);
70 Actor->SetActorTickEnabled(true);
71
72 USpringBasedVegetationComponent* Component = Actor->FindComponentByClass<USpringBasedVegetationComponent>();
73 if (Component)
74 {
75 Component->ResetComponent();
76 }
77}
78
80{
81 TRACE_CPUPROFILER_EVENT_SCOPE(FPooledActor::ActiveActor);
82
83 IsActive = true;
84 Actor->SetActorEnableCollision(true);
85
86 USpringBasedVegetationComponent* Component = Actor->FindComponentByClass<USpringBasedVegetationComponent>();
87 if (Component)
88 {
89 Component->SetComponentTickEnabled(true);
90 }
91
92 USkeletalMeshComponent* SkeletalMesh = Actor->FindComponentByClass<USkeletalMeshComponent>();
93 if (SkeletalMesh)
94 {
95 SkeletalMesh->bNoSkeletonUpdate = false;
96 }
97}
98
100{
101 TRACE_CPUPROFILER_EVENT_SCOPE(FPooledActor::DisableActor);
102
104 {
105 if (TileMeshComponent->bIsAlive)
106 {
107 if (TileMeshComponent->IndicesInUse.Contains(Index))
108 {
109 TileMeshComponent->IndicesInUse.RemoveSingle(Index);
110 }
111 }
112 }
113
114 InUse = false;
115 IsActive = false;
116 Index = -1;
117 TileMeshComponent = nullptr;
118
119 Actor->SetActorEnableCollision(false);
120 Actor->SetActorHiddenInGame(true);
121 Actor->SetActorTickEnabled(false);
122
123 USpringBasedVegetationComponent* Component = Actor->FindComponentByClass<USpringBasedVegetationComponent>();
124 if (Component)
125 {
126 Component->SetComponentTickEnabled(false);
127 }
128
129 USkeletalMeshComponent* SkeletalMesh = Actor->FindComponentByClass<USkeletalMeshComponent>();
130 if (SkeletalMesh)
131 {
132 SkeletalMesh->bNoSkeletonUpdate = true;
133 }
134}
135
136/********************************************************************************/
137/********** FOLIAGE BLUEPRINT STRUCT ********************************************/
138/********************************************************************************/
140{
141 if (BPFullClassName.IsEmpty() || !BPFullClassName.Contains("_C"))
142 return false;
143 return SpawnedClass != nullptr;
144}
145
146bool FFoliageBlueprint::SetBPClassName(const FString& Path)
147{
148 TRACE_CPUPROFILER_EVENT_SCOPE(FoliageBlueprintCache::SetBPClassName);
149 if (Path.IsEmpty())
150 return false;
151 TArray< FString > ParsedString;
152 Path.ParseIntoArray(ParsedString, TEXT("/"), false);
153 int Position = ParsedString.Num() - 1;
154 const FString FullVersion = GetVersionFromFString(ParsedString[Position]);
155 const FString Folder = ParsedString[--Position];
156 const FString BPClassName = "BP_" + Folder + FullVersion;
157 BPFullClassName = "Blueprint'";
158 for (int i = 0; i <= Position; ++i)
159 {
160 BPFullClassName += ParsedString[i];
161 BPFullClassName += '/';
162 }
163 BPFullClassName += BPClassName;
164 BPFullClassName += ".";
165 BPFullClassName += BPClassName;
166 BPFullClassName += "_C'";
167 return true;
168}
169
171{
172 TRACE_CPUPROFILER_EVENT_SCOPE(FoliageBlueprintCache::SetSpawnedClass);
173 UClass* CastedBlueprint = LoadObject< UClass >(nullptr, *BPFullClassName);
174 if (CastedBlueprint)
175 {
176 SpawnedClass = CastedBlueprint;
177 return true;
178 }
179 SpawnedClass = nullptr;
180 return false;
181}
182
183/********************************************************************************/
184/********** TILE DATA STRUCT ****************************************************/
185/********************************************************************************/
186void FTileData::UpdateTileMeshComponent(UInstancedStaticMeshComponent* NewInstancedStaticMeshComponent)
187{
188 UInstancedStaticMeshComponent* Aux { nullptr };
189 for (std::shared_ptr<FTileMeshComponent>& ElementPtr
191 {
192 FTileMeshComponent& Element = *ElementPtr;
193 if (Element.InstancedStaticMeshComponent == NewInstancedStaticMeshComponent)
194 {
195 int32 CurrentCount = Element.InstancedStaticMeshComponent->GetInstanceCount();
196 int32 NewCount = NewInstancedStaticMeshComponent->GetInstanceCount();
197 Element.bIsAlive = true;
198 if (NewCount > CurrentCount)
199 {
200 Element.InstancedStaticMeshComponent = NewInstancedStaticMeshComponent;
201 Element.IndicesInUse.Empty();
202 }
203 }
204 }
205}
206
207bool FTileData::ContainsMesh(const UInstancedStaticMeshComponent* Mesh) const
208{
209 for (const std::shared_ptr<FTileMeshComponent>& Element
211 {
212 if (Element->InstancedStaticMeshComponent == Mesh)
213 return true;
214 }
215 return false;
216}
217
218void FTileData::UpdateMaterialCache(const FLinearColor& Value, bool DebugMaterials)
219{
220 TRACE_CPUPROFILER_EVENT_SCOPE(FTileData::UpdateMaterialCache);
221 for (UMaterialInstanceDynamic* Material : MaterialInstanceDynamicCache)
222 {
223 if (DebugMaterials)
224 Material->SetScalarParameterValue("ActivateDebug", 1);
225 else
226 Material->SetScalarParameterValue("ActivateDebug", 0);
227 Material->SetScalarParameterValue("ActivateOpacity", 1);
228 Material->SetVectorParameterValue("VehiclePosition", Value);
229 }
230}
231
232/********************************************************************************/
233/********** OVERRIDE FROM ACTOR *************************************************/
234/********************************************************************************/
236{
237 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::BeginPlay);
238 Super::BeginPlay();
240 FWorldDelegates::LevelAddedToWorld.AddUObject(this, &AVegetationManager::OnLevelAddedToWorld);
241 FWorldDelegates::LevelRemovedFromWorld.AddUObject(this, &AVegetationManager::OnLevelRemovedFromWorld);
242 FCoreDelegates::PostWorldOriginOffset.AddUObject(this, &AVegetationManager::PostWorldOriginOffset);
243}
244
245void AVegetationManager::Tick(float DeltaTime)
246{
247 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::Tick);
248 {
249 TRACE_CPUPROFILER_EVENT_SCOPE(Parent Tick);
250 Super::Tick(DeltaTime);
251 }
252 if (!LargeMap)
253 return;
255 if (!IsValid(HeroVehicle))
256 return;
257
259 TArray<FString> TilesInUse = GetTilesInUse();
260 if (TilesInUse.Num() == 0)
261 {
262 UE_LOG(LogCarla, Warning, TEXT("No tiles detected."));
263 return;
264 }
265
266 for (const FString& TileName : TilesInUse)
267 {
268 FTileData* Tile = TileCache.Find(TileName);
269 if (!Tile)
270 continue;
271 UpdateMaterials(Tile);
272 TArray<FElementsToSpawn> ElementsToSpawn = GetElementsToSpawn(Tile);
273 SpawnSkeletalFoliages(ElementsToSpawn);
276 }
277
278}
279
280/********************************************************************************/
281/********** VEHICLE *************************************************************/
282/********************************************************************************/
284{
285 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::AddVehicle);
286 if (!IsValid(Vehicle))
287 return;
289 UE_LOG(LogCarla, Display, TEXT("Vehicle added."));
290}
291
293{
294 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::RemoveVehicle);
295 if (!IsValid(Vehicle))
296 return;
297 HeroVehicle = nullptr;
298 UE_LOG(LogCarla, Display, TEXT("Vehicle removed."));
299}
300
301/********************************************************************************/
302/********** CACHES **************************************************************/
303/********************************************************************************/
305{
306 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::CreateOrUpdateTileCache);
307 FTileData TileData {};
308 for (AActor* Actor : InLevel->Actors)
309 {
310 AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
311 if (!IsValid(InstancedFoliageActor))
312 continue;
313 TileData.InstancedFoliageActor = InstancedFoliageActor;
314 break;
315 }
316 if (!IsValid(TileData.InstancedFoliageActor))
317 return;
318
319 for (AActor* Actor : InLevel->Actors)
320 {
321 AProceduralFoliageVolume* ProceduralFoliageVolume = Cast<AProceduralFoliageVolume>(Actor);
322 if (!IsValid(ProceduralFoliageVolume))
323 continue;
324 TileData.ProceduralFoliageVolume = ProceduralFoliageVolume;
325 break;
326 }
327 if (!IsValid(TileData.ProceduralFoliageVolume))
328 return;
329
330 const FString TileName = TileData.InstancedFoliageActor->GetLevel()->GetOuter()->GetName();
331 FTileData* ExistingTileData = TileCache.Find(TileName);
332 if (ExistingTileData)
333 {
334 ExistingTileData->InstancedFoliageActor = TileData.InstancedFoliageActor;
335 ExistingTileData->ProceduralFoliageVolume = TileData.ProceduralFoliageVolume;
336 ExistingTileData->TileMeshesCache.Empty();
337 ExistingTileData->MaterialInstanceDynamicCache.Empty();
338 SetTileDataInternals(*ExistingTileData);
339 }
340 else
341 {
342 SetTileDataInternals(TileData);
343 TileCache.Emplace(TileName, TileData);
344 }
345}
346
348{
349 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::SetTileDataInternals);
351 SetMaterialCache(TileData);
352}
353
355{
357 const TSet<UActorComponent*>& ActorComponents = TileData.InstancedFoliageActor->GetComponents();
358 for (UActorComponent* Component : ActorComponents)
359 {
360 UInstancedStaticMeshComponent* Mesh = Cast<UInstancedStaticMeshComponent>(Component);
361 if (!IsValid(Mesh))
362 continue;
363 const FString Path = Mesh->GetStaticMesh()->GetPathName();
364 const FFoliageBlueprint* BPCache = FoliageBlueprintCache.Find(Path);
365 if (!BPCache)
366 continue;
367
368 if (TileData.ContainsMesh(Mesh))
369 {
370 TileData.UpdateTileMeshComponent(Mesh);
371 }
372 else
373 {
374 std::shared_ptr<FTileMeshComponent> Aux =
375 std::make_shared<FTileMeshComponent>();
376 Aux->InstancedStaticMeshComponent = Mesh;
377 Aux->bIsAlive = true;
378 TileData.TileMeshesCache.Emplace(Aux);
379 }
380 }
381}
382
384{
385 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::SetMaterialCache);
386 if (TileData.MaterialInstanceDynamicCache.Num() > 0)
387 TileData.MaterialInstanceDynamicCache.Empty();
388
389 for (std::shared_ptr<FTileMeshComponent>& ElementPtr
390 : TileData.TileMeshesCache)
391 {
392 FTileMeshComponent& Element = *ElementPtr;
393 UInstancedStaticMeshComponent* Mesh = Element.InstancedStaticMeshComponent;
394 if (!IsValid(Mesh))
395 continue;
396 int32 Index = -1;
397 for (UMaterialInterface* Material : Mesh->GetMaterials())
398 {
399 ++Index;
400 if (!IsValid(Material))
401 continue;
402 UMaterialInstanceDynamic* MaterialInstanceDynamic = UMaterialInstanceDynamic::Create(Material, this);
403 if (!MaterialInstanceDynamic)
404 continue;
405 if (TileData.MaterialInstanceDynamicCache.Contains(MaterialInstanceDynamic))
406 continue;
407 MaterialInstanceDynamic->SetScalarParameterValue("ActivateOpacity", 0);
408 MaterialInstanceDynamic->SetScalarParameterValue("ActivateDebug", 0);
409 MaterialInstanceDynamic->SetScalarParameterValue("Distance", HideMaterialDistance);
410 Mesh->SetMaterial(Index, MaterialInstanceDynamic);
411 TileData.MaterialInstanceDynamicCache.Emplace(MaterialInstanceDynamic);
412 }
413 }
414}
415
417{
418 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::UpdateFoliageBlueprintCache);
419 for (AActor* Actor : InLevel->Actors)
420 {
421 AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
422 if (!IsValid(InstancedFoliageActor))
423 continue;
424 const FString TileName = InstancedFoliageActor->GetLevel()->GetOuter()->GetName();
425 const TSet<UActorComponent*>& ActorComponents = InstancedFoliageActor->GetComponents();
426 for (UActorComponent* Component : ActorComponents)
427 {
428 UInstancedStaticMeshComponent* Mesh = Cast<UInstancedStaticMeshComponent>(Component);
429 if (!IsValid(Mesh))
430 continue;
431 const FString Path = Mesh->GetStaticMesh()->GetPathName();
432 if (!IsFoliageTypeEnabled(Path))
433 continue;
434 if (FoliageBlueprintCache.Contains(Path))
435 continue;
436 FFoliageBlueprint NewFoliageBlueprint;
437 NewFoliageBlueprint.SetBPClassName(Path);
438 NewFoliageBlueprint.SetSpawnedClass();
439
440 if (!NewFoliageBlueprint.IsValid())
441 {
442 UE_LOG(LogCarla, Warning, TEXT("Blueprint %s was invalid."), *NewFoliageBlueprint.BPFullClassName);
443 }
444 else
445 {
446 UE_LOG(LogCarla, Display, TEXT("Blueprint %s created."), *NewFoliageBlueprint.BPFullClassName);
447 FoliageBlueprintCache.Emplace(Path, NewFoliageBlueprint);
448 CreatePoolForBPClass(NewFoliageBlueprint);
449 }
450 }
451 }
452}
453
455{
456 if (!IsValid(InLevel))
457 return;
458 AInstancedFoliageActor* TileInstancedFoliageActor = nullptr;
459 for (AActor* Actor : InLevel->Actors)
460 {
461 if (!IsValid(Actor))
462 continue;
463 AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
464 if (!IsValid(InstancedFoliageActor))
465 continue;
466 TileInstancedFoliageActor = InstancedFoliageActor;
467 break;
468 }
469 if (!IsValid(TileInstancedFoliageActor))
470 return;
471 const FString TileName = TileInstancedFoliageActor->GetLevel()->GetOuter()->GetName();
472 FTileData* ExistingTileData = TileCache.Find(TileName);
473 if (ExistingTileData)
474 {
475
476 ExistingTileData->MaterialInstanceDynamicCache.Empty();
477 for (std::shared_ptr<FTileMeshComponent>& Element
478 : ExistingTileData->TileMeshesCache)
479 {
480 Element->IndicesInUse.Empty();
481 Element->bIsAlive = false;
482 }
483 ExistingTileData->TileMeshesCache.Empty();
484 ExistingTileData->InstancedFoliageActor = nullptr;
485 ExistingTileData->ProceduralFoliageVolume = nullptr;
486
487 TileCache.Remove(TileName);
488 }
489}
490
491/********************************************************************************/
492/********** TICK ****************************************************************/
493/********************************************************************************/
495{
496 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::UpdateMaterials);
497 auto GlobalTransform = HeroVehicle->GetActorTransform();
498 auto Position = GlobalTransform.GetLocation();
499 Tile->UpdateMaterialCache(FLinearColor(Position), DebugMaterials);
500}
501
503{
504 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::GetElementsToSpawn);
505 TArray<FElementsToSpawn> Results;
506 int32 i = -1;
507
508 for (std::shared_ptr<FTileMeshComponent>& ElementPtr
509 : Tile->TileMeshesCache)
510 {
511 FTileMeshComponent& Element = *ElementPtr;
512 TRACE_CPUPROFILER_EVENT_SCOPE(Update Foliage Usage);
513 ++i;
514 UInstancedStaticMeshComponent* InstancedStaticMeshComponent = Element.InstancedStaticMeshComponent;
515 const FString Path = InstancedStaticMeshComponent->GetStaticMesh()->GetPathName();
517 if (!BP)
518 continue;
519 TArray<int32> Indices = HeroVehicle->GetFoliageInstancesCloseToVehicle(InstancedStaticMeshComponent);
520 if (Indices.Num() == 0)
521 continue;
522 TArray<int32> NewIndices;
523 for (int32 Index : Indices)
524 {
525 if (Element.IndicesInUse.Contains(Index))
526 {
527 continue;
528 }
529 NewIndices.Emplace(Index);
530 }
531
532 FElementsToSpawn NewElement {};
533 NewElement.TileMeshComponent = Tile->TileMeshesCache[i];
534 NewElement.BP = *BP;
535 for (int32 Index : NewIndices)
536 {
537 FTransform Transform;
538 InstancedStaticMeshComponent->GetInstanceTransform(Index, Transform, true);
539 NewElement.TransformIndex.Emplace(TPair<FTransform, int32>(Transform, Index));
540 }
541 if (NewElement.TransformIndex.Num() > 0)
542 Results.Emplace(NewElement);
543 }
544 return Results;
545}
546
547void AVegetationManager::SpawnSkeletalFoliages(TArray<FElementsToSpawn>& ElementsToSpawn)
548{
549 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::SpawnSkeletalFoliages);
550 const FTransform HeroTransform = HeroVehicle->GetActorTransform();
551 const FVector HeroLocation = HeroTransform.GetLocation();
552 const float HeroDetectionSizeSquared = HeroVehicle->GetDetectionSize() * HeroVehicle->GetDetectionSize();
553
554 for (FElementsToSpawn& Element : ElementsToSpawn)
555 {
556 TArray<FPooledActor>* Pool = ActorPool.Find(Element.BP.BPFullClassName);
557
558 if (Pool == nullptr)
559 {
560 UE_LOG(LogCarla, Error, TEXT("Pool not valid"));
561 continue;
562 }
563 for (const TPair<FTransform, int32>& TransformIndex : Element.TransformIndex)
564 {
565 const FTransform& Transform = TransformIndex.Key;
566 int32 Index = TransformIndex.Value;
567 if (Element.TileMeshComponent->IndicesInUse.Contains(Index))
568 {
569 continue;
570 }
571 const float Distance = FMath::Abs(FVector::DistSquared(Transform.GetLocation(), HeroLocation));
572 if (Distance > HeroDetectionSizeSquared)
573 {
574 continue;
575 }
576 bool Ok = EnableActorFromPool(Transform, Index, Element.TileMeshComponent, *Pool);
577 if (Ok)
578 {
579 }
580 else
581 {
582 FPooledActor NewElement;
583 NewElement.Actor = CreateFoliage(Element.BP, {});
584 if (IsValid(NewElement.Actor))
585 {
586 NewElement.Actor->SetTickableWhenPaused(false);
587 NewElement.EnableActor(Transform, Index, Element.TileMeshComponent);
588 Pool->Emplace(NewElement);
589 }
590 }
591 }
592 }
593}
594
596{
597 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::ActivePooledActors);
598 const FTransform HeroTransform = HeroVehicle->GetActorTransform();
599 const FVector HeroLocation = HeroTransform.GetLocation();
600 const float SquaredActiveActorDistance = ActiveActorDistance * ActiveActorDistance;
601
602 for (TPair<FString, TArray<FPooledActor>>& Element : ActorPool)
603 {
604 TArray<FPooledActor>& Pool = Element.Value;
605 for (FPooledActor& Actor : Pool)
606 {
607 if (!Actor.InUse)
608 continue;
609 if (Actor.IsActive)
610 continue;
611 const FVector Location = Actor.GlobalTransform.GetLocation();
612 const float Distance = FMath::Abs(FVector::DistSquared(Location, HeroLocation));
613 if (Distance < SquaredActiveActorDistance)
614 {
615 Actor.ActiveActor();
616 }
617 }
618 }
619}
620
622{
623 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::DestroySkeletalFoliages);
624 const FTransform HeroTransform = HeroVehicle->GetActorTransform();
625 const FVector HeroLocation = HeroTransform.GetLocation();
626 const float HeroDetectionSizeSquared = HeroVehicle->GetDetectionSize() * HeroVehicle->GetDetectionSize();
627
628 for (TPair<FString, TArray<FPooledActor>>& Element : ActorPool)
629 {
630 TArray<FPooledActor>& Pool = Element.Value;
631 for (FPooledActor& Actor : Pool)
632 {
633 if (!Actor.InUse)
634 continue;
635 const FVector Location = Actor.GlobalTransform.GetLocation();
636 const float Distance = FMath::Abs(FVector::DistSquared(Location, HeroLocation));
637 if (Distance > HeroDetectionSizeSquared)
638 {
639 Actor.DisableActor();
640 }
641 }
642 }
643}
644
646 const FTransform& Transform,
647 int32 Index,
648 std::shared_ptr<FTileMeshComponent>& TileMeshComponent,
649 TArray<FPooledActor>& Pool)
650{
651 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::EnableActorFromPool);
652 for (FPooledActor& PooledActor : Pool)
653 {
654 if (PooledActor.InUse)
655 continue;
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());
660 else
661 PooledActor.Actor->SetActorScale3D({SpawnScale, SpawnScale, SpawnScale});
662 return true;
663 }
664 return false;
665}
666
667/********************************************************************************/
668/********** POOLS ***************************************************************/
669/********************************************************************************/
671{
672 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::CreatePoolForBPClass);
673 TArray<FPooledActor> AuxPool;
674 const FTransform Transform {};
675 for (int32 i = 0; i < InitialPoolSize; ++i)
676 {
677 FPooledActor NewElement;
678 NewElement.Actor = CreateFoliage(BP, Transform);
679 if (IsValid(NewElement.Actor))
680 {
681 UE_LOG(LogCarla, Display, TEXT("Created actor for pool"));
682 NewElement.Actor->SetTickableWhenPaused(false);
683 NewElement.DisableActor();
684 AuxPool.Emplace(NewElement);
685 }
686 else
687 {
688 UE_LOG(LogCarla, Error, TEXT("Failed to create actor for pool"));
689 }
690 }
691 ActorPool.Emplace(BP.BPFullClassName, AuxPool);
692 UE_LOG(LogCarla, Display, TEXT("CreatePoolForBPClass: %s"), *BP.BPFullClassName);
693}
694
695AActor* AVegetationManager::CreateFoliage(const FFoliageBlueprint& BP, const FTransform& Transform) const
696{
697 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::CreateFoliage);
698
699 AActor* Actor = GetWorld()->SpawnActor<AActor>(BP.SpawnedClass,
700 Transform.GetLocation(), Transform.Rotator());
701
702 if (SpawnScale <= 1.01f && SpawnScale >= 0.99f)
703 Actor->SetActorScale3D(Transform.GetScale3D());
704 else
705 Actor->SetActorScale3D({SpawnScale, SpawnScale, SpawnScale});
706 return Actor;
707}
708
710{
711 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::UpdatePoolBasePosition);
712 for (TPair<FString, TArray<FPooledActor>>& Element : ActorPool)
713 {
714 TArray<FPooledActor>& Pool = Element.Value;
715 for (FPooledActor& PooledActor : Pool)
716 {
717 if (PooledActor.InUse)
718 continue;
719 PooledActor.Actor->SetActorTransform(InactivePoolTransform, true, nullptr, ETeleportType::ResetPhysics);
720 }
721 }
722}
723
724/********************************************************************************/
725/********** EVENTS **************************************************************/
726/********************************************************************************/
727void AVegetationManager::OnLevelAddedToWorld(ULevel* InLevel, UWorld* InWorld)
728{
729 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::OnLevelAddedToWorld);
732}
733
734void AVegetationManager::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld)
735{
736 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::OnLevelRemovedFromWorld);
737 FreeTileCache(InLevel);
738}
739
740void AVegetationManager::PostWorldOriginOffset(UWorld*, FIntVector, FIntVector)
741{
742 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::PostWorldOriginOffset);
743 InactivePoolTransform.SetLocation(FVector(0.0f, 0.0f, 0.0f));
745}
746
747/********************************************************************************/
748/********** TILES ***************************************************************/
749/********************************************************************************/
750bool AVegetationManager::IsFoliageTypeEnabled(const FString& Path) const
751{
752 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::IsFoliageTypeEnabled);
753 if (!SpawnRocks)
754 if (Path.Contains("/Rock"))
755 return false;
756 if (!SpawnTrees)
757 if (Path.Contains("/Tree"))
758 return false;
759 if (!SpawnBushes)
760 if (Path.Contains("/Bush"))
761 return false;
762 if (!SpawnPlants)
763 if (Path.Contains("/Plant"))
764 return false;
765 return true;
766}
767
769{
770 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::CheckForNewTiles);
771 const UObject* World = GetWorld();
772 TArray<AActor*> ActorsInLevel;
773 UGameplayStatics::GetAllActorsOfClass(World, AInstancedFoliageActor::StaticClass(), ActorsInLevel);
774 for (AActor* Actor : ActorsInLevel)
775 {
776 AInstancedFoliageActor* InstancedFoliageActor = Cast<AInstancedFoliageActor>(Actor);
777 if (!IsValid(InstancedFoliageActor))
778 continue;
779 const FString TileName = InstancedFoliageActor->GetLevel()->GetOuter()->GetName();
780 if (!TileCache.Contains(TileName))
781 return true;
782 }
783 return false;
784}
785
787{
788 TRACE_CPUPROFILER_EVENT_SCOPE(AVegetationManager::GetTilesInUse);
789 TArray<FString> Results;
790
791 for (const TPair<FString, FTileData>& Element : TileCache)
792 {
793 const FTileData& TileData = Element.Value;
795 {
796 TileCache.Remove(Element.Key);
797 return Results;
798 }
799 if (Results.Contains(Element.Key))
800 continue;
801 const AProceduralFoliageVolume* Procedural = TileData.ProceduralFoliageVolume;
802 if (!IsValid(Procedural))
803 continue;
804 if (!IsValid(Procedural->ProceduralComponent))
805 continue;
806 const FBox Box = Procedural->ProceduralComponent->GetBounds();
807 if (!Box.IsValid)
808 continue;
809 if (Box.IsInside(HeroVehicle->GetActorTransform().GetLocation()))
810 Results.Emplace(Element.Key);
811 }
812 return Results;
813}
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
ACarlaWheeledVehicle * GetHeroVehicle()
TMap< FString, FTileData > TileCache
FTransform InactivePoolTransform
AActor * CreateFoliage(const FFoliageBlueprint &BP, const FTransform &Transform) const
void SpawnSkeletalFoliages(TArray< FElementsToSpawn > &ElementsToSpawn)
ACarlaWheeledVehicle * HeroVehicle
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 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)
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