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
This commit is contained in:
Jorge Virgos 2025-03-18 15:36:26 +01:00 committed by GitHub
parent 0bfb05bfdd
commit ac66720586
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 915 additions and 3 deletions

View File

@ -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<FBlueprintParameters> &ParameterArray,
TArray<FActorDefinition> &Definitions)
{
FillActorDefinitionArray(ParameterArray, Definitions, &MakeBlueprintDefinition);
}
void UActorBlueprintFunctionLibrary::MakeObstacleDetectorDefinitions(
const FString &Type,
const FString &Id,

View File

@ -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 <util/ue-header-guard-begin.h>
#include "Kismet/BlueprintFunctionLibrary.h"
@ -154,6 +155,17 @@ public:
const TArray<FPropParameters> &ParameterArray,
TArray<FActorDefinition> &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<FBlueprintParameters> &ParameterArray,
TArray<FActorDefinition> &Definitions);
UFUNCTION()
static void MakeObstacleDetectorDefinitions(
const FString &Type,

View File

@ -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 <https://opensource.org/licenses/MIT>.
#pragma once
#include "BlueprintParameters.generated.h"
USTRUCT(BlueprintType)
struct CARLA_API FBlueprintParameters
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Name;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FString Path;
};

View File

@ -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 <https://opensource.org/licenses/MIT>.
#include "Carla/Actor/Factory/BlueprintActorFactory.h"
#include "Carla/Actor/ActorBlueprintFunctionLibrary.h"
#include "Carla/Actor/ActorDefinition.h"
#include "Carla/Game/CarlaEpisode.h"
#include <util/ue-header-guard-begin.h>
#include "Json.h"
#include "JsonUtilities.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include <util/ue-header-guard-end.h>
TArray<FActorDefinition> 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<AActor>(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<FJsonObject> ABlueprintActorFactory::FBlueprintParametersToJsonObject(const FBlueprintParameters& BlueprintParams)
{
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);
JsonObject->SetStringField(TEXT("Name"), BlueprintParams.Name);
JsonObject->SetStringField(TEXT("Path"), BlueprintParams.Path);
return JsonObject;
}
FString ABlueprintActorFactory::FBlueprintParametersArrayToJson(const TArray<FBlueprintParameters>& BlueprintParamsArray)
{
TArray<TSharedPtr<FJsonValue>> JsonArray;
for (const FBlueprintParameters& BlueprintParams : BlueprintParamsArray)
{
// Convert each FBlueprintParameters to a JSON object
TSharedPtr<FJsonObject> JsonObject = FBlueprintParametersToJsonObject(BlueprintParams);
JsonArray.Add(MakeShareable(new FJsonValueObject(JsonObject)));
}
// Convert the array of JSON objects into a single JSON object
TSharedRef<FJsonObject> RootObject = MakeShareable(new FJsonObject);
RootObject->SetArrayField(TEXT("Blueprints"), JsonArray);
// Serialize the JSON object into an FString
FString OutputString;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(RootObject, Writer);
return OutputString;
}
void ABlueprintActorFactory::SaveBlueprintParametersArrayToFile(const TArray<FBlueprintParameters>& 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<FJsonObject> 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<FBlueprintParameters>& OutBlueprintParamsArray)
{
TSharedPtr<FJsonObject> RootObject;
TSharedRef<TJsonReader<>> 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<TSharedPtr<FJsonValue>>* BlueprintsArray;
if (RootObject->TryGetArrayField(TEXT("Blueprints"), BlueprintsArray))
{
OutBlueprintParamsArray.Empty();
for (const TSharedPtr<FJsonValue>& BlueprintValue : *BlueprintsArray)
{
TSharedPtr<FJsonObject> BlueprintObject = BlueprintValue->AsObject();
if (BlueprintObject.IsValid())
{
FBlueprintParameters BlueprintParams;
if (JsonToFBlueprintParameters(BlueprintObject, BlueprintParams))
{
OutBlueprintParamsArray.Add(BlueprintParams);
}
}
}
}
return true;
}
void ABlueprintActorFactory::LoadBlueprintParametersArrayFromFile(const FString& FileName, TArray<FBlueprintParameters>& 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<FBlueprintParameters>
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);
}
}

