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