From c37d00d6710e39996778dd59cea15a685d5fae49 Mon Sep 17 00:00:00 2001 From: Blyron <53337103+Blyron@users.noreply.github.com> Date: Tue, 5 Nov 2024 16:26:36 +0100 Subject: [PATCH] Aaron/programaticvehiclefactory (#8340) * Add Vehicle Factory to be loaded in c++ * Read properly the object class path --- .../Actor/Factory/VehicleActorFactory.cpp | 241 ++++++++++++++++++ .../Carla/Actor/Factory/VehicleActorFactory.h | 52 ++++ 2 files changed, 293 insertions(+) create mode 100644 Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.cpp create mode 100644 Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.h diff --git a/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.cpp b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.cpp new file mode 100644 index 000000000..24341bf45 --- /dev/null +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.cpp @@ -0,0 +1,241 @@ +// Copyright (c) 2024 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/VehicleActorFactory.h" +#include "Carla/Actor/ActorBlueprintFunctionLibrary.h" +#include "Carla/Actor/ActorDefinition.h" + +#include "Carla/Game/CarlaEpisode.h" +#include "Json.h" +#include "JsonUtilities.h" +#include "Misc/FileHelper.h" +#include "Misc/Paths.h" + +TArray AVehicleActorFactory::GetDefinitions() +{ + LoadVehicleParametersArrayFromFile("VehicleParameters.json", VehiclesParams); + UActorBlueprintFunctionLibrary::MakeVehicleDefinitions(VehiclesParams, Definitions); + return Definitions; +} + +FActorSpawnResult AVehicleActorFactory::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( PostProcessVehicle(SpawnedActor, ActorDescription) ){ + SpawnResult.Status = EActorSpawnResultStatus::Success; + return SpawnResult; + } + SpawnResult.Status = EActorSpawnResultStatus::UnknownError; + return SpawnResult; +} + +TSharedPtr AVehicleActorFactory::FVehicleParametersToJsonObject(const FVehicleParameters& VehicleParams) +{ + TSharedPtr JsonObject = MakeShareable(new FJsonObject); + + JsonObject->SetStringField(TEXT("Make"), VehicleParams.Make); + JsonObject->SetStringField(TEXT("Model"), VehicleParams.Model); + JsonObject->SetStringField(TEXT("Class"), VehicleParams.Class ? VehicleParams.Class->GetPathName() : ""); + JsonObject->SetNumberField(TEXT("NumberOfWheels"), VehicleParams.NumberOfWheels); + JsonObject->SetNumberField(TEXT("Generation"), VehicleParams.Generation); + JsonObject->SetStringField(TEXT("ObjectType"), VehicleParams.ObjectType); + JsonObject->SetStringField(TEXT("BaseType"), VehicleParams.BaseType); + JsonObject->SetStringField(TEXT("SpecialType"), VehicleParams.SpecialType); + JsonObject->SetBoolField(TEXT("HasDynamicDoors"), VehicleParams.HasDynamicDoors); + JsonObject->SetBoolField(TEXT("HasLights"), VehicleParams.HasLights); + + // Serialize RecommendedColors + TArray> ColorsArray; + for (const FColor& Color : VehicleParams.RecommendedColors) + { + TSharedPtr ColorObject = MakeShareable(new FJsonObject); + ColorObject->SetNumberField(TEXT("R"), Color.R); + ColorObject->SetNumberField(TEXT("G"), Color.G); + ColorObject->SetNumberField(TEXT("B"), Color.B); + ColorObject->SetNumberField(TEXT("A"), Color.A); + ColorsArray.Add(MakeShareable(new FJsonValueObject(ColorObject))); + } + JsonObject->SetArrayField(TEXT("RecommendedColors"), ColorsArray); + + // Serialize SupportedDrivers + TArray> DriversArray; + for (int32 DriverID : VehicleParams.SupportedDrivers) + { + DriversArray.Add(MakeShareable(new FJsonValueNumber(DriverID))); + } + JsonObject->SetArrayField(TEXT("SupportedDrivers"), DriversArray); + + return JsonObject; +} + +FString AVehicleActorFactory::FVehicleParametersArrayToJson(const TArray& VehicleParamsArray) +{ + TArray> JsonArray; + + for (const FVehicleParameters& VehicleParams : VehicleParamsArray) + { + // Convert each FVehicleParameters to a JSON object + TSharedPtr JsonObject = FVehicleParametersToJsonObject(VehicleParams); + 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("Vehicles"), JsonArray); + + // Serialize the JSON object into an FString + FString OutputString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); + FJsonSerializer::Serialize(RootObject, Writer); + + return OutputString; +} + +void AVehicleActorFactory::SaveVehicleParametersArrayToFile(const TArray& VehicleParamsArray, const FString& FileName) +{ + FString FilePath = FPaths::ProjectContentDir() + TEXT("Carla/Config/") + FileName; + // Convert the array to an FString in JSON format + FString JsonContent = FVehicleParametersArrayToJson(VehicleParamsArray); + + // 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 AVehicleActorFactory::JsonToFVehicleParameters(const TSharedPtr JsonObject, FVehicleParameters& OutVehicleParams) +{ + if (!JsonObject.IsValid()) return false; + + JsonObject->TryGetStringField(TEXT("Make"), OutVehicleParams.Make); + JsonObject->TryGetStringField(TEXT("Model"), OutVehicleParams.Model); + // Convert "Class" string back to a class reference if necessary + FString ClassPath; + JsonObject->TryGetStringField(TEXT("Class"), ClassPath); // Custom conversion required if needed + OutVehicleParams.Class = StaticLoadClass(ACarlaWheeledVehicle::StaticClass(), nullptr, *ClassPath); + + JsonObject->TryGetNumberField(TEXT("NumberOfWheels"), OutVehicleParams.NumberOfWheels); + JsonObject->TryGetNumberField(TEXT("Generation"), OutVehicleParams.Generation); + JsonObject->TryGetStringField(TEXT("ObjectType"), OutVehicleParams.ObjectType); + JsonObject->TryGetStringField(TEXT("BaseType"), OutVehicleParams.BaseType); + JsonObject->TryGetStringField(TEXT("SpecialType"), OutVehicleParams.SpecialType); + JsonObject->TryGetBoolField(TEXT("HasDynamicDoors"), OutVehicleParams.HasDynamicDoors); + JsonObject->TryGetBoolField(TEXT("HasLights"), OutVehicleParams.HasLights); + + // Parse RecommendedColors + const TArray>* ColorsArray; + if (JsonObject->TryGetArrayField(TEXT("RecommendedColors"), ColorsArray)) + { + OutVehicleParams.RecommendedColors.Empty(); + for (const TSharedPtr& ColorValue : *ColorsArray) + { + TSharedPtr ColorObject = ColorValue->AsObject(); + if (ColorObject.IsValid()) + { + FColor Color; + Color.R = ColorObject->GetIntegerField(TEXT("R")); + Color.G = ColorObject->GetIntegerField(TEXT("G")); + Color.B = ColorObject->GetIntegerField(TEXT("B")); + Color.A = ColorObject->GetIntegerField(TEXT("A")); + OutVehicleParams.RecommendedColors.Add(Color); + } + } + } + + // Parse SupportedDrivers + const TArray>* DriversArray; + if (JsonObject->TryGetArrayField(TEXT("SupportedDrivers"), DriversArray)) + { + OutVehicleParams.SupportedDrivers.Empty(); + for (const TSharedPtr& DriverValue : *DriversArray) + { + OutVehicleParams.SupportedDrivers.Add(DriverValue->AsNumber()); + } + } + + return true; +} + +bool AVehicleActorFactory::JsonToFVehicleParametersArray(const FString& JsonString, TArray& OutVehicleParamsArray) +{ + 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 "Vehicles" array from the JSON root object + const TArray>* VehiclesArray; + if (RootObject->TryGetArrayField(TEXT("Vehicles"), VehiclesArray)) + { + OutVehicleParamsArray.Empty(); + for (const TSharedPtr& VehicleValue : *VehiclesArray) + { + TSharedPtr VehicleObject = VehicleValue->AsObject(); + if (VehicleObject.IsValid()) + { + FVehicleParameters VehicleParams; + if (JsonToFVehicleParameters(VehicleObject, VehicleParams)) + { + OutVehicleParamsArray.Add(VehicleParams); + } + } + } + } + + return true; +} + +void AVehicleActorFactory::LoadVehicleParametersArrayFromFile(const FString& FileName, TArray& OutVehicleParamsArray) +{ + 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 (JsonToFVehicleParametersArray(JsonContent, OutVehicleParamsArray)) + { + UE_LOG(LogCarla, Log, TEXT("Vehicle parameters loaded successfully from %s"), *FilePath); + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to parse vehicle parameters from %s"), *FilePath); + } + } + else + { + UE_LOG(LogCarla, Error, TEXT("Failed to load file: %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 new file mode 100644 index 000000000..07eac2bca --- /dev/null +++ b/Unreal/CarlaUnreal/Plugins/Carla/Source/Carla/Actor/Factory/VehicleActorFactory.h @@ -0,0 +1,52 @@ +// Copyright (c) 2024 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/VehicleParameters.h" +#include "Json.h" +#include "JsonUtilities.h" + +#include "VehicleActorFactory.generated.h" + +/// Factory in charge of spawning static meshes. This factory is able to spawn +/// any mesh in content. +UCLASS() +class CARLA_API AVehicleActorFactory : 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 = "VehicleActorFactory") + static void SaveVehicleParametersArrayToFile(const TArray& VehicleParamsArray, const FString& FileName); + UFUNCTION(BlueprintCallable, Category = "VehicleActorFactory") + static void LoadVehicleParametersArrayFromFile(const FString& FileName, TArray& OutVehicleParamsArray); + + UFUNCTION(BlueprintImplementableEvent, Category = "VehicleActorFactory") + bool PostProcessVehicle(AActor* SpawnedActor, const FActorDescription& VehicleParams); +private: + static TSharedPtr FVehicleParametersToJsonObject(const FVehicleParameters& VehicleParams); + static FString FVehicleParametersArrayToJson(const TArray& VehicleParamsArray); + static bool JsonToFVehicleParameters(const TSharedPtr JsonObject, FVehicleParameters& OutVehicleParams); + static bool JsonToFVehicleParametersArray(const FString& JsonString, TArray& OutVehicleParamsArray); + +protected: + + UPROPERTY(EditAnywhere) + TArray Definitions; + UPROPERTY(EditAnywhere) + TArray VehiclesParams; +};