View File

@ -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 <https://opensource.org/licenses/MIT>.
#pragma once
#include "Carla/Actor/ActorSpawnResult.h"
#include "Carla/Actor/CarlaActorFactory.h"
#include "Carla/Actor/BlueprintParameters.h"
#include <util/ue-header-guard-begin.h>
#include "Json.h"
#include "JsonUtilities.h"
#include <util/ue-header-guard-end.h>
#include "BlueprintActorFactory.generated.h"
UCLASS()
class CARLA_API ABlueprintActorFactory : public ACarlaActorFactory
{
GENERATED_BODY()
/// Retrieve the definitions of the static mesh actor
virtual TArray<FActorDefinition> GetDefinitions() override;
virtual FActorSpawnResult SpawnActor(
const FTransform &SpawnAtTransform,
const FActorDescription &ActorDescription) override;
public:
UFUNCTION(BlueprintCallable, Category = "BlueprintActorFactory")
static void SaveBlueprintParametersArrayToFile(const TArray<FBlueprintParameters> &BlueprintParamsArray, const FString &FileName);
UFUNCTION(BlueprintCallable, Category = "BlueprintActorFactory")
static void LoadBlueprintParametersArrayFromFile(const FString &FileName, TArray<FBlueprintParameters> &OutBlueprintParamsArray);
UFUNCTION(BlueprintImplementableEvent, Category = "BlueprintActorFactory")
bool PostProcessBlueprint(AActor* SpawnedActor, const FActorDescription& BlueprintParams);
private:
static TSharedPtr<FJsonObject> FBlueprintParametersToJsonObject(const FBlueprintParameters& BlueprintParams);
static FString FBlueprintParametersArrayToJson(const TArray<FBlueprintParameters>& BlueprintParamsArray);
static bool JsonToFBlueprintParameters(const TSharedPtr<FJsonObject> JsonObject, FBlueprintParameters& OutBlueprintParams);
static bool JsonToFBlueprintParametersArray(const FString& JsonString, TArray<FBlueprintParameters>& OutBlueprintParamsArray);
protected:
UPROPERTY(EditAnywhere)
TArray<FActorDefinition> Definitions;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FBlueprintParameters> BlueprintsParams;
};

View File

@ -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 <https://opensource.org/licenses/MIT>.
#include "Carla/Actor/Factory/PropActorFactory.h"
#include "Carla/Actor/ActorBlueprintFunctionLibrary.h"
#include "Carla/Actor/ActorDefinition.h"
#include "Carla/Game/CarlaEpisode.h"
#include <util/ue-header-guard-begin.h>
#include "Json.h"
#include "JsonUtilities.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include <util/ue-header-guard-end.h>
TArray<FActorDefinition> 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<AActor>(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<FJsonObject> APropActorFactory::FPropParametersToJsonObject(const FPropParameters& PropParams)
{
TSharedPtr<FJsonObject> 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<FPropParameters>& PropParamsArray)
{
TArray<TSharedPtr<FJsonValue>> JsonArray;
for (const FPropParameters& PropParams : PropParamsArray)
{
// Convert each FPropParameters to a JSON object
TSharedPtr<FJsonObject> JsonObject = FPropParametersToJsonObject(PropParams);
JsonArray.Add(MakeShareable(new FJsonValueObject(JsonObject)));
}
// Convert the array of JSON objects into a single JSON object
TSharedRef<FJsonObject> RootObject = MakeShareable(new FJsonObject);
RootObject->SetArrayField(TEXT("Props"), JsonArray);
// Serialize the JSON object into an FString
FString OutputString;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(RootObject, Writer);
return OutputString;
}
void APropActorFactory::SavePropParametersArrayToFile(const TArray<FPropParameters>& 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<FJsonObject> 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<UStaticMesh>(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<FPropParameters>& OutPropParamsArray)
{
TSharedPtr<FJsonObject> RootObject;
TSharedRef<TJsonReader<>> 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<TSharedPtr<FJsonValue>>* PropsArray;
if (RootObject->TryGetArrayField(TEXT("Props"), PropsArray))
{
OutPropParamsArray.Empty();
for (const TSharedPtr<FJsonValue>& PropValue : *PropsArray)
{
TSharedPtr<FJsonObject> PropObject = PropValue->AsObject();
if (PropObject.IsValid())
{
FPropParameters PropParams;
if (JsonToFPropParameters(PropObject, PropParams))
{
OutPropParamsArray.Add(PropParams);
}
}
}
}
return true;
}
void APropActorFactory::LoadPropParametersArrayFromFile(const FString& FileName, TArray<FPropParameters>& 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<FPropParameters>
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);
}
}

View File

@ -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 <https://opensource.org/licenses/MIT>.
#pragma once
#include "Carla/Actor/ActorSpawnResult.h"
#include "Carla/Actor/CarlaActorFactory.h"
#include "Carla/Actor/PropParameters.h"
#include <util/ue-header-guard-begin.h>
#include "Json.h"
#include "JsonUtilities.h"
#include <util/ue-header-guard-end.h>
#include "PropActorFactory.generated.h"
UCLASS()
class CARLA_API APropActorFactory : public ACarlaActorFactory
{
GENERATED_BODY()
/// Retrieve the definitions of the static mesh actor
virtual TArray<FActorDefinition> GetDefinitions() override;
virtual FActorSpawnResult SpawnActor(
const FTransform &SpawnAtTransform,
const FActorDescription &ActorDescription) override;
public:
UFUNCTION(BlueprintCallable, Category = "PropActorFactory")
static void SavePropParametersArrayToFile(const TArray<FPropParameters> &PropParamsArray, const FString &FileName);
UFUNCTION(BlueprintCallable, Category = "PropActorFactory")
static void LoadPropParametersArrayFromFile(const FString &FileName, TArray<FPropParameters> &OutPropParamsArray);
UFUNCTION(BlueprintImplementableEvent, Category = "PropActorFactory")
bool PostProcessProp(AActor* SpawnedActor, const FActorDescription& PropParams);
private:
static TSharedPtr<FJsonObject> FPropParametersToJsonObject(const FPropParameters& PropParams);
static FString FPropParametersArrayToJson(const TArray<FPropParameters>& PropParamsArray);
static bool JsonToFPropParameters(const TSharedPtr<FJsonObject> JsonObject, FPropParameters& OutPropParams);
static bool JsonToFPropParametersArray(const FString& JsonString, TArray<FPropParameters>& OutPropParamsArray);
protected:
UPROPERTY(EditAnywhere)
TArray<FActorDefinition> Definitions;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FPropParameters> PropsParams;
};

View File

@ -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);
}
}

View File

@ -50,8 +50,8 @@ protected:
UPROPERTY(EditAnywhere)
TArray<FActorDefinition> Definitions;
UPROPERTY(EditAnywhere)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FVehicleParameters> VehiclesParams;
UPROPERTY(EditAnywhere)
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FVehicleParameters> MineVehiclesParams;
};

View File

