diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index b1dd8d64..8f09d675 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -10,10 +10,37 @@ r.DefaultFeature.AmbientOcclusion=True r.DefaultFeature.AmbientOcclusionStaticFraction=True r.Lumen.DiffuseIndirect.Allow=True r.Lumen.Reflections.Allow=True -r.RayTracing=True +r.RayTracing=False [/Script/WindowsTargetPlatform.WindowsTargetSettings] DefaultGraphicsRHI=DefaultGraphicsRHI_DX12 +-D3D12TargetedShaderFormats=PCD3D_SM5 ++D3D12TargetedShaderFormats=PCD3D_SM5 +-D3D11TargetedShaderFormats=PCD3D_SM5 ++D3D11TargetedShaderFormats=PCD3D_SM5 +bGenerateNaniteFallbackMeshes=True +Compiler=Default +AudioSampleRate=48000 +AudioCallbackBufferFrameSize=1024 +AudioNumBuffersToEnqueue=1 +AudioMaxChannels=0 +AudioNumSourceWorkers=4 +SpatializationPlugin= +SourceDataOverridePlugin= +ReverbPlugin= +OcclusionPlugin= +CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0) +CacheSizeKB=65536 +MaxChunkSizeOverrideKB=0 +bResampleForDevice=False +MaxSampleRate=48000.000000 +HighSampleRate=32000.000000 +MedSampleRate=24000.000000 +LowSampleRate=12000.000000 +MinSampleRate=8000.000000 +CompressionQualityModifier=1.000000 +AutoStreamingThreshold=0.000000 +SoundCueCookQualityIndex=-1 [/Script/Engine.Engine] +ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/Ralpha") @@ -27,6 +54,9 @@ gc.MaxObjectsNotConsideredByGC=1 [ConsoleVariables] r.Lumen.ScreenProbeGather.FullResolutionJitterWidth=1 +r.RayTracing.EnableOnDemandShaderCompilation=0 +r.D3D12.DXR.AllowSM6ShaderWarning=0 +r.SkinCache.CompileShaders=1 [/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings] bEnablePlugin=True diff --git a/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaMCPServer.cpp b/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaMCPServer.cpp index 847d19d1..9e238ca1 100644 --- a/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaMCPServer.cpp +++ b/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaMCPServer.cpp @@ -350,6 +350,44 @@ FString URalphaMCPServer::ProcessCommand(const FString& JsonCommand) bool bSuccess = ParameterBridge->DeleteActor(ActorId); ResponseObject->SetBoolField(TEXT("success"), bSuccess); } + else if (CommandType == TEXT("spawn_ocean")) + { + FVector Location = FVector::ZeroVector; + const TSharedPtr* LocationObj; + if (JsonObject->TryGetObjectField(TEXT("location"), LocationObj)) + { + Location.X = (*LocationObj)->HasField(TEXT("x")) ? (*LocationObj)->GetNumberField(TEXT("x")) : 0.0f; + Location.Y = (*LocationObj)->HasField(TEXT("y")) ? (*LocationObj)->GetNumberField(TEXT("y")) : 0.0f; + Location.Z = (*LocationObj)->HasField(TEXT("z")) ? (*LocationObj)->GetNumberField(TEXT("z")) : 0.0f; + } + float Size = JsonObject->HasField(TEXT("size")) ? JsonObject->GetNumberField(TEXT("size")) : 100000.0f; + + FString ActorId; + bool bSuccess = ParameterBridge->SpawnOcean(Location, Size, ActorId); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + if (bSuccess) + { + ResponseObject->SetStringField(TEXT("actor_id"), ActorId); + } + } + else if (CommandType == TEXT("set_ocean_waves")) + { + float WaveAmplitude = JsonObject->HasField(TEXT("amplitude")) ? JsonObject->GetNumberField(TEXT("amplitude")) : 100.0f; + float WaveLength = JsonObject->HasField(TEXT("wavelength")) ? JsonObject->GetNumberField(TEXT("wavelength")) : 1000.0f; + float Steepness = JsonObject->HasField(TEXT("steepness")) ? JsonObject->GetNumberField(TEXT("steepness")) : 0.5f; + int32 NumWaves = JsonObject->HasField(TEXT("num_waves")) ? JsonObject->GetIntegerField(TEXT("num_waves")) : 4; + + bool bSuccess = ParameterBridge->SetOceanWaves(WaveAmplitude, WaveLength, Steepness, NumWaves); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else if (CommandType == TEXT("create_landscape")) + { + float Size = JsonObject->HasField(TEXT("size")) ? JsonObject->GetNumberField(TEXT("size")) : 10000.0f; + float HeightOffset = JsonObject->HasField(TEXT("height_offset")) ? JsonObject->GetNumberField(TEXT("height_offset")) : -500.0f; + + bool bSuccess = ParameterBridge->CreateLandscape(Size, HeightOffset); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } else if (CommandType == TEXT("set_actor_material")) { FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); @@ -383,6 +421,28 @@ FString URalphaMCPServer::ProcessCommand(const FString& JsonCommand) bool bSuccess = ParameterBridge->SetActorSimpleMaterial(ActorId, BaseColor, Metallic, Roughness, Opacity); ResponseObject->SetBoolField(TEXT("success"), bSuccess); } + else if (CommandType == TEXT("set_actor_emissive_material")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + + // Parse emissive color - supports RGB object + FLinearColor EmissiveColor = FLinearColor(1.0f, 0.8f, 0.3f); // Default warm yellow + if (JsonObject->HasField(TEXT("color"))) + { + const TSharedPtr* ColorObj; + if (JsonObject->TryGetObjectField(TEXT("color"), ColorObj)) + { + EmissiveColor.R = (*ColorObj)->HasField(TEXT("r")) ? (*ColorObj)->GetNumberField(TEXT("r")) : 1.0f; + EmissiveColor.G = (*ColorObj)->HasField(TEXT("g")) ? (*ColorObj)->GetNumberField(TEXT("g")) : 0.8f; + EmissiveColor.B = (*ColorObj)->HasField(TEXT("b")) ? (*ColorObj)->GetNumberField(TEXT("b")) : 0.3f; + } + } + + float Intensity = JsonObject->HasField(TEXT("intensity")) ? JsonObject->GetNumberField(TEXT("intensity")) : 10.0f; + + bool bSuccess = ParameterBridge->SetActorEmissiveMaterial(ActorId, EmissiveColor, Intensity); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } else if (CommandType == TEXT("list_actors")) { FString ClassFilter = JsonObject->HasField(TEXT("class_filter")) ? JsonObject->GetStringField(TEXT("class_filter")) : TEXT(""); @@ -457,6 +517,367 @@ FString URalphaMCPServer::ProcessCommand(const FString& JsonCommand) ResponseObject->SetStringField(TEXT("error"), TEXT("Only available in editor")); #endif } + else if (CommandType == TEXT("spawn_point_light")) + { + FVector Location = FVector::ZeroVector; + const TSharedPtr* LocationObj; + if (JsonObject->TryGetObjectField(TEXT("location"), LocationObj)) + { + Location.X = (*LocationObj)->HasField(TEXT("x")) ? (*LocationObj)->GetNumberField(TEXT("x")) : 0.0f; + Location.Y = (*LocationObj)->HasField(TEXT("y")) ? (*LocationObj)->GetNumberField(TEXT("y")) : 0.0f; + Location.Z = (*LocationObj)->HasField(TEXT("z")) ? (*LocationObj)->GetNumberField(TEXT("z")) : 0.0f; + } + + float Intensity = JsonObject->HasField(TEXT("intensity")) ? JsonObject->GetNumberField(TEXT("intensity")) : 5000.0f; + float AttenuationRadius = JsonObject->HasField(TEXT("attenuation_radius")) ? JsonObject->GetNumberField(TEXT("attenuation_radius")) : 1000.0f; + FString ActorLabel = JsonObject->HasField(TEXT("actor_label")) ? JsonObject->GetStringField(TEXT("actor_label")) : TEXT(""); + + FLinearColor Color = FLinearColor::White; + if (JsonObject->HasField(TEXT("color"))) + { + const TSharedPtr* ColorObj; + if (JsonObject->TryGetObjectField(TEXT("color"), ColorObj)) + { + Color.R = (*ColorObj)->HasField(TEXT("r")) ? (*ColorObj)->GetNumberField(TEXT("r")) : 1.0f; + Color.G = (*ColorObj)->HasField(TEXT("g")) ? (*ColorObj)->GetNumberField(TEXT("g")) : 1.0f; + Color.B = (*ColorObj)->HasField(TEXT("b")) ? (*ColorObj)->GetNumberField(TEXT("b")) : 1.0f; + } + } + + FString ActorId; + bool bSuccess = ParameterBridge->SpawnPointLight(Location, Intensity, Color, AttenuationRadius, ActorLabel, ActorId); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + if (bSuccess) + { + ResponseObject->SetStringField(TEXT("actor_id"), ActorId); + } + } + else if (CommandType == TEXT("set_point_light")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + const TSharedPtr* Params; + if (JsonObject->TryGetObjectField(TEXT("parameters"), Params)) + { + bool bSuccess = ParameterBridge->SetPointLightParameters(ActorId, *Params); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else + { + ResponseObject->SetBoolField(TEXT("success"), false); + ResponseObject->SetStringField(TEXT("error"), TEXT("Missing parameters")); + } + } + else if (CommandType == TEXT("spawn_spot_light")) + { + FVector Location = FVector::ZeroVector; + const TSharedPtr* LocationObj; + if (JsonObject->TryGetObjectField(TEXT("location"), LocationObj)) + { + Location.X = (*LocationObj)->HasField(TEXT("x")) ? (*LocationObj)->GetNumberField(TEXT("x")) : 0.0f; + Location.Y = (*LocationObj)->HasField(TEXT("y")) ? (*LocationObj)->GetNumberField(TEXT("y")) : 0.0f; + Location.Z = (*LocationObj)->HasField(TEXT("z")) ? (*LocationObj)->GetNumberField(TEXT("z")) : 0.0f; + } + + FRotator Rotation = FRotator::ZeroRotator; + const TSharedPtr* RotationObj; + if (JsonObject->TryGetObjectField(TEXT("rotation"), RotationObj)) + { + Rotation.Pitch = (*RotationObj)->HasField(TEXT("pitch")) ? (*RotationObj)->GetNumberField(TEXT("pitch")) : 0.0f; + Rotation.Yaw = (*RotationObj)->HasField(TEXT("yaw")) ? (*RotationObj)->GetNumberField(TEXT("yaw")) : 0.0f; + Rotation.Roll = (*RotationObj)->HasField(TEXT("roll")) ? (*RotationObj)->GetNumberField(TEXT("roll")) : 0.0f; + } + + float Intensity = JsonObject->HasField(TEXT("intensity")) ? JsonObject->GetNumberField(TEXT("intensity")) : 5000.0f; + float AttenuationRadius = JsonObject->HasField(TEXT("attenuation_radius")) ? JsonObject->GetNumberField(TEXT("attenuation_radius")) : 1000.0f; + float InnerConeAngle = JsonObject->HasField(TEXT("inner_cone_angle")) ? JsonObject->GetNumberField(TEXT("inner_cone_angle")) : 20.0f; + float OuterConeAngle = JsonObject->HasField(TEXT("outer_cone_angle")) ? JsonObject->GetNumberField(TEXT("outer_cone_angle")) : 40.0f; + FString ActorLabel = JsonObject->HasField(TEXT("actor_label")) ? JsonObject->GetStringField(TEXT("actor_label")) : TEXT(""); + + FLinearColor Color = FLinearColor::White; + if (JsonObject->HasField(TEXT("color"))) + { + const TSharedPtr* ColorObj; + if (JsonObject->TryGetObjectField(TEXT("color"), ColorObj)) + { + Color.R = (*ColorObj)->HasField(TEXT("r")) ? (*ColorObj)->GetNumberField(TEXT("r")) : 1.0f; + Color.G = (*ColorObj)->HasField(TEXT("g")) ? (*ColorObj)->GetNumberField(TEXT("g")) : 1.0f; + Color.B = (*ColorObj)->HasField(TEXT("b")) ? (*ColorObj)->GetNumberField(TEXT("b")) : 1.0f; + } + } + + FString ActorId; + bool bSuccess = ParameterBridge->SpawnSpotLight(Location, Rotation, Intensity, Color, AttenuationRadius, InnerConeAngle, OuterConeAngle, ActorLabel, ActorId); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + if (bSuccess) + { + ResponseObject->SetStringField(TEXT("actor_id"), ActorId); + } + } + else if (CommandType == TEXT("set_spot_light")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + const TSharedPtr* Params; + if (JsonObject->TryGetObjectField(TEXT("parameters"), Params)) + { + bool bSuccess = ParameterBridge->SetSpotLightParameters(ActorId, *Params); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else + { + ResponseObject->SetBoolField(TEXT("success"), false); + ResponseObject->SetStringField(TEXT("error"), TEXT("Missing parameters")); + } + } + else if (CommandType == TEXT("spawn_rect_light")) + { + FVector Location = FVector::ZeroVector; + const TSharedPtr* LocationObj; + if (JsonObject->TryGetObjectField(TEXT("location"), LocationObj)) + { + Location.X = (*LocationObj)->HasField(TEXT("x")) ? (*LocationObj)->GetNumberField(TEXT("x")) : 0.0f; + Location.Y = (*LocationObj)->HasField(TEXT("y")) ? (*LocationObj)->GetNumberField(TEXT("y")) : 0.0f; + Location.Z = (*LocationObj)->HasField(TEXT("z")) ? (*LocationObj)->GetNumberField(TEXT("z")) : 0.0f; + } + + FRotator Rotation = FRotator::ZeroRotator; + const TSharedPtr* RotationObj; + if (JsonObject->TryGetObjectField(TEXT("rotation"), RotationObj)) + { + Rotation.Pitch = (*RotationObj)->HasField(TEXT("pitch")) ? (*RotationObj)->GetNumberField(TEXT("pitch")) : 0.0f; + Rotation.Yaw = (*RotationObj)->HasField(TEXT("yaw")) ? (*RotationObj)->GetNumberField(TEXT("yaw")) : 0.0f; + Rotation.Roll = (*RotationObj)->HasField(TEXT("roll")) ? (*RotationObj)->GetNumberField(TEXT("roll")) : 0.0f; + } + + float Intensity = JsonObject->HasField(TEXT("intensity")) ? JsonObject->GetNumberField(TEXT("intensity")) : 5000.0f; + float AttenuationRadius = JsonObject->HasField(TEXT("attenuation_radius")) ? JsonObject->GetNumberField(TEXT("attenuation_radius")) : 1000.0f; + float SourceWidth = JsonObject->HasField(TEXT("source_width")) ? JsonObject->GetNumberField(TEXT("source_width")) : 100.0f; + float SourceHeight = JsonObject->HasField(TEXT("source_height")) ? JsonObject->GetNumberField(TEXT("source_height")) : 100.0f; + FString ActorLabel = JsonObject->HasField(TEXT("actor_label")) ? JsonObject->GetStringField(TEXT("actor_label")) : TEXT(""); + + FLinearColor Color = FLinearColor::White; + if (JsonObject->HasField(TEXT("color"))) + { + const TSharedPtr* ColorObj; + if (JsonObject->TryGetObjectField(TEXT("color"), ColorObj)) + { + Color.R = (*ColorObj)->HasField(TEXT("r")) ? (*ColorObj)->GetNumberField(TEXT("r")) : 1.0f; + Color.G = (*ColorObj)->HasField(TEXT("g")) ? (*ColorObj)->GetNumberField(TEXT("g")) : 1.0f; + Color.B = (*ColorObj)->HasField(TEXT("b")) ? (*ColorObj)->GetNumberField(TEXT("b")) : 1.0f; + } + } + + FString ActorId; + bool bSuccess = ParameterBridge->SpawnRectLight(Location, Rotation, Intensity, Color, SourceWidth, SourceHeight, AttenuationRadius, ActorLabel, ActorId); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + if (bSuccess) + { + ResponseObject->SetStringField(TEXT("actor_id"), ActorId); + } + } + else if (CommandType == TEXT("set_rect_light")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + const TSharedPtr* Params; + if (JsonObject->TryGetObjectField(TEXT("parameters"), Params)) + { + bool bSuccess = ParameterBridge->SetRectLightParameters(ActorId, *Params); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else + { + ResponseObject->SetBoolField(TEXT("success"), false); + ResponseObject->SetStringField(TEXT("error"), TEXT("Missing parameters")); + } + } + else if (CommandType == TEXT("spawn_skeletal_mesh")) + { + FString SkeletalMeshPath = JsonObject->GetStringField(TEXT("skeletal_mesh_path")); + + FVector Location = FVector::ZeroVector; + const TSharedPtr* LocationObj; + if (JsonObject->TryGetObjectField(TEXT("location"), LocationObj)) + { + Location.X = (*LocationObj)->HasField(TEXT("x")) ? (*LocationObj)->GetNumberField(TEXT("x")) : 0.0f; + Location.Y = (*LocationObj)->HasField(TEXT("y")) ? (*LocationObj)->GetNumberField(TEXT("y")) : 0.0f; + Location.Z = (*LocationObj)->HasField(TEXT("z")) ? (*LocationObj)->GetNumberField(TEXT("z")) : 0.0f; + } + + FRotator Rotation = FRotator::ZeroRotator; + const TSharedPtr* RotationObj; + if (JsonObject->TryGetObjectField(TEXT("rotation"), RotationObj)) + { + Rotation.Pitch = (*RotationObj)->HasField(TEXT("pitch")) ? (*RotationObj)->GetNumberField(TEXT("pitch")) : 0.0f; + Rotation.Yaw = (*RotationObj)->HasField(TEXT("yaw")) ? (*RotationObj)->GetNumberField(TEXT("yaw")) : 0.0f; + Rotation.Roll = (*RotationObj)->HasField(TEXT("roll")) ? (*RotationObj)->GetNumberField(TEXT("roll")) : 0.0f; + } + + float Scale = JsonObject->HasField(TEXT("scale")) ? JsonObject->GetNumberField(TEXT("scale")) : 1.0f; + FString ActorLabel = JsonObject->HasField(TEXT("actor_label")) ? JsonObject->GetStringField(TEXT("actor_label")) : TEXT(""); + + FString ActorId; + bool bSuccess = ParameterBridge->SpawnSkeletalMesh(SkeletalMeshPath, Location, Rotation, Scale, ActorLabel, ActorId); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + if (bSuccess) + { + ResponseObject->SetStringField(TEXT("actor_id"), ActorId); + } + } + else if (CommandType == TEXT("play_animation")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + FString AnimationPath = JsonObject->GetStringField(TEXT("animation_path")); + bool bLooping = JsonObject->HasField(TEXT("looping")) ? JsonObject->GetBoolField(TEXT("looping")) : true; + float PlayRate = JsonObject->HasField(TEXT("play_rate")) ? JsonObject->GetNumberField(TEXT("play_rate")) : 1.0f; + + bool bSuccess = ParameterBridge->PlayAnimation(ActorId, AnimationPath, bLooping, PlayRate); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else if (CommandType == TEXT("stop_animation")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + bool bSuccess = ParameterBridge->StopAnimation(ActorId); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else if (CommandType == TEXT("set_animation_pose")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + FString AnimationPath = JsonObject->GetStringField(TEXT("animation_path")); + float Time = JsonObject->HasField(TEXT("time")) ? JsonObject->GetNumberField(TEXT("time")) : 0.0f; + + bool bSuccess = ParameterBridge->SetAnimationPose(ActorId, AnimationPath, Time); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else if (CommandType == TEXT("spawn_sphere_reflection_capture")) + { + FVector Location = FVector::ZeroVector; + const TSharedPtr* LocationObj; + if (JsonObject->TryGetObjectField(TEXT("location"), LocationObj)) + { + Location.X = (*LocationObj)->HasField(TEXT("x")) ? (*LocationObj)->GetNumberField(TEXT("x")) : 0.0f; + Location.Y = (*LocationObj)->HasField(TEXT("y")) ? (*LocationObj)->GetNumberField(TEXT("y")) : 0.0f; + Location.Z = (*LocationObj)->HasField(TEXT("z")) ? (*LocationObj)->GetNumberField(TEXT("z")) : 0.0f; + } + + float InfluenceRadius = JsonObject->HasField(TEXT("influence_radius")) ? JsonObject->GetNumberField(TEXT("influence_radius")) : 3000.0f; + FString ActorLabel = JsonObject->HasField(TEXT("actor_label")) ? JsonObject->GetStringField(TEXT("actor_label")) : TEXT(""); + + FString ActorId; + bool bSuccess = ParameterBridge->SpawnSphereReflectionCapture(Location, InfluenceRadius, ActorLabel, ActorId); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + if (bSuccess) + { + ResponseObject->SetStringField(TEXT("actor_id"), ActorId); + } + } + else if (CommandType == TEXT("spawn_box_reflection_capture")) + { + FVector Location = FVector::ZeroVector; + const TSharedPtr* LocationObj; + if (JsonObject->TryGetObjectField(TEXT("location"), LocationObj)) + { + Location.X = (*LocationObj)->HasField(TEXT("x")) ? (*LocationObj)->GetNumberField(TEXT("x")) : 0.0f; + Location.Y = (*LocationObj)->HasField(TEXT("y")) ? (*LocationObj)->GetNumberField(TEXT("y")) : 0.0f; + Location.Z = (*LocationObj)->HasField(TEXT("z")) ? (*LocationObj)->GetNumberField(TEXT("z")) : 0.0f; + } + + FVector BoxExtent = FVector(1000.0f, 1000.0f, 400.0f); + const TSharedPtr* ExtentObj; + if (JsonObject->TryGetObjectField(TEXT("box_extent"), ExtentObj)) + { + BoxExtent.X = (*ExtentObj)->HasField(TEXT("x")) ? (*ExtentObj)->GetNumberField(TEXT("x")) : 1000.0f; + BoxExtent.Y = (*ExtentObj)->HasField(TEXT("y")) ? (*ExtentObj)->GetNumberField(TEXT("y")) : 1000.0f; + BoxExtent.Z = (*ExtentObj)->HasField(TEXT("z")) ? (*ExtentObj)->GetNumberField(TEXT("z")) : 400.0f; + } + + FString ActorLabel = JsonObject->HasField(TEXT("actor_label")) ? JsonObject->GetStringField(TEXT("actor_label")) : TEXT(""); + + FString ActorId; + bool bSuccess = ParameterBridge->SpawnBoxReflectionCapture(Location, BoxExtent, ActorLabel, ActorId); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + if (bSuccess) + { + ResponseObject->SetStringField(TEXT("actor_id"), ActorId); + } + } + else if (CommandType == TEXT("update_reflection_captures")) + { + bool bSuccess = ParameterBridge->UpdateReflectionCaptures(); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else if (CommandType == TEXT("set_light_ies_profile")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + FString IESProfilePath = JsonObject->GetStringField(TEXT("ies_profile_path")); + + bool bSuccess = ParameterBridge->SetLightIESProfile(ActorId, IESProfilePath); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else if (CommandType == TEXT("list_assets")) + { + FString FolderPath = JsonObject->HasField(TEXT("folder_path")) ? JsonObject->GetStringField(TEXT("folder_path")) : TEXT("/Game"); + FString AssetType = JsonObject->HasField(TEXT("asset_type")) ? JsonObject->GetStringField(TEXT("asset_type")) : TEXT("All"); + bool bRecursive = JsonObject->HasField(TEXT("recursive")) ? JsonObject->GetBoolField(TEXT("recursive")) : true; + + TArray> Results = ParameterBridge->ListAssets(FolderPath, AssetType, bRecursive); + ResponseObject->SetBoolField(TEXT("success"), true); + ResponseObject->SetArrayField(TEXT("assets"), Results); + } + else if (CommandType == TEXT("set_actor_transform")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + bool bRelative = JsonObject->HasField(TEXT("relative")) ? JsonObject->GetBoolField(TEXT("relative")) : false; + + const FVector* LocationPtr = nullptr; + FVector Location; + const TSharedPtr* LocationObj; + if (JsonObject->TryGetObjectField(TEXT("location"), LocationObj)) + { + Location.X = (*LocationObj)->HasField(TEXT("x")) ? (*LocationObj)->GetNumberField(TEXT("x")) : 0.0f; + Location.Y = (*LocationObj)->HasField(TEXT("y")) ? (*LocationObj)->GetNumberField(TEXT("y")) : 0.0f; + Location.Z = (*LocationObj)->HasField(TEXT("z")) ? (*LocationObj)->GetNumberField(TEXT("z")) : 0.0f; + LocationPtr = &Location; + } + + const FRotator* RotationPtr = nullptr; + FRotator Rotation; + const TSharedPtr* RotationObj; + if (JsonObject->TryGetObjectField(TEXT("rotation"), RotationObj)) + { + Rotation.Pitch = (*RotationObj)->HasField(TEXT("pitch")) ? (*RotationObj)->GetNumberField(TEXT("pitch")) : 0.0f; + Rotation.Yaw = (*RotationObj)->HasField(TEXT("yaw")) ? (*RotationObj)->GetNumberField(TEXT("yaw")) : 0.0f; + Rotation.Roll = (*RotationObj)->HasField(TEXT("roll")) ? (*RotationObj)->GetNumberField(TEXT("roll")) : 0.0f; + RotationPtr = &Rotation; + } + + const float* ScalePtr = nullptr; + float Scale; + if (JsonObject->HasField(TEXT("scale"))) + { + Scale = JsonObject->GetNumberField(TEXT("scale")); + ScalePtr = &Scale; + } + + bool bSuccess = ParameterBridge->SetActorTransform(ActorId, LocationPtr, RotationPtr, ScalePtr, bRelative); + ResponseObject->SetBoolField(TEXT("success"), bSuccess); + } + else if (CommandType == TEXT("get_actor_details")) + { + FString ActorId = JsonObject->GetStringField(TEXT("actor_id")); + bool bIncludeComponents = JsonObject->HasField(TEXT("include_components")) ? JsonObject->GetBoolField(TEXT("include_components")) : true; + bool bIncludeMaterials = JsonObject->HasField(TEXT("include_materials")) ? JsonObject->GetBoolField(TEXT("include_materials")) : true; + + TSharedPtr Details = ParameterBridge->GetActorDetails(ActorId, bIncludeComponents, bIncludeMaterials); + if (Details.IsValid()) + { + ResponseObject->SetBoolField(TEXT("success"), true); + ResponseObject->SetObjectField(TEXT("actor"), Details); + } + else + { + ResponseObject->SetBoolField(TEXT("success"), false); + ResponseObject->SetStringField(TEXT("error"), TEXT("Actor not found")); + } + } else { ResponseObject->SetBoolField(TEXT("success"), false); diff --git a/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaParameterBridge.cpp b/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaParameterBridge.cpp index 29df2048..76eba844 100644 --- a/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaParameterBridge.cpp +++ b/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaParameterBridge.cpp @@ -25,6 +25,30 @@ #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() { @@ -769,6 +793,121 @@ bool URalphaParameterBridge::DeleteActor(const FString& ActorId) 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(); @@ -893,6 +1032,81 @@ bool URalphaParameterBridge::SetActorSimpleMaterial(const FString& ActorId, cons 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; @@ -1119,15 +1333,16 @@ TSharedPtr URalphaParameterBridge::SetupScene() } // Check/Create DirectionalLight - if (!FindDirectionalLight()) + ADirectionalLight* Sun = FindDirectionalLight(); + if (!Sun) { FActorSpawnParameters SpawnParams; SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; - ADirectionalLight* Light = World->SpawnActor(FVector::ZeroVector, FRotator(-45.0f, 0.0f, 0.0f), SpawnParams); - if (Light) + Sun = World->SpawnActor(FVector::ZeroVector, FRotator(-45.0f, 0.0f, 0.0f), SpawnParams); + if (Sun) { #if WITH_EDITOR - Light->SetActorLabel(TEXT("Ralpha_Sun")); + Sun->SetActorLabel(TEXT("Ralpha_Sun")); #endif CreatedActors.Add(TEXT("DirectionalLight")); } @@ -1137,6 +1352,17 @@ TSharedPtr URalphaParameterBridge::SetupScene() 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()) { @@ -1262,3 +1488,672 @@ TSharedPtr URalphaParameterBridge::SetupScene() 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; +} diff --git a/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaScreenCapture.cpp b/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaScreenCapture.cpp index b1465022..2335f1be 100644 --- a/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaScreenCapture.cpp +++ b/Plugins/RalphaPlugin/Source/RalphaCore/Private/RalphaScreenCapture.cpp @@ -16,10 +16,22 @@ #include "Framework/Application/SlateApplication.h" #include "Widgets/SWindow.h" #include "UnrealClient.h" +#include "HighResScreenshot.h" +#include "Components/SceneCaptureComponent2D.h" +#include "Engine/TextureRenderTarget2D.h" +#include "TextureResource.h" +#include "RenderingThread.h" +#include "CineCameraActor.h" +#include "CineCameraComponent.h" +#include "Kismet/GameplayStatics.h" +#include "EngineUtils.h" #if WITH_EDITOR #include "Editor.h" #include "LevelEditorViewport.h" +#include "SLevelViewport.h" +#include "LevelEditor.h" +#include "ILevelEditor.h" #endif URalphaScreenCapture::URalphaScreenCapture() @@ -28,52 +40,84 @@ URalphaScreenCapture::URalphaScreenCapture() bool URalphaScreenCapture::CaptureScreenshot(int32 Width, int32 Height, FString& OutBase64, FString& OutFilePath) { - // Get the game viewport - UGameViewportClient* ViewportClient = GEngine ? GEngine->GameViewport : nullptr; - - // In editor, we might not have a game viewport, try to get the active viewport FViewport* Viewport = nullptr; + int32 ViewportWidth = 0; + int32 ViewportHeight = 0; - if (ViewportClient && ViewportClient->Viewport) - { - Viewport = ViewportClient->Viewport; - } #if WITH_EDITOR - else + // In editor, find the active level viewport with valid size + if (GEditor) { - // Try to get the editor viewport - if (GEditor) + // Try to find a viewport with a valid size + for (FLevelEditorViewportClient* LevelVC : GEditor->GetLevelViewportClients()) { - for (FLevelEditorViewportClient* LevelVC : GEditor->GetLevelViewportClients()) + if (LevelVC && LevelVC->Viewport) { - if (LevelVC && LevelVC->Viewport) + FIntPoint Size = LevelVC->Viewport->GetSizeXY(); + if (Size.X > 0 && Size.Y > 0) { Viewport = LevelVC->Viewport; + ViewportWidth = Size.X; + ViewportHeight = Size.Y; + UE_LOG(LogRalphaMCP, Log, TEXT("Found editor viewport: %dx%d"), ViewportWidth, ViewportHeight); break; } } } + + // If still no viewport, try to get it from the level editor module + if (!Viewport) + { + FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked("LevelEditor"); + TSharedPtr LevelEditor = LevelEditorModule.GetFirstLevelEditor(); + if (LevelEditor.IsValid()) + { + TSharedPtr ActiveViewport = LevelEditor->GetActiveViewportInterface(); + if (ActiveViewport.IsValid()) + { + FLevelEditorViewportClient& ViewportClient = ActiveViewport->GetLevelViewportClient(); + if (ViewportClient.Viewport) + { + FIntPoint Size = ViewportClient.Viewport->GetSizeXY(); + if (Size.X > 0 && Size.Y > 0) + { + Viewport = ViewportClient.Viewport; + ViewportWidth = Size.X; + ViewportHeight = Size.Y; + UE_LOG(LogRalphaMCP, Log, TEXT("Found active viewport: %dx%d"), ViewportWidth, ViewportHeight); + } + } + } + } + } } #endif + // Fallback to game viewport if (!Viewport) { - UE_LOG(LogRalphaMCP, Warning, TEXT("No viewport available for screenshot")); + UGameViewportClient* ViewportClient = GEngine ? GEngine->GameViewport : nullptr; + if (ViewportClient && ViewportClient->Viewport) + { + FIntPoint Size = ViewportClient->Viewport->GetSizeXY(); + if (Size.X > 0 && Size.Y > 0) + { + Viewport = ViewportClient->Viewport; + ViewportWidth = Size.X; + ViewportHeight = Size.Y; + UE_LOG(LogRalphaMCP, Log, TEXT("Using game viewport: %dx%d"), ViewportWidth, ViewportHeight); + } + } + } + + if (!Viewport || ViewportWidth <= 0 || ViewportHeight <= 0) + { + UE_LOG(LogRalphaMCP, Warning, TEXT("No valid viewport available for screenshot")); return false; } // Read pixels from the viewport TArray Bitmap; - int32 ViewportWidth = Viewport->GetSizeXY().X; - int32 ViewportHeight = Viewport->GetSizeXY().Y; - - if (ViewportWidth <= 0 || ViewportHeight <= 0) - { - UE_LOG(LogRalphaMCP, Warning, TEXT("Invalid viewport size: %dx%d"), ViewportWidth, ViewportHeight); - return false; - } - - // Read the viewport pixels bool bSuccess = Viewport->ReadPixels(Bitmap); if (!bSuccess || Bitmap.Num() == 0) @@ -151,6 +195,122 @@ FString URalphaScreenCapture::PixelsToBase64PNG(const TArray& Pixels, in return FBase64::Encode(PNGData32); } +bool URalphaScreenCapture::CaptureFromCamera(int32 Width, int32 Height, FString& OutBase64, FString& OutFilePath) +{ + // Get the world + UWorld* World = nullptr; + if (GEngine && GEngine->GetWorldContexts().Num() > 0) + { + World = GEngine->GetWorldContexts()[0].World(); + } + + if (!World) + { + UE_LOG(LogRalphaMCP, Warning, TEXT("No world available for camera capture")); + return false; + } + + // Find the CineCamera + ACineCameraActor* CineCamera = nullptr; + for (TActorIterator It(World); It; ++It) + { + CineCamera = *It; + break; + } + + if (!CineCamera) + { + UE_LOG(LogRalphaMCP, Warning, TEXT("No CineCameraActor found for capture")); + return false; + } + + // Use reasonable defaults if width/height not specified + if (Width <= 0) Width = 1920; + if (Height <= 0) Height = 1080; + + // Create render target + UTextureRenderTarget2D* RenderTarget = NewObject(); + RenderTarget->InitAutoFormat(Width, Height); + RenderTarget->UpdateResourceImmediate(true); + + // Create scene capture component + USceneCaptureComponent2D* SceneCapture = NewObject(CineCamera); + SceneCapture->RegisterComponent(); + SceneCapture->SetWorldLocation(CineCamera->GetActorLocation()); + SceneCapture->SetWorldRotation(CineCamera->GetActorRotation()); + + // Copy camera settings + UCineCameraComponent* CameraComp = CineCamera->GetCineCameraComponent(); + if (CameraComp) + { + SceneCapture->FOVAngle = CameraComp->FieldOfView; + } + + // Configure capture settings + SceneCapture->TextureTarget = RenderTarget; + SceneCapture->CaptureSource = SCS_FinalToneCurveHDR; + SceneCapture->bCaptureEveryFrame = false; + SceneCapture->bCaptureOnMovement = false; + SceneCapture->bAlwaysPersistRenderingState = true; + + // Enable atmosphere and sky rendering in capture + SceneCapture->ShowFlags.SetAtmosphere(true); + SceneCapture->ShowFlags.SetFog(true); + SceneCapture->ShowFlags.SetVolumetricFog(true); + SceneCapture->ShowFlags.SetCloud(true); + SceneCapture->ShowFlags.SetLighting(true); + SceneCapture->ShowFlags.SetPostProcessing(true); + + // Capture the scene + SceneCapture->CaptureScene(); + + // Wait for rendering to complete + FlushRenderingCommands(); + + // Read pixels from render target + TArray Bitmap; + FTextureRenderTargetResource* RTResource = RenderTarget->GameThread_GetRenderTargetResource(); + if (RTResource) + { + FReadSurfaceDataFlags ReadFlags(RCM_UNorm); + RTResource->ReadPixels(Bitmap, ReadFlags); + } + + // Clean up + SceneCapture->UnregisterComponent(); + SceneCapture->DestroyComponent(); + + if (Bitmap.Num() == 0) + { + UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to read pixels from render target")); + return false; + } + + // Convert to base64 PNG + OutBase64 = PixelsToBase64PNG(Bitmap, Width, Height); + + // Save to disk + FString ScreenshotDir = GetScreenshotDirectory(); + FString Filename = FString::Printf(TEXT("Ralpha_%s.png"), *FDateTime::Now().ToString(TEXT("%Y%m%d_%H%M%S"))); + OutFilePath = ScreenshotDir / Filename; + + IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); + TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG); + + if (ImageWrapper.IsValid() && ImageWrapper->SetRaw(Bitmap.GetData(), Bitmap.Num() * sizeof(FColor), Width, Height, ERGBFormat::BGRA, 8)) + { + TArray64 PNGData = ImageWrapper->GetCompressed(0); + if (PNGData.Num() > 0) + { + FFileHelper::SaveArrayToFile(PNGData, *OutFilePath); + } + } + + UE_LOG(LogRalphaMCP, Log, TEXT("Camera capture: %dx%d, saved to %s"), Width, Height, *OutFilePath); + + return true; +} + FString URalphaScreenCapture::GetScreenshotDirectory() { FString ScreenshotDir = FPaths::ProjectSavedDir() / TEXT("Screenshots") / TEXT("Ralpha"); diff --git a/Plugins/RalphaPlugin/Source/RalphaCore/Public/RalphaParameterBridge.h b/Plugins/RalphaPlugin/Source/RalphaCore/Public/RalphaParameterBridge.h index 4d695b23..a6da1381 100644 --- a/Plugins/RalphaPlugin/Source/RalphaCore/Public/RalphaParameterBridge.h +++ b/Plugins/RalphaPlugin/Source/RalphaCore/Public/RalphaParameterBridge.h @@ -67,9 +67,47 @@ public: // Create a simple material with specified properties and apply to actor bool SetActorSimpleMaterial(const FString& ActorId, const FLinearColor& BaseColor, float Metallic, float Roughness, float Opacity); + // Create an emissive (unlit) material for glowing objects like sun, lights, screens + bool SetActorEmissiveMaterial(const FString& ActorId, const FLinearColor& EmissiveColor, float EmissiveIntensity); + // Scene Setup - creates required actors for rendering control TSharedPtr SetupScene(); + // Spawn WaterBodyOcean from Water plugin + bool SpawnOcean(const FVector& Location, float Size, FString& OutActorId); + + // Configure ocean wave parameters + bool SetOceanWaves(float WaveAmplitude, float WaveLength, float Steepness, int32 NumWaves); + + // Create a minimal landscape for water plugin support + bool CreateLandscape(float Size, float HeightOffset); + + // Point Light + bool SpawnPointLight(const FVector& Location, float Intensity, const FLinearColor& Color, float AttenuationRadius, const FString& ActorLabel, FString& OutActorId); + bool SetPointLightParameters(const FString& ActorId, const TSharedPtr& Params); + + // Spot Light + bool SpawnSpotLight(const FVector& Location, const FRotator& Rotation, float Intensity, const FLinearColor& Color, float AttenuationRadius, float InnerConeAngle, float OuterConeAngle, const FString& ActorLabel, FString& OutActorId); + bool SetSpotLightParameters(const FString& ActorId, const TSharedPtr& Params); + + // Rect Light (area lighting for interiors) + bool SpawnRectLight(const FVector& Location, const FRotator& Rotation, float Intensity, const FLinearColor& Color, float SourceWidth, float SourceHeight, float AttenuationRadius, const FString& ActorLabel, FString& OutActorId); + bool SetRectLightParameters(const FString& ActorId, const TSharedPtr& Params); + + // Skeletal Mesh (characters, animated objects) + bool SpawnSkeletalMesh(const FString& SkeletalMeshPath, const FVector& Location, const FRotator& Rotation, float Scale, const FString& ActorLabel, FString& OutActorId); + bool PlayAnimation(const FString& ActorId, const FString& AnimationPath, bool bLooping, float PlayRate); + bool StopAnimation(const FString& ActorId); + bool SetAnimationPose(const FString& ActorId, const FString& AnimationPath, float Time); + + // Reflection Captures + bool SpawnSphereReflectionCapture(const FVector& Location, float InfluenceRadius, const FString& ActorLabel, FString& OutActorId); + bool SpawnBoxReflectionCapture(const FVector& Location, const FVector& BoxExtent, const FString& ActorLabel, FString& OutActorId); + bool UpdateReflectionCaptures(); + + // IES Light Profiles + bool SetLightIESProfile(const FString& ActorId, const FString& IESProfilePath); + private: // Find actors of specific types in the world APostProcessVolume* FindPostProcessVolume(); diff --git a/Plugins/RalphaPlugin/Source/RalphaCore/Public/RalphaScreenCapture.h b/Plugins/RalphaPlugin/Source/RalphaCore/Public/RalphaScreenCapture.h index ffa08a32..5d8f88f5 100644 --- a/Plugins/RalphaPlugin/Source/RalphaCore/Public/RalphaScreenCapture.h +++ b/Plugins/RalphaPlugin/Source/RalphaCore/Public/RalphaScreenCapture.h @@ -38,6 +38,17 @@ public: UFUNCTION(BlueprintCallable, Category = "Ralpha") bool CaptureHighResScreenshot(int32 Width, int32 Height, FString& OutBase64); + /** + * Capture from a specific camera using SceneCapture2D. + * @param Width Desired width + * @param Height Desired height + * @param OutBase64 The captured image as base64-encoded PNG + * @param OutFilePath Path where the image was saved + * @return True if capture succeeded + */ + UFUNCTION(BlueprintCallable, Category = "Ralpha") + bool CaptureFromCamera(int32 Width, int32 Height, FString& OutBase64, FString& OutFilePath); + private: /** Convert raw pixel data to base64 PNG */ FString PixelsToBase64PNG(const TArray& Pixels, int32 Width, int32 Height); diff --git a/Plugins/RalphaPlugin/Source/RalphaCore/RalphaCore.Build.cs b/Plugins/RalphaPlugin/Source/RalphaCore/RalphaCore.Build.cs index 91e17949..29980a9c 100644 --- a/Plugins/RalphaPlugin/Source/RalphaCore/RalphaCore.Build.cs +++ b/Plugins/RalphaPlugin/Source/RalphaCore/RalphaCore.Build.cs @@ -42,12 +42,18 @@ public class RalphaCore : ModuleRules "Slate", "SlateCore", "CinematicCamera", + "Water", + "Landscape", } ); if (Target.bBuildEditor) { - PrivateDependencyModuleNames.Add("UnrealEd"); + PrivateDependencyModuleNames.AddRange(new string[] + { + "UnrealEd", + "LevelEditor", + }); } DynamicallyLoadedModuleNames.AddRange( diff --git a/Ralpha.uproject b/Ralpha.uproject index 445dafda..c2cce92d 100644 --- a/Ralpha.uproject +++ b/Ralpha.uproject @@ -8,6 +8,10 @@ { "Name": "RalphaPlugin", "Enabled": true + }, + { + "Name": "Water", + "Enabled": true } ], "TargetPlatforms": [