Initial UE5 project with RalphaPlugin
- UE5.7 project configured with LFS for binary assets - RalphaPlugin for MCP-based AI rendering control - Ready for content creation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
commit
8a4b9a3f06
|
|
@ -0,0 +1,41 @@
|
|||
# UE5 LFS tracking
|
||||
|
||||
# Unreal binary assets
|
||||
*.uasset filter=lfs diff=lfs merge=lfs -text
|
||||
*.umap filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Textures
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
*.tga filter=lfs diff=lfs merge=lfs -text
|
||||
*.bmp filter=lfs diff=lfs merge=lfs -text
|
||||
*.psd filter=lfs diff=lfs merge=lfs -text
|
||||
*.exr filter=lfs diff=lfs merge=lfs -text
|
||||
*.hdr filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Audio
|
||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||
*.mp3 filter=lfs diff=lfs merge=lfs -text
|
||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Video
|
||||
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
||||
*.mov filter=lfs diff=lfs merge=lfs -text
|
||||
*.avi filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# 3D formats
|
||||
*.fbx filter=lfs diff=lfs merge=lfs -text
|
||||
*.obj filter=lfs diff=lfs merge=lfs -text
|
||||
*.gltf filter=lfs diff=lfs merge=lfs -text
|
||||
*.glb filter=lfs diff=lfs merge=lfs -text
|
||||
*.abc filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Fonts
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
*.otf filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# Compiled/binary
|
||||
*.dll filter=lfs diff=lfs merge=lfs -text
|
||||
*.so filter=lfs diff=lfs merge=lfs -text
|
||||
*.dylib filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# UE5 Project .gitignore
|
||||
|
||||
# Build artifacts (rebuilt locally)
|
||||
Binaries/
|
||||
Intermediate/
|
||||
DerivedDataCache/
|
||||
Build/
|
||||
|
||||
# Saved data (user-specific, logs, autosaves)
|
||||
Saved/
|
||||
|
||||
# Plugin binaries (rebuilt per machine)
|
||||
Plugins/**/Binaries/
|
||||
Plugins/**/Intermediate/
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
*.sln
|
||||
*.suo
|
||||
*.sdf
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
|
||||
# VS Code
|
||||
.vscode/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
*.DS_Store
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Cooked content (generated)
|
||||
**/Cooked/
|
||||
|
||||
# Starter content (if using)
|
||||
**/StarterContent/
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
[/Script/UnrealEd.EditorPerformanceSettings]
|
||||
bMonitorEditorPerformance=True
|
||||
|
||||
[/Script/UnrealEd.CrashReporterSettings]
|
||||
bHideLogFilesOption=False
|
||||
bHideRestartOption=False
|
||||
|
||||
[ContentBrowser]
|
||||
ShowEngineContent=False
|
||||
ShowPluginContent=True
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
[/Script/EngineSettings.GameMapsSettings]
|
||||
EditorStartupMap=/Game/Maps/Main
|
||||
GameDefaultMap=/Game/Maps/Main
|
||||
|
||||
[/Script/Engine.RendererSettings]
|
||||
r.DefaultFeature.AutoExposure=True
|
||||
r.DefaultFeature.MotionBlur=True
|
||||
r.DefaultFeature.Bloom=True
|
||||
r.DefaultFeature.AmbientOcclusion=True
|
||||
r.DefaultFeature.AmbientOcclusionStaticFraction=True
|
||||
r.Lumen.DiffuseIndirect.Allow=True
|
||||
r.Lumen.Reflections.Allow=True
|
||||
r.RayTracing=True
|
||||
|
||||
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
|
||||
[/Script/Engine.Engine]
|
||||
+ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/Ralpha")
|
||||
+ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/Ralpha")
|
||||
|
||||
[URL]
|
||||
GameName=Ralpha
|
||||
|
||||
[/Script/Engine.GarbageCollectionSettings]
|
||||
gc.MaxObjectsNotConsideredByGC=1
|
||||
|
||||
[ConsoleVariables]
|
||||
r.Lumen.ScreenProbeGather.FullResolutionJitterWidth=1
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[/Script/EngineSettings.GeneralProjectSettings]
|
||||
ProjectID=Ralpha2026
|
||||
ProjectName=Ralpha
|
||||
CompanyName=Ralpha
|
||||
CopyrightNotice=Copyright Ralpha Team
|
||||
Description=AI-driven style transfer for infinite world generation
|
||||
bStartInVR=False
|
||||
bStartInAR=False
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
[/Script/Engine.InputSettings]
|
||||
-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Sensitivity=1.f,Exponent=1.f,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Sensitivity=1.f,Exponent=1.f,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Sensitivity=1.f,Exponent=1.f,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Sensitivity=1.f,Exponent=1.f,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
bCaptureMouseOnLaunch=True
|
||||
DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
|
||||
bDefaultViewportMouseLock=False
|
||||
DefaultViewportMouseLockMode=DoNotLock
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[FilterPlugin]
|
||||
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
|
||||
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
|
||||
;
|
||||
; Examples:
|
||||
; /README.txt
|
||||
; /Extras/...
|
||||
; /Binaries/ThirdParty/*.dll
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "Ralpha - AI Style Transfer",
|
||||
"Description": "AI-driven style transfer system for Unreal Engine 5. Enables Claude to control rendering parameters via MCP.",
|
||||
"Category": "Rendering",
|
||||
"CreatedBy": "Ralpha Team",
|
||||
"CreatedByURL": "",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "RalphaCore",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
},
|
||||
{
|
||||
"Name": "RalphaEditor",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "PostEngineInit"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<rect width="20" height="20" rx="3" fill="#1a1a2e"/>
|
||||
<path d="M4 10 L8 6 L12 10 L16 4" stroke="#00d4ff" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="10" cy="14" r="2" fill="#ff6b6b"/>
|
||||
<path d="M6 16 L14 16" stroke="#4ecdc4" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 412 B |
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#include "RalphaCore.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FRalphaCoreModule"
|
||||
|
||||
void FRalphaCoreModule::StartupModule()
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("Ralpha Core Module Started"));
|
||||
}
|
||||
|
||||
void FRalphaCoreModule::ShutdownModule()
|
||||
{
|
||||
UE_LOG(LogTemp, Log, TEXT("Ralpha Core Module Shutdown"));
|
||||
}
|
||||
|
||||
FRalphaCoreModule& FRalphaCoreModule::Get()
|
||||
{
|
||||
return FModuleManager::LoadModuleChecked<FRalphaCoreModule>("RalphaCore");
|
||||
}
|
||||
|
||||
bool FRalphaCoreModule::IsAvailable()
|
||||
{
|
||||
return FModuleManager::Get().IsModuleLoaded("RalphaCore");
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FRalphaCoreModule, RalphaCore)
|
||||
|
|
@ -0,0 +1,448 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#include "RalphaMCPServer.h"
|
||||
#include "RalphaParameterBridge.h"
|
||||
#include "RalphaScreenCapture.h"
|
||||
#include "Dom/JsonObject.h"
|
||||
#include "Dom/JsonValue.h"
|
||||
#include "Serialization/JsonSerializer.h"
|
||||
#include "Serialization/JsonReader.h"
|
||||
#include "Serialization/JsonWriter.h"
|
||||
#include "Misc/Base64.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogRalphaMCP);
|
||||
|
||||
URalphaMCPServer* URalphaMCPServer::Instance = nullptr;
|
||||
|
||||
URalphaMCPServer::URalphaMCPServer()
|
||||
: ListenerSocket(nullptr)
|
||||
, ServerPort(30010)
|
||||
, bIsRunning(false)
|
||||
, ParameterBridge(nullptr)
|
||||
, ScreenCapture(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
URalphaMCPServer::~URalphaMCPServer()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
URalphaMCPServer* URalphaMCPServer::Get()
|
||||
{
|
||||
if (!Instance)
|
||||
{
|
||||
Instance = NewObject<URalphaMCPServer>();
|
||||
Instance->AddToRoot(); // Prevent garbage collection
|
||||
}
|
||||
return Instance;
|
||||
}
|
||||
|
||||
bool URalphaMCPServer::Start(int32 Port)
|
||||
{
|
||||
if (bIsRunning)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("Server already running on port %d"), ServerPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
ServerPort = Port;
|
||||
|
||||
// Create socket
|
||||
ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
|
||||
if (!SocketSubsystem)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Error, TEXT("Failed to get socket subsystem"));
|
||||
return false;
|
||||
}
|
||||
|
||||
ListenerSocket = SocketSubsystem->CreateSocket(NAME_Stream, TEXT("RalphaMCPServer"), false);
|
||||
if (!ListenerSocket)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Error, TEXT("Failed to create socket"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure socket
|
||||
ListenerSocket->SetReuseAddr(true);
|
||||
ListenerSocket->SetNonBlocking(true);
|
||||
|
||||
// Bind to port
|
||||
TSharedRef<FInternetAddr> Addr = SocketSubsystem->CreateInternetAddr();
|
||||
Addr->SetAnyAddress();
|
||||
Addr->SetPort(ServerPort);
|
||||
|
||||
if (!ListenerSocket->Bind(*Addr))
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Error, TEXT("Failed to bind to port %d"), ServerPort);
|
||||
SocketSubsystem->DestroySocket(ListenerSocket);
|
||||
ListenerSocket = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start listening
|
||||
if (!ListenerSocket->Listen(8))
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Error, TEXT("Failed to listen on port %d"), ServerPort);
|
||||
SocketSubsystem->DestroySocket(ListenerSocket);
|
||||
ListenerSocket = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create parameter bridge and screen capture
|
||||
ParameterBridge = NewObject<URalphaParameterBridge>(this);
|
||||
ScreenCapture = NewObject<URalphaScreenCapture>(this);
|
||||
|
||||
// Register tick
|
||||
TickDelegateHandle = FTSTicker::GetCoreTicker().AddTicker(
|
||||
FTickerDelegate::CreateUObject(this, &URalphaMCPServer::Tick), 0.0f);
|
||||
|
||||
bIsRunning = true;
|
||||
UE_LOG(LogRalphaMCP, Log, TEXT("Ralpha MCP Server started on port %d"), ServerPort);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void URalphaMCPServer::Stop()
|
||||
{
|
||||
if (!bIsRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Unregister tick
|
||||
FTSTicker::GetCoreTicker().RemoveTicker(TickDelegateHandle);
|
||||
|
||||
// Close all client connections
|
||||
ClientConnections.Empty();
|
||||
|
||||
// Close listener socket
|
||||
if (ListenerSocket)
|
||||
{
|
||||
ListenerSocket->Close();
|
||||
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ListenerSocket);
|
||||
ListenerSocket = nullptr;
|
||||
}
|
||||
|
||||
bIsRunning = false;
|
||||
UE_LOG(LogRalphaMCP, Log, TEXT("Ralpha MCP Server stopped"));
|
||||
}
|
||||
|
||||
bool URalphaMCPServer::IsRunning() const
|
||||
{
|
||||
return bIsRunning;
|
||||
}
|
||||
|
||||
bool URalphaMCPServer::Tick(float DeltaTime)
|
||||
{
|
||||
if (!bIsRunning || !ListenerSocket)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for new connections
|
||||
bool bHasPendingConnection = false;
|
||||
if (ListenerSocket->HasPendingConnection(bHasPendingConnection) && bHasPendingConnection)
|
||||
{
|
||||
TSharedRef<FInternetAddr> RemoteAddress = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
|
||||
FSocket* ClientSocket = ListenerSocket->Accept(*RemoteAddress, TEXT("RalphaClient"));
|
||||
|
||||
if (ClientSocket)
|
||||
{
|
||||
ClientSocket->SetNonBlocking(true);
|
||||
|
||||
TSharedPtr<FRalphaClientConnection> NewConnection = MakeShared<FRalphaClientConnection>(ClientSocket, this);
|
||||
ClientConnections.Add(NewConnection);
|
||||
|
||||
UE_LOG(LogRalphaMCP, Log, TEXT("New client connected from %s"), *RemoteAddress->ToString(true));
|
||||
}
|
||||
}
|
||||
|
||||
// Process existing connections
|
||||
for (int32 i = ClientConnections.Num() - 1; i >= 0; --i)
|
||||
{
|
||||
if (ClientConnections[i]->IsValid())
|
||||
{
|
||||
ClientConnections[i]->Tick();
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientConnections.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FString URalphaMCPServer::ProcessCommand(const FString& JsonCommand)
|
||||
{
|
||||
TSharedPtr<FJsonObject> JsonObject;
|
||||
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonCommand);
|
||||
|
||||
if (!FJsonSerializer::Deserialize(Reader, JsonObject) || !JsonObject.IsValid())
|
||||
{
|
||||
return TEXT("{\"success\": false, \"error\": \"Invalid JSON\"}");
|
||||
}
|
||||
|
||||
FString CommandType;
|
||||
if (!JsonObject->TryGetStringField(TEXT("type"), CommandType))
|
||||
{
|
||||
return TEXT("{\"success\": false, \"error\": \"Missing command type\"}");
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> ResponseObject = MakeShared<FJsonObject>();
|
||||
|
||||
// Route commands
|
||||
if (CommandType == TEXT("capture_screenshot"))
|
||||
{
|
||||
int32 Width = JsonObject->HasField(TEXT("width")) ? JsonObject->GetIntegerField(TEXT("width")) : 1920;
|
||||
int32 Height = JsonObject->HasField(TEXT("height")) ? JsonObject->GetIntegerField(TEXT("height")) : 1080;
|
||||
|
||||
FString Base64Image;
|
||||
FString FilePath;
|
||||
bool bSuccess = ScreenCapture->CaptureScreenshot(Width, Height, Base64Image, FilePath);
|
||||
|
||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||
if (bSuccess)
|
||||
{
|
||||
ResponseObject->SetStringField(TEXT("base64"), Base64Image);
|
||||
ResponseObject->SetStringField(TEXT("path"), FilePath);
|
||||
}
|
||||
}
|
||||
else if (CommandType == TEXT("get_all_parameters"))
|
||||
{
|
||||
TSharedPtr<FJsonObject> ParamsObject = ParameterBridge->GetAllParameters();
|
||||
ResponseObject->SetBoolField(TEXT("success"), true);
|
||||
ResponseObject->SetObjectField(TEXT("data"), ParamsObject);
|
||||
}
|
||||
else if (CommandType == TEXT("set_post_process"))
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* Params;
|
||||
if (JsonObject->TryGetObjectField(TEXT("parameters"), Params))
|
||||
{
|
||||
bool bSuccess = ParameterBridge->SetPostProcessParameters(*Params);
|
||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||
ResponseObject->SetStringField(TEXT("error"), TEXT("Missing parameters"));
|
||||
}
|
||||
}
|
||||
else if (CommandType == TEXT("set_directional_light"))
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* Params;
|
||||
if (JsonObject->TryGetObjectField(TEXT("parameters"), Params))
|
||||
{
|
||||
bool bSuccess = ParameterBridge->SetDirectionalLightParameters(*Params);
|
||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||
ResponseObject->SetStringField(TEXT("error"), TEXT("Missing parameters"));
|
||||
}
|
||||
}
|
||||
else if (CommandType == TEXT("set_sky_light"))
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* Params;
|
||||
if (JsonObject->TryGetObjectField(TEXT("parameters"), Params))
|
||||
{
|
||||
bool bSuccess = ParameterBridge->SetSkyLightParameters(*Params);
|
||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||
ResponseObject->SetStringField(TEXT("error"), TEXT("Missing parameters"));
|
||||
}
|
||||
}
|
||||
else if (CommandType == TEXT("set_exponential_height_fog"))
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* Params;
|
||||
if (JsonObject->TryGetObjectField(TEXT("parameters"), Params))
|
||||
{
|
||||
bool bSuccess = ParameterBridge->SetFogParameters(*Params);
|
||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||
ResponseObject->SetStringField(TEXT("error"), TEXT("Missing parameters"));
|
||||
}
|
||||
}
|
||||
else if (CommandType == TEXT("set_camera"))
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* Params;
|
||||
if (JsonObject->TryGetObjectField(TEXT("parameters"), Params))
|
||||
{
|
||||
bool bSuccess = ParameterBridge->SetCameraParameters(*Params);
|
||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||
ResponseObject->SetStringField(TEXT("error"), TEXT("Missing parameters"));
|
||||
}
|
||||
}
|
||||
else if (CommandType == TEXT("search_assets"))
|
||||
{
|
||||
FString Query = JsonObject->GetStringField(TEXT("query"));
|
||||
FString AssetType = JsonObject->HasField(TEXT("asset_type")) ? JsonObject->GetStringField(TEXT("asset_type")) : TEXT("All");
|
||||
int32 MaxResults = JsonObject->HasField(TEXT("max_results")) ? JsonObject->GetIntegerField(TEXT("max_results")) : 20;
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> Results = ParameterBridge->SearchAssets(Query, AssetType, MaxResults);
|
||||
ResponseObject->SetBoolField(TEXT("success"), true);
|
||||
ResponseObject->SetArrayField(TEXT("results"), Results);
|
||||
}
|
||||
else if (CommandType == TEXT("spawn_actor"))
|
||||
{
|
||||
FString AssetPath = JsonObject->GetStringField(TEXT("asset_path"));
|
||||
|
||||
const TSharedPtr<FJsonObject>* LocationObj;
|
||||
FVector Location = FVector::ZeroVector;
|
||||
if (JsonObject->TryGetObjectField(TEXT("location"), LocationObj))
|
||||
{
|
||||
Location.X = (*LocationObj)->GetNumberField(TEXT("x"));
|
||||
Location.Y = (*LocationObj)->GetNumberField(TEXT("y"));
|
||||
Location.Z = (*LocationObj)->GetNumberField(TEXT("z"));
|
||||
}
|
||||
|
||||
FRotator Rotation = FRotator::ZeroRotator;
|
||||
const TSharedPtr<FJsonObject>* 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->SpawnActor(AssetPath, Location, Rotation, Scale, ActorLabel, ActorId);
|
||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||
if (bSuccess)
|
||||
{
|
||||
ResponseObject->SetStringField(TEXT("actor_id"), ActorId);
|
||||
}
|
||||
}
|
||||
else if (CommandType == TEXT("delete_actor"))
|
||||
{
|
||||
FString ActorId = JsonObject->GetStringField(TEXT("actor_id"));
|
||||
bool bSuccess = ParameterBridge->DeleteActor(ActorId);
|
||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||
}
|
||||
else if (CommandType == TEXT("list_actors"))
|
||||
{
|
||||
FString ClassFilter = JsonObject->HasField(TEXT("class_filter")) ? JsonObject->GetStringField(TEXT("class_filter")) : TEXT("");
|
||||
FString NameFilter = JsonObject->HasField(TEXT("name_filter")) ? JsonObject->GetStringField(TEXT("name_filter")) : TEXT("");
|
||||
bool bIncludeTransforms = JsonObject->HasField(TEXT("include_transforms")) ? JsonObject->GetBoolField(TEXT("include_transforms")) : true;
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> Actors = ParameterBridge->ListActors(ClassFilter, NameFilter, bIncludeTransforms);
|
||||
ResponseObject->SetBoolField(TEXT("success"), true);
|
||||
ResponseObject->SetArrayField(TEXT("actors"), Actors);
|
||||
}
|
||||
else if (CommandType == TEXT("set_render_quality"))
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* Settings;
|
||||
if (JsonObject->TryGetObjectField(TEXT("settings"), Settings))
|
||||
{
|
||||
bool bSuccess = ParameterBridge->SetRenderQuality(*Settings);
|
||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||
ResponseObject->SetStringField(TEXT("error"), TEXT("Missing settings"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||
ResponseObject->SetStringField(TEXT("error"), FString::Printf(TEXT("Unknown command type: %s"), *CommandType));
|
||||
}
|
||||
|
||||
// Serialize response
|
||||
FString ResponseString;
|
||||
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&ResponseString);
|
||||
FJsonSerializer::Serialize(ResponseObject.ToSharedRef(), Writer);
|
||||
|
||||
return ResponseString;
|
||||
}
|
||||
|
||||
// FRalphaClientConnection implementation
|
||||
|
||||
FRalphaClientConnection::FRalphaClientConnection(FSocket* InSocket, URalphaMCPServer* InServer)
|
||||
: Socket(InSocket)
|
||||
, Server(InServer)
|
||||
{
|
||||
}
|
||||
|
||||
FRalphaClientConnection::~FRalphaClientConnection()
|
||||
{
|
||||
if (Socket)
|
||||
{
|
||||
Socket->Close();
|
||||
ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(Socket);
|
||||
Socket = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool FRalphaClientConnection::IsValid() const
|
||||
{
|
||||
return Socket != nullptr && Socket->GetConnectionState() == SCS_Connected;
|
||||
}
|
||||
|
||||
void FRalphaClientConnection::Tick()
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Receive data
|
||||
uint32 PendingDataSize = 0;
|
||||
while (Socket->HasPendingData(PendingDataSize) && PendingDataSize > 0)
|
||||
{
|
||||
TArray<uint8> ReceivedData;
|
||||
ReceivedData.SetNumUninitialized(FMath::Min(PendingDataSize, 65536u));
|
||||
|
||||
int32 BytesRead = 0;
|
||||
if (Socket->Recv(ReceivedData.GetData(), ReceivedData.Num(), BytesRead))
|
||||
{
|
||||
ReceiveBuffer += FString(UTF8_TO_TCHAR(reinterpret_cast<const char*>(ReceivedData.GetData())));
|
||||
}
|
||||
}
|
||||
|
||||
// Process complete messages (newline-delimited JSON)
|
||||
int32 NewlineIndex;
|
||||
while (ReceiveBuffer.FindChar(TEXT('\n'), NewlineIndex))
|
||||
{
|
||||
FString Message = ReceiveBuffer.Left(NewlineIndex);
|
||||
ReceiveBuffer = ReceiveBuffer.Mid(NewlineIndex + 1);
|
||||
|
||||
if (!Message.IsEmpty())
|
||||
{
|
||||
FString Response = Server->ProcessCommand(Message);
|
||||
SendResponse(Response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FRalphaClientConnection::SendResponse(const FString& Response)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FString ResponseWithNewline = Response + TEXT("\n");
|
||||
FTCHARToUTF8 Converter(*ResponseWithNewline);
|
||||
int32 BytesSent = 0;
|
||||
Socket->Send(reinterpret_cast<const uint8*>(Converter.Get()), Converter.Length(), BytesSent);
|
||||
}
|
||||
|
|
@ -0,0 +1,881 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#include "RalphaParameterBridge.h"
|
||||
#include "RalphaMCPServer.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Engine/PostProcessVolume.h"
|
||||
#include "Engine/DirectionalLight.h"
|
||||
#include "Engine/SkyLight.h"
|
||||
#include "Engine/ExponentialHeightFog.h"
|
||||
#include "CineCameraActor.h"
|
||||
#include "CineCameraComponent.h"
|
||||
#include "Components/PostProcessComponent.h"
|
||||
#include "Components/DirectionalLightComponent.h"
|
||||
#include "Components/SkyLightComponent.h"
|
||||
#include "Components/ExponentialHeightFogComponent.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Engine/StaticMeshActor.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Dom/JsonObject.h"
|
||||
#include "Dom/JsonValue.h"
|
||||
#include "Serialization/JsonSerializer.h"
|
||||
|
||||
URalphaParameterBridge::URalphaParameterBridge()
|
||||
{
|
||||
}
|
||||
|
||||
UWorld* URalphaParameterBridge::GetCurrentWorld()
|
||||
{
|
||||
if (GEngine && GEngine->GetWorldContexts().Num() > 0)
|
||||
{
|
||||
for (const FWorldContext& Context : GEngine->GetWorldContexts())
|
||||
{
|
||||
if (Context.WorldType == EWorldType::Editor || Context.WorldType == EWorldType::PIE)
|
||||
{
|
||||
return Context.World();
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
APostProcessVolume* URalphaParameterBridge::FindPostProcessVolume()
|
||||
{
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return nullptr;
|
||||
|
||||
for (TActorIterator<APostProcessVolume> It(World); It; ++It)
|
||||
{
|
||||
if (It->bUnbound) // Prefer unbound (global) volumes
|
||||
{
|
||||
return *It;
|
||||
}
|
||||
}
|
||||
|
||||
// Return any post-process volume if no unbound one found
|
||||
for (TActorIterator<APostProcessVolume> It(World); It; ++It)
|
||||
{
|
||||
return *It;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ADirectionalLight* URalphaParameterBridge::FindDirectionalLight()
|
||||
{
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return nullptr;
|
||||
|
||||
for (TActorIterator<ADirectionalLight> It(World); It; ++It)
|
||||
{
|
||||
return *It;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ASkyLight* URalphaParameterBridge::FindSkyLight()
|
||||
{
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return nullptr;
|
||||
|
||||
for (TActorIterator<ASkyLight> It(World); It; ++It)
|
||||
{
|
||||
return *It;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AExponentialHeightFog* URalphaParameterBridge::FindExponentialHeightFog()
|
||||
{
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return nullptr;
|
||||
|
||||
for (TActorIterator<AExponentialHeightFog> It(World); It; ++It)
|
||||
{
|
||||
return *It;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ACineCameraActor* URalphaParameterBridge::FindCineCamera()
|
||||
{
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return nullptr;
|
||||
|
||||
for (TActorIterator<ACineCameraActor> It(World); It; ++It)
|
||||
{
|
||||
return *It;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FLinearColor URalphaParameterBridge::ParseHexColor(const FString& HexColor)
|
||||
{
|
||||
FString CleanHex = HexColor;
|
||||
CleanHex.RemoveFromStart(TEXT("#"));
|
||||
|
||||
FColor Color = FColor::FromHex(CleanHex);
|
||||
return FLinearColor(Color);
|
||||
}
|
||||
|
||||
FString URalphaParameterBridge::ColorToHex(const FLinearColor& Color)
|
||||
{
|
||||
FColor SRGBColor = Color.ToFColor(true);
|
||||
return FString::Printf(TEXT("#%02X%02X%02X"), SRGBColor.R, SRGBColor.G, SRGBColor.B);
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> URalphaParameterBridge::GetAllParameters()
|
||||
{
|
||||
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
|
||||
Result->SetObjectField(TEXT("post_process"), GetPostProcessParameters());
|
||||
Result->SetObjectField(TEXT("directional_light"), GetDirectionalLightParameters());
|
||||
Result->SetObjectField(TEXT("sky_light"), GetSkyLightParameters());
|
||||
Result->SetObjectField(TEXT("fog"), GetFogParameters());
|
||||
Result->SetObjectField(TEXT("camera"), GetCameraParameters());
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Post-Process Volume
|
||||
// ============================================================================
|
||||
|
||||
bool URalphaParameterBridge::SetPostProcessParameters(const TSharedPtr<FJsonObject>& Params)
|
||||
{
|
||||
APostProcessVolume* PPV = FindPostProcessVolume();
|
||||
if (!PPV)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("No PostProcessVolume found in scene"));
|
||||
return false;
|
||||
}
|
||||
|
||||
FPostProcessSettings& Settings = PPV->Settings;
|
||||
|
||||
// Exposure
|
||||
if (Params->HasField(TEXT("exposure_compensation")))
|
||||
{
|
||||
Settings.bOverride_AutoExposureBias = true;
|
||||
Settings.AutoExposureBias = Params->GetNumberField(TEXT("exposure_compensation"));
|
||||
}
|
||||
|
||||
// Color Grading
|
||||
if (Params->HasField(TEXT("color_saturation")))
|
||||
{
|
||||
Settings.bOverride_ColorSaturation = true;
|
||||
float Sat = Params->GetNumberField(TEXT("color_saturation"));
|
||||
Settings.ColorSaturation = FVector4(Sat, Sat, Sat, 1.0f);
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("color_contrast")))
|
||||
{
|
||||
Settings.bOverride_ColorContrast = true;
|
||||
float Contrast = Params->GetNumberField(TEXT("color_contrast"));
|
||||
Settings.ColorContrast = FVector4(Contrast, Contrast, Contrast, 1.0f);
|
||||
}
|
||||
|
||||
// White Balance
|
||||
if (Params->HasField(TEXT("white_balance_temp")))
|
||||
{
|
||||
Settings.bOverride_WhiteTemp = true;
|
||||
Settings.WhiteTemp = Params->GetNumberField(TEXT("white_balance_temp"));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("white_balance_tint")))
|
||||
{
|
||||
Settings.bOverride_WhiteTint = true;
|
||||
Settings.WhiteTint = Params->GetNumberField(TEXT("white_balance_tint"));
|
||||
}
|
||||
|
||||
// Bloom
|
||||
if (Params->HasField(TEXT("bloom_intensity")))
|
||||
{
|
||||
Settings.bOverride_BloomIntensity = true;
|
||||
Settings.BloomIntensity = Params->GetNumberField(TEXT("bloom_intensity"));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("bloom_threshold")))
|
||||
{
|
||||
Settings.bOverride_BloomThreshold = true;
|
||||
Settings.BloomThreshold = Params->GetNumberField(TEXT("bloom_threshold"));
|
||||
}
|
||||
|
||||
// Vignette
|
||||
if (Params->HasField(TEXT("vignette_intensity")))
|
||||
{
|
||||
Settings.bOverride_VignetteIntensity = true;
|
||||
Settings.VignetteIntensity = Params->GetNumberField(TEXT("vignette_intensity"));
|
||||
}
|
||||
|
||||
// Film Grain
|
||||
if (Params->HasField(TEXT("film_grain_intensity")))
|
||||
{
|
||||
Settings.bOverride_FilmGrainIntensity = true;
|
||||
Settings.FilmGrainIntensity = Params->GetNumberField(TEXT("film_grain_intensity"));
|
||||
}
|
||||
|
||||
// Chromatic Aberration
|
||||
if (Params->HasField(TEXT("chromatic_aberration_intensity")))
|
||||
{
|
||||
Settings.bOverride_SceneFringeIntensity = true;
|
||||
Settings.SceneFringeIntensity = Params->GetNumberField(TEXT("chromatic_aberration_intensity"));
|
||||
}
|
||||
|
||||
PPV->MarkPackageDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> URalphaParameterBridge::GetPostProcessParameters()
|
||||
{
|
||||
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
|
||||
APostProcessVolume* PPV = FindPostProcessVolume();
|
||||
if (!PPV)
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
const FPostProcessSettings& Settings = PPV->Settings;
|
||||
|
||||
Result->SetNumberField(TEXT("exposure_compensation"), Settings.AutoExposureBias);
|
||||
Result->SetNumberField(TEXT("color_saturation"), Settings.ColorSaturation.X);
|
||||
Result->SetNumberField(TEXT("color_contrast"), Settings.ColorContrast.X);
|
||||
Result->SetNumberField(TEXT("white_balance_temp"), Settings.WhiteTemp);
|
||||
Result->SetNumberField(TEXT("white_balance_tint"), Settings.WhiteTint);
|
||||
Result->SetNumberField(TEXT("bloom_intensity"), Settings.BloomIntensity);
|
||||
Result->SetNumberField(TEXT("bloom_threshold"), Settings.BloomThreshold);
|
||||
Result->SetNumberField(TEXT("vignette_intensity"), Settings.VignetteIntensity);
|
||||
Result->SetNumberField(TEXT("film_grain_intensity"), Settings.FilmGrainIntensity);
|
||||
Result->SetNumberField(TEXT("chromatic_aberration_intensity"), Settings.SceneFringeIntensity);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Directional Light
|
||||
// ============================================================================
|
||||
|
||||
bool URalphaParameterBridge::SetDirectionalLightParameters(const TSharedPtr<FJsonObject>& Params)
|
||||
{
|
||||
ADirectionalLight* Light = FindDirectionalLight();
|
||||
if (!Light)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("No DirectionalLight found in scene"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UDirectionalLightComponent* LightComp = Cast<UDirectionalLightComponent>(Light->GetLightComponent());
|
||||
if (!LightComp) return false;
|
||||
|
||||
if (Params->HasField(TEXT("intensity")))
|
||||
{
|
||||
LightComp->SetIntensity(Params->GetNumberField(TEXT("intensity")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("color")))
|
||||
{
|
||||
FLinearColor Color = ParseHexColor(Params->GetStringField(TEXT("color")));
|
||||
LightComp->SetLightColor(Color);
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("temperature")))
|
||||
{
|
||||
LightComp->bUseTemperature = true;
|
||||
LightComp->SetTemperature(Params->GetNumberField(TEXT("temperature")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("pitch")) || Params->HasField(TEXT("yaw")))
|
||||
{
|
||||
FRotator CurrentRot = Light->GetActorRotation();
|
||||
if (Params->HasField(TEXT("pitch")))
|
||||
{
|
||||
CurrentRot.Pitch = Params->GetNumberField(TEXT("pitch"));
|
||||
}
|
||||
if (Params->HasField(TEXT("yaw")))
|
||||
{
|
||||
CurrentRot.Yaw = Params->GetNumberField(TEXT("yaw"));
|
||||
}
|
||||
Light->SetActorRotation(CurrentRot);
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("source_angle")))
|
||||
{
|
||||
LightComp->LightSourceAngle = Params->GetNumberField(TEXT("source_angle"));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("source_soft_angle")))
|
||||
{
|
||||
LightComp->LightSourceSoftAngle = Params->GetNumberField(TEXT("source_soft_angle"));
|
||||
}
|
||||
|
||||
LightComp->MarkRenderStateDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> URalphaParameterBridge::GetDirectionalLightParameters()
|
||||
{
|
||||
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
|
||||
ADirectionalLight* Light = FindDirectionalLight();
|
||||
if (!Light) return Result;
|
||||
|
||||
UDirectionalLightComponent* LightComp = Cast<UDirectionalLightComponent>(Light->GetLightComponent());
|
||||
if (!LightComp) return Result;
|
||||
|
||||
Result->SetNumberField(TEXT("intensity"), LightComp->Intensity);
|
||||
Result->SetStringField(TEXT("color"), ColorToHex(LightComp->GetLightColor()));
|
||||
Result->SetNumberField(TEXT("temperature"), LightComp->Temperature);
|
||||
Result->SetNumberField(TEXT("pitch"), Light->GetActorRotation().Pitch);
|
||||
Result->SetNumberField(TEXT("yaw"), Light->GetActorRotation().Yaw);
|
||||
Result->SetNumberField(TEXT("source_angle"), LightComp->LightSourceAngle);
|
||||
Result->SetNumberField(TEXT("source_soft_angle"), LightComp->LightSourceSoftAngle);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Sky Light
|
||||
// ============================================================================
|
||||
|
||||
bool URalphaParameterBridge::SetSkyLightParameters(const TSharedPtr<FJsonObject>& Params)
|
||||
{
|
||||
ASkyLight* Light = FindSkyLight();
|
||||
if (!Light)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("No SkyLight found in scene"));
|
||||
return false;
|
||||
}
|
||||
|
||||
USkyLightComponent* LightComp = Light->GetLightComponent();
|
||||
if (!LightComp) return false;
|
||||
|
||||
if (Params->HasField(TEXT("intensity")))
|
||||
{
|
||||
LightComp->SetIntensity(Params->GetNumberField(TEXT("intensity")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("color")))
|
||||
{
|
||||
FLinearColor Color = ParseHexColor(Params->GetStringField(TEXT("color")));
|
||||
LightComp->SetLightColor(Color);
|
||||
}
|
||||
|
||||
LightComp->MarkRenderStateDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> URalphaParameterBridge::GetSkyLightParameters()
|
||||
{
|
||||
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
|
||||
ASkyLight* Light = FindSkyLight();
|
||||
if (!Light) return Result;
|
||||
|
||||
USkyLightComponent* LightComp = Light->GetLightComponent();
|
||||
if (!LightComp) return Result;
|
||||
|
||||
Result->SetNumberField(TEXT("intensity"), LightComp->Intensity);
|
||||
Result->SetStringField(TEXT("color"), ColorToHex(LightComp->GetLightColor()));
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Exponential Height Fog
|
||||
// ============================================================================
|
||||
|
||||
bool URalphaParameterBridge::SetFogParameters(const TSharedPtr<FJsonObject>& Params)
|
||||
{
|
||||
AExponentialHeightFog* Fog = FindExponentialHeightFog();
|
||||
if (!Fog)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("No ExponentialHeightFog found in scene"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UExponentialHeightFogComponent* FogComp = Fog->GetComponent();
|
||||
if (!FogComp) return false;
|
||||
|
||||
if (Params->HasField(TEXT("enabled")))
|
||||
{
|
||||
FogComp->SetVisibility(Params->GetBoolField(TEXT("enabled")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("fog_density")))
|
||||
{
|
||||
FogComp->SetFogDensity(Params->GetNumberField(TEXT("fog_density")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("fog_height_falloff")))
|
||||
{
|
||||
FogComp->SetFogHeightFalloff(Params->GetNumberField(TEXT("fog_height_falloff")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("fog_inscattering_color")))
|
||||
{
|
||||
FLinearColor Color = ParseHexColor(Params->GetStringField(TEXT("fog_inscattering_color")));
|
||||
FogComp->SetFogInscatteringColor(Color);
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("fog_max_opacity")))
|
||||
{
|
||||
FogComp->SetFogMaxOpacity(Params->GetNumberField(TEXT("fog_max_opacity")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("start_distance")))
|
||||
{
|
||||
FogComp->SetStartDistance(Params->GetNumberField(TEXT("start_distance")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("volumetric_fog")))
|
||||
{
|
||||
FogComp->SetVolumetricFog(Params->GetBoolField(TEXT("volumetric_fog")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("volumetric_fog_scattering_distribution")))
|
||||
{
|
||||
FogComp->VolumetricFogScatteringDistribution = Params->GetNumberField(TEXT("volumetric_fog_scattering_distribution"));
|
||||
}
|
||||
|
||||
FogComp->MarkRenderStateDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> URalphaParameterBridge::GetFogParameters()
|
||||
{
|
||||
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
|
||||
AExponentialHeightFog* Fog = FindExponentialHeightFog();
|
||||
if (!Fog) return Result;
|
||||
|
||||
UExponentialHeightFogComponent* FogComp = Fog->GetComponent();
|
||||
if (!FogComp) return Result;
|
||||
|
||||
Result->SetBoolField(TEXT("enabled"), FogComp->IsVisible());
|
||||
Result->SetNumberField(TEXT("fog_density"), FogComp->FogDensity);
|
||||
Result->SetNumberField(TEXT("fog_height_falloff"), FogComp->FogHeightFalloff);
|
||||
Result->SetNumberField(TEXT("fog_max_opacity"), FogComp->FogMaxOpacity);
|
||||
Result->SetNumberField(TEXT("start_distance"), FogComp->StartDistance);
|
||||
Result->SetBoolField(TEXT("volumetric_fog"), FogComp->bEnableVolumetricFog);
|
||||
Result->SetNumberField(TEXT("volumetric_fog_scattering_distribution"), FogComp->VolumetricFogScatteringDistribution);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Camera
|
||||
// ============================================================================
|
||||
|
||||
bool URalphaParameterBridge::SetCameraParameters(const TSharedPtr<FJsonObject>& Params)
|
||||
{
|
||||
ACineCameraActor* Camera = FindCineCamera();
|
||||
if (!Camera)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("No CineCameraActor found in scene"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UCineCameraComponent* CameraComp = Camera->GetCineCameraComponent();
|
||||
if (!CameraComp) return false;
|
||||
|
||||
if (Params->HasField(TEXT("focal_length")))
|
||||
{
|
||||
CameraComp->SetCurrentFocalLength(Params->GetNumberField(TEXT("focal_length")));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("aperture")))
|
||||
{
|
||||
CameraComp->CurrentAperture = Params->GetNumberField(TEXT("aperture"));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("focus_distance")))
|
||||
{
|
||||
CameraComp->FocusSettings.ManualFocusDistance = Params->GetNumberField(TEXT("focus_distance"));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("dof_enabled")))
|
||||
{
|
||||
CameraComp->PostProcessSettings.bOverride_DepthOfFieldFstop = Params->GetBoolField(TEXT("dof_enabled"));
|
||||
}
|
||||
|
||||
if (Params->HasField(TEXT("motion_blur_amount")))
|
||||
{
|
||||
CameraComp->PostProcessSettings.bOverride_MotionBlurAmount = true;
|
||||
CameraComp->PostProcessSettings.MotionBlurAmount = Params->GetNumberField(TEXT("motion_blur_amount"));
|
||||
}
|
||||
|
||||
CameraComp->MarkRenderStateDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> URalphaParameterBridge::GetCameraParameters()
|
||||
{
|
||||
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
|
||||
ACineCameraActor* Camera = FindCineCamera();
|
||||
if (!Camera) return Result;
|
||||
|
||||
UCineCameraComponent* CameraComp = Camera->GetCineCameraComponent();
|
||||
if (!CameraComp) return Result;
|
||||
|
||||
Result->SetNumberField(TEXT("focal_length"), CameraComp->CurrentFocalLength);
|
||||
Result->SetNumberField(TEXT("aperture"), CameraComp->CurrentAperture);
|
||||
Result->SetNumberField(TEXT("focus_distance"), CameraComp->FocusSettings.ManualFocusDistance);
|
||||
Result->SetNumberField(TEXT("motion_blur_amount"), CameraComp->PostProcessSettings.MotionBlurAmount);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Render Quality
|
||||
// ============================================================================
|
||||
|
||||
bool URalphaParameterBridge::SetRenderQuality(const TSharedPtr<FJsonObject>& Settings)
|
||||
{
|
||||
// This would typically use console commands or project settings
|
||||
// For now, we'll log what we would set
|
||||
|
||||
if (Settings->HasField(TEXT("lumen_quality")))
|
||||
{
|
||||
float Quality = Settings->GetNumberField(TEXT("lumen_quality"));
|
||||
UE_LOG(LogRalphaMCP, Log, TEXT("Would set Lumen quality to: %f"), Quality);
|
||||
// r.Lumen.ScreenTracing.MaxIterations, etc.
|
||||
}
|
||||
|
||||
if (Settings->HasField(TEXT("shadow_quality")))
|
||||
{
|
||||
FString Quality = Settings->GetStringField(TEXT("shadow_quality"));
|
||||
UE_LOG(LogRalphaMCP, Log, TEXT("Would set shadow quality to: %s"), *Quality);
|
||||
// sg.ShadowQuality
|
||||
}
|
||||
|
||||
if (Settings->HasField(TEXT("resolution_scale")))
|
||||
{
|
||||
float Scale = Settings->GetNumberField(TEXT("resolution_scale"));
|
||||
UE_LOG(LogRalphaMCP, Log, TEXT("Would set resolution scale to: %f"), Scale);
|
||||
// r.ScreenPercentage
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Asset Search
|
||||
// ============================================================================
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> URalphaParameterBridge::SearchAssets(const FString& Query, const FString& AssetType, int32 MaxResults)
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> Results;
|
||||
|
||||
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
||||
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
|
||||
|
||||
FARFilter Filter;
|
||||
Filter.bRecursivePaths = true;
|
||||
Filter.PackagePaths.Add(TEXT("/Game"));
|
||||
|
||||
if (AssetType == TEXT("StaticMesh"))
|
||||
{
|
||||
Filter.ClassPaths.Add(UStaticMesh::StaticClass()->GetClassPathName());
|
||||
}
|
||||
// Add more type filters as needed
|
||||
|
||||
TArray<FAssetData> AssetList;
|
||||
AssetRegistry.GetAssets(Filter, AssetList);
|
||||
|
||||
int32 Count = 0;
|
||||
for (const FAssetData& Asset : AssetList)
|
||||
{
|
||||
if (Count >= MaxResults) break;
|
||||
|
||||
FString AssetName = Asset.AssetName.ToString();
|
||||
if (AssetName.Contains(Query, ESearchCase::IgnoreCase))
|
||||
{
|
||||
TSharedPtr<FJsonObject> AssetObj = MakeShared<FJsonObject>();
|
||||
AssetObj->SetStringField(TEXT("name"), AssetName);
|
||||
AssetObj->SetStringField(TEXT("asset_path"), Asset.GetObjectPathString());
|
||||
AssetObj->SetStringField(TEXT("class"), Asset.AssetClassPath.GetAssetName().ToString());
|
||||
|
||||
Results.Add(MakeShared<FJsonValueObject>(AssetObj));
|
||||
Count++;
|
||||
}
|
||||
}
|
||||
|
||||
return Results;
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> URalphaParameterBridge::ListAssets(const FString& FolderPath, const FString& AssetType, bool bRecursive)
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> Results;
|
||||
|
||||
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
||||
IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();
|
||||
|
||||
FARFilter Filter;
|
||||
Filter.bRecursivePaths = bRecursive;
|
||||
Filter.PackagePaths.Add(*FolderPath);
|
||||
|
||||
TArray<FAssetData> AssetList;
|
||||
AssetRegistry.GetAssets(Filter, AssetList);
|
||||
|
||||
for (const FAssetData& Asset : AssetList)
|
||||
{
|
||||
TSharedPtr<FJsonObject> AssetObj = MakeShared<FJsonObject>();
|
||||
AssetObj->SetStringField(TEXT("name"), Asset.AssetName.ToString());
|
||||
AssetObj->SetStringField(TEXT("asset_path"), Asset.GetObjectPathString());
|
||||
AssetObj->SetStringField(TEXT("class"), Asset.AssetClassPath.GetAssetName().ToString());
|
||||
|
||||
Results.Add(MakeShared<FJsonValueObject>(AssetObj));
|
||||
}
|
||||
|
||||
return Results;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Actor Management
|
||||
// ============================================================================
|
||||
|
||||
bool URalphaParameterBridge::SpawnActor(const FString& AssetPath, const FVector& Location, const FRotator& Rotation, float Scale, const FString& ActorLabel, FString& OutActorId)
|
||||
{
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return false;
|
||||
|
||||
// Load the asset
|
||||
UObject* LoadedAsset = StaticLoadObject(UStaticMesh::StaticClass(), nullptr, *AssetPath);
|
||||
UStaticMesh* StaticMesh = Cast<UStaticMesh>(LoadedAsset);
|
||||
|
||||
if (!StaticMesh)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to load static mesh: %s"), *AssetPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spawn the actor
|
||||
FActorSpawnParameters SpawnParams;
|
||||
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
|
||||
AStaticMeshActor* NewActor = World->SpawnActor<AStaticMeshActor>(Location, Rotation, SpawnParams);
|
||||
if (!NewActor)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to spawn actor"));
|
||||
return false;
|
||||
}
|
||||
|
||||
NewActor->GetStaticMeshComponent()->SetStaticMesh(StaticMesh);
|
||||
NewActor->SetActorScale3D(FVector(Scale));
|
||||
|
||||
#if WITH_EDITOR
|
||||
if (!ActorLabel.IsEmpty())
|
||||
{
|
||||
NewActor->SetActorLabel(ActorLabel);
|
||||
}
|
||||
#endif
|
||||
|
||||
OutActorId = NewActor->GetName();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool URalphaParameterBridge::DeleteActor(const FString& ActorId)
|
||||
{
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return false;
|
||||
|
||||
for (TActorIterator<AActor> It(World); It; ++It)
|
||||
{
|
||||
if (It->GetName() == ActorId)
|
||||
{
|
||||
World->DestroyActor(*It);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FJsonValue>> URalphaParameterBridge::ListActors(const FString& ClassFilter, const FString& NameFilter, bool bIncludeTransforms)
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> Results;
|
||||
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return Results;
|
||||
|
||||
for (TActorIterator<AActor> It(World); It; ++It)
|
||||
{
|
||||
AActor* Actor = *It;
|
||||
|
||||
// Apply filters
|
||||
if (!ClassFilter.IsEmpty() && !Actor->GetClass()->GetName().Contains(ClassFilter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
if (!NameFilter.IsEmpty() && !Actor->GetActorLabel().Contains(NameFilter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if (!NameFilter.IsEmpty() && !Actor->GetName().Contains(NameFilter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
TSharedPtr<FJsonObject> ActorObj = MakeShared<FJsonObject>();
|
||||
ActorObj->SetStringField(TEXT("actor_id"), Actor->GetName());
|
||||
#if WITH_EDITOR
|
||||
ActorObj->SetStringField(TEXT("label"), Actor->GetActorLabel());
|
||||
#else
|
||||
ActorObj->SetStringField(TEXT("label"), Actor->GetName());
|
||||
#endif
|
||||
ActorObj->SetStringField(TEXT("class"), Actor->GetClass()->GetName());
|
||||
|
||||
if (bIncludeTransforms)
|
||||
{
|
||||
FVector Location = Actor->GetActorLocation();
|
||||
FRotator Rotation = Actor->GetActorRotation();
|
||||
FVector Scale = Actor->GetActorScale3D();
|
||||
|
||||
TSharedPtr<FJsonObject> TransformObj = MakeShared<FJsonObject>();
|
||||
|
||||
TSharedPtr<FJsonObject> LocObj = MakeShared<FJsonObject>();
|
||||
LocObj->SetNumberField(TEXT("x"), Location.X);
|
||||
LocObj->SetNumberField(TEXT("y"), Location.Y);
|
||||
LocObj->SetNumberField(TEXT("z"), Location.Z);
|
||||
TransformObj->SetObjectField(TEXT("location"), LocObj);
|
||||
|
||||
TSharedPtr<FJsonObject> RotObj = MakeShared<FJsonObject>();
|
||||
RotObj->SetNumberField(TEXT("pitch"), Rotation.Pitch);
|
||||
RotObj->SetNumberField(TEXT("yaw"), Rotation.Yaw);
|
||||
RotObj->SetNumberField(TEXT("roll"), Rotation.Roll);
|
||||
TransformObj->SetObjectField(TEXT("rotation"), RotObj);
|
||||
|
||||
TSharedPtr<FJsonObject> ScaleObj = MakeShared<FJsonObject>();
|
||||
ScaleObj->SetNumberField(TEXT("x"), Scale.X);
|
||||
ScaleObj->SetNumberField(TEXT("y"), Scale.Y);
|
||||
ScaleObj->SetNumberField(TEXT("z"), Scale.Z);
|
||||
TransformObj->SetObjectField(TEXT("scale"), ScaleObj);
|
||||
|
||||
ActorObj->SetObjectField(TEXT("transform"), TransformObj);
|
||||
}
|
||||
|
||||
Results.Add(MakeShared<FJsonValueObject>(ActorObj));
|
||||
}
|
||||
|
||||
return Results;
|
||||
}
|
||||
|
||||
bool URalphaParameterBridge::SetActorTransform(const FString& ActorId, const FVector* Location, const FRotator* Rotation, const float* Scale, bool bRelative)
|
||||
{
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return false;
|
||||
|
||||
for (TActorIterator<AActor> It(World); It; ++It)
|
||||
{
|
||||
if (It->GetName() == ActorId)
|
||||
{
|
||||
AActor* Actor = *It;
|
||||
|
||||
if (Location)
|
||||
{
|
||||
if (bRelative)
|
||||
{
|
||||
Actor->SetActorLocation(Actor->GetActorLocation() + *Location);
|
||||
}
|
||||
else
|
||||
{
|
||||
Actor->SetActorLocation(*Location);
|
||||
}
|
||||
}
|
||||
|
||||
if (Rotation)
|
||||
{
|
||||
if (bRelative)
|
||||
{
|
||||
Actor->SetActorRotation(Actor->GetActorRotation() + *Rotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
Actor->SetActorRotation(*Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
if (Scale)
|
||||
{
|
||||
Actor->SetActorScale3D(FVector(*Scale));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> URalphaParameterBridge::GetActorDetails(const FString& ActorId, bool bIncludeComponents, bool bIncludeMaterials)
|
||||
{
|
||||
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||
|
||||
UWorld* World = GetCurrentWorld();
|
||||
if (!World) return Result;
|
||||
|
||||
for (TActorIterator<AActor> It(World); It; ++It)
|
||||
{
|
||||
if (It->GetName() == ActorId)
|
||||
{
|
||||
AActor* Actor = *It;
|
||||
|
||||
Result->SetStringField(TEXT("actor_id"), Actor->GetName());
|
||||
#if WITH_EDITOR
|
||||
Result->SetStringField(TEXT("label"), Actor->GetActorLabel());
|
||||
#else
|
||||
Result->SetStringField(TEXT("label"), Actor->GetName());
|
||||
#endif
|
||||
Result->SetStringField(TEXT("class"), Actor->GetClass()->GetName());
|
||||
|
||||
// Transform
|
||||
FVector Location = Actor->GetActorLocation();
|
||||
FRotator Rotation = Actor->GetActorRotation();
|
||||
FVector Scale = Actor->GetActorScale3D();
|
||||
|
||||
TSharedPtr<FJsonObject> TransformObj = MakeShared<FJsonObject>();
|
||||
TSharedPtr<FJsonObject> LocObj = MakeShared<FJsonObject>();
|
||||
LocObj->SetNumberField(TEXT("x"), Location.X);
|
||||
LocObj->SetNumberField(TEXT("y"), Location.Y);
|
||||
LocObj->SetNumberField(TEXT("z"), Location.Z);
|
||||
TransformObj->SetObjectField(TEXT("location"), LocObj);
|
||||
|
||||
TSharedPtr<FJsonObject> RotObj = MakeShared<FJsonObject>();
|
||||
RotObj->SetNumberField(TEXT("pitch"), Rotation.Pitch);
|
||||
RotObj->SetNumberField(TEXT("yaw"), Rotation.Yaw);
|
||||
RotObj->SetNumberField(TEXT("roll"), Rotation.Roll);
|
||||
TransformObj->SetObjectField(TEXT("rotation"), RotObj);
|
||||
|
||||
Result->SetObjectField(TEXT("transform"), TransformObj);
|
||||
|
||||
if (bIncludeComponents)
|
||||
{
|
||||
TArray<TSharedPtr<FJsonValue>> ComponentsArray;
|
||||
TArray<UActorComponent*> Components;
|
||||
Actor->GetComponents(Components);
|
||||
|
||||
for (UActorComponent* Component : Components)
|
||||
{
|
||||
TSharedPtr<FJsonObject> CompObj = MakeShared<FJsonObject>();
|
||||
CompObj->SetStringField(TEXT("name"), Component->GetName());
|
||||
CompObj->SetStringField(TEXT("class"), Component->GetClass()->GetName());
|
||||
ComponentsArray.Add(MakeShared<FJsonValueObject>(CompObj));
|
||||
}
|
||||
|
||||
Result->SetArrayField(TEXT("components"), ComponentsArray);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#include "RalphaScreenCapture.h"
|
||||
#include "RalphaMCPServer.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "Engine/GameViewportClient.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Slate/SceneViewport.h"
|
||||
#include "ImageUtils.h"
|
||||
#include "IImageWrapper.h"
|
||||
#include "IImageWrapperModule.h"
|
||||
#include "Misc/Base64.h"
|
||||
#include "Misc/FileHelper.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "HAL/PlatformFileManager.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Widgets/SWindow.h"
|
||||
#include "UnrealClient.h"
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "Editor.h"
|
||||
#include "LevelEditorViewport.h"
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
if (ViewportClient && ViewportClient->Viewport)
|
||||
{
|
||||
Viewport = ViewportClient->Viewport;
|
||||
}
|
||||
#if WITH_EDITOR
|
||||
else
|
||||
{
|
||||
// Try to get the editor viewport
|
||||
if (GEditor)
|
||||
{
|
||||
for (FLevelEditorViewportClient* LevelVC : GEditor->GetLevelViewportClients())
|
||||
{
|
||||
if (LevelVC && LevelVC->Viewport)
|
||||
{
|
||||
Viewport = LevelVC->Viewport;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!Viewport)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("No viewport available for screenshot"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read pixels from the viewport
|
||||
TArray<FColor> 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)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to read pixels from viewport"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert to base64 PNG
|
||||
OutBase64 = PixelsToBase64PNG(Bitmap, ViewportWidth, ViewportHeight);
|
||||
|
||||
// Save to disk as well
|
||||
FString ScreenshotDir = GetScreenshotDirectory();
|
||||
FString Filename = FString::Printf(TEXT("Ralpha_%s.png"), *FDateTime::Now().ToString(TEXT("%Y%m%d_%H%M%S")));
|
||||
OutFilePath = ScreenshotDir / Filename;
|
||||
|
||||
// Save the PNG
|
||||
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
|
||||
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
|
||||
|
||||
if (ImageWrapper.IsValid() && ImageWrapper->SetRaw(Bitmap.GetData(), Bitmap.Num() * sizeof(FColor), ViewportWidth, ViewportHeight, ERGBFormat::BGRA, 8))
|
||||
{
|
||||
TArray64<uint8> PNGData = ImageWrapper->GetCompressed(0);
|
||||
if (PNGData.Num() > 0)
|
||||
{
|
||||
FFileHelper::SaveArrayToFile(PNGData, *OutFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogRalphaMCP, Log, TEXT("Screenshot captured: %dx%d, saved to %s"), ViewportWidth, ViewportHeight, *OutFilePath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool URalphaScreenCapture::CaptureHighResScreenshot(int32 Width, int32 Height, FString& OutBase64)
|
||||
{
|
||||
// For high-res screenshots, we would use FHighResScreenshotConfig
|
||||
// This is a simplified implementation
|
||||
|
||||
FString FilePath;
|
||||
return CaptureScreenshot(Width, Height, OutBase64, FilePath);
|
||||
}
|
||||
|
||||
FString URalphaScreenCapture::PixelsToBase64PNG(const TArray<FColor>& Pixels, int32 Width, int32 Height)
|
||||
{
|
||||
// Load the image wrapper module
|
||||
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
|
||||
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
|
||||
|
||||
if (!ImageWrapper.IsValid())
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to create image wrapper"));
|
||||
return FString();
|
||||
}
|
||||
|
||||
// Set the raw pixel data
|
||||
if (!ImageWrapper->SetRaw(Pixels.GetData(), Pixels.Num() * sizeof(FColor), Width, Height, ERGBFormat::BGRA, 8))
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to set raw pixel data"));
|
||||
return FString();
|
||||
}
|
||||
|
||||
// Compress to PNG
|
||||
TArray64<uint8> PNGData = ImageWrapper->GetCompressed(0);
|
||||
if (PNGData.Num() == 0)
|
||||
{
|
||||
UE_LOG(LogRalphaMCP, Warning, TEXT("Failed to compress to PNG"));
|
||||
return FString();
|
||||
}
|
||||
|
||||
// Convert to base64
|
||||
TArray<uint8> PNGData32;
|
||||
PNGData32.Append(PNGData.GetData(), PNGData.Num());
|
||||
|
||||
return FBase64::Encode(PNGData32);
|
||||
}
|
||||
|
||||
FString URalphaScreenCapture::GetScreenshotDirectory()
|
||||
{
|
||||
FString ScreenshotDir = FPaths::ProjectSavedDir() / TEXT("Screenshots") / TEXT("Ralpha");
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
|
||||
if (!PlatformFile.DirectoryExists(*ScreenshotDir))
|
||||
{
|
||||
PlatformFile.CreateDirectoryTree(*ScreenshotDir);
|
||||
}
|
||||
|
||||
return ScreenshotDir;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class RALPHACORE_API FRalphaCoreModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
/** Get the module instance */
|
||||
static FRalphaCoreModule& Get();
|
||||
|
||||
/** Check if module is loaded */
|
||||
static bool IsAvailable();
|
||||
};
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "Sockets.h"
|
||||
#include "SocketSubsystem.h"
|
||||
#include "Containers/Ticker.h"
|
||||
#include "RalphaMCPServer.generated.h"
|
||||
|
||||
class FRalphaClientConnection;
|
||||
class URalphaParameterBridge;
|
||||
class URalphaScreenCapture;
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogRalphaMCP, Log, All);
|
||||
|
||||
/**
|
||||
* TCP Server that receives JSON-RPC commands from the Python MCP server
|
||||
* and executes them against UE5 systems.
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class RALPHACORE_API URalphaMCPServer : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
URalphaMCPServer();
|
||||
virtual ~URalphaMCPServer();
|
||||
|
||||
/** Get the singleton instance */
|
||||
UFUNCTION(BlueprintCallable, Category = "Ralpha")
|
||||
static URalphaMCPServer* Get();
|
||||
|
||||
/** Start the TCP server */
|
||||
UFUNCTION(BlueprintCallable, Category = "Ralpha")
|
||||
bool Start(int32 Port = 30010);
|
||||
|
||||
/** Stop the TCP server */
|
||||
UFUNCTION(BlueprintCallable, Category = "Ralpha")
|
||||
void Stop();
|
||||
|
||||
/** Check if server is running */
|
||||
UFUNCTION(BlueprintCallable, Category = "Ralpha")
|
||||
bool IsRunning() const;
|
||||
|
||||
/** Get the port number */
|
||||
UFUNCTION(BlueprintCallable, Category = "Ralpha")
|
||||
int32 GetPort() const { return ServerPort; }
|
||||
|
||||
/** Process a JSON command and return response */
|
||||
FString ProcessCommand(const FString& JsonCommand);
|
||||
|
||||
protected:
|
||||
/** Tick function to accept new connections */
|
||||
bool Tick(float DeltaTime);
|
||||
|
||||
private:
|
||||
static URalphaMCPServer* Instance;
|
||||
|
||||
FSocket* ListenerSocket;
|
||||
int32 ServerPort;
|
||||
bool bIsRunning;
|
||||
|
||||
TArray<TSharedPtr<FRalphaClientConnection>> ClientConnections;
|
||||
|
||||
UPROPERTY()
|
||||
URalphaParameterBridge* ParameterBridge;
|
||||
|
||||
UPROPERTY()
|
||||
URalphaScreenCapture* ScreenCapture;
|
||||
|
||||
FTSTicker::FDelegateHandle TickDelegateHandle;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles a single client connection
|
||||
*/
|
||||
class RALPHACORE_API FRalphaClientConnection : public TSharedFromThis<FRalphaClientConnection>
|
||||
{
|
||||
public:
|
||||
FRalphaClientConnection(FSocket* InSocket, URalphaMCPServer* InServer);
|
||||
~FRalphaClientConnection();
|
||||
|
||||
/** Process incoming data */
|
||||
void Tick();
|
||||
|
||||
/** Check if connection is valid */
|
||||
bool IsValid() const;
|
||||
|
||||
/** Send response to client */
|
||||
void SendResponse(const FString& Response);
|
||||
|
||||
private:
|
||||
FSocket* Socket;
|
||||
URalphaMCPServer* Server;
|
||||
FString ReceiveBuffer;
|
||||
};
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "Dom/JsonObject.h"
|
||||
#include "Dom/JsonValue.h"
|
||||
#include "RalphaParameterBridge.generated.h"
|
||||
|
||||
class APostProcessVolume;
|
||||
class ADirectionalLight;
|
||||
class ASkyLight;
|
||||
class AExponentialHeightFog;
|
||||
class ACineCameraActor;
|
||||
|
||||
/**
|
||||
* Bridge between MCP commands and UE5 rendering systems.
|
||||
* Handles getting/setting parameters on post-process volumes, lights, fog, cameras, etc.
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class RALPHACORE_API URalphaParameterBridge : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
URalphaParameterBridge();
|
||||
|
||||
// Get all current rendering parameters
|
||||
TSharedPtr<FJsonObject> GetAllParameters();
|
||||
|
||||
// Post-Process Volume
|
||||
bool SetPostProcessParameters(const TSharedPtr<FJsonObject>& Params);
|
||||
TSharedPtr<FJsonObject> GetPostProcessParameters();
|
||||
|
||||
// Directional Light (Sun)
|
||||
bool SetDirectionalLightParameters(const TSharedPtr<FJsonObject>& Params);
|
||||
TSharedPtr<FJsonObject> GetDirectionalLightParameters();
|
||||
|
||||
// Sky Light
|
||||
bool SetSkyLightParameters(const TSharedPtr<FJsonObject>& Params);
|
||||
TSharedPtr<FJsonObject> GetSkyLightParameters();
|
||||
|
||||
// Exponential Height Fog
|
||||
bool SetFogParameters(const TSharedPtr<FJsonObject>& Params);
|
||||
TSharedPtr<FJsonObject> GetFogParameters();
|
||||
|
||||
// Camera
|
||||
bool SetCameraParameters(const TSharedPtr<FJsonObject>& Params);
|
||||
TSharedPtr<FJsonObject> GetCameraParameters();
|
||||
|
||||
// Render Quality
|
||||
bool SetRenderQuality(const TSharedPtr<FJsonObject>& Settings);
|
||||
|
||||
// Asset Search
|
||||
TArray<TSharedPtr<FJsonValue>> SearchAssets(const FString& Query, const FString& AssetType, int32 MaxResults);
|
||||
TArray<TSharedPtr<FJsonValue>> ListAssets(const FString& FolderPath, const FString& AssetType, bool bRecursive);
|
||||
|
||||
// Actor Management
|
||||
bool SpawnActor(const FString& AssetPath, const FVector& Location, const FRotator& Rotation, float Scale, const FString& ActorLabel, FString& OutActorId);
|
||||
bool DeleteActor(const FString& ActorId);
|
||||
TArray<TSharedPtr<FJsonValue>> ListActors(const FString& ClassFilter, const FString& NameFilter, bool bIncludeTransforms);
|
||||
bool SetActorTransform(const FString& ActorId, const FVector* Location, const FRotator* Rotation, const float* Scale, bool bRelative);
|
||||
TSharedPtr<FJsonObject> GetActorDetails(const FString& ActorId, bool bIncludeComponents, bool bIncludeMaterials);
|
||||
|
||||
private:
|
||||
// Find actors of specific types in the world
|
||||
APostProcessVolume* FindPostProcessVolume();
|
||||
ADirectionalLight* FindDirectionalLight();
|
||||
ASkyLight* FindSkyLight();
|
||||
AExponentialHeightFog* FindExponentialHeightFog();
|
||||
ACineCameraActor* FindCineCamera();
|
||||
|
||||
// Get the current world
|
||||
UWorld* GetCurrentWorld();
|
||||
|
||||
// Parse hex color string to FLinearColor
|
||||
FLinearColor ParseHexColor(const FString& HexColor);
|
||||
FString ColorToHex(const FLinearColor& Color);
|
||||
};
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
#include "RalphaScreenCapture.generated.h"
|
||||
|
||||
/**
|
||||
* Handles viewport screenshot capture for the style convergence loop.
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class RALPHACORE_API URalphaScreenCapture : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
URalphaScreenCapture();
|
||||
|
||||
/**
|
||||
* Capture the current viewport as a PNG image.
|
||||
* @param Width Desired width (may be clamped to viewport size)
|
||||
* @param Height Desired height (may be clamped to viewport size)
|
||||
* @param OutBase64 The captured image as base64-encoded PNG
|
||||
* @param OutFilePath Path where the image was saved (if saved to disk)
|
||||
* @return True if capture succeeded
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Ralpha")
|
||||
bool CaptureScreenshot(int32 Width, int32 Height, FString& OutBase64, FString& OutFilePath);
|
||||
|
||||
/**
|
||||
* Capture a high-resolution screenshot.
|
||||
* @param Width Desired width
|
||||
* @param Height Desired height
|
||||
* @param OutBase64 The captured image as base64-encoded PNG
|
||||
* @return True if capture succeeded
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Ralpha")
|
||||
bool CaptureHighResScreenshot(int32 Width, int32 Height, FString& OutBase64);
|
||||
|
||||
private:
|
||||
/** Convert raw pixel data to base64 PNG */
|
||||
FString PixelsToBase64PNG(const TArray<FColor>& Pixels, int32 Width, int32 Height);
|
||||
|
||||
/** Get the screenshot save directory */
|
||||
FString GetScreenshotDirectory();
|
||||
};
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class RalphaCore : ModuleRules
|
||||
{
|
||||
public RalphaCore(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
}
|
||||
);
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Sockets",
|
||||
"Networking",
|
||||
"Json",
|
||||
"JsonUtilities",
|
||||
"RenderCore",
|
||||
"Renderer",
|
||||
"RHI",
|
||||
"ImageWrapper",
|
||||
"AssetRegistry",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"CinematicCamera",
|
||||
}
|
||||
);
|
||||
|
||||
if (Target.bBuildEditor)
|
||||
{
|
||||
PrivateDependencyModuleNames.Add("UnrealEd");
|
||||
}
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#include "RalphaEditor.h"
|
||||
#include "RalphaEditorCommands.h"
|
||||
#include "RalphaEditorStyle.h"
|
||||
#include "ToolMenus.h"
|
||||
#include "RalphaMCPServer.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FRalphaEditorModule"
|
||||
|
||||
void FRalphaEditorModule::StartupModule()
|
||||
{
|
||||
FRalphaEditorStyle::Initialize();
|
||||
FRalphaEditorStyle::ReloadTextures();
|
||||
|
||||
FRalphaEditorCommands::Register();
|
||||
|
||||
PluginCommands = MakeShareable(new FUICommandList);
|
||||
|
||||
PluginCommands->MapAction(
|
||||
FRalphaEditorCommands::Get().StartServer,
|
||||
FExecuteAction::CreateLambda([]()
|
||||
{
|
||||
URalphaMCPServer* Server = URalphaMCPServer::Get();
|
||||
if (Server)
|
||||
{
|
||||
if (!Server->IsRunning())
|
||||
{
|
||||
Server->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Server->Stop();
|
||||
}
|
||||
}
|
||||
}),
|
||||
FCanExecuteAction()
|
||||
);
|
||||
|
||||
UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FRalphaEditorModule::RegisterMenus));
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Ralpha Editor Module Started"));
|
||||
}
|
||||
|
||||
void FRalphaEditorModule::ShutdownModule()
|
||||
{
|
||||
UToolMenus::UnRegisterStartupCallback(this);
|
||||
UToolMenus::UnregisterOwner(this);
|
||||
|
||||
FRalphaEditorStyle::Shutdown();
|
||||
FRalphaEditorCommands::Unregister();
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Ralpha Editor Module Shutdown"));
|
||||
}
|
||||
|
||||
void FRalphaEditorModule::RegisterMenus()
|
||||
{
|
||||
FToolMenuOwnerScoped OwnerScoped(this);
|
||||
|
||||
{
|
||||
UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar.PlayToolBar");
|
||||
{
|
||||
FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Ralpha");
|
||||
{
|
||||
FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FRalphaEditorCommands::Get().StartServer));
|
||||
Entry.SetCommandList(PluginCommands);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FRalphaEditorModule, RalphaEditor)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#include "RalphaEditorCommands.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FRalphaEditorModule"
|
||||
|
||||
void FRalphaEditorCommands::RegisterCommands()
|
||||
{
|
||||
UI_COMMAND(StartServer, "Ralpha", "Start/Stop the Ralpha MCP Server", EUserInterfaceActionType::Button, FInputChord());
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#include "RalphaEditorStyle.h"
|
||||
#include "Styling/SlateStyleRegistry.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Slate/SlateGameResources.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
#include "Styling/SlateStyleMacros.h"
|
||||
|
||||
#define RootToContentDir Style->RootToContentDir
|
||||
|
||||
TSharedPtr<FSlateStyleSet> FRalphaEditorStyle::StyleInstance = nullptr;
|
||||
|
||||
void FRalphaEditorStyle::Initialize()
|
||||
{
|
||||
if (!StyleInstance.IsValid())
|
||||
{
|
||||
StyleInstance = Create();
|
||||
FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void FRalphaEditorStyle::Shutdown()
|
||||
{
|
||||
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance);
|
||||
ensure(StyleInstance.IsUnique());
|
||||
StyleInstance.Reset();
|
||||
}
|
||||
|
||||
FName FRalphaEditorStyle::GetStyleSetName()
|
||||
{
|
||||
static FName StyleSetName(TEXT("RalphaEditorStyle"));
|
||||
return StyleSetName;
|
||||
}
|
||||
|
||||
const ISlateStyle& FRalphaEditorStyle::Get()
|
||||
{
|
||||
return *StyleInstance;
|
||||
}
|
||||
|
||||
void FRalphaEditorStyle::ReloadTextures()
|
||||
{
|
||||
if (FSlateApplication::IsInitialized())
|
||||
{
|
||||
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<FSlateStyleSet> FRalphaEditorStyle::Create()
|
||||
{
|
||||
TSharedRef<FSlateStyleSet> Style = MakeShareable(new FSlateStyleSet("RalphaEditorStyle"));
|
||||
Style->SetContentRoot(IPluginManager::Get().FindPlugin("RalphaPlugin")->GetBaseDir() / TEXT("Resources"));
|
||||
|
||||
Style->Set("Ralpha.StartServer", new IMAGE_BRUSH_SVG(TEXT("Icon20"), FVector2D(20.0f, 20.0f)));
|
||||
|
||||
return Style;
|
||||
}
|
||||
|
||||
#undef RootToContentDir
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FToolBarBuilder;
|
||||
class FMenuBuilder;
|
||||
|
||||
class RALPHAEDITOR_API FRalphaEditorModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
private:
|
||||
void RegisterMenus();
|
||||
|
||||
TSharedPtr<class FUICommandList> PluginCommands;
|
||||
};
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Framework/Commands/Commands.h"
|
||||
#include "RalphaEditorStyle.h"
|
||||
|
||||
class FRalphaEditorCommands : public TCommands<FRalphaEditorCommands>
|
||||
{
|
||||
public:
|
||||
FRalphaEditorCommands()
|
||||
: TCommands<FRalphaEditorCommands>(
|
||||
TEXT("Ralpha"),
|
||||
NSLOCTEXT("Contexts", "Ralpha", "Ralpha Plugin"),
|
||||
NAME_None,
|
||||
FRalphaEditorStyle::GetStyleSetName())
|
||||
{
|
||||
}
|
||||
|
||||
virtual void RegisterCommands() override;
|
||||
|
||||
public:
|
||||
TSharedPtr<FUICommandInfo> StartServer;
|
||||
};
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
|
||||
class FRalphaEditorStyle
|
||||
{
|
||||
public:
|
||||
static void Initialize();
|
||||
static void Shutdown();
|
||||
static void ReloadTextures();
|
||||
static const ISlateStyle& Get();
|
||||
static FName GetStyleSetName();
|
||||
|
||||
private:
|
||||
static TSharedRef<class FSlateStyleSet> Create();
|
||||
static TSharedPtr<class FSlateStyleSet> StyleInstance;
|
||||
};
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright Ralpha Team. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class RalphaEditor : ModuleRules
|
||||
{
|
||||
public RalphaEditor(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
}
|
||||
);
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"RalphaCore",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"UnrealEd",
|
||||
"EditorStyle",
|
||||
"ToolMenus",
|
||||
"LevelEditor",
|
||||
"AssetRegistry",
|
||||
"ContentBrowser",
|
||||
"Projects",
|
||||
}
|
||||
);
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "5.7",
|
||||
"Category": "",
|
||||
"Description": "AI-driven style transfer for infinite world generation",
|
||||
"Modules": [],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "RalphaPlugin",
|
||||
"Enabled": true
|
||||
}
|
||||
],
|
||||
"TargetPlatforms": [
|
||||
"Windows"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue