ralpha-assets/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaParameterBridge.cpp

1017 lines
29 KiB
C++

// Copyright Ralpha Team. All Rights Reserved.
#include "RalphaParameterBridge.h"
#include "RalphaMCPServer.h"
#include "Engine/Engine.h"
#include "Engine/World.h"
#include "Engine/PostProcessVolume.h"
#include "Engine/DirectionalLight.h"
#include "Engine/SkyLight.h"
#include "Engine/ExponentialHeightFog.h"
#include "CineCameraActor.h"
#include "CineCameraComponent.h"
#include "Components/PostProcessComponent.h"
#include "Components/DirectionalLightComponent.h"
#include "Components/SkyLightComponent.h"
#include "Components/ExponentialHeightFogComponent.h"
#include "Kismet/GameplayStatics.h"
#include "EngineUtils.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Engine/StaticMeshActor.h"
#include "Engine/StaticMesh.h"
#include "Dom/JsonObject.h"
#include "Dom/JsonValue.h"
#include "Serialization/JsonSerializer.h"
URalphaParameterBridge::URalphaParameterBridge()
{
}
UWorld* URalphaParameterBridge::GetCurrentWorld()
{
if (GEngine && GEngine->GetWorldContexts().Num() > 0)
{
for (const FWorldContext& Context : GEngine->GetWorldContexts())
{
if (Context.WorldType == EWorldType::Editor || Context.WorldType == EWorldType::PIE)
{
return Context.World();
}
}
}
return nullptr;
}
APostProcessVolume* URalphaParameterBridge::FindPostProcessVolume()
{
UWorld* World = GetCurrentWorld();
if (!World) return nullptr;
for (TActorIterator<APostProcessVolume> It(World); It; ++It)
{
if (It->bUnbound) // Prefer unbound (global) volumes
{
return *It;
}
}
// Return any post-process volume if no unbound one found
for (TActorIterator<APostProcessVolume> It(World); It; ++It)
{
return *It;
}
return nullptr;
}
ADirectionalLight* URalphaParameterBridge::FindDirectionalLight()
{
UWorld* World = GetCurrentWorld();
if (!World) return nullptr;
for (TActorIterator<ADirectionalLight> It(World); It; ++It)
{
return *It;
}
return nullptr;
}
ASkyLight* URalphaParameterBridge::FindSkyLight()
{
UWorld* World = GetCurrentWorld();
if (!World) return nullptr;
for (TActorIterator<ASkyLight> It(World); It; ++It)
{
return *It;
}
return nullptr;
}
AExponentialHeightFog* URalphaParameterBridge::FindExponentialHeightFog()
{
UWorld* World = GetCurrentWorld();
if (!World) return nullptr;
for (TActorIterator<AExponentialHeightFog> It(World); It; ++It)
{
return *It;
}
return nullptr;
}
ACineCameraActor* URalphaParameterBridge::FindCineCamera()
{
UWorld* World = GetCurrentWorld();
if (!World) return nullptr;
for (TActorIterator<ACineCameraActor> It(World); It; ++It)
{
return *It;
}
return nullptr;
}
FLinearColor URalphaParameterBridge::ParseHexColor(const FString& HexColor)
{
FString CleanHex = HexColor;
CleanHex.RemoveFromStart(TEXT("#"));
FColor Color = FColor::FromHex(CleanHex);
return FLinearColor(Color);
}
FString URalphaParameterBridge::ColorToHex(const FLinearColor& Color)
{
FColor SRGBColor = Color.ToFColor(true);
return FString::Printf(TEXT("#%02X%02X%02X"), SRGBColor.R, SRGBColor.G, SRGBColor.B);
}
TSharedPtr<FJsonObject> URalphaParameterBridge::GetAllParameters()
{
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
Result->SetObjectField(TEXT("post_process"), GetPostProcessParameters());
Result->SetObjectField(TEXT("directional_light"), GetDirectionalLightParameters());
Result->SetObjectField(TEXT("sky_light"), GetSkyLightParameters());
Result->SetObjectField(TEXT("fog"), GetFogParameters());
Result->SetObjectField(TEXT("camera"), GetCameraParameters());
return Result;
}
// ============================================================================
// Post-Process Volume
// ============================================================================
bool URalphaParameterBridge::SetPostProcessParameters(const TSharedPtr<FJsonObject>& Params)
{
APostProcessVolume* PPV = FindPostProcessVolume();
if (!PPV)
{
UE_LOG(LogRalphaMCP, Warning, TEXT("No PostProcessVolume found in scene"));
return false;
}
FPostProcessSettings& Settings = PPV->Settings;
// Exposure
if (Params->HasField(TEXT("exposure_compensation")))
{
Settings.bOverride_AutoExposureBias = true;
Settings.AutoExposureBias = Params->GetNumberField(TEXT("exposure_compensation"));
}
// Color Grading
if (Params->HasField(TEXT("color_saturation")))
{
Settings.bOverride_ColorSaturation = true;
float Sat = Params->GetNumberField(TEXT("color_saturation"));
Settings.ColorSaturation = FVector4(Sat, Sat, Sat, 1.0f);
}
if (Params->HasField(TEXT("color_contrast")))
{
Settings.bOverride_ColorContrast = true;
float Contrast = Params->GetNumberField(TEXT("color_contrast"));
Settings.ColorContrast = FVector4(Contrast, Contrast, Contrast, 1.0f);
}
// White Balance
if (Params->HasField(TEXT("white_balance_temp")))
{
Settings.bOverride_WhiteTemp = true;
Settings.WhiteTemp = Params->GetNumberField(TEXT("white_balance_temp"));
}
if (Params->HasField(TEXT("white_balance_tint")))
{
Settings.bOverride_WhiteTint = true;
Settings.WhiteTint = Params->GetNumberField(TEXT("white_balance_tint"));
}
// Bloom
if (Params->HasField(TEXT("bloom_intensity")))
{
Settings.bOverride_BloomIntensity = true;
Settings.BloomIntensity = Params->GetNumberField(TEXT("bloom_intensity"));
}
if (Params->HasField(TEXT("bloom_threshold")))
{
Settings.bOverride_BloomThreshold = true;
Settings.BloomThreshold = Params->GetNumberField(TEXT("bloom_threshold"));
}
// Vignette
if (Params->HasField(TEXT("vignette_intensity")))
{
Settings.bOverride_VignetteIntensity = true;
Settings.VignetteIntensity = Params->GetNumberField(TEXT("vignette_intensity"));
}
// Film Grain
if (Params->HasField(TEXT("film_grain_intensity")))
{
Settings.bOverride_FilmGrainIntensity = true;
Settings.FilmGrainIntensity = Params->GetNumberField(TEXT("film_grain_intensity"));
}
// Chromatic Aberration
if (Params->HasField(TEXT("chromatic_aberration_intensity")))
{
Settings.bOverride_SceneFringeIntensity = true;
Settings.SceneFringeIntensity = Params->GetNumberField(TEXT("chromatic_aberration_intensity"));
}
PPV->MarkPackageDirty();
return true;
}
TSharedPtr<FJsonObject> URalphaParameterBridge::GetPostProcessParameters()
{
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
APostProcessVolume* PPV = FindPostProcessVolume();
if (!PPV)
{
return Result;
}
const FPostProcessSettings& Settings = PPV->Settings;
Result->SetNumberField(TEXT("exposure_compensation"), Settings.AutoExposureBias);
Result->SetNumberField(TEXT("color_saturation"), Settings.ColorSaturation.X);
Result->SetNumberField(TEXT("color_contrast"), Settings.ColorContrast.X);
Result->SetNumberField(TEXT("white_balance_temp"), Settings.WhiteTemp);
Result->SetNumberField(TEXT("white_balance_tint"), Settings.WhiteTint);
Result->SetNumberField(TEXT("bloom_intensity"), Settings.BloomIntensity);
Result->SetNumberField(TEXT("bloom_threshold"), Settings.BloomThreshold);
Result->SetNumberField(TEXT("vignette_intensity"), Settings.VignetteIntensity);
Result->SetNumberField(TEXT("film_grain_intensity"), Settings.FilmGrainIntensity);
Result->SetNumberField(TEXT("chromatic_aberration_intensity"), Settings.SceneFringeIntensity);
return Result;
}
// ============================================================================
// Directional Light
// ============================================================================
bool URalphaParameterBridge::SetDirectionalLightParameters(const TSharedPtr<FJsonObject>& Params)
{
ADirectionalLight* Light = FindDirectionalLight();
if (!Light)
{
UE_LOG(LogRalphaMCP, Warning, TEXT("No DirectionalLight found in scene"));
return false;
}
UDirectionalLightComponent* LightComp = Cast<UDirectionalLightComponent>(Light->GetLightComponent());
if (!LightComp) return false;
if (Params->HasField(TEXT("intensity")))
{
LightComp->SetIntensity(Params->GetNumberField(TEXT("intensity")));
}
if (Params->HasField(TEXT("color")))
{
FLinearColor Color = ParseHexColor(Params->GetStringField(TEXT("color")));
LightComp->SetLightColor(Color);
}
if (Params->HasField(TEXT("temperature")))
{
LightComp->bUseTemperature = true;
LightComp->SetTemperature(Params->GetNumberField(TEXT("temperature")));
}
if (Params->HasField(TEXT("pitch")) || Params->HasField(TEXT("yaw")))
{
FRotator CurrentRot = Light->GetActorRotation();
if (Params->HasField(TEXT("pitch")))
{
CurrentRot.Pitch = Params->GetNumberField(TEXT("pitch"));
}
if (Params->HasField(TEXT("yaw")))
{
CurrentRot.Yaw = Params->GetNumberField(TEXT("yaw"));
}
Light->SetActorRotation(CurrentRot);
}
if (Params->HasField(TEXT("source_angle")))
{
LightComp->LightSourceAngle = Params->GetNumberField(TEXT("source_angle"));
}
if (Params->HasField(TEXT("source_soft_angle")))
{
LightComp->LightSourceSoftAngle = Params->GetNumberField(TEXT("source_soft_angle"));
}
LightComp->MarkRenderStateDirty();
return true;
}
TSharedPtr<FJsonObject> URalphaParameterBridge::GetDirectionalLightParameters()
{
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
ADirectionalLight* Light = FindDirectionalLight();
if (!Light) return Result;
UDirectionalLightComponent* LightComp = Cast<UDirectionalLightComponent>(Light->GetLightComponent());
if (!LightComp) return Result;
Result->SetNumberField(TEXT("intensity"), LightComp->Intensity);
Result->SetStringField(TEXT("color"), ColorToHex(LightComp->GetLightColor()));
Result->SetNumberField(TEXT("temperature"), LightComp->Temperature);
Result->SetNumberField(TEXT("pitch"), Light->GetActorRotation().Pitch);
Result->SetNumberField(TEXT("yaw"), Light->GetActorRotation().Yaw);
Result->SetNumberField(TEXT("source_angle"), LightComp->LightSourceAngle);
Result->SetNumberField(TEXT("source_soft_angle"), LightComp->LightSourceSoftAngle);
return Result;
}
// ============================================================================
// Sky Light
// ============================================================================
bool URalphaParameterBridge::SetSkyLightParameters(const TSharedPtr<FJsonObject>& Params)
{
ASkyLight* Light = FindSkyLight();
if (!Light)
{
UE_LOG(LogRalphaMCP, Warning, TEXT("No SkyLight found in scene"));
return false;
}
USkyLightComponent* LightComp = Light->GetLightComponent();
if (!LightComp) return false;
if (Params->HasField(TEXT("intensity")))
{
LightComp->SetIntensity(Params->GetNumberField(TEXT("intensity")));
}
if (Params->HasField(TEXT("color")))
{
FLinearColor Color = ParseHexColor(Params->GetStringField(TEXT("color")));
LightComp->SetLightColor(Color);
}
LightComp->MarkRenderStateDirty();
return true;
}
TSharedPtr<FJsonObject> URalphaParameterBridge::GetSkyLightParameters()
{
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
ASkyLight* Light = FindSkyLight();
if (!Light) return Result;
USkyLightComponent* LightComp = Light->GetLightComponent();
if (!LightComp) return Result;
Result->SetNumberField(TEXT("intensity"), LightComp->Intensity);
Result->SetStringField(TEXT("color"), ColorToHex(LightComp->GetLightColor()));
return Result;
}
// ============================================================================
// Exponential Height Fog
// ============================================================================
bool URalphaParameterBridge::SetFogParameters(const TSharedPtr<FJsonObject>& Params)
{
AExponentialHeightFog* Fog = FindExponentialHeightFog();
if (!Fog)
{
UE_LOG(LogRalphaMCP, Warning, TEXT("No ExponentialHeightFog found in scene"));
return false;
}
UExponentialHeightFogComponent* FogComp = Fog->GetComponent();
if (!FogComp) return false;
if (Params->HasField(TEXT("enabled")))
{
FogComp->SetVisibility(Params->GetBoolField(TEXT("enabled")));
}
if (Params->HasField(TEXT("fog_density")))
{
FogComp->SetFogDensity(Params->GetNumberField(TEXT("fog_density")));
}
if (Params->HasField(TEXT("fog_height_falloff")))
{
FogComp->SetFogHeightFalloff(Params->GetNumberField(TEXT("fog_height_falloff")));
}
if (Params->HasField(TEXT("fog_inscattering_color")))
{
FLinearColor Color = ParseHexColor(Params->GetStringField(TEXT("fog_inscattering_color")));
FogComp->SetFogInscatteringColor(Color);
}
if (Params->HasField(TEXT("fog_max_opacity")))
{
FogComp->SetFogMaxOpacity(Params->GetNumberField(TEXT("fog_max_opacity")));
}
if (Params->HasField(TEXT("start_distance")))
{
FogComp->SetStartDistance(Params->GetNumberField(TEXT("start_distance")));
}
if (Params->HasField(TEXT("volumetric_fog")))
{
FogComp->SetVolumetricFog(Params->GetBoolField(TEXT("volumetric_fog")));
}
if (Params->HasField(TEXT("volumetric_fog_scattering_distribution")))
{
FogComp->VolumetricFogScatteringDistribution = Params->GetNumberField(TEXT("volumetric_fog_scattering_distribution"));
}
FogComp->MarkRenderStateDirty();
return true;
}
TSharedPtr<FJsonObject> URalphaParameterBridge::GetFogParameters()
{
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
AExponentialHeightFog* Fog = FindExponentialHeightFog();
if (!Fog) return Result;
UExponentialHeightFogComponent* FogComp = Fog->GetComponent();
if (!FogComp) return Result;
Result->SetBoolField(TEXT("enabled"), FogComp->IsVisible());
Result->SetNumberField(TEXT("fog_density"), FogComp->FogDensity);
Result->SetNumberField(TEXT("fog_height_falloff"), FogComp->FogHeightFalloff);
Result->SetNumberField(TEXT("fog_max_opacity"), FogComp->FogMaxOpacity);
Result->SetNumberField(TEXT("start_distance"), FogComp->StartDistance);
Result->SetBoolField(TEXT("volumetric_fog"), FogComp->bEnableVolumetricFog);
Result->SetNumberField(TEXT("volumetric_fog_scattering_distribution"), FogComp->VolumetricFogScatteringDistribution);
return Result;
}
// ============================================================================
// Camera
// ============================================================================
bool URalphaParameterBridge::SetCameraParameters(const TSharedPtr<FJsonObject>& Params)
{
ACineCameraActor* Camera = FindCineCamera();
if (!Camera)
{
UE_LOG(LogRalphaMCP, Warning, TEXT("No CineCameraActor found in scene"));
return false;
}
UCineCameraComponent* CameraComp = Camera->GetCineCameraComponent();
if (!CameraComp) return false;
if (Params->HasField(TEXT("focal_length")))
{
CameraComp->SetCurrentFocalLength(Params->GetNumberField(TEXT("focal_length")));
}
if (Params->HasField(TEXT("aperture")))
{
CameraComp->CurrentAperture = Params->GetNumberField(TEXT("aperture"));
}
if (Params->HasField(TEXT("focus_distance")))
{
CameraComp->FocusSettings.ManualFocusDistance = Params->GetNumberField(TEXT("focus_distance"));
}
if (Params->HasField(TEXT("dof_enabled")))
{
CameraComp->PostProcessSettings.bOverride_DepthOfFieldFstop = Params->GetBoolField(TEXT("dof_enabled"));
}
if (Params->HasField(TEXT("motion_blur_amount")))
{
CameraComp->PostProcessSettings.bOverride_MotionBlurAmount = true;
CameraComp->PostProcessSettings.MotionBlurAmount = Params->GetNumberField(TEXT("motion_blur_amount"));
}
CameraComp->MarkRenderStateDirty();
return true;
}
TSharedPtr<FJsonObject> URalphaParameterBridge::GetCameraParameters()
{
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
ACineCameraActor* Camera = FindCineCamera();
if (!Camera) return Result;
UCineCameraComponent* CameraComp = Camera->GetCineCameraComponent();
if (!CameraComp) return Result;
Result->SetNumberField(TEXT("focal_length"), CameraComp->CurrentFocalLength);
Result->SetNumberField(TEXT("aperture"), CameraComp->CurrentAperture);
Result->SetNumberField(TEXT("focus_distance"), CameraComp->FocusSettings.ManualFocusDistance);
Result->SetNumberField(TEXT("motion_blur_amount"), CameraComp->PostProcessSettings.MotionBlurAmount);
return Result;
}
// ============================================================================
// Render Quality
// ============================================================================
bool URalphaParameterBridge::SetRenderQuality(const TSharedPtr<FJsonObject>& Settings)
{
// This would typically use console commands or project settings
// For now, we'll log what we would set
if (Settings->HasField(TEXT("lumen_quality")))
{
float Quality = Settings->GetNumberField(TEXT("lumen_quality"));
UE_LOG(LogRalphaMCP, Log, TEXT("Would set Lumen quality to: %f"), Quality);
// r.Lumen.ScreenTracing.MaxIterations, etc.
}
if (Settings->HasField(TEXT("shadow_quality")))
{
FString Quality = Settings->GetStringField(TEXT("shadow_quality"));
UE_LOG(LogRalphaMCP, Log, TEXT("Would set shadow quality to: %s"), *Quality);
// sg.ShadowQuality
}
if (Settings->HasField(TEXT("resolution_scale")))
{
float Scale = Settings->GetNumberField(TEXT("resolution_scale"));
UE_LOG(LogRalphaMCP, Log, TEXT("Would set resolution scale to: %f"), Scale);
// r.ScreenPercentage
}
return true;
}
// ============================================================================
// Asset Search
// ============================================================================
TArray<TSharedPtr<FJsonValue>> URalphaParameterBridge::SearchAssets(const FString& Query, const FString& AssetType, int32 MaxResults)
{
TArray<TSharedPtr<FJsonValue>> Results;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
FARFilter Filter;
Filter.bRecursivePaths = true;
Filter.PackagePaths.Add(TEXT("/Game"));
if (AssetType == TEXT("StaticMesh"))
{
Filter.ClassPaths.Add(UStaticMesh::StaticClass()->GetClassPathName());
}
// Add more type filters as needed
TArray<FAssetData> AssetList;
AssetRegistry.GetAssets(Filter, AssetList);
int32 Count = 0;
for (const FAssetData& Asset : AssetList)
{
if (Count >= MaxResults) break;
FString AssetName = Asset.AssetName.ToString();
if (AssetName.Contains(Query, ESearchCase::IgnoreCase))
{
TSharedPtr<FJsonObject> AssetObj = MakeShared<FJsonObject>();
AssetObj->SetStringField(TEXT("name"), AssetName);
AssetObj->SetStringField(TEXT("asset_path"), Asset.GetObjectPathString());
AssetObj->SetStringField(TEXT("class"), Asset.AssetClassPath.GetAssetName().ToString());
Results.Add(MakeShared<FJsonValueObject>(AssetObj));
Count++;
}
}
return Results;
}
TArray<TSharedPtr<FJsonValue>> URalphaParameterBridge::ListAssets(const FString& FolderPath, const FString& AssetType, bool bRecursive)
{
TArray<TSharedPtr<FJsonValue>> Results;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
FARFilter Filter;
Filter.bRecursivePaths = bRecursive;
Filter.PackagePaths.Add(*FolderPath);
TArray<FAssetData> AssetList;
AssetRegistry.GetAssets(Filter, AssetList);
for (const FAssetData& Asset : AssetList)
{
TSharedPtr<FJsonObject> AssetObj = MakeShared<FJsonObject>();
AssetObj->SetStringField(TEXT("name"), Asset.AssetName.ToString());
AssetObj->SetStringField(TEXT("asset_path"), Asset.GetObjectPathString());
AssetObj->SetStringField(TEXT("class"), Asset.AssetClassPath.GetAssetName().ToString());
Results.Add(MakeShared<FJsonValueObject>(AssetObj));
}
return Results;
}
// ============================================================================
// Actor Management
// ============================================================================
bool URalphaParameterBridge::SpawnActor(const FString& AssetPath, const FVector& Location, const FRotator& Rotation, float Scale, const FString& ActorLabel, FString& OutActorId)
{
UWorld* World = GetCurrentWorld();
if (!World) return false;
// Load the asset
UObject* LoadedAsset = StaticLoadObject(UStaticMesh::StaticClass(), nullptr, *AssetPath);
UStaticMesh* StaticMesh = Cast<UStaticMesh>(LoadedAsset);
if (!StaticMesh)
{
UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to load static mesh: %s"), *AssetPath);
return false;
}
// Spawn the actor
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AStaticMeshActor* NewActor = World->SpawnActor<AStaticMeshActor>(Location, Rotation, SpawnParams);
if (!NewActor)
{
UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to spawn actor"));
return false;
}
NewActor->GetStaticMeshComponent()->SetStaticMesh(StaticMesh);
NewActor->SetActorScale3D(FVector(Scale));
#if WITH_EDITOR
if (!ActorLabel.IsEmpty())
{
NewActor->SetActorLabel(ActorLabel);
}
#endif
OutActorId = NewActor->GetName();
return true;
}
bool URalphaParameterBridge::DeleteActor(const FString& ActorId)
{
UWorld* World = GetCurrentWorld();
if (!World) return false;
for (TActorIterator<AActor> It(World); It; ++It)
{
if (It->GetName() == ActorId)
{
World->DestroyActor(*It);
return true;
}
}
return false;
}
TArray<TSharedPtr<FJsonValue>> URalphaParameterBridge::ListActors(const FString& ClassFilter, const FString& NameFilter, bool bIncludeTransforms)
{
TArray<TSharedPtr<FJsonValue>> Results;
UWorld* World = GetCurrentWorld();
if (!World) return Results;
for (TActorIterator<AActor> It(World); It; ++It)
{
AActor* Actor = *It;
// Apply filters
if (!ClassFilter.IsEmpty() && !Actor->GetClass()->GetName().Contains(ClassFilter))
{
continue;
}
#if WITH_EDITOR
if (!NameFilter.IsEmpty() && !Actor->GetActorLabel().Contains(NameFilter))
{
continue;
}
#else
if (!NameFilter.IsEmpty() && !Actor->GetName().Contains(NameFilter))
{
continue;
}
#endif
TSharedPtr<FJsonObject> ActorObj = MakeShared<FJsonObject>();
ActorObj->SetStringField(TEXT("actor_id"), Actor->GetName());
#if WITH_EDITOR
ActorObj->SetStringField(TEXT("label"), Actor->GetActorLabel());
#else
ActorObj->SetStringField(TEXT("label"), Actor->GetName());
#endif
ActorObj->SetStringField(TEXT("class"), Actor->GetClass()->GetName());
if (bIncludeTransforms)
{
FVector Location = Actor->GetActorLocation();
FRotator Rotation = Actor->GetActorRotation();
FVector Scale = Actor->GetActorScale3D();
TSharedPtr<FJsonObject> TransformObj = MakeShared<FJsonObject>();
TSharedPtr<FJsonObject> LocObj = MakeShared<FJsonObject>();
LocObj->SetNumberField(TEXT("x"), Location.X);
LocObj->SetNumberField(TEXT("y"), Location.Y);
LocObj->SetNumberField(TEXT("z"), Location.Z);
TransformObj->SetObjectField(TEXT("location"), LocObj);
TSharedPtr<FJsonObject> RotObj = MakeShared<FJsonObject>();
RotObj->SetNumberField(TEXT("pitch"), Rotation.Pitch);
RotObj->SetNumberField(TEXT("yaw"), Rotation.Yaw);
RotObj->SetNumberField(TEXT("roll"), Rotation.Roll);
TransformObj->SetObjectField(TEXT("rotation"), RotObj);
TSharedPtr<FJsonObject> ScaleObj = MakeShared<FJsonObject>();
ScaleObj->SetNumberField(TEXT("x"), Scale.X);
ScaleObj->SetNumberField(TEXT("y"), Scale.Y);
ScaleObj->SetNumberField(TEXT("z"), Scale.Z);
TransformObj->SetObjectField(TEXT("scale"), ScaleObj);
ActorObj->SetObjectField(TEXT("transform"), TransformObj);
}
Results.Add(MakeShared<FJsonValueObject>(ActorObj));
}
return Results;
}
bool URalphaParameterBridge::SetActorTransform(const FString& ActorId, const FVector* Location, const FRotator* Rotation, const float* Scale, bool bRelative)
{
UWorld* World = GetCurrentWorld();
if (!World) return false;
for (TActorIterator<AActor> It(World); It; ++It)
{
if (It->GetName() == ActorId)
{
AActor* Actor = *It;
if (Location)
{
if (bRelative)
{
Actor->SetActorLocation(Actor->GetActorLocation() + *Location);
}
else
{
Actor->SetActorLocation(*Location);
}
}
if (Rotation)
{
if (bRelative)
{
Actor->SetActorRotation(Actor->GetActorRotation() + *Rotation);
}
else
{
Actor->SetActorRotation(*Rotation);
}
}
if (Scale)
{
Actor->SetActorScale3D(FVector(*Scale));
}
return true;
}
}
return false;
}
TSharedPtr<FJsonObject> URalphaParameterBridge::GetActorDetails(const FString& ActorId, bool bIncludeComponents, bool bIncludeMaterials)
{
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
UWorld* World = GetCurrentWorld();
if (!World) return Result;
for (TActorIterator<AActor> It(World); It; ++It)
{
if (It->GetName() == ActorId)
{
AActor* Actor = *It;
Result->SetStringField(TEXT("actor_id"), Actor->GetName());
#if WITH_EDITOR
Result->SetStringField(TEXT("label"), Actor->GetActorLabel());
#else
Result->SetStringField(TEXT("label"), Actor->GetName());
#endif
Result->SetStringField(TEXT("class"), Actor->GetClass()->GetName());
// Transform
FVector Location = Actor->GetActorLocation();
FRotator Rotation = Actor->GetActorRotation();
FVector Scale = Actor->GetActorScale3D();
TSharedPtr<FJsonObject> TransformObj = MakeShared<FJsonObject>();
TSharedPtr<FJsonObject> LocObj = MakeShared<FJsonObject>();
LocObj->SetNumberField(TEXT("x"), Location.X);
LocObj->SetNumberField(TEXT("y"), Location.Y);
LocObj->SetNumberField(TEXT("z"), Location.Z);
TransformObj->SetObjectField(TEXT("location"), LocObj);
TSharedPtr<FJsonObject> RotObj = MakeShared<FJsonObject>();
RotObj->SetNumberField(TEXT("pitch"), Rotation.Pitch);
RotObj->SetNumberField(TEXT("yaw"), Rotation.Yaw);
RotObj->SetNumberField(TEXT("roll"), Rotation.Roll);
TransformObj->SetObjectField(TEXT("rotation"), RotObj);
Result->SetObjectField(TEXT("transform"), TransformObj);
if (bIncludeComponents)
{
TArray<TSharedPtr<FJsonValue>> ComponentsArray;
TArray<UActorComponent*> Components;
Actor->GetComponents(Components);
for (UActorComponent* Component : Components)
{
TSharedPtr<FJsonObject> CompObj = MakeShared<FJsonObject>();
CompObj->SetStringField(TEXT("name"), Component->GetName());
CompObj->SetStringField(TEXT("class"), Component->GetClass()->GetName());
ComponentsArray.Add(MakeShared<FJsonValueObject>(CompObj));
}
Result->SetArrayField(TEXT("components"), ComponentsArray);
}
return Result;
}
}
return Result;
}
// ============================================================================
// Scene Setup
// ============================================================================
TSharedPtr<FJsonObject> URalphaParameterBridge::SetupScene()
{
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
TArray<FString> CreatedActors;
TArray<FString> ExistingActors;
UWorld* World = GetCurrentWorld();
if (!World)
{
Result->SetBoolField(TEXT("success"), false);
Result->SetStringField(TEXT("error"), TEXT("No world available"));
return Result;
}
// Check/Create PostProcessVolume
if (!FindPostProcessVolume())
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
APostProcessVolume* PPV = World->SpawnActor<APostProcessVolume>(FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams);
if (PPV)
{
PPV->bUnbound = true;
PPV->Settings.bOverride_AutoExposureBias = true;
PPV->Settings.AutoExposureBias = 0.0f;
#if WITH_EDITOR
PPV->SetActorLabel(TEXT("Ralpha_PostProcess"));
#endif
CreatedActors.Add(TEXT("PostProcessVolume"));
}
}
else
{
ExistingActors.Add(TEXT("PostProcessVolume"));
}
// Check/Create DirectionalLight
if (!FindDirectionalLight())
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ADirectionalLight* Light = World->SpawnActor<ADirectionalLight>(FVector::ZeroVector, FRotator(-45.0f, 0.0f, 0.0f), SpawnParams);
if (Light)
{
#if WITH_EDITOR
Light->SetActorLabel(TEXT("Ralpha_Sun"));
#endif
CreatedActors.Add(TEXT("DirectionalLight"));
}
}
else
{
ExistingActors.Add(TEXT("DirectionalLight"));
}
// Check/Create SkyLight
if (!FindSkyLight())
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ASkyLight* Light = World->SpawnActor<ASkyLight>(FVector(0.0f, 0.0f, 1000.0f), FRotator::ZeroRotator, SpawnParams);
if (Light)
{
#if WITH_EDITOR
Light->SetActorLabel(TEXT("Ralpha_SkyLight"));
#endif
CreatedActors.Add(TEXT("SkyLight"));
}
}
else
{
ExistingActors.Add(TEXT("SkyLight"));
}
// Check/Create ExponentialHeightFog
if (!FindExponentialHeightFog())
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
AExponentialHeightFog* Fog = World->SpawnActor<AExponentialHeightFog>(FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams);
if (Fog)
{
#if WITH_EDITOR
Fog->SetActorLabel(TEXT("Ralpha_Fog"));
#endif
CreatedActors.Add(TEXT("ExponentialHeightFog"));
}
}
else
{
ExistingActors.Add(TEXT("ExponentialHeightFog"));
}
// Check/Create CineCameraActor
if (!FindCineCamera())
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ACineCameraActor* Camera = World->SpawnActor<ACineCameraActor>(FVector(0.0f, -500.0f, 200.0f), FRotator(0.0f, 90.0f, 0.0f), SpawnParams);
if (Camera)
{
#if WITH_EDITOR
Camera->SetActorLabel(TEXT("Ralpha_Camera"));
#endif
CreatedActors.Add(TEXT("CineCameraActor"));
}
}
else
{
ExistingActors.Add(TEXT("CineCameraActor"));
}
Result->SetBoolField(TEXT("success"), true);
TArray<TSharedPtr<FJsonValue>> CreatedArray;
for (const FString& Actor : CreatedActors)
{
CreatedArray.Add(MakeShared<FJsonValueString>(Actor));
}
Result->SetArrayField(TEXT("created"), CreatedArray);
TArray<TSharedPtr<FJsonValue>> ExistingArray;
for (const FString& Actor : ExistingActors)
{
ExistingArray.Add(MakeShared<FJsonValueString>(Actor));
}
Result->SetArrayField(TEXT("existing"), ExistingArray);
return Result;
}