// 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 "Components/SkyAtmosphereComponent.h" #include "Components/VolumetricCloudComponent.h" #include "Dom/JsonObject.h" #include "Dom/JsonValue.h" #include "Serialization/JsonSerializer.h" #include "Materials/MaterialInstanceDynamic.h" #include "WaterBodyOceanActor.h" #include "WaterBodyActor.h" #include "WaterBodyComponent.h" #include "GerstnerWaterWaves.h" #include "Landscape.h" #include "LandscapeProxy.h" #include "LandscapeInfo.h" #include "Engine/PointLight.h" #include "Engine/SpotLight.h" #include "Engine/RectLight.h" #include "Components/PointLightComponent.h" #include "Components/SpotLightComponent.h" #include "Components/RectLightComponent.h" #include "Animation/SkeletalMeshActor.h" #include "Components/SkeletalMeshComponent.h" #include "Animation/AnimSequence.h" #include "Animation/AnimSingleNodeInstance.h" #include "Engine/SkeletalMesh.h" #include "Engine/SphereReflectionCapture.h" #include "Engine/BoxReflectionCapture.h" #include "Components/ReflectionCaptureComponent.h" #include "Components/SphereReflectionCaptureComponent.h" #include "Components/BoxReflectionCaptureComponent.h" #include "Engine/TextureLightProfile.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 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 It(World); It; ++It) { return *It; } return nullptr; } ADirectionalLight* URalphaParameterBridge::FindDirectionalLight() { UWorld* World = GetCurrentWorld(); if (!World) return nullptr; for (TActorIterator It(World); It; ++It) { return *It; } return nullptr; } ASkyLight* URalphaParameterBridge::FindSkyLight() { UWorld* World = GetCurrentWorld(); if (!World) return nullptr; for (TActorIterator It(World); It; ++It) { return *It; } return nullptr; } AExponentialHeightFog* URalphaParameterBridge::FindExponentialHeightFog() { UWorld* World = GetCurrentWorld(); if (!World) return nullptr; for (TActorIterator It(World); It; ++It) { return *It; } return nullptr; } ACineCameraActor* URalphaParameterBridge::FindCineCamera() { UWorld* World = GetCurrentWorld(); if (!World) return nullptr; for (TActorIterator 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 URalphaParameterBridge::GetAllParameters() { TSharedPtr Result = MakeShared(); 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& 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 URalphaParameterBridge::GetPostProcessParameters() { TSharedPtr Result = MakeShared(); 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& Params) { ADirectionalLight* Light = FindDirectionalLight(); if (!Light) { UE_LOG(LogRalphaMCP, Warning, TEXT("No DirectionalLight found in scene")); return false; } UDirectionalLightComponent* LightComp = Cast(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(); // Refresh any BP_Sky_Sphere to update based on new sun position RefreshSkySphere(); return true; } void URalphaParameterBridge::RefreshSkySphere() { UWorld* World = GetCurrentWorld(); if (!World) return; ADirectionalLight* DirectionalLight = FindDirectionalLight(); // Find BP_Sky_Sphere and refresh it for (TActorIterator It(World); It; ++It) { FString ClassName = It->GetClass()->GetName(); if (ClassName.Contains(TEXT("BP_Sky"))) { AActor* SkySphere = *It; // Ensure directional light is linked if (DirectionalLight) { FProperty* DirLightProp = SkySphere->GetClass()->FindPropertyByName(TEXT("Directional light actor")); if (!DirLightProp) { DirLightProp = SkySphere->GetClass()->FindPropertyByName(TEXT("DirectionalLightActor")); } if (DirLightProp) { FObjectProperty* ObjProp = CastField(DirLightProp); if (ObjProp) { ObjProp->SetObjectPropertyValue_InContainer(SkySphere, DirectionalLight); } } } // Call RefreshMaterial function UFunction* RefreshFunc = SkySphere->FindFunction(TEXT("RefreshMaterial")); if (RefreshFunc) { SkySphere->ProcessEvent(RefreshFunc, nullptr); UE_LOG(LogRalphaMCP, Log, TEXT("Refreshed BP_Sky_Sphere material")); } break; } } } TSharedPtr URalphaParameterBridge::GetDirectionalLightParameters() { TSharedPtr Result = MakeShared(); ADirectionalLight* Light = FindDirectionalLight(); if (!Light) return Result; UDirectionalLightComponent* LightComp = Cast(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& 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 URalphaParameterBridge::GetSkyLightParameters() { TSharedPtr Result = MakeShared(); 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& 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 URalphaParameterBridge::GetFogParameters() { TSharedPtr Result = MakeShared(); 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& 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; // Camera location const TSharedPtr* LocationObj; if (Params->TryGetObjectField(TEXT("location"), LocationObj)) { FVector Location = Camera->GetActorLocation(); if ((*LocationObj)->HasField(TEXT("x"))) Location.X = (*LocationObj)->GetNumberField(TEXT("x")); if ((*LocationObj)->HasField(TEXT("y"))) Location.Y = (*LocationObj)->GetNumberField(TEXT("y")); if ((*LocationObj)->HasField(TEXT("z"))) Location.Z = (*LocationObj)->GetNumberField(TEXT("z")); Camera->SetActorLocation(Location); } // Camera rotation const TSharedPtr* RotationObj; if (Params->TryGetObjectField(TEXT("rotation"), RotationObj)) { FRotator Rotation = Camera->GetActorRotation(); if ((*RotationObj)->HasField(TEXT("pitch"))) Rotation.Pitch = (*RotationObj)->GetNumberField(TEXT("pitch")); if ((*RotationObj)->HasField(TEXT("yaw"))) Rotation.Yaw = (*RotationObj)->GetNumberField(TEXT("yaw")); if ((*RotationObj)->HasField(TEXT("roll"))) Rotation.Roll = (*RotationObj)->GetNumberField(TEXT("roll")); Camera->SetActorRotation(Rotation); } 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 URalphaParameterBridge::GetCameraParameters() { TSharedPtr Result = MakeShared(); 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& 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> URalphaParameterBridge::SearchAssets(const FString& Query, const FString& AssetType, int32 MaxResults) { TArray> Results; FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("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 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 AssetObj = MakeShared(); AssetObj->SetStringField(TEXT("name"), AssetName); AssetObj->SetStringField(TEXT("asset_path"), Asset.GetObjectPathString()); AssetObj->SetStringField(TEXT("class"), Asset.AssetClassPath.GetAssetName().ToString()); Results.Add(MakeShared(AssetObj)); Count++; } } return Results; } TArray> URalphaParameterBridge::ListAssets(const FString& FolderPath, const FString& AssetType, bool bRecursive) { TArray> Results; FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked("AssetRegistry"); IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); FARFilter Filter; Filter.bRecursivePaths = bRecursive; Filter.PackagePaths.Add(*FolderPath); TArray AssetList; AssetRegistry.GetAssets(Filter, AssetList); for (const FAssetData& Asset : AssetList) { TSharedPtr AssetObj = MakeShared(); 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(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(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(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 It(World); It; ++It) { if (It->GetName() == ActorId) { World->DestroyActor(*It); return true; } } return false; } bool URalphaParameterBridge::SpawnOcean(const FVector& Location, float Size, FString& OutActorId) { UWorld* World = GetCurrentWorld(); if (!World) return false; FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; AWaterBodyOcean* Ocean = World->SpawnActor(Location, FRotator::ZeroRotator, SpawnParams); if (!Ocean) { UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to spawn WaterBodyOcean")); return false; } #if WITH_EDITOR Ocean->SetActorLabel(TEXT("Ralpha_Ocean")); #endif OutActorId = Ocean->GetName(); UE_LOG(LogRalphaMCP, Log, TEXT("Spawned WaterBodyOcean: %s at (%f, %f, %f)"), *OutActorId, Location.X, Location.Y, Location.Z); return true; } bool URalphaParameterBridge::SetOceanWaves(float WaveAmplitude, float WaveLength, float Steepness, int32 NumWaves) { UWorld* World = GetCurrentWorld(); if (!World) return false; // Find the ocean actor AWaterBodyOcean* Ocean = nullptr; for (TActorIterator It(World); It; ++It) { Ocean = *It; break; } if (!Ocean) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetOceanWaves: No WaterBodyOcean found in scene")); return false; } // Create a new GerstnerWaterWaves with a simple generator UGerstnerWaterWaves* WaterWaves = NewObject(Ocean); if (!WaterWaves) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetOceanWaves: Failed to create GerstnerWaterWaves")); return false; } // Create and configure the wave generator UGerstnerWaterWaveGeneratorSimple* WaveGenerator = NewObject(WaterWaves); if (WaveGenerator) { WaveGenerator->NumWaves = NumWaves; WaveGenerator->MinWavelength = WaveLength * 0.5f; WaveGenerator->MaxWavelength = WaveLength * 2.0f; WaveGenerator->MinAmplitude = WaveAmplitude * 0.3f; WaveGenerator->MaxAmplitude = WaveAmplitude; WaveGenerator->SmallWaveSteepness = Steepness; WaveGenerator->LargeWaveSteepness = Steepness * 0.5f; WaveGenerator->WindAngleDeg = -90.0f; // Wind from west WaveGenerator->DirectionAngularSpreadDeg = 90.0f; WaterWaves->GerstnerWaveGenerator = WaveGenerator; } // Recompute waves based on the generator settings WaterWaves->RecomputeWaves(false); // Assign to water body actor Ocean->SetWaterWaves(WaterWaves); UE_LOG(LogRalphaMCP, Log, TEXT("SetOceanWaves: Configured %d waves (Amplitude=%f, WaveLength=%f, Steepness=%f)"), NumWaves, WaveAmplitude, WaveLength, Steepness); return true; } bool URalphaParameterBridge::CreateLandscape(float Size, float HeightOffset) { UWorld* World = GetCurrentWorld(); if (!World) return false; // Check if landscape already exists for (TActorIterator It(World); It; ++It) { UE_LOG(LogRalphaMCP, Log, TEXT("CreateLandscape: Landscape already exists")); return true; } // Create a minimal landscape // Note: Creating landscapes programmatically is complex, using simpler approach FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; FVector Location(0.0f, 0.0f, HeightOffset); ALandscape* Landscape = World->SpawnActor(Location, FRotator::ZeroRotator, SpawnParams); if (!Landscape) { UE_LOG(LogRalphaMCP, Warning, TEXT("CreateLandscape: Failed to spawn landscape actor")); return false; } #if WITH_EDITOR Landscape->SetActorLabel(TEXT("Ralpha_Landscape")); #endif UE_LOG(LogRalphaMCP, Log, TEXT("CreateLandscape: Created landscape at Z=%f"), HeightOffset); return true; } bool URalphaParameterBridge::SetActorMaterial(const FString& ActorId, const FString& MaterialPath, int32 MaterialIndex) { UWorld* World = GetCurrentWorld(); if (!World) return false; // Find the actor AActor* TargetActor = nullptr; for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { TargetActor = *It; break; } } if (!TargetActor) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorMaterial: Actor not found: %s"), *ActorId); return false; } // Load the material UMaterialInterface* Material = Cast(StaticLoadObject(UMaterialInterface::StaticClass(), nullptr, *MaterialPath)); if (!Material) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorMaterial: Failed to load material: %s"), *MaterialPath); return false; } // Try to find a mesh component to apply the material to AStaticMeshActor* MeshActor = Cast(TargetActor); if (MeshActor && MeshActor->GetStaticMeshComponent()) { MeshActor->GetStaticMeshComponent()->SetMaterial(MaterialIndex, Material); UE_LOG(LogRalphaMCP, Log, TEXT("SetActorMaterial: Applied material %s to %s"), *MaterialPath, *ActorId); return true; } // Try generic primitive component UPrimitiveComponent* PrimComp = TargetActor->FindComponentByClass(); if (PrimComp) { PrimComp->SetMaterial(MaterialIndex, Material); UE_LOG(LogRalphaMCP, Log, TEXT("SetActorMaterial: Applied material %s to %s (primitive)"), *MaterialPath, *ActorId); return true; } UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorMaterial: No suitable component found on %s"), *ActorId); return false; } bool URalphaParameterBridge::SetActorSimpleMaterial(const FString& ActorId, const FLinearColor& BaseColor, float Metallic, float Roughness, float Opacity) { UWorld* World = GetCurrentWorld(); if (!World) return false; // Find the actor AActor* TargetActor = nullptr; for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { TargetActor = *It; break; } } if (!TargetActor) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorSimpleMaterial: Actor not found: %s"), *ActorId); return false; } // Load the basic material from engine content - M_Basic_Wall is a simple material that supports our parameters UMaterialInterface* BaseMaterial = Cast(StaticLoadObject(UMaterialInterface::StaticClass(), nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial"))); if (!BaseMaterial) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorSimpleMaterial: Failed to load base material")); return false; } // Create a dynamic material instance UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(BaseMaterial, TargetActor); if (!DynMaterial) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorSimpleMaterial: Failed to create dynamic material")); return false; } // Set material parameters - BasicShapeMaterial uses specific parameter names // Try common parameter names for base color DynMaterial->SetVectorParameterValue(TEXT("Color"), BaseColor); DynMaterial->SetVectorParameterValue(TEXT("BaseColor"), BaseColor); DynMaterial->SetVectorParameterValue(TEXT("Base Color"), BaseColor); // Try common scalar parameters DynMaterial->SetScalarParameterValue(TEXT("Metallic"), Metallic); DynMaterial->SetScalarParameterValue(TEXT("Roughness"), Roughness); DynMaterial->SetScalarParameterValue(TEXT("Opacity"), Opacity); // Apply to the actor's mesh component AStaticMeshActor* MeshActor = Cast(TargetActor); if (MeshActor && MeshActor->GetStaticMeshComponent()) { MeshActor->GetStaticMeshComponent()->SetMaterial(0, DynMaterial); UE_LOG(LogRalphaMCP, Log, TEXT("SetActorSimpleMaterial: Applied dynamic material to %s (Color: R=%.2f G=%.2f B=%.2f, Metallic=%.2f, Roughness=%.2f)"), *ActorId, BaseColor.R, BaseColor.G, BaseColor.B, Metallic, Roughness); return true; } // Try generic primitive component UPrimitiveComponent* PrimComp = TargetActor->FindComponentByClass(); if (PrimComp) { PrimComp->SetMaterial(0, DynMaterial); UE_LOG(LogRalphaMCP, Log, TEXT("SetActorSimpleMaterial: Applied dynamic material to %s (primitive)"), *ActorId); return true; } UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorSimpleMaterial: No suitable component found on %s"), *ActorId); return false; } bool URalphaParameterBridge::SetActorEmissiveMaterial(const FString& ActorId, const FLinearColor& EmissiveColor, float EmissiveIntensity) { UWorld* World = GetCurrentWorld(); if (!World) return false; // Find the actor AActor* TargetActor = nullptr; for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { TargetActor = *It; break; } } if (!TargetActor) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorEmissiveMaterial: Actor not found: %s"), *ActorId); return false; } // Create a new material from scratch that is unlit/emissive // We need to use a material that supports emissive - let's try the basic shape material // and set it to use emissive only UMaterialInterface* BaseMaterial = Cast(StaticLoadObject(UMaterialInterface::StaticClass(), nullptr, TEXT("/Engine/EngineMaterials/DefaultMaterial.DefaultMaterial"))); if (!BaseMaterial) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorEmissiveMaterial: Failed to load default material")); return false; } // Create a dynamic material instance UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(BaseMaterial, TargetActor); if (!DynMaterial) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorEmissiveMaterial: Failed to create dynamic material")); return false; } // Calculate emissive color with intensity FLinearColor ScaledEmissive = EmissiveColor * EmissiveIntensity; // Try to set emissive parameters with common names DynMaterial->SetVectorParameterValue(TEXT("EmissiveColor"), ScaledEmissive); DynMaterial->SetVectorParameterValue(TEXT("Emissive"), ScaledEmissive); DynMaterial->SetVectorParameterValue(TEXT("Emissive Color"), ScaledEmissive); // Also try setting base color for materials that blend base with emissive DynMaterial->SetVectorParameterValue(TEXT("BaseColor"), EmissiveColor); DynMaterial->SetVectorParameterValue(TEXT("Color"), EmissiveColor); // Apply to the actor's mesh component AStaticMeshActor* MeshActor = Cast(TargetActor); if (MeshActor && MeshActor->GetStaticMeshComponent()) { MeshActor->GetStaticMeshComponent()->SetMaterial(0, DynMaterial); UE_LOG(LogRalphaMCP, Log, TEXT("SetActorEmissiveMaterial: Applied emissive material to %s (Color: R=%.2f G=%.2f B=%.2f, Intensity=%.2f)"), *ActorId, EmissiveColor.R, EmissiveColor.G, EmissiveColor.B, EmissiveIntensity); return true; } // Try generic primitive component UPrimitiveComponent* PrimComp = TargetActor->FindComponentByClass(); if (PrimComp) { PrimComp->SetMaterial(0, DynMaterial); UE_LOG(LogRalphaMCP, Log, TEXT("SetActorEmissiveMaterial: Applied emissive material to %s (primitive)"), *ActorId); return true; } UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorEmissiveMaterial: No suitable component found on %s"), *ActorId); return false; } TArray> URalphaParameterBridge::ListActors(const FString& ClassFilter, const FString& NameFilter, bool bIncludeTransforms) { TArray> Results; UWorld* World = GetCurrentWorld(); if (!World) return Results; for (TActorIterator 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 ActorObj = MakeShared(); 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 TransformObj = MakeShared(); TSharedPtr LocObj = MakeShared(); LocObj->SetNumberField(TEXT("x"), Location.X); LocObj->SetNumberField(TEXT("y"), Location.Y); LocObj->SetNumberField(TEXT("z"), Location.Z); TransformObj->SetObjectField(TEXT("location"), LocObj); TSharedPtr RotObj = MakeShared(); RotObj->SetNumberField(TEXT("pitch"), Rotation.Pitch); RotObj->SetNumberField(TEXT("yaw"), Rotation.Yaw); RotObj->SetNumberField(TEXT("roll"), Rotation.Roll); TransformObj->SetObjectField(TEXT("rotation"), RotObj); TSharedPtr ScaleObj = MakeShared(); 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(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 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 URalphaParameterBridge::GetActorDetails(const FString& ActorId, bool bIncludeComponents, bool bIncludeMaterials) { TSharedPtr Result = MakeShared(); UWorld* World = GetCurrentWorld(); if (!World) return Result; for (TActorIterator 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 TransformObj = MakeShared(); TSharedPtr LocObj = MakeShared(); LocObj->SetNumberField(TEXT("x"), Location.X); LocObj->SetNumberField(TEXT("y"), Location.Y); LocObj->SetNumberField(TEXT("z"), Location.Z); TransformObj->SetObjectField(TEXT("location"), LocObj); TSharedPtr RotObj = MakeShared(); 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> ComponentsArray; TArray Components; Actor->GetComponents(Components); for (UActorComponent* Component : Components) { TSharedPtr CompObj = MakeShared(); CompObj->SetStringField(TEXT("name"), Component->GetName()); CompObj->SetStringField(TEXT("class"), Component->GetClass()->GetName()); ComponentsArray.Add(MakeShared(CompObj)); } Result->SetArrayField(TEXT("components"), ComponentsArray); } return Result; } } return Result; } // ============================================================================ // Scene Setup // ============================================================================ TSharedPtr URalphaParameterBridge::SetupScene() { TSharedPtr Result = MakeShared(); TArray CreatedActors; TArray 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(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 ADirectionalLight* Sun = FindDirectionalLight(); if (!Sun) { FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; Sun = World->SpawnActor(FVector::ZeroVector, FRotator(-45.0f, 0.0f, 0.0f), SpawnParams); if (Sun) { #if WITH_EDITOR Sun->SetActorLabel(TEXT("Ralpha_Sun")); #endif CreatedActors.Add(TEXT("DirectionalLight")); } } else { ExistingActors.Add(TEXT("DirectionalLight")); } // Configure DirectionalLight as atmosphere sun for visible sun disc if (Sun) { UDirectionalLightComponent* LightComp = Cast(Sun->GetLightComponent()); if (LightComp) { LightComp->SetAtmosphereSunLight(true); LightComp->SetAtmosphereSunLightIndex(0); } } // Check/Create SkyLight if (!FindSkyLight()) { FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ASkyLight* Light = World->SpawnActor(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(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 Sky Atmosphere (physically-based sky) bool bHasSkyAtmosphere = false; for (TActorIterator It(World); It; ++It) { bHasSkyAtmosphere = true; break; } if (!bHasSkyAtmosphere) { FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ASkyAtmosphere* SkyAtmo = World->SpawnActor(FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams); if (SkyAtmo) { #if WITH_EDITOR SkyAtmo->SetActorLabel(TEXT("Ralpha_SkyAtmosphere")); #endif CreatedActors.Add(TEXT("SkyAtmosphere")); } } else { ExistingActors.Add(TEXT("SkyAtmosphere")); } // Check/Create Volumetric Cloud bool bHasVolumetricCloud = false; for (TActorIterator It(World); It; ++It) { bHasVolumetricCloud = true; break; } if (!bHasVolumetricCloud) { FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; AVolumetricCloud* Cloud = World->SpawnActor(FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams); if (Cloud) { #if WITH_EDITOR Cloud->SetActorLabel(TEXT("Ralpha_VolumetricCloud")); #endif CreatedActors.Add(TEXT("VolumetricCloud")); } } else { ExistingActors.Add(TEXT("VolumetricCloud")); } // Check/Create CineCameraActor if (!FindCineCamera()) { FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ACineCameraActor* Camera = World->SpawnActor(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> CreatedArray; for (const FString& Actor : CreatedActors) { CreatedArray.Add(MakeShared(Actor)); } Result->SetArrayField(TEXT("created"), CreatedArray); TArray> ExistingArray; for (const FString& Actor : ExistingActors) { ExistingArray.Add(MakeShared(Actor)); } Result->SetArrayField(TEXT("existing"), ExistingArray); return Result; } // ============================================================================ // Point Light // ============================================================================ bool URalphaParameterBridge::SpawnPointLight(const FVector& Location, float Intensity, const FLinearColor& Color, float AttenuationRadius, const FString& ActorLabel, FString& OutActorId) { UWorld* World = GetCurrentWorld(); if (!World) return false; FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; APointLight* Light = World->SpawnActor(Location, FRotator::ZeroRotator, SpawnParams); if (!Light) { UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to spawn PointLight")); return false; } UPointLightComponent* LightComp = Light->PointLightComponent; if (LightComp) { LightComp->SetIntensity(Intensity); LightComp->SetLightColor(Color); LightComp->SetAttenuationRadius(AttenuationRadius); } #if WITH_EDITOR if (!ActorLabel.IsEmpty()) { Light->SetActorLabel(ActorLabel); } #endif OutActorId = Light->GetName(); UE_LOG(LogRalphaMCP, Log, TEXT("Spawned PointLight: %s at (%f, %f, %f)"), *OutActorId, Location.X, Location.Y, Location.Z); return true; } bool URalphaParameterBridge::SetPointLightParameters(const FString& ActorId, const TSharedPtr& Params) { UWorld* World = GetCurrentWorld(); if (!World) return false; APointLight* Light = nullptr; for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { Light = *It; break; } } if (!Light) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetPointLightParameters: PointLight not found: %s"), *ActorId); return false; } UPointLightComponent* LightComp = Light->PointLightComponent; 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("attenuation_radius"))) { LightComp->SetAttenuationRadius(Params->GetNumberField(TEXT("attenuation_radius"))); } if (Params->HasField(TEXT("source_radius"))) { LightComp->SourceRadius = Params->GetNumberField(TEXT("source_radius")); } if (Params->HasField(TEXT("soft_source_radius"))) { LightComp->SoftSourceRadius = Params->GetNumberField(TEXT("soft_source_radius")); } if (Params->HasField(TEXT("temperature"))) { LightComp->bUseTemperature = true; LightComp->SetTemperature(Params->GetNumberField(TEXT("temperature"))); } // Location update const TSharedPtr* LocationObj; if (Params->TryGetObjectField(TEXT("location"), LocationObj)) { FVector Location = Light->GetActorLocation(); if ((*LocationObj)->HasField(TEXT("x"))) Location.X = (*LocationObj)->GetNumberField(TEXT("x")); if ((*LocationObj)->HasField(TEXT("y"))) Location.Y = (*LocationObj)->GetNumberField(TEXT("y")); if ((*LocationObj)->HasField(TEXT("z"))) Location.Z = (*LocationObj)->GetNumberField(TEXT("z")); Light->SetActorLocation(Location); } LightComp->MarkRenderStateDirty(); return true; } // ============================================================================ // Spot Light // ============================================================================ bool URalphaParameterBridge::SpawnSpotLight(const FVector& Location, const FRotator& Rotation, float Intensity, const FLinearColor& Color, float AttenuationRadius, float InnerConeAngle, float OuterConeAngle, const FString& ActorLabel, FString& OutActorId) { UWorld* World = GetCurrentWorld(); if (!World) return false; FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ASpotLight* Light = World->SpawnActor(Location, Rotation, SpawnParams); if (!Light) { UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to spawn SpotLight")); return false; } USpotLightComponent* LightComp = Cast(Light->GetLightComponent()); if (LightComp) { LightComp->SetIntensity(Intensity); LightComp->SetLightColor(Color); LightComp->SetAttenuationRadius(AttenuationRadius); LightComp->SetInnerConeAngle(InnerConeAngle); LightComp->SetOuterConeAngle(OuterConeAngle); } #if WITH_EDITOR if (!ActorLabel.IsEmpty()) { Light->SetActorLabel(ActorLabel); } #endif OutActorId = Light->GetName(); UE_LOG(LogRalphaMCP, Log, TEXT("Spawned SpotLight: %s at (%f, %f, %f)"), *OutActorId, Location.X, Location.Y, Location.Z); return true; } bool URalphaParameterBridge::SetSpotLightParameters(const FString& ActorId, const TSharedPtr& Params) { UWorld* World = GetCurrentWorld(); if (!World) return false; ASpotLight* Light = nullptr; for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { Light = *It; break; } } if (!Light) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetSpotLightParameters: SpotLight not found: %s"), *ActorId); return false; } USpotLightComponent* LightComp = Cast(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("attenuation_radius"))) { LightComp->SetAttenuationRadius(Params->GetNumberField(TEXT("attenuation_radius"))); } if (Params->HasField(TEXT("inner_cone_angle"))) { LightComp->SetInnerConeAngle(Params->GetNumberField(TEXT("inner_cone_angle"))); } if (Params->HasField(TEXT("outer_cone_angle"))) { LightComp->SetOuterConeAngle(Params->GetNumberField(TEXT("outer_cone_angle"))); } if (Params->HasField(TEXT("source_radius"))) { LightComp->SourceRadius = Params->GetNumberField(TEXT("source_radius")); } if (Params->HasField(TEXT("temperature"))) { LightComp->bUseTemperature = true; LightComp->SetTemperature(Params->GetNumberField(TEXT("temperature"))); } // Location update const TSharedPtr* LocationObj; if (Params->TryGetObjectField(TEXT("location"), LocationObj)) { FVector Location = Light->GetActorLocation(); if ((*LocationObj)->HasField(TEXT("x"))) Location.X = (*LocationObj)->GetNumberField(TEXT("x")); if ((*LocationObj)->HasField(TEXT("y"))) Location.Y = (*LocationObj)->GetNumberField(TEXT("y")); if ((*LocationObj)->HasField(TEXT("z"))) Location.Z = (*LocationObj)->GetNumberField(TEXT("z")); Light->SetActorLocation(Location); } // Rotation update const TSharedPtr* RotationObj; if (Params->TryGetObjectField(TEXT("rotation"), RotationObj)) { FRotator Rotation = Light->GetActorRotation(); if ((*RotationObj)->HasField(TEXT("pitch"))) Rotation.Pitch = (*RotationObj)->GetNumberField(TEXT("pitch")); if ((*RotationObj)->HasField(TEXT("yaw"))) Rotation.Yaw = (*RotationObj)->GetNumberField(TEXT("yaw")); if ((*RotationObj)->HasField(TEXT("roll"))) Rotation.Roll = (*RotationObj)->GetNumberField(TEXT("roll")); Light->SetActorRotation(Rotation); } LightComp->MarkRenderStateDirty(); return true; } // ============================================================================ // Rect Light // ============================================================================ bool URalphaParameterBridge::SpawnRectLight(const FVector& Location, const FRotator& Rotation, float Intensity, const FLinearColor& Color, float SourceWidth, float SourceHeight, float AttenuationRadius, const FString& ActorLabel, FString& OutActorId) { UWorld* World = GetCurrentWorld(); if (!World) return false; FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ARectLight* Light = World->SpawnActor(Location, Rotation, SpawnParams); if (!Light) { UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to spawn RectLight")); return false; } URectLightComponent* LightComp = Cast(Light->GetLightComponent()); if (LightComp) { LightComp->SetIntensity(Intensity); LightComp->SetLightColor(Color); LightComp->SetSourceWidth(SourceWidth); LightComp->SetSourceHeight(SourceHeight); LightComp->SetAttenuationRadius(AttenuationRadius); } #if WITH_EDITOR if (!ActorLabel.IsEmpty()) { Light->SetActorLabel(ActorLabel); } #endif OutActorId = Light->GetName(); UE_LOG(LogRalphaMCP, Log, TEXT("Spawned RectLight: %s at (%f, %f, %f)"), *OutActorId, Location.X, Location.Y, Location.Z); return true; } bool URalphaParameterBridge::SetRectLightParameters(const FString& ActorId, const TSharedPtr& Params) { UWorld* World = GetCurrentWorld(); if (!World) return false; ARectLight* RectLight = nullptr; for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { RectLight = *It; break; } } if (!RectLight) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetRectLightParameters: RectLight not found: %s"), *ActorId); return false; } URectLightComponent* LightComp = Cast(RectLight->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("source_width"))) { LightComp->SetSourceWidth(Params->GetNumberField(TEXT("source_width"))); } if (Params->HasField(TEXT("source_height"))) { LightComp->SetSourceHeight(Params->GetNumberField(TEXT("source_height"))); } if (Params->HasField(TEXT("attenuation_radius"))) { LightComp->SetAttenuationRadius(Params->GetNumberField(TEXT("attenuation_radius"))); } if (Params->HasField(TEXT("barn_door_angle"))) { LightComp->BarnDoorAngle = Params->GetNumberField(TEXT("barn_door_angle")); } if (Params->HasField(TEXT("barn_door_length"))) { LightComp->BarnDoorLength = Params->GetNumberField(TEXT("barn_door_length")); } if (Params->HasField(TEXT("temperature"))) { LightComp->bUseTemperature = true; LightComp->SetTemperature(Params->GetNumberField(TEXT("temperature"))); } // Location update const TSharedPtr* LocationObj; if (Params->TryGetObjectField(TEXT("location"), LocationObj)) { FVector Location = RectLight->GetActorLocation(); if ((*LocationObj)->HasField(TEXT("x"))) Location.X = (*LocationObj)->GetNumberField(TEXT("x")); if ((*LocationObj)->HasField(TEXT("y"))) Location.Y = (*LocationObj)->GetNumberField(TEXT("y")); if ((*LocationObj)->HasField(TEXT("z"))) Location.Z = (*LocationObj)->GetNumberField(TEXT("z")); RectLight->SetActorLocation(Location); } // Rotation update const TSharedPtr* RotationObj; if (Params->TryGetObjectField(TEXT("rotation"), RotationObj)) { FRotator Rotation = RectLight->GetActorRotation(); if ((*RotationObj)->HasField(TEXT("pitch"))) Rotation.Pitch = (*RotationObj)->GetNumberField(TEXT("pitch")); if ((*RotationObj)->HasField(TEXT("yaw"))) Rotation.Yaw = (*RotationObj)->GetNumberField(TEXT("yaw")); if ((*RotationObj)->HasField(TEXT("roll"))) Rotation.Roll = (*RotationObj)->GetNumberField(TEXT("roll")); RectLight->SetActorRotation(Rotation); } LightComp->MarkRenderStateDirty(); return true; } // ============================================================================ // Skeletal Mesh // ============================================================================ bool URalphaParameterBridge::SpawnSkeletalMesh(const FString& SkeletalMeshPath, const FVector& Location, const FRotator& Rotation, float Scale, const FString& ActorLabel, FString& OutActorId) { UWorld* World = GetCurrentWorld(); if (!World) return false; // Load the skeletal mesh USkeletalMesh* SkeletalMesh = Cast(StaticLoadObject(USkeletalMesh::StaticClass(), nullptr, *SkeletalMeshPath)); if (!SkeletalMesh) { UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to load skeletal mesh: %s"), *SkeletalMeshPath); return false; } FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ASkeletalMeshActor* Actor = World->SpawnActor(Location, Rotation, SpawnParams); if (!Actor) { UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to spawn SkeletalMeshActor")); return false; } USkeletalMeshComponent* MeshComp = Actor->GetSkeletalMeshComponent(); if (MeshComp) { MeshComp->SetSkeletalMesh(SkeletalMesh); MeshComp->SetAnimationMode(EAnimationMode::AnimationSingleNode); } Actor->SetActorScale3D(FVector(Scale)); #if WITH_EDITOR if (!ActorLabel.IsEmpty()) { Actor->SetActorLabel(ActorLabel); } #endif OutActorId = Actor->GetName(); UE_LOG(LogRalphaMCP, Log, TEXT("Spawned SkeletalMeshActor: %s at (%f, %f, %f)"), *OutActorId, Location.X, Location.Y, Location.Z); return true; } bool URalphaParameterBridge::PlayAnimation(const FString& ActorId, const FString& AnimationPath, bool bLooping, float PlayRate) { UWorld* World = GetCurrentWorld(); if (!World) return false; ASkeletalMeshActor* Actor = nullptr; for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { Actor = *It; break; } } if (!Actor) { UE_LOG(LogRalphaMCP, Warning, TEXT("PlayAnimation: SkeletalMeshActor not found: %s"), *ActorId); return false; } UAnimSequence* AnimSequence = Cast(StaticLoadObject(UAnimSequence::StaticClass(), nullptr, *AnimationPath)); if (!AnimSequence) { UE_LOG(LogRalphaMCP, Warning, TEXT("PlayAnimation: Failed to load animation: %s"), *AnimationPath); return false; } USkeletalMeshComponent* MeshComp = Actor->GetSkeletalMeshComponent(); if (MeshComp) { MeshComp->SetAnimationMode(EAnimationMode::AnimationSingleNode); MeshComp->PlayAnimation(AnimSequence, bLooping); MeshComp->SetPlayRate(PlayRate); UE_LOG(LogRalphaMCP, Log, TEXT("PlayAnimation: Playing %s on %s (loop=%d, rate=%f)"), *AnimationPath, *ActorId, bLooping, PlayRate); return true; } return false; } bool URalphaParameterBridge::StopAnimation(const FString& ActorId) { UWorld* World = GetCurrentWorld(); if (!World) return false; ASkeletalMeshActor* Actor = nullptr; for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { Actor = *It; break; } } if (!Actor) { UE_LOG(LogRalphaMCP, Warning, TEXT("StopAnimation: SkeletalMeshActor not found: %s"), *ActorId); return false; } USkeletalMeshComponent* MeshComp = Actor->GetSkeletalMeshComponent(); if (MeshComp) { MeshComp->Stop(); return true; } return false; } bool URalphaParameterBridge::SetAnimationPose(const FString& ActorId, const FString& AnimationPath, float Time) { UWorld* World = GetCurrentWorld(); if (!World) return false; ASkeletalMeshActor* Actor = nullptr; for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { Actor = *It; break; } } if (!Actor) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetAnimationPose: SkeletalMeshActor not found: %s"), *ActorId); return false; } UAnimSequence* AnimSequence = Cast(StaticLoadObject(UAnimSequence::StaticClass(), nullptr, *AnimationPath)); if (!AnimSequence) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetAnimationPose: Failed to load animation: %s"), *AnimationPath); return false; } USkeletalMeshComponent* MeshComp = Actor->GetSkeletalMeshComponent(); if (MeshComp) { MeshComp->SetAnimationMode(EAnimationMode::AnimationSingleNode); MeshComp->SetAnimation(AnimSequence); MeshComp->SetPosition(Time); MeshComp->SetPlayRate(0.0f); UE_LOG(LogRalphaMCP, Log, TEXT("SetAnimationPose: Set %s to pose at time %f"), *ActorId, Time); return true; } return false; } // ============================================================================ // Reflection Captures // ============================================================================ bool URalphaParameterBridge::SpawnSphereReflectionCapture(const FVector& Location, float InfluenceRadius, const FString& ActorLabel, FString& OutActorId) { UWorld* World = GetCurrentWorld(); if (!World) return false; FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ASphereReflectionCapture* Capture = World->SpawnActor(Location, FRotator::ZeroRotator, SpawnParams); if (!Capture) { UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to spawn SphereReflectionCapture")); return false; } USphereReflectionCaptureComponent* CaptureComp = Cast(Capture->GetCaptureComponent()); if (CaptureComp) { CaptureComp->InfluenceRadius = InfluenceRadius; } #if WITH_EDITOR if (!ActorLabel.IsEmpty()) { Capture->SetActorLabel(ActorLabel); } #endif OutActorId = Capture->GetName(); UE_LOG(LogRalphaMCP, Log, TEXT("Spawned SphereReflectionCapture: %s at (%f, %f, %f)"), *OutActorId, Location.X, Location.Y, Location.Z); return true; } bool URalphaParameterBridge::SpawnBoxReflectionCapture(const FVector& Location, const FVector& BoxExtent, const FString& ActorLabel, FString& OutActorId) { UWorld* World = GetCurrentWorld(); if (!World) return false; FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; ABoxReflectionCapture* Capture = World->SpawnActor(Location, FRotator::ZeroRotator, SpawnParams); if (!Capture) { UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to spawn BoxReflectionCapture")); return false; } UBoxReflectionCaptureComponent* CaptureComp = Cast(Capture->GetCaptureComponent()); if (CaptureComp) { CaptureComp->BoxTransitionDistance = BoxExtent.X; } #if WITH_EDITOR if (!ActorLabel.IsEmpty()) { Capture->SetActorLabel(ActorLabel); } #endif Capture->SetActorScale3D(BoxExtent / 100.0f); OutActorId = Capture->GetName(); UE_LOG(LogRalphaMCP, Log, TEXT("Spawned BoxReflectionCapture: %s at (%f, %f, %f)"), *OutActorId, Location.X, Location.Y, Location.Z); return true; } bool URalphaParameterBridge::UpdateReflectionCaptures() { UWorld* World = GetCurrentWorld(); if (!World) return false; #if WITH_EDITOR for (TActorIterator It(World); It; ++It) { UReflectionCaptureComponent* CaptureComp = It->GetCaptureComponent(); if (CaptureComp) { CaptureComp->MarkDirtyForRecapture(); } } GEditor->BuildReflectionCaptures(); UE_LOG(LogRalphaMCP, Log, TEXT("UpdateReflectionCaptures: Triggered rebuild")); return true; #else UE_LOG(LogRalphaMCP, Warning, TEXT("UpdateReflectionCaptures: Only available in editor")); return false; #endif } // ============================================================================ // IES Light Profiles // ============================================================================ bool URalphaParameterBridge::SetLightIESProfile(const FString& ActorId, const FString& IESProfilePath) { UWorld* World = GetCurrentWorld(); if (!World) return false; UTextureLightProfile* IESProfile = nullptr; if (!IESProfilePath.IsEmpty()) { IESProfile = Cast(StaticLoadObject(UTextureLightProfile::StaticClass(), nullptr, *IESProfilePath)); if (!IESProfile) { UE_LOG(LogRalphaMCP, Warning, TEXT("SetLightIESProfile: Failed to load IES profile: %s"), *IESProfilePath); return false; } } for (TActorIterator It(World); It; ++It) { if (It->GetName() == ActorId) { ULightComponent* LightComp = It->GetLightComponent(); if (LightComp) { LightComp->IESTexture = IESProfile; LightComp->bUseIESBrightness = (IESProfile != nullptr); LightComp->MarkRenderStateDirty(); UE_LOG(LogRalphaMCP, Log, TEXT("SetLightIESProfile: Applied IES profile to %s"), *ActorId); return true; } } } UE_LOG(LogRalphaMCP, Warning, TEXT("SetLightIESProfile: Light not found: %s"), *ActorId); return false; }