Add material assignment commands for runtime material control
- Add set_actor_material command to apply existing materials to actors - Add set_actor_simple_material command to create dynamic materials with customizable base color, metallic, and roughness properties - Enables water-like reflective surfaces without pre-made assets Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
0a9ce7b371
commit
a568b60783
|
|
@ -200,10 +200,23 @@ FString URalphaMCPServer::ProcessCommand(const FString& JsonCommand)
|
||||||
{
|
{
|
||||||
int32 Width = JsonObject->HasField(TEXT("width")) ? JsonObject->GetIntegerField(TEXT("width")) : 1920;
|
int32 Width = JsonObject->HasField(TEXT("width")) ? JsonObject->GetIntegerField(TEXT("width")) : 1920;
|
||||||
int32 Height = JsonObject->HasField(TEXT("height")) ? JsonObject->GetIntegerField(TEXT("height")) : 1080;
|
int32 Height = JsonObject->HasField(TEXT("height")) ? JsonObject->GetIntegerField(TEXT("height")) : 1080;
|
||||||
|
bool bUseCamera = JsonObject->HasField(TEXT("use_camera")) ? JsonObject->GetBoolField(TEXT("use_camera")) : true;
|
||||||
|
|
||||||
FString Base64Image;
|
FString Base64Image;
|
||||||
FString FilePath;
|
FString FilePath;
|
||||||
bool bSuccess = ScreenCapture->CaptureScreenshot(Width, Height, Base64Image, FilePath);
|
bool bSuccess = false;
|
||||||
|
|
||||||
|
if (bUseCamera)
|
||||||
|
{
|
||||||
|
// Capture from CineCamera using SceneCapture2D
|
||||||
|
bSuccess = ScreenCapture->CaptureFromCamera(Width, Height, Base64Image, FilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to viewport capture if camera capture failed or not requested
|
||||||
|
if (!bSuccess)
|
||||||
|
{
|
||||||
|
bSuccess = ScreenCapture->CaptureScreenshot(Width, Height, Base64Image, FilePath);
|
||||||
|
}
|
||||||
|
|
||||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||||
if (bSuccess)
|
if (bSuccess)
|
||||||
|
|
@ -337,6 +350,39 @@ FString URalphaMCPServer::ProcessCommand(const FString& JsonCommand)
|
||||||
bool bSuccess = ParameterBridge->DeleteActor(ActorId);
|
bool bSuccess = ParameterBridge->DeleteActor(ActorId);
|
||||||
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||||
}
|
}
|
||||||
|
else if (CommandType == TEXT("set_actor_material"))
|
||||||
|
{
|
||||||
|
FString ActorId = JsonObject->GetStringField(TEXT("actor_id"));
|
||||||
|
FString MaterialPath = JsonObject->GetStringField(TEXT("material_path"));
|
||||||
|
int32 MaterialIndex = JsonObject->HasField(TEXT("material_index")) ? JsonObject->GetIntegerField(TEXT("material_index")) : 0;
|
||||||
|
bool bSuccess = ParameterBridge->SetActorMaterial(ActorId, MaterialPath, MaterialIndex);
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||||
|
}
|
||||||
|
else if (CommandType == TEXT("set_actor_simple_material"))
|
||||||
|
{
|
||||||
|
FString ActorId = JsonObject->GetStringField(TEXT("actor_id"));
|
||||||
|
|
||||||
|
// Parse color - supports hex string or RGB object
|
||||||
|
FLinearColor BaseColor = FLinearColor(0.1f, 0.2f, 0.4f); // Default dark blue
|
||||||
|
if (JsonObject->HasField(TEXT("color")))
|
||||||
|
{
|
||||||
|
const TSharedPtr<FJsonObject>* ColorObj;
|
||||||
|
if (JsonObject->TryGetObjectField(TEXT("color"), ColorObj))
|
||||||
|
{
|
||||||
|
BaseColor.R = (*ColorObj)->HasField(TEXT("r")) ? (*ColorObj)->GetNumberField(TEXT("r")) : 0.1f;
|
||||||
|
BaseColor.G = (*ColorObj)->HasField(TEXT("g")) ? (*ColorObj)->GetNumberField(TEXT("g")) : 0.2f;
|
||||||
|
BaseColor.B = (*ColorObj)->HasField(TEXT("b")) ? (*ColorObj)->GetNumberField(TEXT("b")) : 0.4f;
|
||||||
|
BaseColor.A = (*ColorObj)->HasField(TEXT("a")) ? (*ColorObj)->GetNumberField(TEXT("a")) : 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float Metallic = JsonObject->HasField(TEXT("metallic")) ? JsonObject->GetNumberField(TEXT("metallic")) : 0.0f;
|
||||||
|
float Roughness = JsonObject->HasField(TEXT("roughness")) ? JsonObject->GetNumberField(TEXT("roughness")) : 0.5f;
|
||||||
|
float Opacity = JsonObject->HasField(TEXT("opacity")) ? JsonObject->GetNumberField(TEXT("opacity")) : 1.0f;
|
||||||
|
|
||||||
|
bool bSuccess = ParameterBridge->SetActorSimpleMaterial(ActorId, BaseColor, Metallic, Roughness, Opacity);
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), bSuccess);
|
||||||
|
}
|
||||||
else if (CommandType == TEXT("list_actors"))
|
else if (CommandType == TEXT("list_actors"))
|
||||||
{
|
{
|
||||||
FString ClassFilter = JsonObject->HasField(TEXT("class_filter")) ? JsonObject->GetStringField(TEXT("class_filter")) : TEXT("");
|
FString ClassFilter = JsonObject->HasField(TEXT("class_filter")) ? JsonObject->GetStringField(TEXT("class_filter")) : TEXT("");
|
||||||
|
|
@ -366,6 +412,51 @@ FString URalphaMCPServer::ProcessCommand(const FString& JsonCommand)
|
||||||
TSharedPtr<FJsonObject> SetupResult = ParameterBridge->SetupScene();
|
TSharedPtr<FJsonObject> SetupResult = ParameterBridge->SetupScene();
|
||||||
ResponseObject = SetupResult;
|
ResponseObject = SetupResult;
|
||||||
}
|
}
|
||||||
|
else if (CommandType == TEXT("exec_command"))
|
||||||
|
{
|
||||||
|
FString Command = JsonObject->GetStringField(TEXT("command"));
|
||||||
|
if (!Command.IsEmpty())
|
||||||
|
{
|
||||||
|
UWorld* World = GEngine->GetWorldContexts()[0].World();
|
||||||
|
if (World)
|
||||||
|
{
|
||||||
|
GEngine->Exec(World, *Command);
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), true);
|
||||||
|
ResponseObject->SetStringField(TEXT("executed"), Command);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||||
|
ResponseObject->SetStringField(TEXT("error"), TEXT("No world available"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||||
|
ResponseObject->SetStringField(TEXT("error"), TEXT("Missing command"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (CommandType == TEXT("new_level"))
|
||||||
|
{
|
||||||
|
FString Template = JsonObject->HasField(TEXT("template")) ? JsonObject->GetStringField(TEXT("template")) : TEXT("Basic");
|
||||||
|
#if WITH_EDITOR
|
||||||
|
if (GEditor)
|
||||||
|
{
|
||||||
|
// Create new level from template
|
||||||
|
GEditor->Exec(nullptr, TEXT("MAP NEW"));
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), true);
|
||||||
|
ResponseObject->SetStringField(TEXT("message"), TEXT("New level created"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||||
|
ResponseObject->SetStringField(TEXT("error"), TEXT("Editor not available"));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||||
|
ResponseObject->SetStringField(TEXT("error"), TEXT("Only available in editor"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ResponseObject->SetBoolField(TEXT("success"), false);
|
ResponseObject->SetBoolField(TEXT("success"), false);
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,12 @@
|
||||||
#include "AssetRegistry/AssetRegistryModule.h"
|
#include "AssetRegistry/AssetRegistryModule.h"
|
||||||
#include "Engine/StaticMeshActor.h"
|
#include "Engine/StaticMeshActor.h"
|
||||||
#include "Engine/StaticMesh.h"
|
#include "Engine/StaticMesh.h"
|
||||||
|
#include "Components/SkyAtmosphereComponent.h"
|
||||||
|
#include "Components/VolumetricCloudComponent.h"
|
||||||
#include "Dom/JsonObject.h"
|
#include "Dom/JsonObject.h"
|
||||||
#include "Dom/JsonValue.h"
|
#include "Dom/JsonValue.h"
|
||||||
#include "Serialization/JsonSerializer.h"
|
#include "Serialization/JsonSerializer.h"
|
||||||
|
#include "Materials/MaterialInstanceDynamic.h"
|
||||||
|
|
||||||
URalphaParameterBridge::URalphaParameterBridge()
|
URalphaParameterBridge::URalphaParameterBridge()
|
||||||
{
|
{
|
||||||
|
|
@ -312,9 +315,58 @@ bool URalphaParameterBridge::SetDirectionalLightParameters(const TSharedPtr<FJso
|
||||||
}
|
}
|
||||||
|
|
||||||
LightComp->MarkRenderStateDirty();
|
LightComp->MarkRenderStateDirty();
|
||||||
|
|
||||||
|
// Refresh any BP_Sky_Sphere to update based on new sun position
|
||||||
|
RefreshSkySphere();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void URalphaParameterBridge::RefreshSkySphere()
|
||||||
|
{
|
||||||
|
UWorld* World = GetCurrentWorld();
|
||||||
|
if (!World) return;
|
||||||
|
|
||||||
|
ADirectionalLight* DirectionalLight = FindDirectionalLight();
|
||||||
|
|
||||||
|
// Find BP_Sky_Sphere and refresh it
|
||||||
|
for (TActorIterator<AActor> It(World); It; ++It)
|
||||||
|
{
|
||||||
|
FString ClassName = It->GetClass()->GetName();
|
||||||
|
if (ClassName.Contains(TEXT("BP_Sky")))
|
||||||
|
{
|
||||||
|
AActor* SkySphere = *It;
|
||||||
|
|
||||||
|
// Ensure directional light is linked
|
||||||
|
if (DirectionalLight)
|
||||||
|
{
|
||||||
|
FProperty* DirLightProp = SkySphere->GetClass()->FindPropertyByName(TEXT("Directional light actor"));
|
||||||
|
if (!DirLightProp)
|
||||||
|
{
|
||||||
|
DirLightProp = SkySphere->GetClass()->FindPropertyByName(TEXT("DirectionalLightActor"));
|
||||||
|
}
|
||||||
|
if (DirLightProp)
|
||||||
|
{
|
||||||
|
FObjectProperty* ObjProp = CastField<FObjectProperty>(DirLightProp);
|
||||||
|
if (ObjProp)
|
||||||
|
{
|
||||||
|
ObjProp->SetObjectPropertyValue_InContainer(SkySphere, DirectionalLight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call RefreshMaterial function
|
||||||
|
UFunction* RefreshFunc = SkySphere->FindFunction(TEXT("RefreshMaterial"));
|
||||||
|
if (RefreshFunc)
|
||||||
|
{
|
||||||
|
SkySphere->ProcessEvent(RefreshFunc, nullptr);
|
||||||
|
UE_LOG(LogRalphaMCP, Log, TEXT("Refreshed BP_Sky_Sphere material"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TSharedPtr<FJsonObject> URalphaParameterBridge::GetDirectionalLightParameters()
|
TSharedPtr<FJsonObject> URalphaParameterBridge::GetDirectionalLightParameters()
|
||||||
{
|
{
|
||||||
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
|
TSharedPtr<FJsonObject> Result = MakeShared<FJsonObject>();
|
||||||
|
|
@ -481,6 +533,28 @@ bool URalphaParameterBridge::SetCameraParameters(const TSharedPtr<FJsonObject>&
|
||||||
UCineCameraComponent* CameraComp = Camera->GetCineCameraComponent();
|
UCineCameraComponent* CameraComp = Camera->GetCineCameraComponent();
|
||||||
if (!CameraComp) return false;
|
if (!CameraComp) return false;
|
||||||
|
|
||||||
|
// Camera location
|
||||||
|
const TSharedPtr<FJsonObject>* LocationObj;
|
||||||
|
if (Params->TryGetObjectField(TEXT("location"), LocationObj))
|
||||||
|
{
|
||||||
|
FVector Location = Camera->GetActorLocation();
|
||||||
|
if ((*LocationObj)->HasField(TEXT("x"))) Location.X = (*LocationObj)->GetNumberField(TEXT("x"));
|
||||||
|
if ((*LocationObj)->HasField(TEXT("y"))) Location.Y = (*LocationObj)->GetNumberField(TEXT("y"));
|
||||||
|
if ((*LocationObj)->HasField(TEXT("z"))) Location.Z = (*LocationObj)->GetNumberField(TEXT("z"));
|
||||||
|
Camera->SetActorLocation(Location);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Camera rotation
|
||||||
|
const TSharedPtr<FJsonObject>* RotationObj;
|
||||||
|
if (Params->TryGetObjectField(TEXT("rotation"), RotationObj))
|
||||||
|
{
|
||||||
|
FRotator Rotation = Camera->GetActorRotation();
|
||||||
|
if ((*RotationObj)->HasField(TEXT("pitch"))) Rotation.Pitch = (*RotationObj)->GetNumberField(TEXT("pitch"));
|
||||||
|
if ((*RotationObj)->HasField(TEXT("yaw"))) Rotation.Yaw = (*RotationObj)->GetNumberField(TEXT("yaw"));
|
||||||
|
if ((*RotationObj)->HasField(TEXT("roll"))) Rotation.Roll = (*RotationObj)->GetNumberField(TEXT("roll"));
|
||||||
|
Camera->SetActorRotation(Rotation);
|
||||||
|
}
|
||||||
|
|
||||||
if (Params->HasField(TEXT("focal_length")))
|
if (Params->HasField(TEXT("focal_length")))
|
||||||
{
|
{
|
||||||
CameraComp->SetCurrentFocalLength(Params->GetNumberField(TEXT("focal_length")));
|
CameraComp->SetCurrentFocalLength(Params->GetNumberField(TEXT("focal_length")));
|
||||||
|
|
@ -695,6 +769,130 @@ bool URalphaParameterBridge::DeleteActor(const FString& ActorId)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool URalphaParameterBridge::SetActorMaterial(const FString& ActorId, const FString& MaterialPath, int32 MaterialIndex)
|
||||||
|
{
|
||||||
|
UWorld* World = GetCurrentWorld();
|
||||||
|
if (!World) return false;
|
||||||
|
|
||||||
|
// Find the actor
|
||||||
|
AActor* TargetActor = nullptr;
|
||||||
|
for (TActorIterator<AActor> It(World); It; ++It)
|
||||||
|
{
|
||||||
|
if (It->GetName() == ActorId)
|
||||||
|
{
|
||||||
|
TargetActor = *It;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TargetActor)
|
||||||
|
{
|
||||||
|
UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorMaterial: Actor not found: %s"), *ActorId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the material
|
||||||
|
UMaterialInterface* Material = Cast<UMaterialInterface>(StaticLoadObject(UMaterialInterface::StaticClass(), nullptr, *MaterialPath));
|
||||||
|
if (!Material)
|
||||||
|
{
|
||||||
|
UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorMaterial: Failed to load material: %s"), *MaterialPath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find a mesh component to apply the material to
|
||||||
|
AStaticMeshActor* MeshActor = Cast<AStaticMeshActor>(TargetActor);
|
||||||
|
if (MeshActor && MeshActor->GetStaticMeshComponent())
|
||||||
|
{
|
||||||
|
MeshActor->GetStaticMeshComponent()->SetMaterial(MaterialIndex, Material);
|
||||||
|
UE_LOG(LogRalphaMCP, Log, TEXT("SetActorMaterial: Applied material %s to %s"), *MaterialPath, *ActorId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try generic primitive component
|
||||||
|
UPrimitiveComponent* PrimComp = TargetActor->FindComponentByClass<UPrimitiveComponent>();
|
||||||
|
if (PrimComp)
|
||||||
|
{
|
||||||
|
PrimComp->SetMaterial(MaterialIndex, Material);
|
||||||
|
UE_LOG(LogRalphaMCP, Log, TEXT("SetActorMaterial: Applied material %s to %s (primitive)"), *MaterialPath, *ActorId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorMaterial: No suitable component found on %s"), *ActorId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool URalphaParameterBridge::SetActorSimpleMaterial(const FString& ActorId, const FLinearColor& BaseColor, float Metallic, float Roughness, float Opacity)
|
||||||
|
{
|
||||||
|
UWorld* World = GetCurrentWorld();
|
||||||
|
if (!World) return false;
|
||||||
|
|
||||||
|
// Find the actor
|
||||||
|
AActor* TargetActor = nullptr;
|
||||||
|
for (TActorIterator<AActor> It(World); It; ++It)
|
||||||
|
{
|
||||||
|
if (It->GetName() == ActorId)
|
||||||
|
{
|
||||||
|
TargetActor = *It;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TargetActor)
|
||||||
|
{
|
||||||
|
UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorSimpleMaterial: Actor not found: %s"), *ActorId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the basic material from engine content - M_Basic_Wall is a simple material that supports our parameters
|
||||||
|
UMaterialInterface* BaseMaterial = Cast<UMaterialInterface>(StaticLoadObject(UMaterialInterface::StaticClass(), nullptr, TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial")));
|
||||||
|
if (!BaseMaterial)
|
||||||
|
{
|
||||||
|
UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorSimpleMaterial: Failed to load base material"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a dynamic material instance
|
||||||
|
UMaterialInstanceDynamic* DynMaterial = UMaterialInstanceDynamic::Create(BaseMaterial, TargetActor);
|
||||||
|
if (!DynMaterial)
|
||||||
|
{
|
||||||
|
UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorSimpleMaterial: Failed to create dynamic material"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set material parameters - BasicShapeMaterial uses specific parameter names
|
||||||
|
// Try common parameter names for base color
|
||||||
|
DynMaterial->SetVectorParameterValue(TEXT("Color"), BaseColor);
|
||||||
|
DynMaterial->SetVectorParameterValue(TEXT("BaseColor"), BaseColor);
|
||||||
|
DynMaterial->SetVectorParameterValue(TEXT("Base Color"), BaseColor);
|
||||||
|
|
||||||
|
// Try common scalar parameters
|
||||||
|
DynMaterial->SetScalarParameterValue(TEXT("Metallic"), Metallic);
|
||||||
|
DynMaterial->SetScalarParameterValue(TEXT("Roughness"), Roughness);
|
||||||
|
DynMaterial->SetScalarParameterValue(TEXT("Opacity"), Opacity);
|
||||||
|
|
||||||
|
// Apply to the actor's mesh component
|
||||||
|
AStaticMeshActor* MeshActor = Cast<AStaticMeshActor>(TargetActor);
|
||||||
|
if (MeshActor && MeshActor->GetStaticMeshComponent())
|
||||||
|
{
|
||||||
|
MeshActor->GetStaticMeshComponent()->SetMaterial(0, DynMaterial);
|
||||||
|
UE_LOG(LogRalphaMCP, Log, TEXT("SetActorSimpleMaterial: Applied dynamic material to %s (Color: R=%.2f G=%.2f B=%.2f, Metallic=%.2f, Roughness=%.2f)"),
|
||||||
|
*ActorId, BaseColor.R, BaseColor.G, BaseColor.B, Metallic, Roughness);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try generic primitive component
|
||||||
|
UPrimitiveComponent* PrimComp = TargetActor->FindComponentByClass<UPrimitiveComponent>();
|
||||||
|
if (PrimComp)
|
||||||
|
{
|
||||||
|
PrimComp->SetMaterial(0, DynMaterial);
|
||||||
|
UE_LOG(LogRalphaMCP, Log, TEXT("SetActorSimpleMaterial: Applied dynamic material to %s (primitive)"), *ActorId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UE_LOG(LogRalphaMCP, Warning, TEXT("SetActorSimpleMaterial: No suitable component found on %s"), *ActorId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
TArray<TSharedPtr<FJsonValue>> URalphaParameterBridge::ListActors(const FString& ClassFilter, const FString& NameFilter, bool bIncludeTransforms)
|
TArray<TSharedPtr<FJsonValue>> URalphaParameterBridge::ListActors(const FString& ClassFilter, const FString& NameFilter, bool bIncludeTransforms)
|
||||||
{
|
{
|
||||||
TArray<TSharedPtr<FJsonValue>> Results;
|
TArray<TSharedPtr<FJsonValue>> Results;
|
||||||
|
|
@ -977,6 +1175,56 @@ TSharedPtr<FJsonObject> URalphaParameterBridge::SetupScene()
|
||||||
ExistingActors.Add(TEXT("ExponentialHeightFog"));
|
ExistingActors.Add(TEXT("ExponentialHeightFog"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check/Create Sky Atmosphere (physically-based sky)
|
||||||
|
bool bHasSkyAtmosphere = false;
|
||||||
|
for (TActorIterator<ASkyAtmosphere> It(World); It; ++It)
|
||||||
|
{
|
||||||
|
bHasSkyAtmosphere = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!bHasSkyAtmosphere)
|
||||||
|
{
|
||||||
|
FActorSpawnParameters SpawnParams;
|
||||||
|
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||||
|
ASkyAtmosphere* SkyAtmo = World->SpawnActor<ASkyAtmosphere>(FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams);
|
||||||
|
if (SkyAtmo)
|
||||||
|
{
|
||||||
|
#if WITH_EDITOR
|
||||||
|
SkyAtmo->SetActorLabel(TEXT("Ralpha_SkyAtmosphere"));
|
||||||
|
#endif
|
||||||
|
CreatedActors.Add(TEXT("SkyAtmosphere"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ExistingActors.Add(TEXT("SkyAtmosphere"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check/Create Volumetric Cloud
|
||||||
|
bool bHasVolumetricCloud = false;
|
||||||
|
for (TActorIterator<AVolumetricCloud> It(World); It; ++It)
|
||||||
|
{
|
||||||
|
bHasVolumetricCloud = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!bHasVolumetricCloud)
|
||||||
|
{
|
||||||
|
FActorSpawnParameters SpawnParams;
|
||||||
|
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||||
|
AVolumetricCloud* Cloud = World->SpawnActor<AVolumetricCloud>(FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams);
|
||||||
|
if (Cloud)
|
||||||
|
{
|
||||||
|
#if WITH_EDITOR
|
||||||
|
Cloud->SetActorLabel(TEXT("Ralpha_VolumetricCloud"));
|
||||||
|
#endif
|
||||||
|
CreatedActors.Add(TEXT("VolumetricCloud"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ExistingActors.Add(TEXT("VolumetricCloud"));
|
||||||
|
}
|
||||||
|
|
||||||
// Check/Create CineCameraActor
|
// Check/Create CineCameraActor
|
||||||
if (!FindCineCamera())
|
if (!FindCineCamera())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,10 @@ public:
|
||||||
TArray<TSharedPtr<FJsonValue>> ListActors(const FString& ClassFilter, const FString& NameFilter, bool bIncludeTransforms);
|
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);
|
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);
|
TSharedPtr<FJsonObject> GetActorDetails(const FString& ActorId, bool bIncludeComponents, bool bIncludeMaterials);
|
||||||
|
bool SetActorMaterial(const FString& ActorId, const FString& MaterialPath, int32 MaterialIndex);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
// Scene Setup - creates required actors for rendering control
|
// Scene Setup - creates required actors for rendering control
|
||||||
TSharedPtr<FJsonObject> SetupScene();
|
TSharedPtr<FJsonObject> SetupScene();
|
||||||
|
|
@ -80,4 +84,7 @@ private:
|
||||||
// Parse hex color string to FLinearColor
|
// Parse hex color string to FLinearColor
|
||||||
FLinearColor ParseHexColor(const FString& HexColor);
|
FLinearColor ParseHexColor(const FString& HexColor);
|
||||||
FString ColorToHex(const FLinearColor& Color);
|
FString ColorToHex(const FLinearColor& Color);
|
||||||
|
|
||||||
|
// Refresh BP_Sky_Sphere when sun position changes
|
||||||
|
void RefreshSkySphere();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue