1490 lines
61 KiB
C++
1490 lines
61 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "WaterBrushManager.h"
|
|
#include "Camera/CameraTypes.h"
|
|
#include "JumpFloodComponent2D.h"
|
|
#include "Components/SceneCaptureComponent2D.h"
|
|
#include "Editor.h"
|
|
#include "WaterBodyIslandActor.h"
|
|
#include "Engine/Canvas.h"
|
|
#include "Engine/Texture2D.h"
|
|
#include "Materials/MaterialInstanceDynamic.h"
|
|
#include "Kismet/KismetSystemLibrary.h"
|
|
#include "Kismet/KismetRenderingLibrary.h"
|
|
#include "WaterBodyActor.h"
|
|
#include "WaterSplineComponent.h"
|
|
#include "Components/SplineMeshComponent.h"
|
|
#include "Materials/MaterialParameterCollectionInstance.h"
|
|
#include "Materials/MaterialParameterCollection.h"
|
|
#include "WaterEditorModule.h"
|
|
#include "WaterEditorSubsystem.h"
|
|
#include "WaterEditorSettings.h"
|
|
#include "WaterSubsystem.h"
|
|
#include "WaterUtils.h"
|
|
#include "WaterVersion.h"
|
|
#include "Curves/CurveFloat.h"
|
|
#include "EngineUtils.h"
|
|
#include "Landscape.h"
|
|
#include "Misc/UObjectToken.h"
|
|
#include "Misc/MapErrors.h"
|
|
#include "Logging/MessageLog.h"
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(WaterBrushManager)
|
|
|
|
#define LOCTEXT_NAMESPACE "WaterBrushManager"
|
|
|
|
AWaterBrushManager::AWaterBrushManager(const FObjectInitializer& ObjectInitializer)
|
|
: Super()
|
|
, WorldSize(FVector::ZeroVector)
|
|
, LandscapeRTRes(0, 0)
|
|
, LandscapeTransform(FTransform::Identity)
|
|
{
|
|
JumpFloodComponent2D = CreateDefaultSubobject<UJumpFloodComponent2D>(TEXT("JumpFloodComponent2D"));
|
|
|
|
SceneCaptureComponent2D = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("SceneCaptureComponent2D"));
|
|
SceneCaptureComponent2D->CreationMethod = EComponentCreationMethod::Native;
|
|
SceneCaptureComponent2D->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
|
|
SceneCaptureComponent2D->ProjectionType = ECameraProjectionMode::Type::Orthographic;
|
|
SceneCaptureComponent2D->PrimitiveRenderMode = ESceneCapturePrimitiveRenderMode::PRM_UseShowOnlyList;
|
|
SceneCaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_SceneDepth;
|
|
SceneCaptureComponent2D->bCaptureEveryFrame = false;
|
|
SceneCaptureComponent2D->bCaptureOnMovement = false;
|
|
SceneCaptureComponent2D->bExcludeFromSceneTextureExtents = true; // Don't let this influence the render target size used for other purposes.
|
|
SceneCaptureComponent2D->SetRelativeRotation(FRotator(-90.0f, 0.0f, -90.0f));
|
|
SceneCaptureComponent2D->SetRelativeScale3D(FVector(0.01f, 0.01f, 0.01f));
|
|
|
|
TArray<FEngineShowFlagsSetting> ShowFlagSettings;
|
|
// HACK [jonathan.bard] : Nanite doesn't support USceneCaptureComponent's ShowOnlyComponents ATM so just disable Nanite during captures :
|
|
ShowFlagSettings.Add(FEngineShowFlagsSetting { TEXT("NaniteMeshes"), false } );
|
|
// These also need to be disabled to get a clean capture of just the water info material output
|
|
ShowFlagSettings.Add(FEngineShowFlagsSetting { TEXT("Atmosphere"), false } );
|
|
ShowFlagSettings.Add(FEngineShowFlagsSetting { TEXT("Bloom"), false } );
|
|
ShowFlagSettings.Add(FEngineShowFlagsSetting { TEXT("Lighting"), false } );
|
|
ShowFlagSettings.Add(FEngineShowFlagsSetting { TEXT("Fog"), false } );
|
|
SceneCaptureComponent2D->SetShowFlagSettings(ShowFlagSettings);
|
|
|
|
PrimaryActorTick.TickGroup = ETickingGroup::TG_PrePhysics;
|
|
bIsEditorOnlyActor = false;
|
|
}
|
|
|
|
void AWaterBrushManager::Serialize(FArchive& Ar)
|
|
{
|
|
Super::Serialize(Ar);
|
|
|
|
Ar.UsingCustomVersion(FWaterCustomVersion::GUID);
|
|
Ar.UsingCustomVersion(FFortniteMainBranchObjectVersion::GUID);
|
|
}
|
|
|
|
void AWaterBrushManager::PostLoad()
|
|
{
|
|
Super::PostLoad();
|
|
|
|
if (GetLinkerCustomVersion(FWaterCustomVersion::GUID) < FWaterCustomVersion::MoveBrushMaterialsToWaterBrushManager)
|
|
{
|
|
// Only setup some default materials for water brushes that were NOT instantiated by blueprint, otherwise we would override the default values contained in the BP :
|
|
if (GetClass()->ClassGeneratedBy == nullptr)
|
|
{
|
|
SetupDefaultMaterials();
|
|
}
|
|
}
|
|
|
|
if (GetLinkerCustomVersion(FWaterCustomVersion::GUID) < FWaterCustomVersion::MoveJumpFloodMaterialsToWaterBrushManager)
|
|
{
|
|
if (JumpFloodComponent2D != nullptr)
|
|
{
|
|
if (JumpFloodComponent2D->BlurEdgesMaterial != nullptr)
|
|
{
|
|
BlurEdgesMaterial = JumpFloodComponent2D->BlurEdgesMaterial;
|
|
}
|
|
if (JumpFloodComponent2D->FindEdgesMaterial != nullptr)
|
|
{
|
|
FindEdgesMaterial = JumpFloodComponent2D->FindEdgesMaterial;
|
|
}
|
|
if (JumpFloodComponent2D->JumpStepMaterial != nullptr)
|
|
{
|
|
JumpStepMaterial = JumpFloodComponent2D->JumpStepMaterial;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
if (GetLinkerCustomVersion(FWaterCustomVersion::GUID) < FWaterCustomVersion::MoveWaterMPCParamsToWaterMesh)
|
|
{
|
|
// OnPostLoad, the world is not set so we cannot retrieve the water mesh actor, we have to delay it to post-init :
|
|
OnWorldPostInitHandle = FWorldDelegates::OnPostWorldInitialization.AddLambda([this](UWorld* World, const UWorld::InitializationValues IVS)
|
|
{
|
|
if (World == GetWorld())
|
|
{
|
|
TActorIterator<AWaterZone> It(World);
|
|
if (AWaterZone* WaterZoneActor = It ? *It : nullptr)
|
|
{
|
|
FVector RTWorldLocation, RTWorldSizeVector;
|
|
if (DeprecateWaterLandscapeInfo(RTWorldLocation, RTWorldSizeVector))
|
|
{
|
|
SetMPCParams();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Each time a level is added, it might contain a landscape component, hence we need to deprecate RTWorldLocationand RTWorldSizeVector accordingly :
|
|
OnLevelAddedToWorldHandle = FWorldDelegates::LevelAddedToWorld.AddLambda([this](ULevel* Level, UWorld* World)
|
|
{
|
|
if ((World == GetWorld()) && (Level != nullptr))
|
|
{
|
|
TActorIterator<AWaterZone> It(World);
|
|
if (AWaterZone* WaterZoneActor = It ? *It : nullptr)
|
|
{
|
|
FVector RTWorldLocation, RTWorldSizeVector;
|
|
if (DeprecateWaterLandscapeInfo(RTWorldLocation, RTWorldSizeVector))
|
|
{
|
|
SetMPCParams();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
if (!IsTemplate()
|
|
&& (bNeedsForceUpdate || (GetLinkerCustomVersion(FFortniteMainBranchObjectVersion::GUID) < FFortniteMainBranchObjectVersion::RemoveLandscapeWaterInfo)))
|
|
{
|
|
// The removal of LandscapeWaterInfo is accompanied by a change in how the water velocity height texture is encoded so we need to regenerate it :
|
|
bNeedsForceUpdate = true;
|
|
|
|
ShowForceUpdateMapCheckError();
|
|
|
|
// Show MapCheck window
|
|
FMessageLog("MapCheck").Open(EMessageSeverity::Warning);
|
|
}
|
|
#endif // WITH_EDITOR
|
|
}
|
|
|
|
void AWaterBrushManager::BeginDestroy()
|
|
{
|
|
Super::BeginDestroy();
|
|
|
|
FWorldDelegates::OnPostWorldInitialization.Remove(OnWorldPostInitHandle);
|
|
OnWorldPostInitHandle.Reset();
|
|
|
|
FWorldDelegates::LevelAddedToWorld.Remove(OnLevelAddedToWorldHandle);
|
|
OnLevelAddedToWorldHandle.Reset();
|
|
}
|
|
|
|
|
|
void AWaterBrushManager::PostLoadSubobjects(FObjectInstancingGraph* OuterInstanceGraph)
|
|
{
|
|
Super::PostLoadSubobjects(OuterInstanceGraph);
|
|
|
|
if (JumpFloodComponent2D)
|
|
{
|
|
JumpFloodComponent2D->CreationMethod = EComponentCreationMethod::Native;
|
|
}
|
|
if (SceneCaptureComponent2D)
|
|
{
|
|
SceneCaptureComponent2D->CreationMethod = EComponentCreationMethod::Native;
|
|
}
|
|
}
|
|
|
|
UCurveFloat* AWaterBrushManager::GetElevationCurveAsset(const FWaterCurveSettings& CurveSettings)
|
|
{
|
|
if (CurveSettings.ElevationCurveAsset)
|
|
{
|
|
return CurveSettings.ElevationCurveAsset;
|
|
}
|
|
|
|
static FSoftObjectPath DefaultCurve(TEXT("/Water/Curves/FloatCurve.FloatCurve"));
|
|
return Cast<UCurveFloat>(DefaultCurve.TryLoad());
|
|
}
|
|
|
|
UTextureRenderTarget2D* AWaterBrushManager::VelocityPingPongRead(const FBrushRenderContext& BrushRenderContext) const
|
|
{
|
|
if (BrushRenderContext.VelocityRTIndex % 2)
|
|
{
|
|
return CombinedVelocityAndHeightRTA;
|
|
}
|
|
else
|
|
{
|
|
return CombinedVelocityAndHeightRTB;
|
|
}
|
|
}
|
|
|
|
|
|
UTextureRenderTarget2D* AWaterBrushManager::VelocityPingPongWrite(const FBrushRenderContext& BrushRenderContext) const
|
|
{
|
|
if (BrushRenderContext.VelocityRTIndex % 2)
|
|
{
|
|
return CombinedVelocityAndHeightRTB;
|
|
}
|
|
else
|
|
{
|
|
return CombinedVelocityAndHeightRTA;
|
|
}
|
|
}
|
|
|
|
|
|
UTextureRenderTarget2D* AWaterBrushManager::HeightPingPongRead(const FBrushRenderContext& BrushRenderContext) const
|
|
{
|
|
if (BrushRenderContext.RTIndex == 0)
|
|
{
|
|
return LandscapeRTRef;
|
|
}
|
|
else if (BrushRenderContext.RTIndex % 2)// Odd
|
|
{
|
|
return HeightmapRTA;
|
|
}
|
|
else // Even
|
|
{
|
|
return HeightmapRTB;
|
|
}
|
|
}
|
|
|
|
|
|
UTextureRenderTarget2D* AWaterBrushManager::HeightPingPongWrite(const FBrushRenderContext& BrushRenderContext) const
|
|
{
|
|
if (BrushRenderContext.RTIndex % 2)// Odd
|
|
{
|
|
return HeightmapRTB;
|
|
}
|
|
else // Even
|
|
{
|
|
return HeightmapRTA;
|
|
}
|
|
}
|
|
|
|
|
|
UTextureRenderTarget2D* AWaterBrushManager::WeightPingPongRead(const FBrushRenderContext& BrushRenderContext) const
|
|
{
|
|
if (BrushRenderContext.RTIndex == 0)
|
|
{
|
|
return LandscapeRTRef;
|
|
}
|
|
else if (BrushRenderContext.RTIndex % 2)// Odd
|
|
{
|
|
return WeightmapRTA;
|
|
}
|
|
else // Even
|
|
{
|
|
return WeightmapRTB;
|
|
}
|
|
}
|
|
|
|
|
|
UTextureRenderTarget2D* AWaterBrushManager::WeightPingPongWrite(const FBrushRenderContext& BrushRenderContext) const
|
|
{
|
|
if (BrushRenderContext.RTIndex % 2)// Odd
|
|
{
|
|
return WeightmapRTB;
|
|
}
|
|
else // Even
|
|
{
|
|
return WeightmapRTA;
|
|
}
|
|
}
|
|
|
|
void AWaterBrushManager::AddDependencyIfValid(UObject* Dependency, TSet<UObject*>& OutDependencies)
|
|
{
|
|
if (IsValid(Dependency))
|
|
{
|
|
OutDependencies.Add(Dependency);
|
|
}
|
|
}
|
|
|
|
void AWaterBrushManager::ClearCurveCache()
|
|
{
|
|
for (auto& CacheEntry : BrushCurveRTCache)
|
|
{
|
|
// Stop listening to OnUpdateCurve events on all curves in the cache :
|
|
CacheEntry.Key->OnUpdateCurve.RemoveAll(this);
|
|
}
|
|
BrushCurveRTCache.Empty();
|
|
}
|
|
|
|
void AWaterBrushManager::OnCurveUpdated(UCurveBase* Curve, EPropertyChangeType::Type ChangeType)
|
|
{
|
|
UCurveFloat* CurveFloat = CastChecked<UCurveFloat>(Curve);
|
|
|
|
// Rebuild the cache entry for this curve :
|
|
FWaterBodyBrushCache* CacheEntry = BrushCurveRTCache.Find(CurveFloat);
|
|
check(CacheEntry != nullptr);
|
|
CacheEntry->CacheIsValid = false;
|
|
|
|
// And trigger a rebuild of all water brush actor cache entries using this curve :
|
|
for (TWeakInterfacePtr<IWaterBrushActorInterface> BrushActor : GetActorsAffectingLandscape())
|
|
{
|
|
if (BrushActor.IsValid())
|
|
{
|
|
const FWaterCurveSettings& LocalCurveSettings = BrushActor->GetWaterCurveSettings();
|
|
UCurveFloat* ElevationCurveAsset = GetElevationCurveAsset(LocalCurveSettings);
|
|
if (ElevationCurveAsset == CurveFloat)
|
|
{
|
|
UWaterBodyBrushCacheContainer* CacheContainer = nullptr;
|
|
FWaterBodyBrushCache WaterBrushCache;
|
|
GetWaterCacheKey(CastChecked<AActor>(BrushActor.GetObject()), /*out*/ CacheContainer, /*out*/ WaterBrushCache);
|
|
if (CacheContainer != nullptr)
|
|
{
|
|
CacheContainer->Cache.CacheIsValid = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RequestLandscapeUpdate();
|
|
}
|
|
|
|
void AWaterBrushManager::BlueprintOnRenderTargetTexturesUpdated_Native(UTexture2D* VelocityTexture)
|
|
{
|
|
VelocityTexture->LODBias = 0;
|
|
UseDynamicPreviewRT = false;
|
|
}
|
|
|
|
void AWaterBrushManager::ForceUpdate()
|
|
{
|
|
#if WITH_EDITOR
|
|
if (bNeedsForceUpdate)
|
|
{
|
|
// We need to mark our own package as dirty to force save the water brush manager and stop dispaying the ForceUpdate message
|
|
Modify();
|
|
}
|
|
#endif // WITH_EDITOR
|
|
|
|
bKillCache = true;
|
|
ClearCurveCache();
|
|
ALandscapeBlueprintBrushBase::RequestLandscapeUpdate();
|
|
}
|
|
|
|
void AWaterBrushManager::SingleJumpStep()
|
|
{
|
|
if (!::IsValid(DebugDistanceFieldMID))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("DebugDistanceFieldMaterial must be set to use this debug function"));
|
|
return;
|
|
}
|
|
|
|
UTextureRenderTarget2D* RenderTarget = JumpFloodComponent2D->SingleJumpStep();
|
|
DebugDistanceFieldMID->UMaterialInstanceDynamic::SetTextureParameterValue(FName(TEXT("RT")), RenderTarget);
|
|
}
|
|
|
|
void AWaterBrushManager::SingleBlurStep()
|
|
{
|
|
if (!::IsValid(DebugDistanceFieldMID))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("DebugDistanceFieldMaterial must be set to use this debug function"));
|
|
return;
|
|
}
|
|
|
|
UTextureRenderTarget2D* RenderTarget = JumpFloodComponent2D->SingleBlurStep();
|
|
DebugDistanceFieldMID->UMaterialInstanceDynamic::SetTextureParameterValue(FName(TEXT("RT")), RenderTarget);
|
|
}
|
|
|
|
void AWaterBrushManager::FindEdges()
|
|
{
|
|
if (!::IsValid(DebugDistanceFieldMID))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("DebugDistanceFieldMaterial must be set to use this debug function"));
|
|
return;
|
|
}
|
|
|
|
UTextureRenderTarget2D* RenderTarget = JumpFloodComponent2D->FindEdges(DepthAndShapeRT, 50000.0f, FLinearColor(0.0f, 0.0f, 0.0f, 1.0), false, 99.0f);
|
|
DebugDistanceFieldMID->UMaterialInstanceDynamic::SetTextureParameterValue(FName(TEXT("RT")), RenderTarget);
|
|
}
|
|
|
|
void AWaterBrushManager::BlueprintWaterBodyChanged_Native(AActor* Actor)
|
|
{
|
|
if (::IsValid(Actor))
|
|
{
|
|
UWaterBodyBrushCacheContainer* ContainerObject;
|
|
FWaterBodyBrushCache WaterBrushCache;
|
|
GetWaterCacheKey(Actor, /*out*/ ContainerObject, /*out*/ WaterBrushCache);
|
|
|
|
if (::IsValid(ContainerObject))
|
|
{
|
|
ContainerObject->Cache.CacheIsValid = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AWaterBrushManager::Initialize_Native(FTransform const& InLandscapeTransform, FIntPoint const& InLandscapeSize, FIntPoint const& InLandscapeRenderTargetSize)
|
|
{
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Updated Landscape Transform"));
|
|
|
|
bool bNeedsFullUpdate = false;
|
|
if (LandscapeQuads != InLandscapeSize)
|
|
{
|
|
LandscapeQuads = InLandscapeSize;
|
|
bNeedsFullUpdate = true;
|
|
}
|
|
if (LandscapeRTRes != InLandscapeRenderTargetSize)
|
|
{
|
|
LandscapeRTRes = InLandscapeRenderTargetSize;
|
|
bNeedsFullUpdate = true;
|
|
}
|
|
if (!InLandscapeTransform.Equals(LandscapeTransform))
|
|
{
|
|
LandscapeTransform = InLandscapeTransform;
|
|
bNeedsFullUpdate = true;
|
|
}
|
|
|
|
if (bNeedsFullUpdate)
|
|
{
|
|
check(SceneCaptureComponent2D != nullptr);
|
|
|
|
FVector Scale = LandscapeTransform.GetScale3D();
|
|
WorldSize.Set(Scale.X * (float)LandscapeQuads.X, Scale.Y * (float)LandscapeQuads.Y, 0.512f);
|
|
|
|
const FVector Temp(Scale.X * (float)LandscapeRTRes.X, Scale.Y * (float)LandscapeRTRes.Y, 0.512f);
|
|
SceneCaptureComponent2D->OrthoWidth = FMath::Max(Temp.X, Temp.Y);
|
|
|
|
FVector LocationVector(Temp - Scale);
|
|
LocationVector *= 0.5f;
|
|
LocationVector = LandscapeTransform.GetRotation().RotateVector(LocationVector);
|
|
LocationVector += LandscapeTransform.GetLocation();
|
|
LocationVector.Z = 50000.0f;
|
|
SceneCaptureComponent2D->SetWorldLocation(LocationVector);
|
|
|
|
// If the transform or resolution changes, the distance fields need to be recomputed entirely :
|
|
bKillCache = true;
|
|
}
|
|
}
|
|
|
|
void AWaterBrushManager::CaptureMeshDepth(const TArrayView<UStaticMeshComponent*>& MeshComponents)
|
|
{
|
|
SceneCaptureComponent2D->ClearShowOnlyComponents();
|
|
SceneCaptureComponent2D->ShowOnlyActors.Empty();
|
|
|
|
TArray<bool> PreviousVisibilities;
|
|
TArray<bool> PreviousHiddenInGame;
|
|
PreviousVisibilities.SetNum(MeshComponents.Num());
|
|
PreviousHiddenInGame.SetNum(MeshComponents.Num());
|
|
|
|
for (int32 PrimitiveComponentIndex = 0; PrimitiveComponentIndex < MeshComponents.Num(); ++PrimitiveComponentIndex)
|
|
{
|
|
UStaticMeshComponent* PrimitiveComponent = MeshComponents[PrimitiveComponentIndex];
|
|
PreviousVisibilities[PrimitiveComponentIndex] = PrimitiveComponent->GetVisibleFlag();
|
|
PreviousHiddenInGame[PrimitiveComponentIndex] = PrimitiveComponent->bHiddenInGame;
|
|
|
|
PrimitiveComponent->SetVisibility(true);
|
|
PrimitiveComponent->SetHiddenInGame(false);
|
|
|
|
SceneCaptureComponent2D->ShowOnlyComponent(PrimitiveComponent);
|
|
}
|
|
|
|
SceneCaptureComponent2D->CaptureScene();
|
|
|
|
for (int32 PrimitiveComponentIndex = 0; PrimitiveComponentIndex < MeshComponents.Num(); ++PrimitiveComponentIndex)
|
|
{
|
|
UStaticMeshComponent* PrimitiveComponent = MeshComponents[PrimitiveComponentIndex];
|
|
PrimitiveComponent->SetVisibility(PreviousVisibilities[PrimitiveComponentIndex]);
|
|
PrimitiveComponent->SetHiddenInGame(PreviousHiddenInGame[PrimitiveComponentIndex]);
|
|
}
|
|
|
|
// Avoid keeping references to Captured components
|
|
SceneCaptureComponent2D->ClearShowOnlyComponents();
|
|
}
|
|
|
|
void AWaterBrushManager::GetWaterCacheKey(AActor* WaterBrush, /*out*/ UWaterBodyBrushCacheContainer*& ContainerObject, /*out*/ FWaterBodyBrushCache& Value)
|
|
{
|
|
ContainerObject = CastChecked<UWaterBodyBrushCacheContainer>(GetActorCache(WaterBrush, UWaterBodyBrushCacheContainer::StaticClass()), ECastCheckedType::NullAllowed);
|
|
if (IsValid(ContainerObject))
|
|
{
|
|
Value = ContainerObject->Cache;
|
|
}
|
|
}
|
|
|
|
void AWaterBrushManager::CacheBrushDistanceField(const FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
check(::IsValid(BrushActorRenderContext.CacheContainer));
|
|
UKismetRenderingLibrary::ClearRenderTarget2D(this, BrushActorRenderContext.CacheContainer->Cache.CacheRenderTarget, FLinearColor::Black);
|
|
UKismetRenderingLibrary::DrawMaterialToRenderTarget(this, BrushActorRenderContext.CacheContainer->Cache.CacheRenderTarget, DistanceFieldCacheMID);
|
|
BrushActorRenderContext.CacheContainer->Cache.CacheIsValid = true;
|
|
}
|
|
|
|
void AWaterBrushManager::GetRenderDependencies(TSet<UObject*>& OutDependencies)
|
|
{
|
|
Super::GetRenderDependencies(OutDependencies);
|
|
|
|
// Add all materials as depencies : we don't want to render the brush if one of those materials isn't compiled yet :
|
|
AddDependencyIfValid(BrushAngleFalloffMaterial, OutDependencies);
|
|
AddDependencyIfValid(BrushWidthFalloffMaterial, OutDependencies);
|
|
AddDependencyIfValid(DistanceFieldCacheMaterial, OutDependencies);
|
|
AddDependencyIfValid(RenderRiverSplineDepthMaterial, OutDependencies);
|
|
AddDependencyIfValid(DebugDistanceFieldMaterial, OutDependencies);
|
|
AddDependencyIfValid(WeightmapMaterial, OutDependencies);
|
|
AddDependencyIfValid(DrawCanvasMaterial, OutDependencies);
|
|
AddDependencyIfValid(CompositeWaterBodyTextureMaterial, OutDependencies);
|
|
AddDependencyIfValid(IslandFalloffMaterial, OutDependencies);
|
|
AddDependencyIfValid(JumpStepMaterial, OutDependencies);
|
|
AddDependencyIfValid(FindEdgesMaterial, OutDependencies);
|
|
AddDependencyIfValid(BlurEdgesMaterial, OutDependencies);
|
|
}
|
|
|
|
bool AWaterBrushManager::SetupRiverSplineRenderMIDs(const FBrushActorRenderContext& BrushActorRenderContext, bool bRestoreMIDs, TArray<UMaterialInterface*>& InOutMIDs)
|
|
{
|
|
AWaterBody* WaterBody = BrushActorRenderContext.GetActorAs<AWaterBody>();
|
|
check(WaterBody->GetWaterBodyType() == EWaterBodyType::River);
|
|
|
|
const UWaterSplineComponent* SplineComponent = WaterBody->GetWaterSpline();
|
|
const int32 NumSplineMids = SplineComponent->GetNumberOfSplinePoints() - 1;
|
|
|
|
TArray<UPrimitiveComponent*> BrushRenderableComponents = WaterBody->GetBrushRenderableComponents();
|
|
TInlineComponentArray<USplineMeshComponent*> SplineMeshComponents;
|
|
SplineMeshComponents.Reserve(BrushRenderableComponents.Num());
|
|
Algo::Transform(BrushRenderableComponents, SplineMeshComponents, [](UPrimitiveComponent* PrimitiveComponent) { return StaticCast<USplineMeshComponent*>(PrimitiveComponent); });
|
|
|
|
if (SplineMeshComponents.Num() != NumSplineMids)
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid River spline mesh component count."));
|
|
return false;
|
|
}
|
|
|
|
if (NumSplineMids > RiverSplineMIDs.Num())
|
|
{
|
|
RiverSplineMIDs.SetNumZeroed(NumSplineMids);
|
|
}
|
|
|
|
if (bRestoreMIDs)
|
|
{
|
|
check(InOutMIDs.Num() == NumSplineMids);
|
|
}
|
|
else
|
|
{
|
|
InOutMIDs.SetNumZeroed(NumSplineMids);
|
|
}
|
|
|
|
for (int32 MIDIndex = 0; MIDIndex < NumSplineMids; ++MIDIndex)
|
|
{
|
|
USplineMeshComponent* SplineMeshComponent = SplineMeshComponents[MIDIndex];
|
|
check(SplineMeshComponent != nullptr);
|
|
if (bRestoreMIDs)
|
|
{
|
|
SplineMeshComponent->SetMaterial(0, InOutMIDs[MIDIndex]);
|
|
}
|
|
else
|
|
{
|
|
RiverSplineMIDs[MIDIndex] = FWaterUtils::GetOrCreateTransientMID(RiverSplineMIDs[MIDIndex], TEXT("RiverSplineMID"), RenderRiverSplineDepthMaterial);
|
|
UMaterialInstanceDynamic* TempMID = RiverSplineMIDs[MIDIndex];
|
|
if (TempMID != nullptr)
|
|
{
|
|
const float Offset = BrushActorRenderContext.WaterBrushActor->GetWaterHeightmapSettings().FalloffSettings.ZOffset;
|
|
TempMID->SetScalarParameterValue(FName(TEXT("DepthA")), SplineComponent->GetFloatPropertyAtSplinePoint(MIDIndex, FName(TEXT("Depth"))) + Offset);
|
|
TempMID->SetScalarParameterValue(FName(TEXT("DepthB")), SplineComponent->GetFloatPropertyAtSplinePoint(MIDIndex + 1, FName(TEXT("Depth"))) + Offset);
|
|
TempMID->SetScalarParameterValue(FName(TEXT("VelA")), SplineComponent->GetFloatPropertyAtSplinePoint(MIDIndex, FName(TEXT("WaterVelocityScalar"))));
|
|
TempMID->SetScalarParameterValue(FName(TEXT("VelB")), SplineComponent->GetFloatPropertyAtSplinePoint(MIDIndex + 1, FName(TEXT("WaterVelocityScalar"))));
|
|
|
|
// Save the previous material for restoring it further on :
|
|
InOutMIDs[MIDIndex] = (SplineMeshComponent->GetMaterial(0));
|
|
SplineMeshComponent->SetMaterial(0, TempMID);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid River spline material for Water Brush."));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void AWaterBrushManager::CaptureRiverDepthAndVelocity(const FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
TArray<UMaterialInterface*> MIDsToRestore;
|
|
if (!SetupRiverSplineRenderMIDs(BrushActorRenderContext, /*bRestoreMIDs = */false, MIDsToRestore))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Error in setup River spline render material for Water Brush. Aborting CaptureRiverDepthAndVelocity."));
|
|
return;
|
|
}
|
|
|
|
AWaterBody* WaterBody = BrushActorRenderContext.GetActorAs<AWaterBody>();
|
|
const bool Hidden = WaterBody->IsTemporarilyHiddenInEditor();
|
|
WaterBody->SetIsTemporarilyHiddenInEditor(false);
|
|
|
|
TArray<UPrimitiveComponent*> BrushRenderableComponents = WaterBody->GetBrushRenderableComponents();
|
|
TArray<UStaticMeshComponent*, TInlineAllocator<32>> SplineMeshComponents;
|
|
SplineMeshComponents.Reserve(BrushRenderableComponents.Num());
|
|
Algo::Transform(BrushRenderableComponents, SplineMeshComponents, [](UPrimitiveComponent* PrimitiveComponent) { return StaticCast<UStaticMeshComponent*>(PrimitiveComponent); });
|
|
|
|
SceneCaptureComponent2D->TextureTarget = WaterDepthAndVelocityRT;
|
|
SceneCaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_SceneColorSceneDepth;
|
|
CaptureMeshDepth(SplineMeshComponents);
|
|
|
|
SceneCaptureComponent2D->CaptureSource = ESceneCaptureSource::SCS_SceneDepth;
|
|
SceneCaptureComponent2D->TextureTarget = DepthAndShapeRT;
|
|
CaptureMeshDepth(SplineMeshComponents);
|
|
|
|
WaterBody->SetIsTemporarilyHiddenInEditor(Hidden);
|
|
|
|
// Cleanup the spline components at the end (we're not supposed to have modified the water actors) :
|
|
SetupRiverSplineRenderMIDs(BrushActorRenderContext, /*bRestoreMIDs = */true, MIDsToRestore);
|
|
}
|
|
|
|
void AWaterBrushManager::DrawCanvasShape(const FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
TArray<FCanvasUVTri> CanvasUVTris;
|
|
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Actor used for Spline Canvas Render: %s"), *UKismetSystemLibrary::GetDisplayName(BrushActorRenderContext.WaterBrushActor.GetObject()));
|
|
|
|
UWaterSplineComponent* SplineComponent = CastChecked<UWaterSplineComponent>(BrushActorRenderContext.GetActor()->GetComponentByClass(UWaterSplineComponent::StaticClass()));
|
|
ensure(SplineComponent);
|
|
|
|
int32 TruncSegments = FMath::TruncToInt(SplineComponent->GetSplineLength() / CanvasSegmentSize);
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Spline Segment Canvas Segments: %d"), TruncSegments);
|
|
|
|
for (int32 ii = 0; ii < TruncSegments; ++ii)
|
|
{
|
|
FVector LocationA = SplineComponent->GetLocationAtDistanceAlongSpline(0.0f, ESplineCoordinateSpace::World);
|
|
FVector LocationB = SplineComponent->GetLocationAtDistanceAlongSpline(float(ii + 1) * CanvasSegmentSize, ESplineCoordinateSpace::World);
|
|
FVector LocationC = SplineComponent->GetLocationAtDistanceAlongSpline(float(ii + 2) * CanvasSegmentSize, ESplineCoordinateSpace::World);
|
|
|
|
LocationA = LandscapeTransform.InverseTransformPosition(LocationA) + 0.5f;
|
|
LocationB = LandscapeTransform.InverseTransformPosition(LocationB) + 0.5f;
|
|
LocationC = LandscapeTransform.InverseTransformPosition(LocationC) + 0.5f;
|
|
|
|
FCanvasUVTri& CanvasUVTri = CanvasUVTris.AddDefaulted_GetRef();
|
|
CanvasUVTri.V0_Pos = FVector2D(LocationA);
|
|
CanvasUVTri.V0_UV = FVector2D::ZeroVector;
|
|
CanvasUVTri.V0_Color = FLinearColor::Red;
|
|
CanvasUVTri.V1_Pos = FVector2D(LocationC);
|
|
CanvasUVTri.V1_UV = FVector2D::ZeroVector;
|
|
CanvasUVTri.V1_Color = FLinearColor::Green;
|
|
CanvasUVTri.V2_Pos = FVector2D(LocationB);
|
|
CanvasUVTri.V2_UV = FVector2D::ZeroVector;
|
|
CanvasUVTri.V2_Color = FLinearColor::Blue;
|
|
}
|
|
|
|
// TODO [jonathan.bard] : missing DynamicPointActor/DynamicPreviewPoint from BP here
|
|
|
|
UKismetRenderingLibrary::ClearRenderTarget2D(this, DepthAndShapeRT, FLinearColor::Black);
|
|
|
|
UCanvas* Canvas;
|
|
FVector2D CanvasToRenderTargetSize;
|
|
FDrawToRenderTargetContext RenderTargetContext;
|
|
UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(this, DepthAndShapeRT, /*out*/ Canvas, /*out*/ CanvasToRenderTargetSize, /*out*/ RenderTargetContext);
|
|
check(::IsValid(Canvas));
|
|
check(::IsValid(DrawCanvasMID));
|
|
|
|
Canvas->UCanvas::K2_DrawMaterialTriangle(DrawCanvasMID, CanvasUVTris);
|
|
|
|
UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(this, RenderTargetContext);
|
|
}
|
|
|
|
void AWaterBrushManager::DrawBrushMaterial(const FBrushRenderContext& BrushRenderContext, const FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
if (BrushRenderContext.bHeightmapRender)
|
|
{
|
|
UKismetRenderingLibrary::ClearRenderTarget2D(this, HeightPingPongWrite(BrushRenderContext), FLinearColor::Black);
|
|
UKismetRenderingLibrary::DrawMaterialToRenderTarget(this, HeightPingPongWrite(BrushRenderContext), BrushActorRenderContext.MID);
|
|
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Render Target Write Target: %s"), *UKismetSystemLibrary::GetDisplayName(HeightPingPongWrite(BrushRenderContext)));
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Brush MID Parent: %s"), *UKismetSystemLibrary::GetDisplayName(BrushActorRenderContext.MID));
|
|
}
|
|
else
|
|
{
|
|
UKismetRenderingLibrary::ClearRenderTarget2D(this, WeightPingPongWrite(BrushRenderContext), FLinearColor::Black);
|
|
UKismetRenderingLibrary::DrawMaterialToRenderTarget(this, WeightPingPongWrite(BrushRenderContext), WeightmapMID);
|
|
}
|
|
}
|
|
|
|
void AWaterBrushManager::UpdateCurveCacheKeys()
|
|
{
|
|
for (TWeakInterfacePtr<IWaterBrushActorInterface> BrushActor : GetActorsAffectingLandscape())
|
|
{
|
|
if (BrushActor.IsValid())
|
|
{
|
|
const FWaterCurveSettings& LocalCurveSettings = BrushActor->GetWaterCurveSettings();
|
|
UCurveFloat* ElevationCurveAsset = GetElevationCurveAsset(LocalCurveSettings);
|
|
if (ensure(ElevationCurveAsset != nullptr))
|
|
{
|
|
FWaterBodyBrushCache* WaterBrushCache = BrushCurveRTCache.Find(ElevationCurveAsset);
|
|
if ((WaterBrushCache == nullptr) || (WaterBrushCache->CacheRenderTarget == nullptr))
|
|
{
|
|
UTextureRenderTarget2D* CurveRT = FWaterUtils::GetOrCreateTransientRenderTarget2D(nullptr, TEXT("CurveRT"), FIntPoint(256, 1), ETextureRenderTargetFormat::RTF_R16f);
|
|
BrushCurveRTCache.Add(ElevationCurveAsset, FWaterBodyBrushCache{ CurveRT, false });
|
|
ElevationCurveAsset->OnUpdateCurve.AddUObject(this, &AWaterBrushManager::OnCurveUpdated);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AWaterBrushManager::UpdateCurves()
|
|
{
|
|
TArray<typename decltype(BrushCurveRTCache)::KeyType> Keys;
|
|
BrushCurveRTCache.GetKeys(Keys);
|
|
|
|
for (const TObjectPtr<UCurveFloat>& CurCurveFloat : Keys)
|
|
{
|
|
if (CurCurveFloat)
|
|
{
|
|
const FWaterBodyBrushCache& CurveCache = *BrushCurveRTCache.Find(CurCurveFloat);
|
|
|
|
// Curve cache needs to be refreshed :
|
|
if (!CurveCache.CacheIsValid && CurveCache.CacheRenderTarget)
|
|
{
|
|
check(CurCurveFloat);
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Water Body Curve Cache Invalid : Refreshing Curve RT"));
|
|
UKismetRenderingLibrary::ClearRenderTarget2D(this, CurveCache.CacheRenderTarget, FLinearColor::Black);
|
|
|
|
check(CurveCache.CacheRenderTarget);
|
|
|
|
UCanvas* Canvas = nullptr;
|
|
FVector2D CanvasToRenderTargetSize;
|
|
FDrawToRenderTargetContext RenderTargetContext;
|
|
UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(this, CurveCache.CacheRenderTarget, /*out*/ Canvas, /*out*/ CanvasToRenderTargetSize, /*out*/ RenderTargetContext);
|
|
|
|
// TODO [jonathan.bard] : this is just an upload of texture data, this shouldn't be done this way :
|
|
check(Canvas);
|
|
const FVector2D ScreenSize(1.0f, 2.0f);
|
|
for (int32 ii = 0; ii < 256; ++ii)
|
|
{
|
|
float LinearValue = CurCurveFloat->GetFloatValue((float)ii / 255.0f);
|
|
const FLinearColor Color(LinearValue, LinearValue, LinearValue);
|
|
Canvas->K2_DrawBox(FVector2D((float)ii, 0.0f), ScreenSize, 1.0f, Color);
|
|
}
|
|
|
|
BrushCurveRTCache[CurCurveFloat].CacheIsValid = true;
|
|
UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(this, RenderTargetContext);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AWaterBrushManager::BrushRenderSetup()
|
|
{
|
|
if (!AllocateRTs())
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid Render Target for Water Brush. Aborting BrushRenderSetup."));
|
|
return false;
|
|
}
|
|
|
|
JumpFloodComponent2D->BlurEdgesMaterial = BlurEdgesMaterial;
|
|
JumpFloodComponent2D->FindEdgesMaterial = FindEdgesMaterial;
|
|
JumpFloodComponent2D->JumpStepMaterial = JumpStepMaterial;
|
|
|
|
// TODO [jonathan.bard] make sure that this works : (probably do the MID setup in CreateMIDs and use AWaterUtils::GetOrCreateTransientMID
|
|
if (::IsValid(DebugDistanceFieldMaterial))
|
|
{
|
|
UStaticMeshComponent* StaticMeshComponent = CastChecked<UStaticMeshComponent>(AActor::AddComponent(FName(TEXT("NODE_AddStaticMeshComponent-0")), false, FTransform(FRotator::ZeroRotator, FVector::ZeroVector, WorldSize), this), ECastCheckedType::NullAllowed);
|
|
if (DebugDistanceFieldMaterial->IsA<UMaterialInstanceDynamic>())
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid DebugDistanceFieldMaterial Material : must be either a Material Instance Constant or a Material"));
|
|
}
|
|
else
|
|
{
|
|
// Transient MID : no outer, no name :
|
|
DebugDistanceFieldMID = UMaterialInstanceDynamic::Create(DebugDistanceFieldMaterial, nullptr);
|
|
check((DebugDistanceFieldMID != nullptr) && (DebugDistanceFieldMID->GetMaterial() == DebugDistanceFieldMaterial->GetMaterial()));
|
|
StaticMeshComponent->SetMaterial(0, DebugDistanceFieldMID);
|
|
|
|
DebugDistanceFieldMID->SetScalarParameterValue(FName(TEXT("ShowGrid")), (float)ShowGrid);
|
|
DebugDistanceFieldMID->SetScalarParameterValue(FName(TEXT("ShowDistance")), (float)ShowDistance);
|
|
DebugDistanceFieldMID->SetScalarParameterValue(FName(TEXT("ShowGradient")), (float)ShowGradient);
|
|
DebugDistanceFieldMID->SetScalarParameterValue(FName(TEXT("DistanceDivisor")), DistanceDivisor);
|
|
}
|
|
}
|
|
|
|
UpdateBrushCacheKeys();
|
|
UpdateCurveCacheKeys();
|
|
UpdateCurves();
|
|
SetMPCParams();
|
|
|
|
if (!CreateMIDs())
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid material setup for Water Brush. Aborting BrushRenderSetup."));
|
|
return false;
|
|
}
|
|
|
|
// Success at last!
|
|
return true;
|
|
}
|
|
|
|
void AWaterBrushManager::DistanceFieldCaching(const FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
check(::IsValid(BrushActorRenderContext.MID));
|
|
check(::IsValid(DistanceFieldCacheMID));
|
|
check(::IsValid(BrushActorRenderContext.CacheContainer));
|
|
|
|
BrushActorRenderContext.MID->SetTextureParameterValue(FName(TEXT("CachedDistanceFieldHeight")), BrushActorRenderContext.CacheContainer->Cache.CacheRenderTarget);
|
|
DistanceFieldCacheMID->SetTextureParameterValue(FName(TEXT("MeshDepth")), DepthAndShapeRT);
|
|
|
|
const FWaterBodyHeightmapSettings& HeightmapSettings = BrushActorRenderContext.WaterBrushActor->GetWaterHeightmapSettings();
|
|
|
|
float JumpFloodRTValue = LandscapeRTRes.GetMax();
|
|
JumpFloodRTValue = FMath::Log2(JumpFloodRTValue);
|
|
|
|
int32 Ceiling = FMath::CeilToInt(JumpFloodRTValue);
|
|
Ceiling += HeightmapSettings.Effects.Blurring.Radius;
|
|
|
|
DistanceFieldCacheMID->SetTextureParameterValue(FName(TEXT("JumpFloodRT")), (Ceiling % 2 == 0) ? JumpFloodRTA : JumpFloodRTB);
|
|
|
|
DistanceFieldCacheMID->SetScalarParameterValue(FName(TEXT("UseBlur")), HeightmapSettings.Effects.Blurring.bBlurShape ? 1.0f : 0.0f);
|
|
DistanceFieldCacheMID->SetScalarParameterValue(FName(TEXT("Bluroffset")), (float)HeightmapSettings.Effects.Blurring.Radius);
|
|
|
|
AWaterBody* WaterBody = BrushActorRenderContext.TryGetActorAs<AWaterBody>();
|
|
|
|
bool bDoInvert = (WaterBody != nullptr) && (WaterBody->GetWaterBodyType() == EWaterBodyType::Ocean);
|
|
DistanceFieldCacheMID->SetScalarParameterValue(FName(TEXT("Invert")), bDoInvert ? 1.0f : 0.0f);
|
|
|
|
const FWaterBrushEffectCurlNoise& CurlNoise = HeightmapSettings.Effects.CurlNoise;
|
|
FLinearColor NoiseColor(CurlNoise.Curl1Tiling, CurlNoise.Curl1Amount, CurlNoise.Curl2Tiling, CurlNoise.Curl2Amount);
|
|
DistanceFieldCacheMID->SetVectorParameterValue(FName(TEXT("Curl")), NoiseColor);
|
|
|
|
DistanceFieldCacheMID->SetScalarParameterValue(FName(TEXT("ZOffset")), HeightmapSettings.FalloffSettings.ZOffset);
|
|
DistanceFieldCacheMID->SetTextureParameterValue(FName(TEXT("ChannelDepth")), WaterDepthAndVelocityRT);
|
|
|
|
bool bDoMeshDepth = (WaterBody != nullptr) && (WaterBody->GetWaterBodyType() == EWaterBodyType::River);
|
|
DistanceFieldCacheMID->SetScalarParameterValue(FName(TEXT("UseMeshDepth")), bDoMeshDepth ? 1.0f : 0.0f);
|
|
}
|
|
|
|
void AWaterBrushManager::CurvesSmoothingAndTerracing(const FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
const FWaterBodyHeightmapSettings& HeightmapSettings = BrushActorRenderContext.WaterBrushActor->GetWaterHeightmapSettings();
|
|
const FWaterBrushEffectSmoothBlending& SmoothBlending = HeightmapSettings.Effects.SmoothBlending;
|
|
const FWaterFalloffSettings& FalloffSettings = HeightmapSettings.FalloffSettings;
|
|
const FWaterBrushEffectTerracing& Terracing = HeightmapSettings.Effects.Terracing;
|
|
const FWaterCurveSettings& CurveSettings = BrushActorRenderContext.WaterBrushActor->GetWaterCurveSettings();
|
|
|
|
check(::IsValid(BrushActorRenderContext.MID));
|
|
check(::IsValid(DistanceFieldCacheMID));
|
|
|
|
const FLinearColor SmoothingParamsColor(FMath::Max(SmoothBlending.InnerSmoothDistance, 0.01f), FMath::Max(SmoothBlending.OuterSmoothDistance, 0.01f), 0.0f, 0.0f);
|
|
BrushActorRenderContext.MID->SetVectorParameterValue(FName(TEXT("SmoothingParams")), SmoothingParamsColor);
|
|
|
|
float CurveChannelDepthValue = (CurveSettings.ChannelDepth + FalloffSettings.ZOffset) * (CurveSettings.bUseCurveChannel ? 1.0f : 0.0f);
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("CurveChannelDepth")), CurveChannelDepthValue);
|
|
DistanceFieldCacheMID->SetScalarParameterValue(FName(TEXT("CurveChannelDepth")), CurveChannelDepthValue);
|
|
|
|
float ChannelEdgeOffsetValue = CurveSettings.ChannelEdgeOffset - (SplineMeshExtension / 2.0f);
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("ChannelEdgeOffset")), ChannelEdgeOffsetValue);
|
|
DistanceFieldCacheMID->SetScalarParameterValue(FName(TEXT("ChannelEdgeOffset")), ChannelEdgeOffsetValue);
|
|
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("CurveRampWidth")), CurveSettings.CurveRampWidth);
|
|
DistanceFieldCacheMID->SetScalarParameterValue(FName(TEXT("CurveRampWidth")), CurveSettings.CurveRampWidth);
|
|
|
|
UCurveFloat* ElevationCurveAsset = GetElevationCurveAsset(CurveSettings);
|
|
UTextureRenderTarget2D* CurveRTValue = BrushCurveRTCache.Find(ElevationCurveAsset)->CacheRenderTarget;
|
|
check(CurveRTValue);
|
|
BrushActorRenderContext.MID->SetTextureParameterValue(FName(TEXT("CurveRT")), CurveRTValue);
|
|
DistanceFieldCacheMID->SetTextureParameterValue(FName(TEXT("CurveRT")), CurveRTValue);
|
|
|
|
FLinearColor TerraceParamsColor(Terracing.TerraceAlpha, Terracing.TerraceSmoothness, Terracing.TerraceSpacing, Terracing.MaskLength);
|
|
BrushActorRenderContext.MID->SetVectorParameterValue(FName(TEXT("TerraceParams")), TerraceParamsColor);
|
|
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("TerraceOffset")), Terracing.MaskStartOffset);
|
|
}
|
|
|
|
void AWaterBrushManager::FalloffAndBlendMode(const FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
check(::IsValid(BrushActorRenderContext.MID));
|
|
check(::IsValid(WeightmapMID));
|
|
|
|
const FWaterBodyHeightmapSettings& HeightmapSettings = BrushActorRenderContext.WaterBrushActor->GetWaterHeightmapSettings();
|
|
const FWaterFalloffSettings& FalloffSettings = HeightmapSettings.FalloffSettings;
|
|
|
|
float FalloffTangentValue = FMath::Clamp(FalloffSettings.FalloffAngle, 1.0f, 89.0f);
|
|
FalloffTangentValue = FMath::Tan(FMath::DegreesToRadians(FalloffTangentValue));
|
|
|
|
const float EdgeWidth = FMath::Max(FalloffSettings.FalloffWidth, 0.1f);
|
|
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("FalloffTangent")), FalloffTangentValue);
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("UseMeshDepth")), 1.0f);
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("CapShapeInterior")), 1.0f);
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("EdgeWidth")), EdgeWidth);
|
|
|
|
float EdgeCenterOffsetValue = FalloffSettings.EdgeOffset - (SplineMeshExtension / 2.0f);
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("EdgeCenterOffset")), EdgeCenterOffsetValue);
|
|
|
|
float ModeValue = 3.0f;
|
|
switch (HeightmapSettings.BlendMode)
|
|
{
|
|
case EWaterBrushBlendType::AlphaBlend:
|
|
ModeValue = 0.0f;
|
|
break;
|
|
case EWaterBrushBlendType::Min:
|
|
ModeValue = 1.0f;
|
|
break;
|
|
case EWaterBrushBlendType::Max:
|
|
ModeValue = 2.0f;
|
|
break;
|
|
default:
|
|
ModeValue = 3.0f;
|
|
}
|
|
|
|
WeightmapMID->UMaterialInstanceDynamic::SetScalarParameterValue(FName(TEXT("BlendMode")), ModeValue);
|
|
BrushActorRenderContext.MID->UMaterialInstanceDynamic::SetScalarParameterValue(FName(TEXT("BlendMode")), ModeValue);
|
|
}
|
|
|
|
void AWaterBrushManager::DisplacementSettings(const FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
check(::IsValid(BrushActorRenderContext.MID));
|
|
|
|
const FWaterBodyHeightmapSettings& HeightmapSettings = BrushActorRenderContext.WaterBrushActor->GetWaterHeightmapSettings();
|
|
const FWaterBrushEffectDisplacement& Displacement = HeightmapSettings.Effects.Displacement;
|
|
|
|
float BrushTexHeightValue = DisableBrushTextureEffects ? 0.0f : Displacement.DisplacementHeight;
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("BrushTexHeight")), BrushTexHeightValue);
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("T")), Displacement.DisplacementTiling);
|
|
BrushActorRenderContext.MID->SetTextureParameterValue(FName(TEXT("BrushRT")), Displacement.Texture.Get());
|
|
BrushActorRenderContext.MID->SetScalarParameterValue(FName(TEXT("Displacement Midpoint")), Displacement.Midpoint);
|
|
BrushActorRenderContext.MID->SetVectorParameterValue(FName(TEXT("DisplacementChannel")), Displacement.Channel);
|
|
}
|
|
|
|
void AWaterBrushManager::ApplyWeightmapSettings(const FBrushRenderContext& BrushRenderContext, const FBrushActorRenderContext& BrushActorRenderContext, const FWaterBodyWeightmapSettings& WMSettings)
|
|
{
|
|
check(::IsValid(WeightmapMID));
|
|
check(::IsValid(BrushActorRenderContext.CacheContainer));
|
|
|
|
const float GradientWidth = FMath::Max(WMSettings.FalloffWidth, 0.1f);
|
|
WeightmapMID->SetTextureParameterValue(FName(TEXT("CachedDistanceFieldHeight")), BrushActorRenderContext.CacheContainer->Cache.CacheRenderTarget);
|
|
WeightmapMID->SetScalarParameterValue(FName(TEXT("GradientWidth")), GradientWidth);
|
|
|
|
float EdgeOffsetValue = WMSettings.EdgeOffset - (SplineMeshExtension / 2.0f);
|
|
WeightmapMID->SetScalarParameterValue(FName(TEXT("EdgeOffset")), EdgeOffsetValue);
|
|
|
|
WeightmapMID->SetTextureParameterValue(FName(TEXT("BrushRT")), WMSettings.ModulationTexture.Get());
|
|
WeightmapMID->SetScalarParameterValue(FName(TEXT("T")), WMSettings.TextureTiling);
|
|
|
|
float WeightmapInfluenceValue = WMSettings.TextureInfluence * (float)!DisableBrushTextureEffects;
|
|
WeightmapMID->SetScalarParameterValue(FName(TEXT("WeightmapInfluence")), WeightmapInfluenceValue);
|
|
|
|
WeightmapMID->SetScalarParameterValue(FName(TEXT("Displacement Midpoint")), WMSettings.Midpoint);
|
|
WeightmapMID->SetScalarParameterValue(FName(TEXT("Opacity")), WMSettings.FinalOpacity);
|
|
|
|
WeightmapMID->SetTextureParameterValue(FName(TEXT("HeightRT")), WeightPingPongRead(BrushRenderContext));
|
|
}
|
|
|
|
void AWaterBrushManager::SetBrushMIDParams(const FBrushRenderContext& BrushRenderContext, FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
const FWaterBodyHeightmapSettings& HeightmapSettings = BrushActorRenderContext.WaterBrushActor->GetWaterHeightmapSettings();
|
|
JumpFloodComponent2D->UseBlur = HeightmapSettings.Effects.Blurring.bBlurShape;
|
|
JumpFloodComponent2D->BlurPasses = HeightmapSettings.Effects.Blurring.Radius;
|
|
|
|
if (BrushActorRenderContext.TryGetActorAs<AWaterBodyIsland>() != nullptr)
|
|
{
|
|
BrushActorRenderContext.MID = IslandFalloffMID;
|
|
}
|
|
else
|
|
{
|
|
if (HeightmapSettings.FalloffSettings.FalloffMode == EWaterBrushFalloffMode::Angle)
|
|
{
|
|
BrushActorRenderContext.MID = BrushAngleFalloffMID;
|
|
}
|
|
else
|
|
{
|
|
BrushActorRenderContext.MID = BrushWidthFalloffMID;
|
|
}
|
|
}
|
|
|
|
check(::IsValid(BrushActorRenderContext.MID));
|
|
|
|
DistanceFieldCaching(BrushActorRenderContext);
|
|
CurvesSmoothingAndTerracing(BrushActorRenderContext);
|
|
|
|
if (BrushRenderContext.bHeightmapRender)
|
|
{
|
|
BrushActorRenderContext.MID->UMaterialInstanceDynamic::SetTextureParameterValue(FName(TEXT("HeightRT")), HeightPingPongRead(BrushRenderContext));
|
|
BrushActorRenderContext.MID->UMaterialInstanceDynamic::SetTextureParameterValue(FName(TEXT("CombinedAlphaAndHeightRT")), VelocityPingPongRead(BrushRenderContext));
|
|
|
|
FalloffAndBlendMode(BrushActorRenderContext);
|
|
DisplacementSettings(BrushActorRenderContext);
|
|
}
|
|
else
|
|
{
|
|
const FWaterBodyWeightmapSettings* WMSettings = BrushActorRenderContext.WaterBrushActor->GetLayerWeightmapSettings().Find(BrushRenderContext.WeightmapLayerName);
|
|
check(WMSettings != nullptr);
|
|
ApplyWeightmapSettings(BrushRenderContext, BrushActorRenderContext, *WMSettings);
|
|
}
|
|
}
|
|
|
|
|
|
void AWaterBrushManager::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
|
|
FName ChangedProperty = PropertyChangedEvent.GetPropertyName();
|
|
if (ChangedProperty == GET_MEMBER_NAME_CHECKED(AWaterBrushManager, RenderRiverSplineDepthMaterial))
|
|
{
|
|
RiverSplineMIDs.Reset();
|
|
}
|
|
}
|
|
|
|
bool AWaterBrushManager::AllocateRTs()
|
|
{
|
|
bool bSuccess = true;
|
|
HeightmapRTA = FWaterUtils::GetOrCreateTransientRenderTarget2D(HeightmapRTA, TEXT("HeightmapRTA"), LandscapeRTRes, RTF_RGBA8);
|
|
HeightmapRTB = FWaterUtils::GetOrCreateTransientRenderTarget2D(HeightmapRTB, TEXT("HeightmapRTB"), LandscapeRTRes, RTF_RGBA8);
|
|
if ((HeightmapRTA == nullptr) || (HeightmapRTB == nullptr))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid Heightmap Render Target for Water Brush. Aborting AllocateRTs."));
|
|
bSuccess = false;
|
|
}
|
|
|
|
JumpFloodRTA = FWaterUtils::GetOrCreateTransientRenderTarget2D(JumpFloodRTA, TEXT("JumpFloodRTA"), LandscapeRTRes, RTF_RGBA32f);
|
|
JumpFloodRTB = FWaterUtils::GetOrCreateTransientRenderTarget2D(JumpFloodRTB, TEXT("JumpFloodRTB"), LandscapeRTRes, RTF_RGBA32f);
|
|
if ((JumpFloodRTA != nullptr) && (JumpFloodRTB != nullptr))
|
|
{
|
|
JumpFloodRTA->AddressX = TextureAddress::TA_Clamp;
|
|
JumpFloodRTA->AddressY = TextureAddress::TA_Clamp;
|
|
|
|
JumpFloodRTB->AddressX = TextureAddress::TA_Clamp;
|
|
JumpFloodRTB->AddressY = TextureAddress::TA_Clamp;
|
|
|
|
JumpFloodComponent2D->AssignRenderTargets(JumpFloodRTA, JumpFloodRTB);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid JumpFlood Render Target for Water Brush. Aborting AllocateRTs."));
|
|
bSuccess = false;
|
|
}
|
|
|
|
DepthAndShapeRT = FWaterUtils::GetOrCreateTransientRenderTarget2D(DepthAndShapeRT, TEXT("DepthAndShapeRT"), LandscapeRTRes, RTF_RG32f);
|
|
if (DepthAndShapeRT != nullptr)
|
|
{
|
|
SceneCaptureComponent2D->TextureTarget = DepthAndShapeRT;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid DepthAndShape Render Target for Water Brush. Aborting AllocateRTs."));
|
|
bSuccess = false;
|
|
}
|
|
|
|
WaterDepthAndVelocityRT = FWaterUtils::GetOrCreateTransientRenderTarget2D(WaterDepthAndVelocityRT, TEXT("WaterDepthAndVelocityRT"), LandscapeRTRes, RTF_RGBA32f);
|
|
if (WaterDepthAndVelocityRT == nullptr)
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid WaterDepthAndVelocity Render Target for Water Brush. Aborting AllocateRTs."));
|
|
bSuccess = false;
|
|
}
|
|
|
|
WeightmapRTA = FWaterUtils::GetOrCreateTransientRenderTarget2D(WeightmapRTA, TEXT("WeightmapRTA"), LandscapeRTRes, RTF_R8);
|
|
WeightmapRTB = FWaterUtils::GetOrCreateTransientRenderTarget2D(WeightmapRTB, TEXT("WeightmapRTB"), LandscapeRTRes, RTF_R8);
|
|
if ((WeightmapRTA == nullptr) || (WeightmapRTB == nullptr))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid Weightmap Render Target for Water Brush. Aborting AllocateRTs."));
|
|
bSuccess = false;
|
|
}
|
|
|
|
CombinedVelocityAndHeightRTA = FWaterUtils::GetOrCreateTransientRenderTarget2D(CombinedVelocityAndHeightRTA, TEXT("CombinedVelocityAndHeightRTA"), LandscapeRTRes, RTF_RGBA16f);
|
|
CombinedVelocityAndHeightRTB = FWaterUtils::GetOrCreateTransientRenderTarget2D(CombinedVelocityAndHeightRTB, TEXT("CombinedVelocityAndHeightRTB"), LandscapeRTRes, RTF_RGBA16f);
|
|
if ((CombinedVelocityAndHeightRTA == nullptr) || (CombinedVelocityAndHeightRTB == nullptr))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid CombinedVelocityAndHeight Render Target for Water Brush. Aborting AllocateRTs."));
|
|
bSuccess = false;
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
void AWaterBrushManager::OnConstruction(const FTransform& Transform)
|
|
{
|
|
Super::OnConstruction(Transform);
|
|
SetMPCParams();
|
|
}
|
|
|
|
void AWaterBrushManager::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
SetMPCParams();
|
|
}
|
|
|
|
void AWaterBrushManager::ComputeWaterLandscapeInfo(FVector& OutRTWorldLocation, FVector& OutRTWorldSizeVector) const
|
|
{
|
|
FVector LandscapeScale = LandscapeTransform.GetScale3D();
|
|
OutRTWorldSizeVector = FVector(LandscapeRTRes) * LandscapeScale;
|
|
OutRTWorldSizeVector.Z = 1.0f;
|
|
OutRTWorldLocation = LandscapeTransform.GetLocation();
|
|
OutRTWorldLocation -= FVector(LandscapeScale.X, LandscapeScale.Y, 0.0f) * 0.5f;
|
|
}
|
|
|
|
bool AWaterBrushManager::DeprecateWaterLandscapeInfo(FVector& OutRTWorldLocation, FVector& OutRTWorldSizeVector)
|
|
{
|
|
#if WITH_EDITOR
|
|
if (ALandscape* Landscape = GetOwningLandscape())
|
|
{
|
|
FIntPoint LandscapeSize;
|
|
if (Landscape->ComputeLandscapeLayerBrushInfo(LandscapeTransform, LandscapeSize, LandscapeRTRes))
|
|
{
|
|
ComputeWaterLandscapeInfo(OutRTWorldLocation, OutRTWorldSizeVector);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
#endif // WITH_EDITOR
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
void AWaterBrushManager::ShowForceUpdateMapCheckError()
|
|
{
|
|
FFormatNamedArguments Arguments;
|
|
Arguments.Add(TEXT("WaterBrush"), FText::FromString(GetActorNameOrLabel()));
|
|
Arguments.Add(TEXT("Outer"), FText::FromString(GetPackage()->GetPathName()));
|
|
|
|
FMessageLog("MapCheck").Warning()
|
|
->AddToken(FUObjectToken::Create(this))
|
|
->AddToken(FTextToken::Create(FText::Format(LOCTEXT("AWaterBrushManager_ShowForceUpdateMapCheckError_Message_ForceUpdateNeeded", "Water brush {WaterBrush} in package {Outer} is out of date and needs updating for water to render properly."), Arguments)))
|
|
->AddToken(FMapErrorToken::Create(TEXT("AWaterBrushManager_ShowForceUpdateMapCheckError_MapError_ForceUpdateNeeded")))
|
|
->AddToken(FActionToken::Create(LOCTEXT("AWaterBrushManager_ShowForceUpdateMapCheckError_ActionName_ForceUpdateNeeded", "Update water brush"), FText(),
|
|
FOnActionTokenExecuted::CreateUObject(this, &AWaterBrushManager::ForceUpdate), true));
|
|
}
|
|
|
|
#endif // WITH_EDITOR
|
|
|
|
void AWaterBrushManager::SetMPCParams()
|
|
{
|
|
UWorld* World = GetWorld();
|
|
UWaterSubsystem* WaterSubsystem = UWaterSubsystem::GetWaterSubsystem(World);
|
|
if ((World != nullptr) && (WaterSubsystem != nullptr))
|
|
{
|
|
FVector RTWorldLocation, RTWorldSizeVector;
|
|
ComputeWaterLandscapeInfo(RTWorldLocation, RTWorldSizeVector);
|
|
|
|
if (GEditor == nullptr)
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("GEditor is null for AWaterBrushManager::SetMPCParams(): %s"), *GetFullName());
|
|
return;
|
|
}
|
|
|
|
UWaterEditorSubsystem* WaterEditorSubsystem = GEditor->GetEditorSubsystem<UWaterEditorSubsystem>();
|
|
check(WaterEditorSubsystem);
|
|
UMaterialParameterCollection* LandscapeCollection = WaterEditorSubsystem->GetLandscapeMaterialParameterCollection();
|
|
if (LandscapeCollection == nullptr)
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("No Landscape MaterialParameterCollection Assigned"));
|
|
return;
|
|
}
|
|
|
|
UMaterialParameterCollectionInstance* LandscapeCollectionInstance = World->GetParameterCollectionInstance(CastChecked<UMaterialParameterCollection>(LandscapeCollection));
|
|
check(LandscapeCollectionInstance != nullptr);
|
|
|
|
if (!LandscapeCollectionInstance->SetScalarParameterValue(FName(TEXT("RTResX")), (float)LandscapeRTRes.X))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"RTResX\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
|
|
if (!LandscapeCollectionInstance->SetScalarParameterValue(FName(TEXT("RTResY")), (float)LandscapeRTRes.Y))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"RTResY\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
|
|
if (!LandscapeCollectionInstance->SetScalarParameterValue(FName(TEXT("LSQuadsX")), (float)LandscapeQuads.X))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"LSQuadsX\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
if (!LandscapeCollectionInstance->SetScalarParameterValue(FName(TEXT("LSQuadsY")), (float)LandscapeQuads.Y))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"LSQuadsY\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
|
|
if (!LandscapeCollectionInstance->SetScalarParameterValue(FName(TEXT("WorldSizeX")), WorldSize.X))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"WorldSizeX\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
if (!LandscapeCollectionInstance->SetScalarParameterValue(FName(TEXT("WorldSizeY")), WorldSize.Y))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"WorldSizeY\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
|
|
if (!LandscapeCollectionInstance->SetVectorParameterValue(FName(TEXT("LandscapeLocation")), FLinearColor(LandscapeTransform.GetLocation())))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"LandscapeLocation\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
if (!LandscapeCollectionInstance->SetScalarParameterValue(FName(TEXT("LandscapeZLocation")), LandscapeTransform.GetLocation().Z))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"LandscapeZLocation\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
// TODO [jonathan.bard] : find out what this 128.0f corresponds to and put in a constant : ZSCALE in LandscapeLayersPS.usf maybe ??
|
|
if (!LandscapeCollectionInstance->SetScalarParameterValue(FName(TEXT("LandscapeZScale")), LandscapeTransform.GetScale3D().Z / 128.0f))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"LandscapeZScale\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
if (!LandscapeCollectionInstance->SetVectorParameterValue(FName(TEXT("RTWorldSize")), FLinearColor(RTWorldSizeVector)))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"RTWorldSize\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
if (!LandscapeCollectionInstance->SetVectorParameterValue(FName(TEXT("RTWorldLocation")), FLinearColor(RTWorldLocation)))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"RTWorldLocation\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
if (!LandscapeCollectionInstance->SetScalarParameterValue(FName(TEXT("WaterClearHeight")), WaterClearHeight))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Failed to set \"WaterClearHeight\" on Landscape MaterialParameterCollection"));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void AWaterBrushManager::ApplyToCompositeWaterBodyTexture(FBrushRenderContext& BrushRenderContext, const FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
AWaterBody* WaterBody = BrushActorRenderContext.TryGetActorAs<AWaterBody>();
|
|
if (BrushRenderContext.bHeightmapRender && (WaterBody != nullptr))
|
|
{
|
|
check(::IsValid(BrushActorRenderContext.CacheContainer));
|
|
const FWaterBodyHeightmapSettings& HeightmapSettings = BrushActorRenderContext.WaterBrushActor->GetWaterHeightmapSettings();
|
|
|
|
CompositeWaterBodyTextureMID->SetTextureParameterValue(FName(TEXT("CachedDistanceFieldHeight")), BrushActorRenderContext.CacheContainer->Cache.CacheRenderTarget);
|
|
CompositeWaterBodyTextureMID->SetTextureParameterValue(FName(TEXT("CombinedVelocityAndHeight")), VelocityPingPongRead(BrushRenderContext));
|
|
CompositeWaterBodyTextureMID->SetTextureParameterValue(FName(TEXT("LandscapeHeight")), HeightPingPongRead(BrushRenderContext));
|
|
CompositeWaterBodyTextureMID->SetScalarParameterValue(FName(TEXT("ZOffset")), HeightmapSettings.FalloffSettings.ZOffset);
|
|
CompositeWaterBodyTextureMID->SetScalarParameterValue(FName(TEXT("Shape Dilation")), WaterBody->GetWaterBodyComponent()->ShapeDilation);
|
|
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Rendering Water Body Velocity/Height to Combined Texture: %s"), *UKismetSystemLibrary::GetDisplayName(VelocityPingPongWrite(BrushRenderContext)));
|
|
|
|
UKismetRenderingLibrary::ClearRenderTarget2D(this, VelocityPingPongWrite(BrushRenderContext), FLinearColor::Black);
|
|
UKismetRenderingLibrary::DrawMaterialToRenderTarget(this, VelocityPingPongWrite(BrushRenderContext), CompositeWaterBodyTextureMID);
|
|
|
|
++BrushRenderContext.VelocityRTIndex;
|
|
}
|
|
}
|
|
|
|
void AWaterBrushManager::RenderBrushActorContext(FBrushRenderContext& BrushRenderContext, FBrushActorRenderContext& BrushActorRenderContext)
|
|
{
|
|
if (!BrushRenderContext.bHeightmapRender)
|
|
{
|
|
if (!(BrushActorRenderContext.WaterBrushActor->GetLayerWeightmapSettings().Find(BrushRenderContext.WeightmapLayerName)))
|
|
{
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Actor does NOT affect this layer, Skipping"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
UWaterBodyBrushCacheContainer* CacheContainer = nullptr;
|
|
FWaterBodyBrushCache WaterBrushCache;
|
|
GetWaterCacheKey(BrushActorRenderContext.GetActor(), /*out*/ CacheContainer, /*out*/ WaterBrushCache);
|
|
BrushActorRenderContext.CacheContainer = CacheContainer;
|
|
|
|
SetBrushMIDParams(BrushRenderContext, BrushActorRenderContext);
|
|
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("===================================="));
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Current Actor: %s"), *UKismetSystemLibrary::GetDisplayName(BrushActorRenderContext.WaterBrushActor.GetObject()));
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Type: %s"), *BrushActorRenderContext.WaterBrushActor.GetObject()->GetClass()->GetName());
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Cache is Valid: %s"), (BrushActorRenderContext.CacheContainer->Cache.CacheIsValid ? TEXT("true") : TEXT("false")));
|
|
|
|
if (BrushActorRenderContext.CacheContainer->Cache.CacheIsValid)
|
|
{
|
|
if (bKillCache)
|
|
{
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Kill Cache Detected, running full render pass for Brush"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UseDynamicPreviewRT = true;
|
|
}
|
|
|
|
AWaterBody* WaterBody = BrushActorRenderContext.TryGetActorAs<AWaterBody>();
|
|
if (!BrushActorRenderContext.CacheContainer->Cache.CacheIsValid || bKillCache)
|
|
{
|
|
const FWaterBodyHeightmapSettings& HeightmapSettings = BrushActorRenderContext.WaterBrushActor->GetWaterHeightmapSettings();
|
|
const FWaterBrushEffectCurlNoise& CurlNoise = HeightmapSettings.Effects.CurlNoise;
|
|
FLinearColor CurlColor(CurlNoise.Curl1Tiling, CurlNoise.Curl1Amount, CurlNoise.Curl2Tiling, CurlNoise.Curl2Amount);
|
|
if ((WaterBody != nullptr) && (WaterBody->GetWaterBodyType() == EWaterBodyType::River))
|
|
{
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("River depth and verlocity render"));
|
|
CaptureRiverDepthAndVelocity(BrushActorRenderContext);
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Jump flood"));
|
|
JumpFloodComponent2D->JumpFlood(DepthAndShapeRT, 50000.0f, CurlColor, true, 0.0f);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Canvas shape render"));
|
|
DrawCanvasShape(BrushActorRenderContext);
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Jump flood"));
|
|
JumpFloodComponent2D->JumpFlood(DepthAndShapeRT, 50000.0f, CurlColor, false, BrushActorRenderContext.GetActor()->GetActorLocation().Z);
|
|
}
|
|
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Distance Field generation"));
|
|
CacheBrushDistanceField(BrushActorRenderContext);
|
|
}
|
|
|
|
DrawBrushMaterial(BrushRenderContext, BrushActorRenderContext);
|
|
// TODO [jonathan.bard] : this part is weird in the BP :
|
|
if (BrushRenderContext.bHeightmapRender)
|
|
{
|
|
// TODO [jonathan.bard] : along with MakePreviewRT?
|
|
//++HeightOnlyIndex;
|
|
}
|
|
++BrushRenderContext.RTIndex;
|
|
|
|
ApplyToCompositeWaterBodyTexture(BrushRenderContext, BrushActorRenderContext);
|
|
}
|
|
|
|
bool AWaterBrushManager::CreateMIDs()
|
|
{
|
|
BrushAngleFalloffMID = FWaterUtils::GetOrCreateTransientMID(BrushAngleFalloffMID, TEXT("BrushAngleFalloffMID"), BrushAngleFalloffMaterial);
|
|
IslandFalloffMID = FWaterUtils::GetOrCreateTransientMID(IslandFalloffMID, TEXT("IslandFalloffMID"), IslandFalloffMaterial);
|
|
BrushWidthFalloffMID = FWaterUtils::GetOrCreateTransientMID(BrushWidthFalloffMID, TEXT("BrushWidthFalloffMID"), BrushWidthFalloffMaterial);
|
|
WeightmapMID = FWaterUtils::GetOrCreateTransientMID(WeightmapMID, TEXT("WeightmapMID"), WeightmapMaterial);
|
|
DistanceFieldCacheMID = FWaterUtils::GetOrCreateTransientMID(DistanceFieldCacheMID, TEXT("DistanceFieldCacheMID"), DistanceFieldCacheMaterial);
|
|
CompositeWaterBodyTextureMID = FWaterUtils::GetOrCreateTransientMID(CompositeWaterBodyTextureMID, TEXT("CompositeWaterBodyTextureMID"), CompositeWaterBodyTextureMaterial);
|
|
DrawCanvasMID = FWaterUtils::GetOrCreateTransientMID(DrawCanvasMID, TEXT("DrawCanvasMID"), DrawCanvasMaterial);
|
|
|
|
if ((BrushAngleFalloffMID == nullptr)
|
|
|| (IslandFalloffMID == nullptr)
|
|
|| (BrushWidthFalloffMID == nullptr)
|
|
|| (WeightmapMID == nullptr)
|
|
|| (DistanceFieldCacheMID == nullptr)
|
|
|| (CompositeWaterBodyTextureMID == nullptr)
|
|
|| (DrawCanvasMID == nullptr))
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid water brush materials."));
|
|
return false;
|
|
}
|
|
|
|
return JumpFloodComponent2D->CreateMIDs();
|
|
}
|
|
|
|
void AWaterBrushManager::SortWaterBodiesForBrushRender_Implementation(TArray<AWaterBody*>& InOutWaterBodies) const
|
|
{
|
|
InOutWaterBodies.Sort([](const AWaterBody& LHS, const AWaterBody& RHS)
|
|
{
|
|
// Render the Ocean first, then the others, so as not to stomp the latter with the former
|
|
static constexpr int32 WaterBrushPriorityByType[] =
|
|
{
|
|
2, // EWaterBodyType::River
|
|
1, // EWaterBodyType::Lake
|
|
0, // EWaterBodyType::Ocean
|
|
3, // EWaterBodyType::Transition
|
|
};
|
|
static_assert(sizeof(WaterBrushPriorityByType) / sizeof(WaterBrushPriorityByType[0]) == (size_t)EWaterBodyType::Num, "Invalid priority count");
|
|
|
|
int32 LHSPriority = WaterBrushPriorityByType[(int32)LHS.GetWaterBodyType()];
|
|
int32 RHSPriority = WaterBrushPriorityByType[(int32)RHS.GetWaterBodyType()];
|
|
if (LHSPriority == RHSPriority)
|
|
{
|
|
// Return a deterministic order based on their full names :
|
|
return LHS.GetFullName() < RHS.GetFullName();
|
|
}
|
|
return LHSPriority < RHSPriority;
|
|
});
|
|
}
|
|
|
|
void AWaterBrushManager::SetupDefaultMaterials()
|
|
{
|
|
const UWaterEditorSettings* WaterEditorSettings = GetDefault<UWaterEditorSettings>();
|
|
check(WaterEditorSettings != nullptr);
|
|
|
|
BrushAngleFalloffMaterial = WaterEditorSettings->GetDefaultBrushAngleFalloffMaterial();
|
|
BrushWidthFalloffMaterial = WaterEditorSettings->GetDefaultBrushWidthFalloffMaterial();
|
|
DistanceFieldCacheMaterial = WaterEditorSettings->GetDefaultCacheDistanceFieldCacheMaterial();
|
|
RenderRiverSplineDepthMaterial = WaterEditorSettings->GetDefaultRenderRiverSplineDepthsMaterial();
|
|
WeightmapMaterial = WaterEditorSettings->GetDefaultBrushWeightmapMaterial();
|
|
DrawCanvasMaterial = WaterEditorSettings->GetDefaultDrawCanvasMaterial();
|
|
CompositeWaterBodyTextureMaterial = WaterEditorSettings->GetDefaultCompositeWaterBodyTextureMaterial();
|
|
IslandFalloffMaterial = WaterEditorSettings->GetDefaultBrushIslandFalloffMaterial();
|
|
|
|
JumpStepMaterial = WaterEditorSettings->GetDefaultJumpFloodStepMaterial();
|
|
BlurEdgesMaterial = WaterEditorSettings->GetDefaultBlurEdgesMaterial();
|
|
FindEdgesMaterial = WaterEditorSettings->GetDefaultFindEdgesMaterial();
|
|
}
|
|
|
|
UTextureRenderTarget2D* AWaterBrushManager::RenderLayer_Native(const FLandscapeBrushParameters& InParameters)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(AWaterBrushManager::RenderLayer_Native);
|
|
|
|
LandscapeRTRef = InParameters.CombinedResult;
|
|
|
|
FBrushRenderContext BrushRenderContext;
|
|
BrushRenderContext.bHeightmapRender = InParameters.LayerType == ELandscapeToolTargetType::Heightmap;
|
|
BrushRenderContext.WeightmapLayerName = InParameters.WeightmapLayerName;
|
|
|
|
if (!BrushRenderSetup())
|
|
{
|
|
UE_LOG(LogWaterEditor, Error, TEXT("Invalid setup for water brush. Aborting Render."));
|
|
return nullptr;
|
|
}
|
|
|
|
if (BrushRenderContext.bHeightmapRender)
|
|
{
|
|
const FLinearColor ClearColor(0.000000, 0.000000, WaterClearHeight, 1.000000);
|
|
UKismetRenderingLibrary::ClearRenderTarget2D(this, CombinedVelocityAndHeightRTA, ClearColor);
|
|
UKismetRenderingLibrary::ClearRenderTarget2D(this, CombinedVelocityAndHeightRTB, ClearColor);
|
|
}
|
|
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("===================================="));
|
|
|
|
if (BrushRenderContext.bHeightmapRender)
|
|
{
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("BrushManager: Heightmap Render Pass"));
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogWaterEditor, Verbose, TEXT("Brush Manager Render: Weightmap Render Pass: %s"), *BrushRenderContext.WeightmapLayerName.ToString());
|
|
}
|
|
|
|
TArray<AWaterBody*> WaterBodies;
|
|
AWaterLandscapeBrush::GetWaterBodies(AWaterBody::StaticClass(), WaterBodies);
|
|
|
|
SortWaterBodiesForBrushRender(WaterBodies);
|
|
|
|
for (AWaterBody* WaterBody : WaterBodies)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(RenderWaterBody);
|
|
FBrushActorRenderContext BrushActorRenderContext(WaterBody);
|
|
RenderBrushActorContext(BrushRenderContext, BrushActorRenderContext);
|
|
}
|
|
|
|
// Inner Loop for Islands
|
|
TArray<AWaterBodyIsland*> WaterbodyIslands;
|
|
GetWaterBodyIslands(AWaterBodyIsland::StaticClass(), WaterbodyIslands);
|
|
|
|
for (AWaterBodyIsland* WaterBodyIsland : WaterbodyIslands)
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(RenderWaterIsland);
|
|
FBrushActorRenderContext BrushActorRenderContext(WaterBodyIsland);
|
|
RenderBrushActorContext(BrushRenderContext, BrushActorRenderContext);
|
|
}
|
|
|
|
// Render Completed
|
|
UTextureRenderTarget2D* ReturnRT = (InParameters.LayerType == ELandscapeToolTargetType::Heightmap) ? HeightPingPongRead(BrushRenderContext) : WeightPingPongRead(BrushRenderContext);
|
|
|
|
bKillCache = false;
|
|
|
|
bNeedsForceUpdate = false;
|
|
|
|
return ReturnRT;
|
|
}
|
|
|
|
void AWaterBrushManager::UpdateBrushCacheKeys()
|
|
{
|
|
for (TWeakInterfacePtr<IWaterBrushActorInterface> BrushActor : GetActorsAffectingLandscape())
|
|
{
|
|
if (BrushActor.IsValid())
|
|
{
|
|
ETextureRenderTargetFormat Format = BrushActor->GetBrushRenderTargetFormat();
|
|
|
|
AActor* Actor = CastChecked<AActor>(BrushActor.GetObject());
|
|
UWaterBodyBrushCacheContainer* WaterBrushCacheContainer = Cast<UWaterBodyBrushCacheContainer>(GetActorCache(Actor, UWaterBodyBrushCacheContainer::StaticClass()));
|
|
// If no cache entry or the cache entry is invalid, create one and associate it to the actor :
|
|
if (!::IsValid(WaterBrushCacheContainer))
|
|
{
|
|
WaterBrushCacheContainer = NewObject<UWaterBodyBrushCacheContainer>(this, NAME_None, RF_Transactional);
|
|
check(!WaterBrushCacheContainer->Cache.CacheIsValid);
|
|
SetActorCache(Actor, WaterBrushCacheContainer);
|
|
}
|
|
// Make sure there's an appropriate render target in that cache :
|
|
WaterBrushCacheContainer->Cache.CacheRenderTarget = FWaterUtils::GetOrCreateTransientRenderTarget2D(WaterBrushCacheContainer->Cache.CacheRenderTarget, FName(*FString::Printf(TEXT("BrushCacheRT_%s"), *Actor->GetActorNameOrLabel())), LandscapeRTRes, Format);
|
|
check(WaterBrushCacheContainer->Cache.CacheRenderTarget != nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
void AWaterBrushManager::CheckForErrors()
|
|
{
|
|
Super::CheckForErrors();
|
|
|
|
// If a force update action was requested but hasn't been performed yet, display the message again :
|
|
if (bNeedsForceUpdate)
|
|
{
|
|
ShowForceUpdateMapCheckError();
|
|
}
|
|
}
|
|
|
|
#endif // WITH_EDITOR
|
|
|
|
#undef LOCTEXT_NAMESPACE
|
|
|