diff --git a/CHANGELOG.md b/CHANGELOG.md index a361dd37a..3a9a28109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## Latest Changes - * Prevent from segfault on failing SignalReference identification when loading OpenDrive files + * Prevent from segfault on failing SignalReference identification when loading OpenDrive files * Added vehicle doors to the recorder + * Creating SensorSpawnerActor to spawn custom sensors in the editor. ## CARLA 0.9.15 diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameInstance.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameInstance.cpp index e522d3686..3ced77070 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameInstance.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameInstance.cpp @@ -19,4 +19,4 @@ UCarlaGameInstance::UCarlaGameInstance() { CarlaSettings->LogSettings(); } -UCarlaGameInstance::~UCarlaGameInstance() = default; +UCarlaGameInstance::~UCarlaGameInstance() = default; \ No newline at end of file diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameInstance.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameInstance.h index 20c36be84..da78b8220 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameInstance.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameInstance.h @@ -130,5 +130,5 @@ private: UPROPERTY() FString _MapPath; - -}; + +}; \ No newline at end of file diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.cpp index ca2e81601..bc2eb3b10 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.cpp +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.cpp @@ -204,6 +204,7 @@ void ACarlaGameModeBase::BeginPlay() Episode->InitializeAtBeginPlay(); GameInstance->NotifyBeginEpisode(*Episode); + OnEpisodeInitialisedDelegate.Broadcast(Episode); if (Episode->Weather != nullptr) { diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.h index 9fa4797c5..575966222 100644 --- a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.h +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Game/CarlaGameModeBase.h @@ -30,6 +30,8 @@ #include "CarlaGameModeBase.generated.h" +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnEpisodeInitialisedDelegate, UCarlaEpisode*, InitialisedEpisode); + /// Base class for the CARLA Game Mode. UCLASS(HideCategories=(ActorTick)) class CARLA_API ACarlaGameModeBase : public AGameModeBase @@ -111,6 +113,8 @@ public: const carla::rpc::MaterialParameter& TextureParam); TArray GetNamesOfAllActors(); + + FOnEpisodeInitialisedDelegate OnEpisodeInitialisedDelegate; protected: diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/SensorSpawnerActor.cpp b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/SensorSpawnerActor.cpp new file mode 100644 index 000000000..d968b5d8c --- /dev/null +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/SensorSpawnerActor.cpp @@ -0,0 +1,147 @@ +// Copyright (c) 2017 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 . + + +#include "Util/SensorSpawnerActor.h" +#include "Carla/Game/CarlaEpisode.h" +#include "Game/CarlaGameModeBase.h" +#include "Sensor/Sensor.h" + +DEFINE_LOG_CATEGORY_STATIC(LogSensorSpawnerActor, Verbose, All); + +ASensorSpawnerActor::ASensorSpawnerActor() +{ + PrimaryActorTick.bCanEverTick = false; + + SceneComp = CreateDefaultSubobject(TEXT("SceneComp")); + RootComponent = SceneComp; +} + +void ASensorSpawnerActor::BeginPlay() +{ + Super::BeginPlay(); + + // Wait for the CarlaEpisode initialisation. It is done on CarlaGameMode BeginPlay(). + if(ACarlaGameModeBase* CarlaGameMode = Cast(UGameplayStatics::GetGameMode(GetWorld()))) + { + CarlaGameMode->OnEpisodeInitialisedDelegate.AddDynamic(this, &ASensorSpawnerActor::OnEpisodeInitialised); + } +} + +void ASensorSpawnerActor::OnEpisodeInitialised(UCarlaEpisode* InitialisedEpisode) +{ + if(IsValid(InitialisedEpisode)) + { + CarlaEpisode = InitialisedEpisode; + + // Spawn cameras with initial delay if set. + GetWorldTimerManager().SetTimer(InitialDelaySpawnTimerHandle, this, &ASensorSpawnerActor::SpawnSensors, InitialDelay); + } +} + +void ASensorSpawnerActor::SpawnSensors() +{ + // Check if we are doing a delayed spawn. If so, don't do nothing. + if(!SensorsToSpawnCopy.IsEmpty()) + { + UE_LOG(LogSensorSpawnerActor, Warning, TEXT("Warning: ASensorSpawnerActor::SpawnSensors - Delayed spawn already in progress, wait until it ends")); + return; + } + + if(DelayBetweenSpawns > 0.f) + { + SensorsToSpawnCopy = SensorsToSpawn; + GetWorldTimerManager().SetTimer(SpawnSensorsDelayedTimerHandle, this, &ASensorSpawnerActor::SpawnSensorsDelayed, DelayBetweenSpawns, true); + return; + } + + for(const auto& SensorStruct : SensorsToSpawn) + { + if(const FActorDefinition* SensorDefinition = GetActorDefinitionByClass(SensorStruct.SensorClass)) + { + FActorDescription SensorDescription; + GenerateSensorActorDescription(SensorDefinition, SensorDescription); + + for(int i = 0; i < SensorStruct.Amount; i++) + { + SpawnSensorActor(SensorDescription); + } + } + } +} + +const FActorDefinition* ASensorSpawnerActor::GetActorDefinitionByClass(const TSubclassOf ActorClass) const +{ + if(!ActorClass || !IsValid(CarlaEpisode)) + { + return nullptr; + } + + const TArray& ActorDefinitions = CarlaEpisode->GetActorDefinitions(); + // Checks that the class is exactly the same. If we want to allow also child classes use: ActorDef.Class->IsChildOf(ActorClass) + const FActorDefinition* ActorDefinition = ActorDefinitions.FindByPredicate([&](const FActorDefinition& ActorDef){ return ActorDef.Class == ActorClass; }); + + return ActorDefinition; +} + +void ASensorSpawnerActor::SpawnSensorActor(const FActorDescription& SensorDescription) const +{ + if(IsValid(CarlaEpisode)) + { + FTransform Transform; + GetRandomTransform(Transform); + + CarlaEpisode->SpawnActorWithInfo(Transform, SensorDescription); + } +} + +void ASensorSpawnerActor::GenerateSensorActorDescription(const FActorDefinition* Definition, FActorDescription& SensorDescription) const +{ + SensorDescription.UId = Definition->UId; + SensorDescription.Id = Definition->Id; + SensorDescription.Class = Definition->Class; + SensorDescription.Variations.Reserve(Definition->Variations.Num()); + + FActorAttribute CreatedAttribute; + for(const FActorVariation& Variation : Definition->Variations) + { + if(Variation.RecommendedValues.IsValidIndex(0)) + { + CreatedAttribute.Id = Variation.Id; + CreatedAttribute.Type = Variation.Type; + CreatedAttribute.Value = Variation.RecommendedValues[0]; + SensorDescription.Variations.Emplace(CreatedAttribute.Id, CreatedAttribute); + } + } +} + +void ASensorSpawnerActor::GetRandomTransform(FTransform &Transform) const +{ + Transform = FTransform::Identity; + const float PosX = FMath::FRandRange(MinSpawnLocation.X, MaxSpawnLocation.X); + const float PosY = FMath::FRandRange(MinSpawnLocation.Y, MaxSpawnLocation.Y); + const float PosZ = FMath::FRandRange(MinSpawnLocation.Z, MaxSpawnLocation.Z); + Transform.SetLocation(FVector(PosX, PosY, PosZ)); +} + +void ASensorSpawnerActor::SpawnSensorsDelayed() +{ + if(SensorsToSpawnCopy.IsEmpty()) + { + GetWorldTimerManager().ClearTimer(SpawnSensorsDelayedTimerHandle); + return; + } + + if(const FActorDefinition* SensorDefinition = GetActorDefinitionByClass(SensorsToSpawnCopy[0].SensorClass)) + { + FActorDescription SensorDescription; + GenerateSensorActorDescription(SensorDefinition, SensorDescription); + SpawnSensorActor(SensorDescription); + } + + SensorsToSpawnCopy[0].Amount--; + + if(SensorsToSpawnCopy[0].Amount <= 0) + { + SensorsToSpawnCopy.RemoveAt(0); + } +} \ No newline at end of file diff --git a/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/SensorSpawnerActor.h b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/SensorSpawnerActor.h new file mode 100644 index 000000000..cb3315f5c --- /dev/null +++ b/Unreal/CarlaUE4/Plugins/Carla/Source/Carla/Util/SensorSpawnerActor.h @@ -0,0 +1,97 @@ +// Copyright (c) 2017 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 . + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "SensorSpawnerActor.generated.h" + +class ASensor; +class USceneComponent; +class UCarlaEpisode; + + +USTRUCT(BlueprintType) +struct FSensorTuple +{ + GENERATED_BODY() + + // sensor class to spawn. + UPROPERTY(EditAnywhere, BlueprintReadOnly) + TSubclassOf SensorClass; + + // Number of sensors to spawn of the SensorClass. + UPROPERTY(EditAnywhere, BlueprintReadOnly) + int Amount = 1; +}; + + +UCLASS(Blueprintable) +class CARLA_API ASensorSpawnerActor : public AActor +{ + GENERATED_BODY() + +public: + ASensorSpawnerActor(); + + // Called OnBeginPlay(). + UFUNCTION(BlueprintCallable) + void SpawnSensors(); + + +protected: + // Called when the game starts or when spawned. + virtual void BeginPlay() override; + + // Root + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Components") + USceneComponent* SceneComp; + + // Array with sensors to spawn + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Config") + TArray SensorsToSpawn; + + // Initial delay until the sensors start spawning. + UPROPERTY(EditAnywhere, BlueprintReadOnly, meta=(ClampMin=0.f, ClampMax=40.f), Category="Config") + float InitialDelay = 6.f; + + // Delay between spawns. Set to 0 for no delay. + UPROPERTY(EditAnywhere, BlueprintReadOnly, meta=(ClampMin=0.f, ClampMax=40.f), Category="Config") + float DelayBetweenSpawns = 0.f; + + // Max spawn location of the sensor. Spawn at random location between MaxSpawnLocation and MinSpawnLocation. + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Config") + FVector MaxSpawnLocation {-110000.f, -100000.f, 9600.f}; + + // Min spawn location of the sensor. Spawn at random location between MaxSpawnLocation and MinSpawnLocation. + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Config") + FVector MinSpawnLocation {-120000.f, -110000.f, 9300.f}; + + +private: + UFUNCTION() + void OnEpisodeInitialised(UCarlaEpisode* InitialisedEpisode); + + UFUNCTION() + void SpawnSensorsDelayed(); + + void GenerateSensorActorDescription(const FActorDefinition* Definition, FActorDescription& SensorDescription) const; + + // Gets a transform with a random location between MaxSpawnLocation and MinSpawnLocation. + void GetRandomTransform(FTransform &Transform) const; + + void SpawnSensorActor(const FActorDescription& SensorDescription) const; + + const FActorDefinition* GetActorDefinitionByClass(const TSubclassOf ActorClass) const; + + FTimerHandle InitialDelaySpawnTimerHandle; + + UPROPERTY() + UCarlaEpisode* CarlaEpisode; + + // Used for delayed spawn + FTimerHandle SpawnSensorsDelayedTimerHandle; + + // Used for delayed spawn + TArray SensorsToSpawnCopy; +};