@ -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 <https://opensource.org/licenses/MIT>.
#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 <util/ue-header-guard-begin.h>
#include "Json.h"
#include "JsonUtilities.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include <util/ue-header-guard-end.h>
TArray<FActorDefinition> 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<AActor>(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<FJsonObject> AWalkerActorFactory::FWalkerParametersToJsonObject(const FPedestrianParameters& WalkerParams)
{
TSharedPtr<FJsonObject> 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<TSharedPtr<FJsonValue>> SpeedArray;
for (const float& Speed : WalkerParams.Speed)
{
TSharedPtr<FJsonObject> 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<FPedestrianParameters>& WalkerParamsArray)
{
TArray<TSharedPtr<FJsonValue>> JsonArray;
for (const FPedestrianParameters& WalkerParams : WalkerParamsArray)
{
// Convert each FPedestrianParameters to a JSON object
TSharedPtr<FJsonObject> JsonObject = FWalkerParametersToJsonObject(WalkerParams);
JsonArray.Add(MakeShareable(new FJsonValueObject(JsonObject)));
}
// Convert the array of JSON objects into a single JSON object
TSharedRef<FJsonObject> RootObject = MakeShareable(new FJsonObject);
RootObject->SetArrayField(TEXT("Walkers"), JsonArray);
// Serialize the JSON object into an FString
FString OutputString;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(RootObject, Writer);
return OutputString;
}
void AWalkerActorFactory::SaveWalkerParametersArrayToFile(const TArray<FPedestrianParameters>& 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<FJsonObject> 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<TSharedPtr<FJsonValue>>* SpeedsArray;
if (JsonObject->TryGetArrayField(TEXT("Speeds"), SpeedsArray))
{
OutWalkerParams.Speed.Empty();
for (const TSharedPtr<FJsonValue>& SpeedValue : *SpeedsArray)
{
TSharedPtr<FJsonObject> 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<FPedestrianParameters>& OutWalkerParamsArray)
{
TSharedPtr<FJsonObject> RootObject;
TSharedRef<TJsonReader<>> 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<TSharedPtr<FJsonValue>>* WalkersArray;
if (RootObject->TryGetArrayField(TEXT("Walkers"), WalkersArray))
{
OutWalkerParamsArray.Empty();
for (const TSharedPtr<FJsonValue>& WalkerValue : *WalkersArray)
{
TSharedPtr<FJsonObject> WalkerObject = WalkerValue->AsObject();
if (WalkerObject.IsValid())
{
FPedestrianParameters WalkerParams;
if (JsonToFWalkerParameters(WalkerObject, WalkerParams))
{
OutWalkerParamsArray.Add(WalkerParams);
}
}
}
}
return true;
}
void AWalkerActorFactory::LoadWalkerParametersArrayFromFile(const FString& FileName, TArray<FPedestrianParameters>& 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<FPedestrianParameters>
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);
}
}

View File

@ -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 <https://opensource.org/licenses/MIT>.
#pragma once
#include "Carla/Actor/ActorSpawnResult.h"
#include "Carla/Actor/CarlaActorFactory.h"
#include "Carla/Actor/PedestrianParameters.h"
#include <util/ue-header-guard-begin.h>
#include "Json.h"
#include "JsonUtilities.h"
#include <util/ue-header-guard-end.h>
#include "WalkerActorFactory.generated.h"
UCLASS()
class CARLA_API AWalkerActorFactory : public ACarlaActorFactory
{
GENERATED_BODY()
/// Retrieve the definitions of the static mesh actor
virtual TArray<FActorDefinition> GetDefinitions() override;
virtual FActorSpawnResult SpawnActor(
const FTransform &SpawnAtTransform,
const FActorDescription &ActorDescription) override;
public:
UFUNCTION(BlueprintCallable, Category = "WalkerActorFactory")
static void SaveWalkerParametersArrayToFile(const TArray<FPedestrianParameters> &WalkerParamsArray, const FString &FileName);
UFUNCTION(BlueprintCallable, Category = "WalkerActorFactory")
static void LoadWalkerParametersArrayFromFile(const FString &FileName, TArray<FPedestrianParameters> &OutWalkerParamsArray);
UFUNCTION(BlueprintImplementableEvent, Category = "WalkerActorFactory")
bool PostProcessWalker(AActor* SpawnedActor, const FActorDescription& WalkerParams);
private:
static TSharedPtr<FJsonObject> FWalkerParametersToJsonObject(const FPedestrianParameters& WalkerParams);
static FString FWalkerParametersArrayToJson(const TArray<FPedestrianParameters>& WalkerParamsArray);
static bool JsonToFWalkerParameters(const TSharedPtr<FJsonObject> JsonObject, FPedestrianParameters& OutWalkerParams);
static bool JsonToFWalkerParametersArray(const FString& JsonString, TArray<FPedestrianParameters>& OutWalkerParamsArray);
protected:
UPROPERTY(EditAnywhere)
TArray<FActorDefinition> Definitions;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FPedestrianParameters> WalkersParams;
};