From ac667205868bd41d339baab375c5bb25d00f7d86 Mon Sep 17 00:00:00 2001 From: Jorge Virgos <164354042+jorge-kabuto@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:36:26 +0100 Subject: [PATCH] Refactoring Carla Actor Factories to be Programmable (#8761) * Initial push for programmable Prop Factory This extends the design of Vehicle Factories to the Prop Factory. Final JSON generation still needs to be implemented Mesh loading needs to be implemented Planning to templatize functions at the FActorFactory class level to avoid code repetition and easier implementation of future factories * Walker, Prop and Blueprint Factories refactored to be programmable * Cleanup and visibility changes for programmable factories --- .../Actor/ActorBlueprintFunctionLibrary.cpp | 25 ++ .../Actor/ActorBlueprintFunctionLibrary.h | 12 + .../Source/Carla/Actor/BlueprintParameters.h | 22 ++ .../Actor/Factory/BlueprintActorFactory.cpp | 174 +++++++++++ .../Actor/Factory/BlueprintActorFactory.h | 52 ++++ .../Carla/Actor/Factory/PropActorFactory.cpp | 229 ++++++++++++++ .../Carla/Actor/Factory/PropActorFactory.h | 52 ++++ .../Actor/Factory/VehicleActorFactory.cpp | 6 +- .../Carla/Actor/Factory/VehicleActorFactory.h | 4 +- .../Actor/Factory/WalkerActorFactory.cpp | 290 ++++++++++++++++++ .../Carla/Actor/Factory/WalkerActorFactory.h | 52 ++++ 11 files changed, 915 insertions(+), 3 deletions(-) create mode 100644 Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/BlueprintParameters.h create mode 100644 Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/BlueprintActorFactory.cpp create mode 100644 Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/BlueprintActorFactory.h create mode 100644 Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/PropActorFactory.cpp create mode 100644 Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/PropActorFactory.h create mode 100644 Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/WalkerActorFactory.cpp create mode 100644 Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/WalkerActorFactory.h diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/ActorBlueprintFunctionLibrary.cpp b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/ActorBlueprintFunctionLibrary.cpp index 3e9956823..62b7aefee 100644 --- a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/ActorBlueprintFunctionLibrary.cpp +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/ActorBlueprintFunctionLibrary.cpp @@ -1416,6 +1416,31 @@ void UActorBlueprintFunctionLibrary::MakePropDefinitions( FillActorDefinitionArray(ParameterArray, Definitions, &MakePropDefinition); } +void UActorBlueprintFunctionLibrary::MakeBlueprintDefinition( + const FBlueprintParameters &Parameters, + bool &Success, + FActorDefinition &Definition) +{ + FillIdAndTags(Definition, TEXT("blueprint"), Parameters.Name); + AddRecommendedValuesForActorRoleName(Definition, {TEXT("blueprint")}); + + // Definition.Attributes.Emplace(FActorAttribute{ + // EActorAttributeType::String, + // Parameters.ObjectType}); + + + Success = CheckActorDefinition(Definition); + +} + +void UActorBlueprintFunctionLibrary::MakeBlueprintDefinitions( + const TArray &ParameterArray, + TArray &Definitions) +{ + FillActorDefinitionArray(ParameterArray, Definitions, &MakeBlueprintDefinition); +} + + void UActorBlueprintFunctionLibrary::MakeObstacleDetectorDefinitions( const FString &Type, const FString &Id, diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/ActorBlueprintFunctionLibrary.h b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/ActorBlueprintFunctionLibrary.h index f40e728a5..566728cca 100644 --- a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/ActorBlueprintFunctionLibrary.h +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/ActorBlueprintFunctionLibrary.h @@ -10,6 +10,7 @@ #include "Carla/Actor/PedestrianParameters.h" #include "Carla/Actor/PropParameters.h" #include "Carla/Actor/VehicleParameters.h" +#include "Carla/Actor/BlueprintParameters.h" #include #include "Kismet/BlueprintFunctionLibrary.h" @@ -154,6 +155,17 @@ public: const TArray &ParameterArray, TArray &Definitions); + UFUNCTION(Category = "Carla Actor", BlueprintCallable) + static void MakeBlueprintDefinition( + const FBlueprintParameters &Parameters, + bool &Success, + FActorDefinition &Definition); + + UFUNCTION(Category = "Carla Actor", BlueprintCallable) + static void MakeBlueprintDefinitions( + const TArray &ParameterArray, + TArray &Definitions); + UFUNCTION() static void MakeObstacleDetectorDefinitions( const FString &Type, diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/BlueprintParameters.h b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/BlueprintParameters.h new file mode 100644 index 000000000..68bc39da4 --- /dev/null +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/BlueprintParameters.h @@ -0,0 +1,22 @@ +// Copyright (c) 2025 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 "BlueprintParameters.generated.h" + +USTRUCT(BlueprintType) +struct CARLA_API FBlueprintParameters +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FString Name; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FString Path; + +}; diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/BlueprintActorFactory.cpp b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/BlueprintActorFactory.cpp new file mode 100644 index 000000000..fda790891 --- /dev/null +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/BlueprintActorFactory.cpp @@ -0,0 +1,174 @@ +// Copyright (c) 2025 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 "Carla/Actor/Factory/BlueprintActorFactory.h" +#include "Carla/Actor/ActorBlueprintFunctionLibrary.h" +#include "Carla/Actor/ActorDefinition.h" +#include "Carla/Game/CarlaEpisode.h" + +#include +#include "Json.h" +#include "JsonUtilities.h" +#include "Misc/FileHelper.h" +#include "Misc/Paths.h" +#include + +TArray ABlueprintActorFactory::GetDefinitions() +{ + LoadBlueprintParametersArrayFromFile("BlueprintParameters.json", BlueprintsParams); + + UActorBlueprintFunctionLibrary::MakeBlueprintDefinitions(BlueprintsParams, Definitions); + return Definitions; +} + +FActorSpawnResult ABlueprintActorFactory::SpawnActor( + const FTransform &SpawnAtTransform, + const FActorDescription &ActorDescription) +{ + FActorSpawnResult SpawnResult; + if(!IsValid(ActorDescription.Class)) + { + UE_LOG(LogCarla, Error, TEXT("Actor Description Class is null.")); + SpawnResult.Status = EActorSpawnResultStatus::InvalidDescription; + return SpawnResult; + } + + AActor* SpawnedActor = GetWorld()->SpawnActor(ActorDescription.Class, SpawnAtTransform); + SpawnResult.Actor = SpawnedActor; + + if(SpawnedActor == nullptr) + { + SpawnResult.Status = EActorSpawnResultStatus::Collision; + return SpawnResult; + } + + if(PostProcessBlueprint(SpawnedActor, ActorDescription)) + { + SpawnResult.Status = EActorSpawnResultStatus::Success; + return SpawnResult; + } + + SpawnResult.Status = EActorSpawnResultStatus::UnknownError; + return SpawnResult; +} + +TSharedPtr ABlueprintActorFactory::FBlueprintParametersToJsonObject(const FBlueprintParameters& BlueprintParams) +{ + TSharedPtr JsonObject = MakeShareable(new FJsonObject); + JsonObject->SetStringField(TEXT("Name"), BlueprintParams.Name); + JsonObject->SetStringField(TEXT("Path"), BlueprintParams.Path); + + return JsonObject; +} + +FString ABlueprintActorFactory::FBlueprintParametersArrayToJson(const TArray& BlueprintParamsArray) +{ + TArray> JsonArray; + + for (const FBlueprintParameters& BlueprintParams : BlueprintParamsArray) + { + // Convert each FBlueprintParameters to a JSON object + TSharedPtr JsonObject = FBlueprintParametersToJsonObject(BlueprintParams); + JsonArray.Add(MakeShareable(new FJsonValueObject(JsonObject))); + } + + // Convert the array of JSON objects into a single JSON object + TSharedRef RootObject = MakeShareable(new FJsonObject); + RootObject->SetArrayField(TEXT("Blueprints"), JsonArray); + + // Serialize the JSON object into an FString + FString OutputString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); + FJsonSerializer::Serialize(RootObject, Writer); + + return OutputString; +} + +void ABlueprintActorFactory::SaveBlueprintParametersArrayToFile(const TArray& BlueprintParametersArray, const FString& FileName) +{ + FString FilePath = FPaths::ProjectContentDir() + TEXT("Carla/Config/") + FileName; + // Convert the array to an FString in JSON format + FString JsonContent = FBlueprintParametersArrayToJson(BlueprintParametersArray); + + // Save the JSON to a file + if (FFileHelper::SaveStringToFile(JsonContent, *FilePath)) + { + UE_LOG(LogCarla, Log, TEXT("JSON file successfully saved at: %s"), *FilePath); + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to save JSON file at: %s"), *FilePath); + } +} + +bool ABlueprintActorFactory::JsonToFBlueprintParameters(const TSharedPtr JsonObject, FBlueprintParameters& OutBlueprintParams) +{ + if (!JsonObject.IsValid()) return false; + + JsonObject->TryGetStringField(TEXT("Name"), OutBlueprintParams.Name); + JsonObject->TryGetStringField(TEXT("Path"), OutBlueprintParams.Path); + + return true; +} + +bool ABlueprintActorFactory::JsonToFBlueprintParametersArray(const FString& JsonString, TArray& OutBlueprintParamsArray) +{ + TSharedPtr RootObject; + TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonString); + + if (!FJsonSerializer::Deserialize(Reader, RootObject) || !RootObject.IsValid()) + { + UE_LOG(LogCarla, Error, TEXT("Failed to parse JSON.")); + return false; + } + + // Get the "Blueprints" array from the JSON root object + const TArray>* BlueprintsArray; + if (RootObject->TryGetArrayField(TEXT("Blueprints"), BlueprintsArray)) + { + OutBlueprintParamsArray.Empty(); + for (const TSharedPtr& BlueprintValue : *BlueprintsArray) + { + TSharedPtr BlueprintObject = BlueprintValue->AsObject(); + if (BlueprintObject.IsValid()) + { + FBlueprintParameters BlueprintParams; + if (JsonToFBlueprintParameters(BlueprintObject, BlueprintParams)) + { + OutBlueprintParamsArray.Add(BlueprintParams); + } + } + } + } + + return true; +} + +void ABlueprintActorFactory::LoadBlueprintParametersArrayFromFile(const FString& FileName, TArray& OutBlueprintParamsArray) +{ + FString JsonString; + FString FilePath = FPaths::ProjectContentDir() + TEXT("Carla/Config/") + FileName; + FString JsonContent; + + // Load the JSON file content into an FString + if (FFileHelper::LoadFileToString(JsonContent, *FilePath)) + { + // Parse the JSON and populate the TArray + if (JsonToFBlueprintParametersArray(JsonContent, OutBlueprintParamsArray)) + { + UE_LOG(LogCarla, Log, TEXT("Blueprint parameters loaded successfully from %s"), *FilePath); + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to parse Blueprint parameters from %s"), *FilePath); + } + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to load file: %s"), *FilePath); + } +} \ No newline at end of file diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/BlueprintActorFactory.h b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/BlueprintActorFactory.h new file mode 100644 index 000000000..14adbc6c1 --- /dev/null +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/BlueprintActorFactory.h @@ -0,0 +1,52 @@ +// Copyright (c) 2025 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 "Carla/Actor/ActorSpawnResult.h" +#include "Carla/Actor/CarlaActorFactory.h" +#include "Carla/Actor/BlueprintParameters.h" + +#include +#include "Json.h" +#include "JsonUtilities.h" +#include + +#include "BlueprintActorFactory.generated.h" + +UCLASS() +class CARLA_API ABlueprintActorFactory : public ACarlaActorFactory +{ + GENERATED_BODY() + + /// Retrieve the definitions of the static mesh actor + virtual TArray GetDefinitions() override; + + virtual FActorSpawnResult SpawnActor( + const FTransform &SpawnAtTransform, + const FActorDescription &ActorDescription) override; + +public: + UFUNCTION(BlueprintCallable, Category = "BlueprintActorFactory") + static void SaveBlueprintParametersArrayToFile(const TArray &BlueprintParamsArray, const FString &FileName); + UFUNCTION(BlueprintCallable, Category = "BlueprintActorFactory") + static void LoadBlueprintParametersArrayFromFile(const FString &FileName, TArray &OutBlueprintParamsArray); + + UFUNCTION(BlueprintImplementableEvent, Category = "BlueprintActorFactory") + bool PostProcessBlueprint(AActor* SpawnedActor, const FActorDescription& BlueprintParams); + +private: + static TSharedPtr FBlueprintParametersToJsonObject(const FBlueprintParameters& BlueprintParams); + static FString FBlueprintParametersArrayToJson(const TArray& BlueprintParamsArray); + static bool JsonToFBlueprintParameters(const TSharedPtr JsonObject, FBlueprintParameters& OutBlueprintParams); + static bool JsonToFBlueprintParametersArray(const FString& JsonString, TArray& OutBlueprintParamsArray); + +protected: + UPROPERTY(EditAnywhere) + TArray Definitions; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray BlueprintsParams; +}; \ No newline at end of file diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/PropActorFactory.cpp b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/PropActorFactory.cpp new file mode 100644 index 000000000..9fe60d934 --- /dev/null +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/PropActorFactory.cpp @@ -0,0 +1,229 @@ +// Copyright (c) 2025 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 "Carla/Actor/Factory/PropActorFactory.h" +#include "Carla/Actor/ActorBlueprintFunctionLibrary.h" +#include "Carla/Actor/ActorDefinition.h" +#include "Carla/Game/CarlaEpisode.h" + +#include +#include "Json.h" +#include "JsonUtilities.h" +#include "Misc/FileHelper.h" +#include "Misc/Paths.h" +#include + +TArray APropActorFactory::GetDefinitions() +{ + LoadPropParametersArrayFromFile("PropParameters.json", PropsParams); + + UActorBlueprintFunctionLibrary::MakePropDefinitions(PropsParams, Definitions); + return Definitions; +} + +FActorSpawnResult APropActorFactory::SpawnActor( + const FTransform &SpawnAtTransform, + const FActorDescription &ActorDescription) +{ + FActorSpawnResult SpawnResult; + if(!IsValid(ActorDescription.Class)) + { + UE_LOG(LogCarla, Error, TEXT("Actor Description Class is null.")); + SpawnResult.Status = EActorSpawnResultStatus::InvalidDescription; + return SpawnResult; + } + + AActor* SpawnedActor = GetWorld()->SpawnActor(ActorDescription.Class, SpawnAtTransform); + SpawnResult.Actor = SpawnedActor; + + if(SpawnedActor == nullptr) + { + SpawnResult.Status = EActorSpawnResultStatus::Collision; + return SpawnResult; + } + + if(PostProcessProp(SpawnedActor, ActorDescription)) + { + SpawnResult.Status = EActorSpawnResultStatus::Success; + return SpawnResult; + } + + SpawnResult.Status = EActorSpawnResultStatus::UnknownError; + return SpawnResult; +} + +TSharedPtr APropActorFactory::FPropParametersToJsonObject(const FPropParameters& PropParams) +{ + TSharedPtr JsonObject = MakeShareable(new FJsonObject); + + JsonObject->SetStringField(TEXT("Name"), PropParams.Name); + JsonObject->SetStringField(TEXT("Mesh"), PropParams.Mesh->GetPathName()); + + FString PropSizeString; + switch(PropParams.Size) + { + case EPropSize::Tiny: + PropSizeString = FString("Tiny"); + break; + case EPropSize::Small: + PropSizeString = FString("Small"); + break; + case EPropSize::Medium: + PropSizeString = FString("Medium"); + break; + case EPropSize::Big: + PropSizeString = FString("Big"); + break; + case EPropSize::Huge: + PropSizeString = FString("Huge"); + break; + default: + PropSizeString = FString("INVALID"); + break; + } + JsonObject->SetStringField(TEXT("Size"), PropSizeString); + return JsonObject; +} + +FString APropActorFactory::FPropParametersArrayToJson(const TArray& PropParamsArray) +{ + TArray> JsonArray; + + for (const FPropParameters& PropParams : PropParamsArray) + { + // Convert each FPropParameters to a JSON object + TSharedPtr JsonObject = FPropParametersToJsonObject(PropParams); + JsonArray.Add(MakeShareable(new FJsonValueObject(JsonObject))); + } + + // Convert the array of JSON objects into a single JSON object + TSharedRef RootObject = MakeShareable(new FJsonObject); + RootObject->SetArrayField(TEXT("Props"), JsonArray); + + // Serialize the JSON object into an FString + FString OutputString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); + FJsonSerializer::Serialize(RootObject, Writer); + + return OutputString; +} + +void APropActorFactory::SavePropParametersArrayToFile(const TArray& PropParametersArray, const FString& FileName) +{ + FString FilePath = FPaths::ProjectContentDir() + TEXT("Carla/Config/") + FileName; + // Convert the array to an FString in JSON format + FString JsonContent = FPropParametersArrayToJson(PropParametersArray); + + // Save the JSON to a file + if (FFileHelper::SaveStringToFile(JsonContent, *FilePath)) + { + UE_LOG(LogCarla, Log, TEXT("JSON file successfully saved at: %s"), *FilePath); + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to save JSON file at: %s"), *FilePath); + } +} + +bool APropActorFactory::JsonToFPropParameters(const TSharedPtr JsonObject, FPropParameters& OutPropParams) +{ + if (!JsonObject.IsValid()) return false; + + JsonObject->TryGetStringField(TEXT("Name"), OutPropParams.Name); + + // Convert "Mesh" string back to a FMesh reference + FString MeshPath; + JsonObject->TryGetStringField(TEXT("Mesh"), MeshPath); + OutPropParams.Mesh = Cast(StaticLoadObject(UStaticMesh::StaticClass(), nullptr, *(MeshPath))); + + FString PropSizeString; + JsonObject->TryGetStringField(TEXT("Size"), PropSizeString); + + if(PropSizeString == FString("Tiny")){ + OutPropParams.Size = EPropSize::Tiny; + } + else if(PropSizeString == FString("Small")) + { + OutPropParams.Size = EPropSize::Small; + } + else if(PropSizeString == FString("Medium")) + { + OutPropParams.Size = EPropSize::Medium; + } + else if(PropSizeString == FString("Big")) + { + OutPropParams.Size = EPropSize::Big; + } + else if(PropSizeString == FString("Huge")) + { + OutPropParams.Size = EPropSize::Huge; + } + else + { + OutPropParams.Size = EPropSize::INVALID; + } + + return true; +} + +bool APropActorFactory::JsonToFPropParametersArray(const FString& JsonString, TArray& OutPropParamsArray) +{ + TSharedPtr RootObject; + TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonString); + + if (!FJsonSerializer::Deserialize(Reader, RootObject) || !RootObject.IsValid()) + { + UE_LOG(LogCarla, Error, TEXT("Failed to parse JSON.")); + return false; + } + + // Get the "Props" array from the JSON root object + const TArray>* PropsArray; + if (RootObject->TryGetArrayField(TEXT("Props"), PropsArray)) + { + OutPropParamsArray.Empty(); + for (const TSharedPtr& PropValue : *PropsArray) + { + TSharedPtr PropObject = PropValue->AsObject(); + if (PropObject.IsValid()) + { + FPropParameters PropParams; + if (JsonToFPropParameters(PropObject, PropParams)) + { + OutPropParamsArray.Add(PropParams); + } + } + } + } + + return true; +} + +void APropActorFactory::LoadPropParametersArrayFromFile(const FString& FileName, TArray& OutPropParamsArray) +{ + FString JsonString; + FString FilePath = FPaths::ProjectContentDir() + TEXT("Carla/Config/") + FileName; + FString JsonContent; + + // Load the JSON file content into an FString + if (FFileHelper::LoadFileToString(JsonContent, *FilePath)) + { + // Parse the JSON and populate the TArray + if (JsonToFPropParametersArray(JsonContent, OutPropParamsArray)) + { + UE_LOG(LogCarla, Log, TEXT("Prop parameters loaded successfully from %s"), *FilePath); + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to parse Prop parameters from %s"), *FilePath); + } + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to load file: %s"), *FilePath); + } +} \ No newline at end of file diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/PropActorFactory.h b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/PropActorFactory.h new file mode 100644 index 000000000..1a77d2ec7 --- /dev/null +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/PropActorFactory.h @@ -0,0 +1,52 @@ +// Copyright (c) 2025 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 "Carla/Actor/ActorSpawnResult.h" +#include "Carla/Actor/CarlaActorFactory.h" +#include "Carla/Actor/PropParameters.h" + +#include +#include "Json.h" +#include "JsonUtilities.h" +#include + +#include "PropActorFactory.generated.h" + +UCLASS() +class CARLA_API APropActorFactory : public ACarlaActorFactory +{ + GENERATED_BODY() + + /// Retrieve the definitions of the static mesh actor + virtual TArray GetDefinitions() override; + + virtual FActorSpawnResult SpawnActor( + const FTransform &SpawnAtTransform, + const FActorDescription &ActorDescription) override; + +public: + UFUNCTION(BlueprintCallable, Category = "PropActorFactory") + static void SavePropParametersArrayToFile(const TArray &PropParamsArray, const FString &FileName); + UFUNCTION(BlueprintCallable, Category = "PropActorFactory") + static void LoadPropParametersArrayFromFile(const FString &FileName, TArray &OutPropParamsArray); + + UFUNCTION(BlueprintImplementableEvent, Category = "PropActorFactory") + bool PostProcessProp(AActor* SpawnedActor, const FActorDescription& PropParams); + +private: + static TSharedPtr FPropParametersToJsonObject(const FPropParameters& PropParams); + static FString FPropParametersArrayToJson(const TArray& PropParamsArray); + static bool JsonToFPropParameters(const TSharedPtr JsonObject, FPropParameters& OutPropParams); + static bool JsonToFPropParametersArray(const FString& JsonString, TArray& OutPropParamsArray); + +protected: + UPROPERTY(EditAnywhere) + TArray Definitions; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray PropsParams; +}; \ No newline at end of file diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.cpp b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.cpp index 2d2781513..03f5ca884 100644 --- a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.cpp +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.cpp @@ -242,8 +242,12 @@ void AVehicleActorFactory::LoadVehicleParametersArrayFromFile(const FString& Fil UE_LOG(LogCarla, Error, TEXT("Failed to parse vehicle parameters from %s"), *FilePath); } } - else + else if(FileName == "VehicleParameters.json") { UE_LOG(LogCarla, Error, TEXT("Failed to load file: %s"), *FilePath); } + else + { + UE_LOG(LogCarla, Warning, TEXT("Additional file not found: %s"), *FilePath); + } } diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.h b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.h index d2080658b..5c560cec7 100644 --- a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.h +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.h @@ -50,8 +50,8 @@ protected: UPROPERTY(EditAnywhere) TArray Definitions; - UPROPERTY(EditAnywhere) + UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray VehiclesParams; - UPROPERTY(EditAnywhere) + UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray MineVehiclesParams; }; diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/WalkerActorFactory.cpp b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/WalkerActorFactory.cpp new file mode 100644 index 000000000..079c3acda --- /dev/null +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/WalkerActorFactory.cpp @@ -0,0 +1,290 @@ +// Copyright (c) 2025 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 "Carla/Actor/Factory/WalkerActorFactory.h" +#include "Carla/Actor/ActorBlueprintFunctionLibrary.h" +#include "Carla/Actor/ActorDefinition.h" +#include "Carla/Game/CarlaEpisode.h" +#include "Carla/Walker/WalkerBase.h" + +#include +#include "Json.h" +#include "JsonUtilities.h" +#include "Misc/FileHelper.h" +#include "Misc/Paths.h" +#include + +TArray AWalkerActorFactory::GetDefinitions() +{ + LoadWalkerParametersArrayFromFile("WalkerParameters.json", WalkersParams); + + UActorBlueprintFunctionLibrary::MakePedestrianDefinitions(WalkersParams, Definitions); + return Definitions; +} + +FActorSpawnResult AWalkerActorFactory::SpawnActor( + const FTransform &SpawnAtTransform, + const FActorDescription &ActorDescription) +{ + FActorSpawnResult SpawnResult; + if(!IsValid(ActorDescription.Class)) + { + UE_LOG(LogCarla, Error, TEXT("Actor Description Class is null.")); + SpawnResult.Status = EActorSpawnResultStatus::InvalidDescription; + return SpawnResult; + } + + AActor* SpawnedActor = GetWorld()->SpawnActor(ActorDescription.Class, SpawnAtTransform); + SpawnResult.Actor = SpawnedActor; + + if(SpawnedActor == nullptr) + { + SpawnResult.Status = EActorSpawnResultStatus::Collision; + return SpawnResult; + } + + if(PostProcessWalker(SpawnedActor, ActorDescription)) + { + SpawnResult.Status = EActorSpawnResultStatus::Success; + return SpawnResult; + } + + SpawnResult.Status = EActorSpawnResultStatus::UnknownError; + return SpawnResult; +} + +TSharedPtr AWalkerActorFactory::FWalkerParametersToJsonObject(const FPedestrianParameters& WalkerParams) +{ + TSharedPtr JsonObject = MakeShareable(new FJsonObject); + JsonObject->SetStringField(TEXT("Id"), WalkerParams.Id); + JsonObject->SetStringField(TEXT("Class"), WalkerParams.Class ? WalkerParams.Class->GetPathName() : ""); + + FString GenderString; + switch(WalkerParams.Gender) + { + case EPedestrianGender::Other: + GenderString = FString("Other"); + break; + case EPedestrianGender::Female: + GenderString = FString("Female"); + break; + case EPedestrianGender::Male: + GenderString = FString("Male"); + break; + default: + GenderString = FString("INVALID"); + break; + } + JsonObject->SetStringField(TEXT("Gender"), GenderString); + + FString AgeString; + switch(WalkerParams.Age) + { + case EPedestrianAge::Child: + AgeString = FString("Child"); + break; + case EPedestrianAge::Teenager: + AgeString = FString("Teenager"); + break; + case EPedestrianAge::Adult: + AgeString = FString("Adult"); + break; + case EPedestrianAge::Elderly: + AgeString = FString("Elderly"); + break; + default: + AgeString = FString("INVALID"); + break; + } + JsonObject->SetStringField(TEXT("Age"), AgeString); + + //JsonObject->SetNumberField(TEXT("Speed"), InSpeed); + TArray> SpeedArray; + for (const float& Speed : WalkerParams.Speed) + { + TSharedPtr SpeedObject = MakeShareable(new FJsonObject); + SpeedObject->SetNumberField(TEXT("Speed"), Speed); + SpeedArray.Add(MakeShareable(new FJsonValueObject(SpeedObject))); + } + JsonObject->SetArrayField(TEXT("Speeds"), SpeedArray); + + JsonObject->SetNumberField(TEXT("Generation"), WalkerParams.Generation); + + return JsonObject; +} + +FString AWalkerActorFactory::FWalkerParametersArrayToJson(const TArray& WalkerParamsArray) +{ + TArray> JsonArray; + + for (const FPedestrianParameters& WalkerParams : WalkerParamsArray) + { + // Convert each FPedestrianParameters to a JSON object + TSharedPtr JsonObject = FWalkerParametersToJsonObject(WalkerParams); + JsonArray.Add(MakeShareable(new FJsonValueObject(JsonObject))); + } + + // Convert the array of JSON objects into a single JSON object + TSharedRef RootObject = MakeShareable(new FJsonObject); + RootObject->SetArrayField(TEXT("Walkers"), JsonArray); + + // Serialize the JSON object into an FString + FString OutputString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); + FJsonSerializer::Serialize(RootObject, Writer); + + return OutputString; +} + +void AWalkerActorFactory::SaveWalkerParametersArrayToFile(const TArray& WalkerParametersArray, const FString& FileName) +{ + FString FilePath = FPaths::ProjectContentDir() + TEXT("Carla/Config/") + FileName; + // Convert the array to an FString in JSON format + FString JsonContent = FWalkerParametersArrayToJson(WalkerParametersArray); + + // Save the JSON to a file + if (FFileHelper::SaveStringToFile(JsonContent, *FilePath)) + { + UE_LOG(LogCarla, Log, TEXT("JSON file successfully saved at: %s"), *FilePath); + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to save JSON file at: %s"), *FilePath); + } +} + +bool AWalkerActorFactory::JsonToFWalkerParameters(const TSharedPtr JsonObject, FPedestrianParameters& OutWalkerParams) +{ + if (!JsonObject.IsValid()) return false; + + JsonObject->TryGetStringField(TEXT("Id"), OutWalkerParams.Id); + + FString ClassPath; + JsonObject->TryGetStringField(TEXT("Class"), ClassPath); // Custom conversion required if needed + OutWalkerParams.Class = StaticLoadClass(AWalkerBase::StaticClass(), nullptr, *ClassPath); + + FString GenderString; + JsonObject->TryGetStringField(TEXT("Gender"), GenderString); + + if(GenderString == FString("Other")){ + OutWalkerParams.Gender = EPedestrianGender::Other; + } + else if(GenderString == FString("Female")) + { + OutWalkerParams.Gender = EPedestrianGender::Female; + } + else if(GenderString == FString("Male")) + { + OutWalkerParams.Gender = EPedestrianGender::Male; + } + else + { + OutWalkerParams.Gender = EPedestrianGender::INVALID; + } + + FString AgeString; + JsonObject->TryGetStringField(TEXT("Age"), AgeString); + + if(AgeString == FString("Child")){ + OutWalkerParams.Age = EPedestrianAge::Child; + } + else if(AgeString == FString("Teenager")) + { + OutWalkerParams.Age = EPedestrianAge::Teenager; + } + else if(AgeString == FString("Adult")) + { + OutWalkerParams.Age = EPedestrianAge::Adult; + } + else if(AgeString == FString("Elderly")) + { + OutWalkerParams.Age = EPedestrianAge::Elderly; + } + else + { + OutWalkerParams.Age = EPedestrianAge::INVALID; + } + + // Parse Speeds + const TArray>* SpeedsArray; + if (JsonObject->TryGetArrayField(TEXT("Speeds"), SpeedsArray)) + { + OutWalkerParams.Speed.Empty(); + for (const TSharedPtr& SpeedValue : *SpeedsArray) + { + TSharedPtr SpeedObject = SpeedValue->AsObject(); + if (SpeedObject.IsValid()) + { + float Speed; + Speed = SpeedObject->GetNumberField(TEXT("Speed")); + OutWalkerParams.Speed.Add(Speed); + } + } + } + + JsonObject->TryGetNumberField(TEXT("Generation"), OutWalkerParams.Generation); + + return true; +} + +bool AWalkerActorFactory::JsonToFWalkerParametersArray(const FString& JsonString, TArray& OutWalkerParamsArray) +{ + TSharedPtr RootObject; + TSharedRef> Reader = TJsonReaderFactory<>::Create(JsonString); + + if (!FJsonSerializer::Deserialize(Reader, RootObject) || !RootObject.IsValid()) + { + UE_LOG(LogCarla, Error, TEXT("Failed to parse JSON.")); + return false; + } + + // Get the "Walkers" array from the JSON root object + const TArray>* WalkersArray; + if (RootObject->TryGetArrayField(TEXT("Walkers"), WalkersArray)) + { + OutWalkerParamsArray.Empty(); + for (const TSharedPtr& WalkerValue : *WalkersArray) + { + TSharedPtr WalkerObject = WalkerValue->AsObject(); + if (WalkerObject.IsValid()) + { + FPedestrianParameters WalkerParams; + if (JsonToFWalkerParameters(WalkerObject, WalkerParams)) + { + OutWalkerParamsArray.Add(WalkerParams); + } + } + } + } + + return true; +} + +void AWalkerActorFactory::LoadWalkerParametersArrayFromFile(const FString& FileName, TArray& OutWalkerParamsArray) +{ + FString JsonString; + FString FilePath = FPaths::ProjectContentDir() + TEXT("Carla/Config/") + FileName; + FString JsonContent; + + // Load the JSON file content into an FString + if (FFileHelper::LoadFileToString(JsonContent, *FilePath)) + { + // Parse the JSON and populate the TArray + if (JsonToFWalkerParametersArray(JsonContent, OutWalkerParamsArray)) + { + UE_LOG(LogCarla, Log, TEXT("Walker parameters loaded successfully from %s"), *FilePath); + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to parse Walker parameters from %s"), *FilePath); + } + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to load file: %s"), *FilePath); + } +} \ No newline at end of file diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/WalkerActorFactory.h b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/WalkerActorFactory.h new file mode 100644 index 000000000..4a91171fa --- /dev/null +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/WalkerActorFactory.h @@ -0,0 +1,52 @@ +// Copyright (c) 2025 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 "Carla/Actor/ActorSpawnResult.h" +#include "Carla/Actor/CarlaActorFactory.h" +#include "Carla/Actor/PedestrianParameters.h" + +#include +#include "Json.h" +#include "JsonUtilities.h" +#include + +#include "WalkerActorFactory.generated.h" + +UCLASS() +class CARLA_API AWalkerActorFactory : public ACarlaActorFactory +{ + GENERATED_BODY() + + /// Retrieve the definitions of the static mesh actor + virtual TArray GetDefinitions() override; + + virtual FActorSpawnResult SpawnActor( + const FTransform &SpawnAtTransform, + const FActorDescription &ActorDescription) override; + +public: + UFUNCTION(BlueprintCallable, Category = "WalkerActorFactory") + static void SaveWalkerParametersArrayToFile(const TArray &WalkerParamsArray, const FString &FileName); + UFUNCTION(BlueprintCallable, Category = "WalkerActorFactory") + static void LoadWalkerParametersArrayFromFile(const FString &FileName, TArray &OutWalkerParamsArray); + + UFUNCTION(BlueprintImplementableEvent, Category = "WalkerActorFactory") + bool PostProcessWalker(AActor* SpawnedActor, const FActorDescription& WalkerParams); + +private: + static TSharedPtr FWalkerParametersToJsonObject(const FPedestrianParameters& WalkerParams); + static FString FWalkerParametersArrayToJson(const TArray& WalkerParamsArray); + static bool JsonToFWalkerParameters(const TSharedPtr JsonObject, FPedestrianParameters& OutWalkerParams); + static bool JsonToFWalkerParametersArray(const FString& JsonString, TArray& OutWalkerParamsArray); + +protected: + UPROPERTY(EditAnywhere) + TArray Definitions; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray WalkersParams; +}; \ No newline at end of file