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