600 lines
17 KiB
C++
600 lines
17 KiB
C++
// Copyright Ngonart OU. All Rights Reserved.
|
|
// UE5 Modular Game Framework — BPC_PlanarCapture implementation
|
|
|
|
#include "Capture/BPC_PlanarCapture.h"
|
|
#include "Capture/BP_PlanarCaptureActor.h"
|
|
#include "Capture/SS_PlanarCaptureManager.h"
|
|
#include "Capture/PlanarCaptureCameraUtils.h"
|
|
#include "Components/SceneCaptureComponent2D.h"
|
|
#include "Engine/TextureRenderTarget2D.h"
|
|
#include "Engine/World.h"
|
|
#include "GameFramework/PlayerController.h"
|
|
#include "Materials/MaterialParameterCollection.h"
|
|
#include "Materials/MaterialParameterCollectionInstance.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
|
|
UBPC_PlanarCapture::UBPC_PlanarCapture()
|
|
{
|
|
PrimaryComponentTick.bCanEverTick = true;
|
|
PrimaryComponentTick.bStartWithTickEnabled = true;
|
|
PrimaryComponentTick.TickInterval = 0.0f; // We throttle internally via TimeSinceLastCapture
|
|
|
|
// Initialize default quality profiles
|
|
QualityProfiles.SetNum(4);
|
|
|
|
// Low — 256px, 4fps
|
|
QualityProfiles[0].RenderTargetSize = 256;
|
|
QualityProfiles[0].CaptureInterval = 0.25f;
|
|
QualityProfiles[0].bEnableShadows = false;
|
|
QualityProfiles[0].bEnablePostProcess = false;
|
|
QualityProfiles[0].bEnableFog = false;
|
|
QualityProfiles[0].bEnableBloom = false;
|
|
QualityProfiles[0].bEnableAO = false;
|
|
QualityProfiles[0].bEnableLumen = false;
|
|
QualityProfiles[0].bEnableMotionBlur = false;
|
|
QualityProfiles[0].bEnableClipPlane = false;
|
|
|
|
// Medium — 512px, 15fps
|
|
QualityProfiles[1].RenderTargetSize = 512;
|
|
QualityProfiles[1].CaptureInterval = 0.0667f;
|
|
QualityProfiles[1].bEnableShadows = true;
|
|
QualityProfiles[1].bEnablePostProcess = false;
|
|
QualityProfiles[1].bEnableFog = false;
|
|
QualityProfiles[1].bEnableBloom = false;
|
|
QualityProfiles[1].bEnableAO = false;
|
|
QualityProfiles[1].bEnableLumen = false;
|
|
QualityProfiles[1].bEnableMotionBlur = false;
|
|
QualityProfiles[1].bEnableClipPlane = true;
|
|
|
|
// High — 1024px, 30fps
|
|
QualityProfiles[2].RenderTargetSize = 1024;
|
|
QualityProfiles[2].CaptureInterval = 0.0333f;
|
|
QualityProfiles[2].bEnableShadows = true;
|
|
QualityProfiles[2].bEnablePostProcess = true;
|
|
QualityProfiles[2].bEnableFog = true;
|
|
QualityProfiles[2].bEnableBloom = false;
|
|
QualityProfiles[2].bEnableAO = true;
|
|
QualityProfiles[2].bEnableLumen = true;
|
|
QualityProfiles[2].bEnableMotionBlur = false;
|
|
QualityProfiles[2].bEnableClipPlane = true;
|
|
|
|
// Hero — 2048px, 60fps
|
|
QualityProfiles[3].RenderTargetSize = 2048;
|
|
QualityProfiles[3].CaptureInterval = 0.0167f;
|
|
QualityProfiles[3].bEnableShadows = true;
|
|
QualityProfiles[3].bEnablePostProcess = true;
|
|
QualityProfiles[3].bEnableFog = true;
|
|
QualityProfiles[3].bEnableBloom = true;
|
|
QualityProfiles[3].bEnableAO = true;
|
|
QualityProfiles[3].bEnableLumen = true;
|
|
QualityProfiles[3].bEnableMotionBlur = true;
|
|
QualityProfiles[3].bEnableClipPlane = true;
|
|
}
|
|
|
|
void UBPC_PlanarCapture::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
CachedOwningActor = Cast<ABP_PlanarCaptureActor>(GetOwner());
|
|
if (!CachedOwningActor)
|
|
{
|
|
UE_LOG(LogTemp, Warning, TEXT("BPC_PlanarCapture: Owner is not a BP_PlanarCaptureActor! Capture disabled."));
|
|
SetComponentTickEnabled(false);
|
|
return;
|
|
}
|
|
|
|
// Cache manager reference
|
|
if (UWorld* World = GetWorld())
|
|
{
|
|
CachedManager = World->GetSubsystem<USS_PlanarCaptureManager>();
|
|
}
|
|
|
|
ResolveSoftReferences();
|
|
}
|
|
|
|
void UBPC_PlanarCapture::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
ShutdownCapture();
|
|
Super::EndPlay(EndPlayReason);
|
|
}
|
|
|
|
void UBPC_PlanarCapture::TickComponent(float DeltaTime, ELevelTick TickType,
|
|
FActorComponentTickFunction* ThisTickFunction)
|
|
{
|
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
|
|
|
if (!bIsCapturing || !SceneCapture || CurrentQualityTier == EPlanarCaptureQualityTier::Off)
|
|
{
|
|
return;
|
|
}
|
|
|
|
TimeSinceLastCapture += DeltaTime;
|
|
|
|
if (TimeSinceLastCapture >= ActiveProfile.CaptureInterval)
|
|
{
|
|
TimeSinceLastCapture = 0.0f;
|
|
|
|
// Get viewer camera for transform computation
|
|
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
|
|
if (!PC || !PC->PlayerCameraManager)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FTransform ViewerTransform = FTransform(
|
|
PC->PlayerCameraManager->GetCameraRotation(),
|
|
PC->PlayerCameraManager->GetCameraLocation()
|
|
);
|
|
|
|
// Compute capture camera position
|
|
const FTransform CaptureTransform = ComputeCaptureCameraTransform(ViewerTransform);
|
|
SceneCapture->SetWorldTransform(CaptureTransform);
|
|
|
|
// Capture the scene
|
|
SceneCapture->CaptureScene();
|
|
|
|
// Push MPC parameters
|
|
if (CachedOwningActor && CachedOwningActor->SurfaceMPC)
|
|
{
|
|
PushMPCParameters(CachedOwningActor->SurfaceMPC);
|
|
}
|
|
|
|
OnCaptureRendered.Broadcast();
|
|
}
|
|
}
|
|
|
|
// ========================================================================
|
|
// Public API
|
|
// ========================================================================
|
|
|
|
EPlanarCaptureInitResult UBPC_PlanarCapture::InitializeCapture()
|
|
{
|
|
if (!CachedOwningActor)
|
|
{
|
|
return EPlanarCaptureInitResult::InvalidSurfaceMesh;
|
|
}
|
|
|
|
if (CurrentQualityTier == EPlanarCaptureQualityTier::Off)
|
|
{
|
|
return EPlanarCaptureInitResult::Success; // Nothing to initialize
|
|
}
|
|
|
|
// Request render target from pool
|
|
const int32 ProfileIndex = static_cast<int32>(CurrentQualityTier) - 1; // Skip "Off" (0)
|
|
if (ProfileIndex < 0 || ProfileIndex >= QualityProfiles.Num())
|
|
{
|
|
return EPlanarCaptureInitResult::NoRenderTargetPool;
|
|
}
|
|
|
|
ActiveProfile = QualityProfiles[ProfileIndex];
|
|
|
|
if (CachedManager)
|
|
{
|
|
CaptureRenderTarget = CachedManager->RequestRenderTarget(ActiveProfile.RenderTargetSize);
|
|
}
|
|
|
|
if (!CaptureRenderTarget)
|
|
{
|
|
UE_LOG(LogTemp, Error, TEXT("BPC_PlanarCapture: Failed to allocate render target (%dx%d)"),
|
|
ActiveProfile.RenderTargetSize, ActiveProfile.RenderTargetSize);
|
|
return EPlanarCaptureInitResult::NoRenderTargetPool;
|
|
}
|
|
|
|
CreateSceneCaptureComponent();
|
|
|
|
bIsCapturing = true;
|
|
OnCaptureInitialized.Broadcast(EPlanarCaptureInitResult::Success);
|
|
return EPlanarCaptureInitResult::Success;
|
|
}
|
|
|
|
void UBPC_PlanarCapture::ShutdownCapture()
|
|
{
|
|
bIsCapturing = false;
|
|
|
|
if (SceneCapture)
|
|
{
|
|
SceneCapture->DestroyComponent();
|
|
SceneCapture = nullptr;
|
|
}
|
|
|
|
if (CaptureRenderTarget && CachedManager)
|
|
{
|
|
CachedManager->ReleaseRenderTarget(CaptureRenderTarget);
|
|
CaptureRenderTarget = nullptr;
|
|
}
|
|
|
|
// Clean up frame ring buffer
|
|
for (UTextureRenderTarget2D* RT : FrameRingBuffer)
|
|
{
|
|
if (RT && CachedManager)
|
|
{
|
|
CachedManager->ReleaseRenderTarget(RT);
|
|
}
|
|
}
|
|
FrameRingBuffer.Empty();
|
|
}
|
|
|
|
void UBPC_PlanarCapture::ApplyQualityTier(EPlanarCaptureQualityTier Tier)
|
|
{
|
|
const EPlanarCaptureQualityTier OldTier = CurrentQualityTier;
|
|
CurrentQualityTier = Tier;
|
|
|
|
if (Tier == EPlanarCaptureQualityTier::Off)
|
|
{
|
|
ShutdownCapture();
|
|
OnCaptureQualityChanged.Broadcast(OldTier, Tier);
|
|
return;
|
|
}
|
|
|
|
// If transitioning from Off to active, initialize
|
|
if (OldTier == EPlanarCaptureQualityTier::Off)
|
|
{
|
|
InitializeCapture();
|
|
}
|
|
else
|
|
{
|
|
// Update active profile and resize render target if needed
|
|
const int32 ProfileIndex = static_cast<int32>(Tier) - 1;
|
|
if (ProfileIndex >= 0 && ProfileIndex < QualityProfiles.Num())
|
|
{
|
|
ActiveProfile = QualityProfiles[ProfileIndex];
|
|
ApplyShowFlags();
|
|
}
|
|
}
|
|
|
|
OnCaptureQualityChanged.Broadcast(OldTier, Tier);
|
|
}
|
|
|
|
void UBPC_PlanarCapture::CaptureNow()
|
|
{
|
|
if (!SceneCapture)
|
|
{
|
|
return;
|
|
}
|
|
|
|
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
|
|
if (!PC || !PC->PlayerCameraManager)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FTransform ViewerTransform = FTransform(
|
|
PC->PlayerCameraManager->GetCameraRotation(),
|
|
PC->PlayerCameraManager->GetCameraLocation()
|
|
);
|
|
|
|
const FTransform CaptureTransform = ComputeCaptureCameraTransform(ViewerTransform);
|
|
SceneCapture->SetWorldTransform(CaptureTransform);
|
|
SceneCapture->CaptureScene();
|
|
|
|
TimeSinceLastCapture = 0.0f;
|
|
OnCaptureRendered.Broadcast();
|
|
}
|
|
|
|
void UBPC_PlanarCapture::ActivateHorrorReflection()
|
|
{
|
|
// Save current ShowOnly list
|
|
SavedShowOnlyActors.Empty();
|
|
for (const FPlanarCaptureActorListEntry& Entry : ShowOnlyActors)
|
|
{
|
|
SavedShowOnlyActors.Add(Entry.Actor);
|
|
}
|
|
|
|
// Clear and set wrong reflection actor
|
|
ShowOnlyActors.Empty();
|
|
if (WrongReflectionActor.IsValid())
|
|
{
|
|
FPlanarCaptureActorListEntry NewEntry;
|
|
NewEntry.Actor = WrongReflectionActor;
|
|
NewEntry.bActive = true;
|
|
ShowOnlyActors.Add(NewEntry);
|
|
}
|
|
|
|
UpdateActorLists();
|
|
}
|
|
|
|
void UBPC_PlanarCapture::DeactivateHorrorReflection()
|
|
{
|
|
ShowOnlyActors.Empty();
|
|
for (const TSoftObjectPtr<AActor>& SavedActor : SavedShowOnlyActors)
|
|
{
|
|
FPlanarCaptureActorListEntry NewEntry;
|
|
NewEntry.Actor = SavedActor;
|
|
NewEntry.bActive = true;
|
|
ShowOnlyActors.Add(NewEntry);
|
|
}
|
|
|
|
SavedShowOnlyActors.Empty();
|
|
UpdateActorLists();
|
|
}
|
|
|
|
void UBPC_PlanarCapture::PushDelayedFrame()
|
|
{
|
|
if (ActiveProfile.DelayedFrameCount <= 0 || !CaptureRenderTarget)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Ensure ring buffer is properly sized
|
|
while (FrameRingBuffer.Num() < ActiveProfile.DelayedFrameCount)
|
|
{
|
|
UTextureRenderTarget2D* NewRT = nullptr;
|
|
if (CachedManager)
|
|
{
|
|
NewRT = CachedManager->RequestRenderTarget(ActiveProfile.RenderTargetSize);
|
|
}
|
|
FrameRingBuffer.Add(NewRT);
|
|
}
|
|
|
|
// Copy current render target to ring buffer slot
|
|
RingBufferWriteIndex = (RingBufferWriteIndex + 1) % FrameRingBuffer.Num();
|
|
|
|
// Push the oldest frame to the material as the delayed reflection
|
|
if (FrameRingBuffer.IsValidIndex(RingBufferWriteIndex) && FrameRingBuffer[RingBufferWriteIndex])
|
|
{
|
|
// The material samples the delayed frame via the MPC texture parameter
|
|
// This is set in PushMPCParameters
|
|
}
|
|
}
|
|
|
|
void UBPC_PlanarCapture::SetScriptedPriority(float Priority)
|
|
{
|
|
ScriptedPriorityOverride = FMath::Clamp(Priority, 0.0f, 1.0f);
|
|
}
|
|
|
|
void UBPC_PlanarCapture::PushMPCParameters(UMaterialParameterCollection* MPC)
|
|
{
|
|
if (!MPC)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UMaterialParameterCollectionInstance* MPCInstance = GetWorld()->GetParameterCollectionInstance(MPC);
|
|
if (!MPCInstance)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Push standard parameters
|
|
// These are used by M_CaptureSurface_Master to drive steam, dirt, horror effects
|
|
MPCInstance->SetScalarParameterValue(FName("SteamIntensity"), 0.0f);
|
|
MPCInstance->SetScalarParameterValue(FName("DirtOpacity"), 0.0f);
|
|
MPCInstance->SetScalarParameterValue(FName("CondensationFlow"), 0.0f);
|
|
MPCInstance->SetScalarParameterValue(FName("DistortionAmplitude"), 0.0f);
|
|
MPCInstance->SetScalarParameterValue(FName("MirrorDarkness"), 0.0f);
|
|
MPCInstance->SetScalarParameterValue(FName("WrongReflectionBlend"), 0.0f);
|
|
MPCInstance->SetScalarParameterValue(FName("TextRevealProgress"), 0.0f);
|
|
MPCInstance->SetScalarParameterValue(FName("SteamEmissiveIntensity"), 0.0f);
|
|
MPCInstance->SetScalarParameterValue(FName("DelayedReflectionBlend"), 0.0f);
|
|
MPCInstance->SetScalarParameterValue(FName("SurfaceAge"), 0.0f);
|
|
}
|
|
|
|
// ========================================================================
|
|
// Compute
|
|
// ========================================================================
|
|
|
|
FTransform UBPC_PlanarCapture::ComputeCaptureCameraTransform(const FTransform& ViewerCameraTransform) const
|
|
{
|
|
switch (CaptureMode)
|
|
{
|
|
case EPlanarCaptureMode::Mirror:
|
|
case EPlanarCaptureMode::HorrorMirror:
|
|
{
|
|
const FTransform SurfaceTransform = CachedOwningActor
|
|
? CachedOwningActor->GetActorTransform()
|
|
: FTransform::Identity;
|
|
return UPlanarCaptureCameraUtils::ComputeMirroredTransform(ViewerCameraTransform, SurfaceTransform);
|
|
}
|
|
|
|
case EPlanarCaptureMode::Portal:
|
|
case EPlanarCaptureMode::HorrorPortal:
|
|
{
|
|
if (LinkedTargetSurface.IsValid())
|
|
{
|
|
const FTransform SourceTransform = CachedOwningActor
|
|
? CachedOwningActor->GetActorTransform()
|
|
: FTransform::Identity;
|
|
const FTransform TargetTransform = LinkedTargetSurface->GetActorTransform();
|
|
return UPlanarCaptureCameraUtils::ComputePortalTransform(
|
|
ViewerCameraTransform, SourceTransform, TargetTransform);
|
|
}
|
|
return ViewerCameraTransform;
|
|
}
|
|
|
|
case EPlanarCaptureMode::Monitor:
|
|
{
|
|
if (FixedCameraActor.IsValid())
|
|
{
|
|
return FixedCameraActor->GetActorTransform();
|
|
}
|
|
return ViewerCameraTransform;
|
|
}
|
|
|
|
case EPlanarCaptureMode::FakeWindow:
|
|
{
|
|
// Fake windows use a fixed offset from the surface
|
|
const FTransform SurfaceTransform = CachedOwningActor
|
|
? CachedOwningActor->GetActorTransform()
|
|
: FTransform::Identity;
|
|
const FVector SurfaceNormal = SurfaceTransform.GetUnitAxis(EAxis::Z);
|
|
return FTransform(SurfaceTransform.GetRotation(),
|
|
SurfaceTransform.GetLocation() + SurfaceNormal * 200.0f);
|
|
}
|
|
|
|
default:
|
|
return ViewerCameraTransform;
|
|
}
|
|
}
|
|
|
|
FPlanarCaptureScore UBPC_PlanarCapture::GetCurrentScore() const
|
|
{
|
|
FPlanarCaptureScore Score;
|
|
|
|
APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
|
|
if (!PC || !PC->PlayerCameraManager || !CachedOwningActor)
|
|
{
|
|
return Score;
|
|
}
|
|
|
|
const FVector ViewerPos = PC->PlayerCameraManager->GetCameraLocation();
|
|
const FTransform ViewerTransform = FTransform(
|
|
PC->PlayerCameraManager->GetCameraRotation(),
|
|
ViewerPos
|
|
);
|
|
|
|
const FVector SurfacePos = CachedOwningActor->GetActorLocation();
|
|
const FVector SurfaceNormal = CachedOwningActor->GetActorUpVector();
|
|
|
|
Score.DistanceToViewer = FVector::Dist(ViewerPos, SurfacePos);
|
|
Score.FacingAngle = FVector::DotProduct(
|
|
PC->PlayerCameraManager->GetCameraRotation().Vector(),
|
|
SurfaceNormal
|
|
);
|
|
|
|
// Compute screen coverage using bounding box
|
|
const FBox SurfaceBounds = CachedOwningActor->GetComponentsBoundingBox();
|
|
Score.ScreenCoverage = UPlanarCaptureCameraUtils::ComputeScreenCoverage(
|
|
SurfaceBounds, ViewerTransform, CaptureFOV, 1920, 1080);
|
|
|
|
Score.bInFrustum = UPlanarCaptureCameraUtils::IsSurfaceVisibleToViewer(
|
|
SurfaceBounds, ViewerTransform, CaptureFOV, 1.777f, 10.0f, MaxViewDistance);
|
|
|
|
Score.ScriptedPriority = ScriptedPriorityOverride;
|
|
|
|
Score.CompositeScore = UPlanarCaptureCameraUtils::ComputeCompositeScore(
|
|
Score.ScreenCoverage, Score.FacingAngle, Score.DistanceToViewer,
|
|
CachedManager ? CachedManager->MaxCaptureDistance : 10000.0f,
|
|
Score.ScriptedPriority);
|
|
|
|
return Score;
|
|
}
|
|
|
|
// ========================================================================
|
|
// Internal Methods
|
|
// ========================================================================
|
|
|
|
void UBPC_PlanarCapture::CreateSceneCaptureComponent()
|
|
{
|
|
if (!GetOwner())
|
|
{
|
|
return;
|
|
}
|
|
|
|
SceneCapture = NewObject<USceneCaptureComponent2D>(GetOwner(), USceneCaptureComponent2D::StaticClass());
|
|
if (!SceneCapture)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SceneCapture->RegisterComponent();
|
|
SceneCapture->AttachToComponent(GetOwner()->GetRootComponent(),
|
|
FAttachmentTransformRules::SnapToTargetNotIncludingScale);
|
|
|
|
SceneCapture->TextureTarget = CaptureRenderTarget;
|
|
SceneCapture->FOVAngle = CaptureFOV;
|
|
SceneCapture->bCaptureEveryFrame = false; // We control capture timing
|
|
SceneCapture->bCaptureOnMovement = false;
|
|
|
|
// Configure for planar surface capture
|
|
SceneCapture->ProjectionType = ECameraProjectionMode::Perspective;
|
|
SceneCapture->PrimitiveRenderMode = ESceneCapturePrimitiveRenderMode::PRM_UseShowOnlyList;
|
|
|
|
ApplyShowFlags();
|
|
UpdateActorLists();
|
|
}
|
|
|
|
void UBPC_PlanarCapture::ApplyShowFlags()
|
|
{
|
|
if (!SceneCapture)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Apply show flags based on active quality profile
|
|
// UE5.7: FEngineShowFlags members are set directly, not via setter methods
|
|
if (!ActiveProfile.bEnableShadows)
|
|
{
|
|
SceneCapture->ShowFlags.DynamicShadows = false;
|
|
SceneCapture->ShowFlags.ContactShadows = false;
|
|
}
|
|
if (!ActiveProfile.bEnableFog)
|
|
{
|
|
SceneCapture->ShowFlags.Fog = 0;
|
|
}
|
|
if (!ActiveProfile.bEnableBloom)
|
|
{
|
|
SceneCapture->ShowFlags.Bloom = 0;
|
|
}
|
|
if (!ActiveProfile.bEnableAO)
|
|
{
|
|
SceneCapture->ShowFlags.AmbientOcclusion = 0;
|
|
SceneCapture->ShowFlags.ScreenSpaceAO = 0;
|
|
}
|
|
if (!ActiveProfile.bEnableMotionBlur)
|
|
{
|
|
SceneCapture->ShowFlags.MotionBlur = 0;
|
|
}
|
|
|
|
// Lumen is controlled via post-process settings on the capture component
|
|
SceneCapture->PostProcessSettings.bOverride_DynamicGlobalIlluminationMethod = true;
|
|
SceneCapture->PostProcessSettings.DynamicGlobalIlluminationMethod = ActiveProfile.bEnableLumen
|
|
? EDynamicGlobalIlluminationMethod::Lumen
|
|
: EDynamicGlobalIlluminationMethod::None;
|
|
|
|
// Post-process toggle
|
|
SceneCapture->PostProcessSettings.bOverride_BloomIntensity = !ActiveProfile.bEnablePostProcess;
|
|
SceneCapture->PostProcessBlendWeight = ActiveProfile.bEnablePostProcess ? 1.0f : 0.0f;
|
|
|
|
// Disable Lumen reflections on the capture itself to prevent double-rendering
|
|
SceneCapture->PostProcessSettings.bOverride_ReflectionMethod = true;
|
|
SceneCapture->PostProcessSettings.ReflectionMethod = EReflectionMethod::None;
|
|
}
|
|
|
|
void UBPC_PlanarCapture::UpdateActorLists()
|
|
{
|
|
if (!SceneCapture)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SceneCapture->ShowOnlyActors.Empty();
|
|
SceneCapture->HiddenActors.Empty();
|
|
|
|
for (const FPlanarCaptureActorListEntry& Entry : ShowOnlyActors)
|
|
{
|
|
if (Entry.bActive && Entry.Actor.IsValid())
|
|
{
|
|
SceneCapture->ShowOnlyActors.Add(Entry.Actor.Get());
|
|
}
|
|
}
|
|
|
|
for (const FPlanarCaptureActorListEntry& Entry : HiddenActors)
|
|
{
|
|
if (Entry.bActive && Entry.Actor.IsValid())
|
|
{
|
|
SceneCapture->HiddenActors.Add(Entry.Actor.Get());
|
|
}
|
|
}
|
|
}
|
|
|
|
void UBPC_PlanarCapture::ResolveSoftReferences()
|
|
{
|
|
if (SurfaceMeshComponent.IsValid())
|
|
{
|
|
// Surface mesh is resolved — ready for capture
|
|
}
|
|
}
|
|
|
|
FPlane UBPC_PlanarCapture::GetSurfacePlane() const
|
|
{
|
|
if (!CachedOwningActor)
|
|
{
|
|
return FPlane(FVector::ZeroVector, FVector::UpVector);
|
|
}
|
|
|
|
const FVector SurfaceLocation = CachedOwningActor->GetActorLocation();
|
|
const FVector SurfaceNormal = CachedOwningActor->GetActorUpVector();
|
|
|
|
return FPlane(SurfaceLocation, SurfaceNormal);
|
|
}
|