Hello im porting FluidSurfaceComponent (from UE3) to UE5, but does not render anything except debug, help me to fix it.

Hello im doing a project plugin for a game where im porting UE3 code from FluidSurfaceComponent to UE5, but i have an couple of weeks to find a fix why my fluid surface doesn’t render anything.
I am doing with a custom Unreal Engine of 5.5.4, but does not contain another code from my custom engine.
I will share the project, and some code from rendering, for find the error and fixing it.
Screenshot:

Code:
FluidSurfaceComponent.h:

// By penguin21 and backported from UE3

#pragma once

#include "CoreMinimal.h"
#include "Components/PrimitiveComponent.h"
#include "FluidInfluenceComponent.generated.h"

class AFluidSurfaceActor;

UENUM(BlueprintType)
enum class EInfluenceType : uint8
{
	Fluid_Flow,
	Fluid_Raindrops,
	Fluid_Wave,
	Fluid_Sphere,
};

UCLASS(ClassGroup=(Fluid), meta=(BlueprintSpawnableComponent), HideCategories=(Object,Collision,Lighting,Physics,PrimitiveComponent,Rendering), EditInlineNew, AutoExpandCategories=(FluidInfluenceComponent))
class FLUIDSURFACE_API UFluidInfluenceComponent : public UPrimitiveComponent
{
	GENERATED_UCLASS_BODY()
	/** If a specific FluidSurfaceActor is set, this influence won't automatically affect any other fluid and MaxDistance is ignored. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<AFluidSurfaceActor> FluidActor;
	/** Type of fluid influence (a flow of waves, raindrops, or a single wave). */
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	EInfluenceType InfluenceType;
	/** Maximum distance (from the fluid plane) from where this influence will affect a fluid. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float MaxDistance;

	/** Strength of the influencing force. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidWave)
	float WaveStrength;
	/** Wave frequency (can be 0 for a standing wave). */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidWave, meta=(ClampMin="0"))
	float WaveFrequency;
	/** Angular phase, in 0-360 degrees. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidWave)
	float WavePhase;
	/** Radius of the wave, in world space units. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidWave)
	float WaveRadius;

	/** Whether raindrops should fill the entire fluid (TRUE), or just in a circular area around the influenceactor (FALSE). */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidRaindrops)
	uint8 RaindropFillEntireFluid:1;
	/** Radius of the area where raindrops fall. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidRaindrops)
	float RaindropAreaRadius;
	/** Radius of each raindrop, in world space units. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidRaindrops, meta=(ClampMin="0.0001"))
	float RaindropRadius;
	/** Radius of each raindrop, in world space units. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidRaindrops)
	float RaindropStrength;
	/** Number of raindrops per second. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidRaindrops, meta=(ClampMin="0.0001"))
	float RaindropRate;

	/** How fast the flow moves thru the fluid, in world space units per second. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidFlow)
	float FlowSpeed;
	/** Number of flow ripples generated on the fluid surface. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidFlow, meta=(ClampMin=1))
	int32 FlowNumRipples;
	/** How much each flow ripple should oscillate sideways while moving down that flow direction. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidFlow)
	float FlowSideMotionRadius;
	/** Radius of each flow wave, in world space units. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidFlow)
	float FlowWaveRadius;
	/** Strength of each wave ripple. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidFlow)
	float FlowStrength;
	/** Frequency of up/down and sideways motion of each ripple. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidFlow)
	float FlowFrequency;

	/** Outer radius of the 3D sphere. While inside this radius, the force will increase as it moves closer to the fluid plane. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidSphere, meta=(ClampMin="0.0001"))
	float SphereOuterRadius;
	/** Inner radius of the 3D sphere. While inside this radius, the force will decrease as it moves closer to the fluid plane. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidSphere, meta=(ClampMin="0.0001"))
	float SphereInnerRadius;
	/** Strength of the force applied by the sphere. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Interp, Category=FluidSphere)
	float SphereStrength;

	/** The "toggle" Kismet event will set this to true, which will enable/disable the influence for 1 tick, then automatically go back to its previous state. */
	UPROPERTY(Transient)
	uint8 bIsToggleTriggered:1;
private:
	UPROPERTY(Transient)
	float CurrentAngle;
	UPROPERTY(Transient)
	float CurrentTimer;

	/** The currently affected FluidSurfaceActor. */
	UPROPERTY(Transient)
	TObjectPtr<AFluidSurfaceActor> CurrentFluidActor;
public:
	// ~ Begin UObject Interface
	virtual void PostLoad() override;
#if WITH_EDITOR
	virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
	// ~ End UObject Interface
	
	// ~ Begin UPrimitiveComponent Interface
	virtual void UpdateBounds() override;
#endif
	// ~ End UPrimitiveComponent Interface
	
	// ~ Begin ActorComponent Interface
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
	// ~ End ActorComponent Interface
private:
	void UpdateFlow(float DeltaSeconds);
	void UpdateRaindrops(float DeltaSeconds);
	void UpdateWave(float DeltaSeconds);
	void UpdateSphere(float DeltaSeconds);
	void CheckSettings(bool bUpdateIcon);
	bool IsTouching(AFluidSurfaceActor* Fluid) const;
};

FluidSurfaceComponent.cpp:

// Game not affilated with Gears for Breakfast, Aps.

#include "FluidSurfaceComponent.h"
#include "MaterialDomain.h"
#include "Engine/LocalPlayer.h"
#include "FluidSurface.h"
#include "FluidSurfaceManager.h"
#include "FluidSurfaceModule.h"
#include "FluidSurfaceSubsystem.h"
#include "PhysicsEngine/BodySetup.h"

DECLARE_CYCLE_STAT(TEXT("FluidSurfaceComp Tick"), STAT_FluidSurfaceComponentTickTime, STATGROUP_Fluids);

/** When this is TRUE, all fluids will be forced to be deactivated. */
static TAutoConsoleVariable<int32> GForceFluidDeactivation(
	TEXT("r.FluidSurface.Disabled"),
	0,
	TEXT("<=0: Shows Fluid Surface")
	TEXT(">=1: Dsiables Fluid Surface"),
	ECVF_Cheat
	);

// How many seconds until a fluid can deactivate, given the right conditions.
#define DEACTIVATION_TIME	3.0f
#define MaxFluidNumVerts	1048576

// Sets default values for this component's properties
UFluidSurfaceComponent::UFluidSurfaceComponent(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, NoSimulationForLowDetailMode(0)
	, FarFluidMaterialForLowDetailMode(0)
	, TargetSimulation(nullptr)
	, TargetDetail(nullptr)
	, bTestRippleCenterOnDetail(false)
	, SimulationPosition(FVector::ZeroVector)
	, DetailPosition(FVector::ZeroVector)
	, FluidSimulation(nullptr)
{
	PrimaryComponentTick.bCanEverTick = true;
	PrimaryComponentTick.bAllowTickOnDedicatedServer = false; // why we need render water in server side?
	bAutoActivate = true;

	FarMaterialDist = 3000;
	BoundsExtension = 0.0;
	LightMapResolution = 128;
	EnableSimulation = true;
	EnableDetail = true;
	DeactivationDistance = 3000.0;
	FluidUpdateRate = 30.0;
	DetailUpdateRate = 30.0;
	bTiling = false;
	FluidHeightScale = 1.0;
	DetailHeightScale = 1.0;
	bPause = false;
	SimulationQuadsX = 200;
	SimulationQuadsY = 200;
	GridSpacing = 10.0;
	GridSpacingLowRes = 800.0;
	DetailResolution = 256;
	DetailSize = 500;
	FluidDamping = 1.0;
	FluidTravelSpeed = 1.0;
	DetailDamping = 1.0;
	DetailTravelSpeed = 1.0;
	DetailTransfer = 0.5;
	bTestRipple = false;
	TestRippleSpeed = 1;
	TestRippleRadius = 30;
	TestRippleFrequency = 1.0;
	bShowFluidDetail = true;
	bShowFluidSimulation = true;
	bShowDetailNormals = false;
	bShowDetailPosition = false;
	bShowSimulationNormals = false;
	bShowSimulationPosition = false;
	NormalLength = 10.0;
	LightingContrast = 1.0;
	GPUTessellationFactor = 1.0;

	ForceImpact = -3.0;
	ForceContinuous = -200.0;

	FluidSize.X = 2000.0;
	FluidSize.Y = 2000.0;
	TestRippleTime = 0.0;
	TestRippleAngle = 0.0;
	DeactivationTimer = 10.0;
	ViewDistance = 0.0;

	bTickInEditor = true;
	CastShadow = false;
}

void UFluidSurfaceComponent::ApplyForce(FVector WorldPos, float Strength, float WorldRadius, bool bImpulse)
{
	if ( FluidSimulation.IsValid() )
	{
		struct FParameters
		{
			FParameters(const FVector& InLocalPos, float InStrength, float InLocalRadius, bool bInImpulse)
				: LocalPos(InLocalPos), Strength(InStrength), LocalRadius(InLocalRadius), bImpulse(bInImpulse) {}
			FVector LocalPos;
			float Strength;
			float LocalRadius;
			bool bImpulse;
		};

		const FVector DrawScale3D	= GetComponentScale();
		const float AvgScale		= (DrawScale3D.X + DrawScale3D.Y + DrawScale3D.Z) / 3.0f;
		const float LocalRadius	= WorldRadius / AvgScale;
		const FVector LocalPos	= FluidSimulation->GetWorldToLocal().TransformPosition(WorldPos);

		if ( (EnableSimulation && FluidSimulation.Get()->IsWithinSimulationGrid( LocalPos, LocalRadius )) ||
			 (EnableDetail && FluidSimulation.Get()->IsWithinDetailGrid( LocalPos, LocalRadius )) )
		{
			// Check to see if we should enable full CPU/GPU simulation.
			if ( FluidSimulation.Get()->IsActive() == false && !IsFluidRenderDisabled() )
			{
				const float Distance = ViewDistance;
				if ( Distance < DeactivationDistance )
				{
					{
						MarkRenderStateDirty();
						InitResources( true );
					}
					
					SetDetailPosition( DetailPosition );
					SetSimulationPosition( SimulationPosition );
				}
				else
				{
					return;
				}
			}
			FParameters Parameters(LocalPos,Strength,LocalRadius,bImpulse);
			ENQUEUE_RENDER_COMMAND(ApplyForceCommand)([this, Parameters](FRHICommandListImmediate&)
			{
				if(FluidSimulation.IsValid())
					FluidSimulation.Get()->AddForce( Parameters.LocalPos, Parameters.Strength, Parameters.LocalRadius, Parameters.bImpulse );
			});
		}
	}
}

void UFluidSurfaceComponent::SetDetailPosition(FVector InWorldPos)
{
	DetailPosition = InWorldPos;
	if ( FluidSimulation.IsValid() )
	{
		FVector LocalPos = FluidSimulation->GetWorldToLocal().TransformPosition(InWorldPos);
		ENQUEUE_RENDER_COMMAND(SetDetailPositionCommand)(
		[this, LocalPos](FRHICommandListImmediate&)
		{
			if(FluidSimulation.IsValid())
				FluidSimulation.Get()->SetDetailPosition( LocalPos );
		});
	}
}

void UFluidSurfaceComponent::SetSimulationPosition(FVector InWorldPos)
{
	SimulationPosition = InWorldPos;
	if ( FluidSimulation.IsValid() )
	{
		FVector LocalPos = FluidSimulation->GetWorldToLocal().TransformPosition(InWorldPos);
		ENQUEUE_RENDER_COMMAND(SetSimulationPositionCommand)(
		[this, LocalPos](FRHICommandListImmediate&)
		{
			if(FluidSimulation.IsValid())
				FluidSimulation.Get()->SetDetailPosition( LocalPos );
		});
	}
}

UMaterialInterface* UFluidSurfaceComponent::GetMaterialFluid() const
{
	if(FluidMaterial)
		return FluidMaterial;
	return UMaterial::GetDefaultMaterial(EMaterialDomain::MD_Surface);
}

UMaterialInterface* UFluidSurfaceComponent::GetLowResMaterial() const
{
	if(LowResFluidMaterial)
		return LowResFluidMaterial;
	return UMaterial::GetDefaultMaterial(EMaterialDomain::MD_Surface);
}

UMaterialInterface* UFluidSurfaceComponent::GetFarMaterial() const
{
	if(FarFluidMaterial)
		return FarFluidMaterial;
	return UMaterial::GetDefaultMaterial(EMaterialDomain::MD_Surface);
}

FMaterialRelevance UFluidSurfaceComponent::GetMaterialViewRelevance(ERHIFeatureLevel::Type InFeatureLevel) const
{
	FMaterialRelevance Result;
	if(GetMaterialFluid())
	{
		Result |= GetMaterialFluid()->GetRelevance_Concurrent(InFeatureLevel);
	}
	return Result;
}

FMaterialRelevance UFluidSurfaceComponent::GetLowResMaterialViewRelevance(ERHIFeatureLevel::Type InFeatureLevel) const
{
	FMaterialRelevance Result;
	if(GetLowResMaterial())
	{
		Result |= GetLowResMaterial()->GetRelevance_Concurrent(InFeatureLevel);
	}
	return Result;
}

FMaterialRelevance UFluidSurfaceComponent::GetFarMaterialViewRelevance(ERHIFeatureLevel::Type InFeatureLevel) const
{
	FMaterialRelevance Result;
	if(GetFarMaterial())
	{
		Result |= GetFarMaterial()->GetRelevance_Concurrent(InFeatureLevel);
	}
	return Result;
}

const FFluidGPUResource* UFluidSurfaceComponent::GetFluidGPUResource() const
{
	return FluidSimulation.IsValid() ? FluidSimulation.Get()->GetGPUResource() : nullptr;
}

void UFluidSurfaceComponent::InitResources(bool bActive)
{
	if ( IsFluidRenderDisabled() )
	{
		bActive = false;
	}

	if ( FluidSimulation.IsValid() )
	{
		ReleaseResources(true);
	}
#if UE_BUILD_SHIPPING || UE_BUILD_TEST
	// Clear out debug showflags in finalrelease builds.
	bShowDetailNormals		= false;
	bShowDetailPosition		= false;
	bShowSimulationNormals	= false;
	bShowSimulationPosition	= false;
#endif
	const FVector TopLeft		= GetRenderMatrix().TransformPosition( FVector(-FluidSize.X*0.5f, -FluidSize.Y*0.5f, 0.0f) );
	const FVector TopRight		= GetRenderMatrix().TransformPosition( FVector(FluidSize.X*0.5f, -FluidSize.Y*0.5f, 0.0f) );
	const FVector BottomLeft	= GetRenderMatrix().TransformPosition( FVector(-FluidSize.X*0.5f, FluidSize.Y*0.5f, 0.0f) );
	float Width			= (TopLeft - TopRight).Size();
	float Height		= (TopLeft - BottomLeft).Size();

	// During a copy/paste, LocalToWorld scale factor may be zero. :/
	if (FMath::IsNearlyZero(Width) || FMath::IsNearlyZero(Height) )
	{
		Width	= FluidSize.X;
		Height	= FluidSize.Y;
	}

	// Limit GridSpacingLowRes for a maximum of ~64K vertices
	GridSpacingLowRes	= FMath::Max<float>(GridSpacingLowRes, 1.0f);
	int32 NumLowResCellsX = FMath::Max<int32>(FMath::TruncToInt(Width / GridSpacingLowRes), 1);
	int32 NumLowResCellsY = FMath::Max<int32>(FMath::TruncToInt(Height / GridSpacingLowRes), 1);
	const int32 NumLowResVertices = (NumLowResCellsX + 1) * (NumLowResCellsY + 1);
	constexpr int32 MaxVert = 65000;
	if ( NumLowResVertices > MaxVert )
	{
		// Solve: 65530 = (k*NumLowResCellsX + 1) * (k*NumLowResCellsY + 1)
		const float A = static_cast<float>(NumLowResCellsX) * static_cast<float>(NumLowResCellsY);
		const float B = static_cast<float>(NumLowResCellsX) + static_cast<float>(NumLowResCellsY);
		constexpr float C = -(MaxVert - 1);
		const float ScaleFactor = (-B + FMath::Sqrt(B*B - 4.0f*A*C)) / (2.0f*A);

		// Adjust the low-res grid.
		NumLowResCellsX = FMath::TruncToInt(ScaleFactor * NumLowResCellsX);
		NumLowResCellsY = FMath::TruncToInt(ScaleFactor * NumLowResCellsY);
		GridSpacingLowRes = FMath::Max<float>(Width / NumLowResCellsX, Height / NumLowResCellsY);
	}

	FluidUpdateRate		=  FMath::Max<float>(FluidUpdateRate, 1.0f);
	GridSpacing			=  FMath::Max<float>(GridSpacing, 1.0f);
	float CellWidth		= GridSpacing;
	float CellHeight	= GridSpacing;
	int32 TotalNumCellsX	=  FMath::Max<int32>(FMath::TruncToInt(Width / CellWidth), 1);
	int32 TotalNumCellsY	=  FMath::Max<int32>(FMath::TruncToInt(Height / CellHeight), 1);
	if ( EnableSimulation == false || bActive == false )
	{
		TotalNumCellsX	= GPUTESSELLATION + 1;
		TotalNumCellsY	= GPUTESSELLATION + 1;
		CellWidth		= Width / TotalNumCellsX;
		CellHeight		= Height / TotalNumCellsY;
	}

	// Limit number of vertices to avoid driver crashes
	int32 GridNumCellsX	= SimulationQuadsX;
	int32 GridNumCellsY	= SimulationQuadsY;
	const int32 NumVertices = (GridNumCellsX+1) * (GridNumCellsY+1);
	if ( NumVertices > MaxFluidNumVerts )
	{
		const float K	= FMath::InvSqrt( static_cast<float>(NumVertices) / static_cast<float>(MaxFluidNumVerts) );
		GridNumCellsX	= FMath::TruncToInt( GridNumCellsX * K );
		GridNumCellsY	=  FMath::TruncToInt( GridNumCellsY * K );
	}

	// Align to default tessellation level (making the number of inner vertices a multiple of GPUTESSELLATION).
	TotalNumCellsX	= FMath::Max<int32>( TotalNumCellsX, GPUTESSELLATION + 1 );
	TotalNumCellsY	= FMath::Max<int32>( TotalNumCellsY, GPUTESSELLATION + 1 );
	TotalNumCellsX	= Align<int32>(TotalNumCellsX - 1, GPUTESSELLATION) + 1;
	TotalNumCellsY	= Align<int32>(TotalNumCellsY - 1, GPUTESSELLATION) + 1;
	GridNumCellsX	= Align<int32>(GridNumCellsX - 1, GPUTESSELLATION) + 1;
	GridNumCellsY	= Align<int32>(GridNumCellsY - 1, GPUTESSELLATION) + 1;

	// Clamp the simulation grid to the total fluid plane
	GridNumCellsX	= FMath::Min<int32>(GridNumCellsX, TotalNumCellsX);
	GridNumCellsY	= FMath::Min<int32>(GridNumCellsY, TotalNumCellsY);

	FluidSize.X		= TotalNumCellsX * CellWidth;
	FluidSize.Y		= TotalNumCellsY * CellHeight;

	// Dont show fluid surfaces on server
	if ( GIsClient )
	{
		FluidSimulation = MakeUnique<FFluidSimulation>(this, bActive, GridNumCellsX, GridNumCellsY, CellWidth,
			CellHeight, TotalNumCellsX, TotalNumCellsY );
		UpdateFluidSimulationProxy();
	}
	TestRippleTime	= TestRippleFrequency;
	TestRippleAngle = 0.0f;

	DeactivationTimer = DEACTIVATION_TIME;
	UpdateFluidBounds();
}

void UFluidSurfaceComponent::ReleaseResources(bool bBlockOnRelease)
{
	if ( FluidSimulation.IsValid() )
	{
		// The scene is tracking fluid surfaces and storing a pointer to the fluid resource, so we can only call ReleaseResources if detached
		//check(!IsRegistered());

		FluidSimulation.Get()->ReleaseResources( bBlockOnRelease );
		if ( bBlockOnRelease )
		{
			// delete FluidSimulation;
			FluidSimulation = nullptr;
			UpdateFluidSimulationProxy();
		}
	}
}

void UFluidSurfaceComponent::RebuildClampMap()
{
#if !DISABLE_CLAMPMAP
	if ( EnableSimulation )
	{
		GridSpacing = FMath::Max<FLOAT>(GridSpacing, 1.0f);
		int NumCellsX = FMath::Max<int>(FMath::TruncToInt(FluidWidth / GridSpacing), 1);
		int NumCellsY = Max<int>(appTrunc(FluidHeight / GridSpacing), 1);
		int NumVertices = (NumCellsX + 1) * (NumCellsY + 1);
		if (ClampMap.Num() != NumVertices)
		{
			ClampMap.Empty( NumVertices );
			ClampMap.AddZeroed( NumVertices );
		}

		for ( int Y=0; Y < NumCellsY; ++Y )
		{
			for ( int X=0; X < NumCellsX; ++X )
			{
				FVector LocalPos( X * GridSpacing - FluidWidth * 0.5f, Y * GridSpacing - FluidHeight * 0.5f, 0.0f );
				FVector WorldPos = GetRenderMatrix().TransformPosition(LocalPos);
				FHitResult Result;
				bool bCollision = GetWorld()->EncroachingWorldGeometry( Result, WorldPos, FVector(0,0,0), FALSE );
				ClampMap[Y * (NumCellsX + 1) + X] = bCollision;
			}
		}
	}
	else
#endif
	{
		ClampMap.Empty( 0 );
	}
}

UMaterialInterface* UFluidSurfaceComponent::GetMaterial(int32 ElementIndex) const
{
	return GetMaterialFluid();
}

void UFluidSurfaceComponent::GetUsedMaterials(TArray<UMaterialInterface*>& OutMaterials, bool bGetDebugMaterials) const
{
	OutMaterials.Add( GetMaterialFluid() );
	if(LowResFluidMaterial)
		OutMaterials.Add( LowResFluidMaterial );
	if(FarFluidMaterial)
		OutMaterials.Add( FarFluidMaterial );
}

void UFluidSurfaceComponent::CreateRenderState_Concurrent(FRegisterComponentContext* Context)
{
	Super::CreateRenderState_Concurrent(Context);
	// FRegisterComponentContext::SendRenderDynamicData(Context, this);
	if(!FluidSimulation.IsValid())
		InitResources(false);
	AddFluidSurfaceIfNeeded();
}

void UFluidSurfaceComponent::DestroyRenderState_Concurrent()
{
	Super::DestroyRenderState_Concurrent();
	if(FluidSimulation.IsValid())
		ReleaseResources(true);
	UFluidSurfaceSubsystem::GetFluidSurfaceManager(GetWorld())->RemoveFluidSurfaceComponent(this);
}

void UFluidSurfaceComponent::SendRenderTransform_Concurrent()
{
	Super::SendRenderTransform_Concurrent();
	UFluidSurfaceSubsystem::GetFluidSurfaceManager(GetWorld())->RemoveFluidSurfaceComponent(this);
	AddFluidSurfaceIfNeeded();
}

bool UFluidSurfaceComponent::LineTraceComponent(FHitResult& OutHit, const FVector Start, const FVector End, ECollisionChannel TraceChannel,
                                                const FCollisionQueryParams& Params, const FCollisionResponseParams& ResponseParams, const FCollisionObjectQueryParams& ObjectParams)
{
	bool ResultCheck = true;
	if ( FluidSimulation.IsValid() && FluidSimulation.Get()->LineCheck( OutHit, End, Start, FVector(0,0,0) ) == false )
	{
		OutHit.HitObjectHandle.SetCachedActor(GetOwner());
		OutHit.Component = this;
		ResultCheck = false;
	}
	return ResultCheck && Super::LineTraceComponent(OutHit, Start, End, TraceChannel, Params, ResponseParams, ObjectParams);
}

void UFluidSurfaceComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	//checkf(IsRunningDedicatedServer(), TEXT("Your waves will cause lag for clients, so we've crashed the game. So tell to developer."));
	SCOPE_CYCLE_COUNTER( STAT_FluidSurfaceComponentTickTime );
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
	
	// Calculate the position and distance of the closest player.
	ViewDistance = 0.0f;
	FVector ClosestPlayerViewLocation = GetComponentLocation();
	if(FluidSimulation.IsValid())
	{
		ViewDistance = WORLD_MAX;
		for( FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator )
		{
			APlayerController* PlayerController = Iterator->Get();
			if( PlayerController )
			{
				if(const ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(PlayerController->Player) )
				{
					const FVector& ViewLocation = LocalPlayer->LastViewLocation;
					const float Distance = CalcDistance( ViewLocation );
					if( Distance < ViewDistance )
					{
						ClosestPlayerViewLocation = ViewLocation;
						ViewDistance = Distance;
					}
				}
			}
		}
	}

	DetailPosition = GetComponentLocation();
	if ( EnableDetail )
	{
		// If a detail target has been specified, use that for the detail position
		DetailPosition = TargetDetail ? TargetDetail->GetActorLocation() : ClosestPlayerViewLocation;
		SetDetailPosition( DetailPosition );
	}
	
	// Update the position of the simulation grid (CPU-simulation)
	if ( EnableSimulation )
	{
		// If a detail target has been specified, use that for the detail position
		SetSimulationPosition( TargetSimulation ? TargetSimulation->GetActorLocation() : ClosestPlayerViewLocation );
	}

	if ( FluidSimulation.IsValid() )
	{
		UpdateMemory( DeltaTime );

		// Apply Test Ripple force
		if( bTestRipple && !bPause )
		{
			TestRippleAngle += DeltaTime * TestRippleSpeed;
			TestRippleTime -= DeltaTime;
			if ( TestRippleTime < 0.0f )
			{
				float RippleRadius;
				if ( bTestRippleCenterOnDetail )
				{
					RippleRadius = 0.3f * DetailSize;
				}
				else
				{
					RippleRadius = 0.3f * FMath::Min(FluidSimulation->GetWidth(), FluidSimulation->GetHeight());
				}

				float Force;
				FVector LocalRipplePos = FVector(RippleRadius * FMath::Sin(TestRippleAngle), RippleRadius * FMath::Cos(TestRippleAngle), 0);

				if ( bTestRippleCenterOnDetail )
				{
					const FVector LocalDetailPos = FluidSimulation->GetWorldToLocal().TransformPosition(DetailPosition);
					LocalRipplePos += LocalDetailPos;
				}

				const FVector WorldRipplePos = GetRenderMatrix().TransformPosition(LocalRipplePos);

				bool bImpulse;
				if ( FMath::Abs(TestRippleFrequency) < 0.01f )
				{
					Force = ForceContinuous;
					bImpulse = false;
				}
				else
				{
					Force = ForceImpact;
					bImpulse = true;
				}

				ApplyForce(WorldRipplePos, Force, TestRippleRadius, bImpulse);
				TestRippleTime = TestRippleFrequency;
			}
		}

		//enqueue GPUApplyForce();
		//enqueue GPUSimulate(DeltaTime);
		//enqueue renderthreadCPUTick(DeltaTime);
		FluidSimulation.Get()->GameThreadTick( DeltaTime );
		// MarkRenderDynamicDataDirty();
	}
}

void UFluidSurfaceComponent::BeginDestroy()
{
	Super::BeginDestroy();
	ReleaseResources(false);
}

bool UFluidSurfaceComponent::IsReadyForFinishDestroy()
{
	const bool bReady = Super::IsReadyForFinishDestroy();
	return bReady && (!FluidSimulation.IsValid() || FluidSimulation.Get()->IsReleased());
}

void UFluidSurfaceComponent::FinishDestroy()
{
	if ( FluidSimulation.IsValid() )
	{
		// delete FluidSimulation;
		FluidSimulation = nullptr;
	}
	
	Super::FinishDestroy();
}

#if WITH_EDITOR
void UFluidSurfaceComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	// No needed with clamps metadata
	/*GPUTessellationFactor = FMath::Clamp<float>(GPUTessellationFactor, 0.01f, 100.0f);
	FluidTravelSpeed = FMath::Clamp<float>(FluidTravelSpeed, 0.0f, 1.0f);
	DetailTravelSpeed = FMath::Clamp<float>(DetailTravelSpeed, 0.0f, 1.0f);*/
	Super::PostEditChangeProperty(PropertyChangedEvent);

	if( LightMapResolution > 0 )
	{
		LightMapResolution = FMath::Max(LightMapResolution + 3 & ~3,4);
	}
	else
	{
		LightMapResolution = 0;
	}
	
	FProperty* PropertyThatChanged = PropertyChangedEvent.Property;
	if ( PropertyNeedsResourceRecreation(PropertyThatChanged) )
	{
		MarkRenderStateDirty();
		const bool bCIsActive = FluidSimulation != nullptr ? FluidSimulation->IsActive() : false;
		InitResources( bCIsActive );
	}
}

void UFluidSurfaceComponent::PreEditChange(FProperty* PropertyThatWillChange)
{
	Super::PreEditChange(PropertyThatWillChange);
	if ( PropertyNeedsResourceRecreation(PropertyThatWillChange) )
	{
		ReleaseResources(true);
	}
}

bool UFluidSurfaceComponent::PropertyNeedsResourceRecreation(FProperty* Property) const
{
	if ( IsTemplate() )
	{
		return false;
	}
	const FName PropertyName = Property->GetFName();
	if (PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, bPause) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, NormalLength) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, bShowDetailPosition) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, bShowSimulationPosition) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, LightingContrast) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, bShowFluidSimulation) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, bShowDetailNormals) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, bShowFluidDetail) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, GPUTessellationFactor) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, TargetDetail) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, DetailUpdateRate) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, DetailDamping) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, FluidTravelSpeed) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, DetailTravelSpeed) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, DetailTransfer) &&
		PropertyName == GET_MEMBER_NAME_CHECKED(UFluidSurfaceComponent, DetailHeightScale))
	{
		return true;
	}
	return false;
}

void UFluidSurfaceComponent::UpdateBounds()
{
	UpdateFluidBounds();
}
#endif

void UFluidSurfaceComponent::AddFluidSurfaceIfNeeded()
{
	if (ShouldComponentAddToScene() && ShouldRender() && IsRegistered())
	{
		UFluidSurfaceSubsystem::GetFluidSurfaceManager(GetWorld())->AddFluidSurfaceComponent(this);
	}
}

void UFluidSurfaceComponent::UpdateMemory(float DeltaTime)
{
	// Force deactivation of the fluid?
	if ( FluidSimulation.Get()->IsActive() && IsFluidRenderDisabled() )
	{
		MarkRenderStateDirty();
		InitResources(false);
	}

	// Check to see if we should disable full CPU/GPU simulation again.
	if ( (EnableSimulation || EnableDetail) && FluidSimulation.Get()->IsActive() && ViewDistance > DeactivationDistance )
	{
		DeactivationTimer -= DeltaTime;
		if ( DeactivationTimer < 0.0f )
		{
			MarkRenderStateDirty();
			InitResources(false);
		}
	}
	else
	{
		DeactivationTimer = DEACTIVATION_TIME;
	}
}

float UFluidSurfaceComponent::CalcDistance(const FVector& WorldPosition) const
{
	const FVector LocalPosition = FluidSimulation.Get()->GetWorldToLocal().TransformPosition( WorldPosition );
	const float Dx = FMath::Max<float>( FMath::Abs<float>(LocalPosition.X) - FluidSize.X * 0.5f, 0.0f );
	const float Dy = FMath::Max<float>( FMath::Abs<float>(LocalPosition.Y) - FluidSize.X * 0.5f, 0.0f );
	const float Distance = FMath::Sqrt( Dx*Dx + Dy*Dy );
	return Distance;
}

float UFluidSurfaceComponent::GetDiffuseBoost(int32 ElementIndex) const
{
	return LightmassSettings.DiffuseBoost;
}

float UFluidSurfaceComponent::GetEmissiveBoost(int32 ElementIndex) const
{
	return LightmassSettings.EmissiveBoost;
}

bool UFluidSurfaceComponent::GetLightMapResolution(int32& Width, int32& Height) const
{
	Width = LightMapResolution;
	Height = LightMapResolution;
	return false;
}

int32 UFluidSurfaceComponent::GetStaticLightMapResolution() const
{
	return LightMapResolution;
}

void UFluidSurfaceComponent::GetLightAndShadowMapMemoryUsage(int32& LightMapMemoryUsage, int32& ShadowMapMemoryUsage) const
{
	constexpr float MipFactor = 1.33f;
	ShadowMapMemoryUsage		= 0;
	LightMapMemoryUsage			= 0;

	int32 LightMapWidth			= 0;
	int32 LightMapHeight		= 0;
	GetLightMapResolution( LightMapWidth, LightMapHeight );

	UWorld* World = GetWorld();
	ERHIFeatureLevel::Type FeatureLevel = World ? World->GetFeatureLevel() : GMaxRHIFeatureLevel;

	if (AllowHighQualityLightmaps(FeatureLevel))
	{
		LightMapMemoryUsage = FMath::TruncToInt(NUM_HQ_LIGHTMAP_COEF * MipFactor * LightMapWidth * LightMapHeight); // DXT5
	}
	else
	{
		LightMapMemoryUsage = FMath::TruncToInt(NUM_LQ_LIGHTMAP_COEF * MipFactor * LightMapWidth * LightMapHeight / 2); // DXT1
	}
}

void UFluidSurfaceComponent::GetStreamingRenderAssetInfo(FStreamingTextureLevelContext& LevelContext,
                                                         TArray<FStreamingRenderAssetPrimitiveInfo>& OutStreamingRenderAssets) const
{
	/*const FSphere BoundingSphere = Bounds.GetSphere();
	const float WorldTexelFactor = FMath::Max<float>( FluidSize.X, FluidSize.Y );
	
	LevelContext.BindBuildData(nullptr);

	TArray<UMaterialInterface*> UsedMaterials;
	GetUsedMaterials(UsedMaterials);
	
	FPrimitiveMaterialInfo MaterialData;
	MaterialData.PackedRelativeBox = PackedRelativeBox_Identity;
	static const FMeshUVChannelInfo UVChannelData(1.f);
	MaterialData.UVChannelData = &UVChannelData;
	for(int i = 0;i<UsedMaterials.Num();i++)
	{
		const UMaterialInterface* MaterialInterface = UsedMaterials[i];
		if(!MaterialInterface) continue;
		MaterialData.Material = MaterialInterface;
		LevelContext.ProcessMaterial(Bounds, MaterialData, BoundingSphere.GetVolume() * WorldTexelFactor, OutStreamingRenderAssets, bIsValidTextureStreamingBuiltData, this);
	}*/
	Super::GetStreamingRenderAssetInfo(LevelContext, OutStreamingRenderAssets);
}

UBodySetup* UFluidSurfaceComponent::GetBodySetup()
{
	UpdateBodyCollision();
	return BodySetup;
}

bool UFluidSurfaceComponent::ShouldUseFarMaterial(const FVector& ViewOrigin) const
{
	return FarFluidMaterialForLowDetailMode && GetWorld()->GetDetailMode() <= 0 || Bounds.ComputeSquaredDistanceFromBoxToPoint(ViewOrigin) >= FMath::Square(FarMaterialDist);
}

bool UFluidSurfaceComponent::IsFluidRenderDisabled()
{
	return GForceFluidDeactivation.GetValueOnGameThread() > 0;
}

void UFluidSurfaceComponent::UpdateBodyCollision()
{
	if( BodySetup == NULL )
	{
		/* Create physics body */
		BodySetup = NewObject<UBodySetup>(this, UBodySetup::StaticClass());
	}
	BodySetup->AggGeom.EmptyElements( );

	const FVector BoxExtents = Bounds.GetBox().GetExtent();
	FKBoxElem& BoxElem = *new ( BodySetup->AggGeom.BoxElems ) FKBoxElem( BoxExtents.X * 2, BoxExtents.Y * 2, BoxExtents.Z * 2 );
	BoxElem.Center = FVector::ZeroVector;
	BoxElem.Rotation = FRotator( GetComponentQuat( ) );
	
	BodySetup->ClearPhysicsMeshes();
	BodySetup->CreatePhysicsMeshes();

	/* Setup collision defaults */
	BodyInstance.SetCollisionProfileName(TEXT("OverlapAllDynamic"));
	BodyInstance.SetInstanceNotifyRBCollision( true );
}

void UFluidSurfaceComponent::UpdateFluidBounds()
{
	FVector Corners[8];
	const float HalfWidth = FluidSize.X * 0.5f;
	const float HalfHeight = FluidSize.Y * 0.5f;
	Corners[0] = GetRenderMatrix().TransformPosition( FVector(-HalfWidth, -HalfHeight, -10.0f) );
	Corners[1] = GetRenderMatrix().TransformPosition( FVector( HalfWidth, -HalfHeight, -10.0f) );
	Corners[2] = GetRenderMatrix().TransformPosition( FVector( HalfWidth,  HalfHeight, -10.0f) );
	Corners[3] = GetRenderMatrix().TransformPosition( FVector(-HalfWidth,  HalfHeight, -10.0f) );
	Corners[4] = GetRenderMatrix().TransformPosition( FVector(-HalfWidth, -HalfHeight,  10.0f) );
	Corners[5] = GetRenderMatrix().TransformPosition( FVector( HalfWidth, -HalfHeight,  10.0f) );
	Corners[6] = GetRenderMatrix().TransformPosition( FVector( HalfWidth,  HalfHeight,  10.0f) );
	Corners[7] = GetRenderMatrix().TransformPosition( FVector(-HalfWidth,  HalfHeight,  10.0f) );
	const FBox BoundingBox( Corners, 8 );
	Bounds = FBoxSphereBounds(BoundingBox);

	if ( FluidSimulation.IsValid() )
	{
		Corners[0] = GetRenderMatrix().TransformPosition( FVector(-HalfWidth, -HalfHeight, 0.0f) );
		Corners[1] = GetRenderMatrix().TransformPosition( FVector( HalfWidth, -HalfHeight, 0.0f) );
		Corners[2] = GetRenderMatrix().TransformPosition( FVector( HalfWidth,  HalfHeight, 0.0f) );
		Corners[3] = GetRenderMatrix().TransformPosition( FVector(-HalfWidth,  HalfHeight, 0.0f) );
		const FPlane Plane( Corners[0], Corners[1], Corners[2] );
		const FVector& Normal = Plane;
		const FPlane Edges[4] =
		{
			FPlane( Corners[0], ((Corners[1] - Corners[0]) ^ Normal).GetUnsafeNormal() ),
			FPlane( Corners[1], ((Corners[2] - Corners[1]) ^ Normal).GetUnsafeNormal() ),
			FPlane( Corners[2], ((Corners[3] - Corners[2]) ^ Normal).GetUnsafeNormal() ),
			FPlane( Corners[3], ((Corners[0] - Corners[3]) ^ Normal).GetUnsafeNormal() )
		};
		FluidSimulation.Get()->SetExtents( GetRenderMatrix(), Plane, Edges );
	}
}

FluidSurface.h:

// By penguin21 and backported from UE3

/*=============================================================================
	FluidSurface.h: Class definitions for fluid surfaces.
=============================================================================*/
#pragma once

#ifndef FLUIDSURFACE_H
#define FLUIDSURFACE_H

#include "CoreMinimal.h"
#include "FluidSurfaceGPUSimulation.h"
#include "VertexFactory.h"
#include "LocalVertexFactory.h"
#include "RawGPUIndexBuffer.h"

class UFluidSurfaceComponent;
class FFluidSimulation;

#define ENABLE_XBOX_LEFTOVER 0

/** How many quads a CPU grid cell should be split up into by default, along each side. (Xbox-only) */
#define GPUTESSELLATION 4

/** Set to 1 or 0, depending on if you want to use clampmaps or not. */
#define DISABLE_CLAMPMAP 1

/*-----------------------------------------------------------------------------
	FFluidSurfaceStats
-----------------------------------------------------------------------------*/
DECLARE_STATS_GROUP(TEXT("Fluids"),STATGROUP_Fluids, STATCAT_Advanced);

/**
 * Fluid vertex
 */
struct FFluidVertex
{
	float			Height;
	FVector2f		UV;
	FVector2f		HeightDelta;	// Height difference along the X- and Y-axis
	
	FFluidVertex();
	FString ToString() const;
};

/**
 * Fluid vertex buffer
 */
class FFluidVertexBuffer : public FVertexBuffer
{
public:
	enum EBufferType
	{
		BT_Simulation	= 0,
		BT_Border		= 1,
		BT_Quad			= 2,
	};

	FFluidVertexBuffer();
	void			Setup( FFluidSimulation* InOwner, uint32 InMaxNumVertices, EBufferType InBufferType, int32 NumQuadsX=0, int32 NumQuadsY=0 );
	FFluidVertex*	Lock(FRHICommandListBase& RHICmdList);
	void			Unlock(FRHICommandListBase& RHICmdList);
	bool			IsLocked() const;
	uint32			GetMaxNumVertices() const;
	int32			GetNumQuadsX() const;
	int32			GetNumQuadsY() const;

	// FRenderResource interface.
	virtual void InitRHI(FRHICommandListBase& RHICmdList) override;
	virtual void ReleaseRHI() override;
	virtual FString GetFriendlyName() const override { return TEXT("FluidVertexBuffer"); }
private:
	FFluidSimulation* Owner;
	uint32			NumVerts;
	uint32			MaxNumVertices;
	uint8			bIsLocked:1;
	EBufferType		BufferType;
	uint8			bBorderGeometry:1;
	int32			NumQuadsX;
	int32			NumQuadsY;
};

/**
 *	Vertex factory for fluid surfaces, using a vertex buffer.
 */
class FFluidVertexFactory : public FVertexFactory
{
	DECLARE_VERTEX_FACTORY_TYPE_API(FFluidVertexFactory, FLUIDSURFACE_API);
public:
	/** Default constructor (Unsafe) */
	FFluidVertexFactory() : FFluidVertexFactory(ERHIFeatureLevel::Num){}
	/** Default constructor */
	FFluidVertexFactory(ERHIFeatureLevel::Type InFeatureLevel);
	virtual ~FFluidVertexFactory() override{}
	
	// Get Fluid Simulation Onwer
	FFluidSimulation* GetSimulation() const;
#if ENABLE_XBOX_LEFTOVER
	inline FTextureRHIRef& GetHeightmapTexture() const;
#endif
	void InitResources(FRHICommandListBase& RHICmdList, const FFluidVertexBuffer& VertexBuffer, FFluidSimulation* FluidSimulation );
	
	// FRenderResource interface.
	virtual void InitRHI(FRHICommandListBase& RHICmdList) override;
	virtual FString	GetFriendlyName() const override;

	static void ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment);
	static bool ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters);
	/**
	 * Get vertex elements used when during PSO precaching materials using this vertex factory type
	 */
	static void GetPSOPrecacheVertexFetchElements(EVertexInputStreamType VertexInputStreamType, FVertexDeclarationElementList& Elements);
	
	/** Returns whether the vertex shader should generate vertices (TRUE) or if it should use a vertex buffer (FALSE). */
	bool UseGPUTessellation() const;
protected:
	/** The stream to read the vertex height from. */
	FVertexStreamComponent Height;

	/** The streams to read the texture coordinates from. */
	FVertexStreamComponent TexCoord;

	/** The streams to read the tangent basis from. */
	FVertexStreamComponent HeightDelta;

	/** Owner FluidSimulation. */
	FFluidSimulation* FluidSimulation = nullptr;
	/** Whether the vertex shader should generate vertices (TRUE) or if it should use a vertex buffer (FALSE). */
	uint8 bUseGPUTessellation:1;
};

/**
 *	Vertex factory for fluid surfaces, letting the GPU generate the fluid vertices.
 */
class FFluidTessellationVertexFactory : public FFluidVertexFactory
{
	DECLARE_VERTEX_FACTORY_TYPE_API(FFluidTessellationVertexFactory, FLUIDSURFACE_API);
public:
	/** Default constructor (Unsafe) */
	FFluidTessellationVertexFactory() : FFluidTessellationVertexFactory(ERHIFeatureLevel::Num){}
	/** Default constructor */
	FFluidTessellationVertexFactory(ERHIFeatureLevel::Type InFeatureLevel);
};

// Dynamic Data from Component
/*struct FFluidSurfaceComponentDynamicData
{
	int32 DetailResolution;
	float DetailSize;
	TArray<uint8> ClampMap;
	uint8 bPause:1;
	uint8 bShowFluidSimulation:1;
	float FluidHeightScale;
	uint8 bShowSimulationNormals:1;
	float LightingContrast;
	FTransform TransformComponent;
	float DetailUpdateRate;
	float DetailDamping;
	float DetailTravelSpeed;
	float DetailTransfer;
	float DetailHeightScale;
	uint8 bTiling:1;
	float FluidDamping;
	float FluidTravelSpeed;
	float GPUTessellationFactor;
	float GridSpacing;
	int32 SimulationQuadsX;
	int32 SimulationQuadsY;
	uint8 bHasSplitscreen:1;
	uint8 bShowDetailPosition:1;
	uint8 EnableDetail:1;
	uint8 FarFluidMaterialForLowDetailMode:1;
	int32 DetailMode;
	float FarMaterialDist;
	FBoxSphereBounds Bounds;
	uint8 bShowSimulationPosition:1;
	uint8 EnableSimulation:1;
	float NormalLength;
	uint8 bShowFluidDetail:1;

	FFluidSurfaceComponentDynamicData();
	FFluidSurfaceComponentDynamicData(const UFluidSurfaceComponent* Component);

	bool ShouldUseFarMaterial(const FVector& ViewOrigin) const
	{
		return FarFluidMaterialForLowDetailMode && DetailMode <= 0 || Bounds.ComputeSquaredDistanceFromBoxToPoint(ViewOrigin) >= FMath::Square(FarMaterialDist);
	}
};*/

/**
 * FluidSimulation - the main fluid class that keeps all parts together.
 */
class FFluidSimulation : public IQueuedWork
{
public:
	FFluidSimulation( UFluidSurfaceComponent* InComponent, bool bActive, int32 InSimulationQuadsX, int32 InSimulationQuadsY, float InCellWidth, float InCellHeight, int32 InTotalNumCellsX, int32 InTotalNumCellsY );
	virtual ~FFluidSimulation() override;

	// FQueuedWork API
	void				DoWork();
	virtual void		Abandon() override {}
	virtual void		DoThreadedWork() override;
	virtual const TCHAR* GetDebugName() const override { return TEXT("FluidSurfaceComponentThread"); }

	// Called from the Gamethread
	void				ReleaseResources( bool bBlockOnRelease );
	bool				IsReleased( ) const;
	float				GetWidth( ) const;
	float				GetHeight( ) const;
	int32				GetNumCellsX( ) const;
	int32				GetNumCellsY( ) const;
	const FIntPoint&	GetTotalSize( ) const;
	void				GameThreadTick( float DeltaTime );
	bool				LineCheck( FHitResult& Result, const FVector& End, const FVector& Start, const FVector& Extent ) const;
	bool				PointCheck( FHitResult& Result, const FVector& Location, const FVector& Extent ) const;
	void				SetExtents( const FMatrix& LocalToWorld, const FPlane& Plane, const FPlane* Edges );
	bool				IsActive( ) const;
	void				DrawDebug(FDisplayDebugManager& DisplayDebugManager) const;
	const FFluidGPUResource* GetGPUResource() const { return &DetailGPUResource; }

	// Called from the Renderthread
	void				AddForce( const FVector& LocalPos, float Strength, float LocalRadius, float bImpulse=false );
	void				SetDetailPosition(FVector LocalPos);
	void				SetSimulationPosition(FVector LocalPos);
	void				RenderThreadTick(FRHICommandList& RHICmdList, float DeltaTime );
	void				UpdateBorderGeometry( FFluidVertex* Vertices ) const;
	const FMatrix&		GetWorldToLocal( ) const;
	const FVector4&		GetGridSize( ) const;
	const FIntPoint&	GetSimulationPosition( ) const;
#if ENABLE_XBOX_LEFTOVER
	const FVector4&		GetTessellationParameters() const;
	const FVector4&		GetTessellationFactors1() const;
	const FVector4&		GetTessellationFactors2() const;
	const FVector4&		GetTexcoordScaleBias() const;
#endif

	// Called from any thread
	void				BlockOnSimulation();
	void				GetSimulationRect( FVector2D& TopLeft, FVector2D& LowerRight ) const;			// Returns the rectangle of the simulation grid, in fluid local space.
	void				GetDetailRect( FVector2D& TopLeft, FVector2D& LowerRight ) const;				// Returns the rectangle of the detailmap, in fluid local space.
	bool				IsWithinSimulationGrid( const FVector& LocalPos, float Radius ) const;
	bool				IsWithinDetailGrid( const FVector& LocalPos, float Radius ) const;
protected:
	// Called from the simulation thread
	bool			IsClampedVertex( int32 X, int32 Y ) const;
	void			ApplyForce( const FVector& LocalPos, float Strength, float LocalRadius );
	void			Simulate( float DeltaTime );
	bool			UpdateRenderData();

	// Called from the Gamethread
	void			InitResources();
	/**
	 * Allocates texture memory on the gamethread. Will try to stream out high-res mip-levels if there's not enough room.
	 * @return	A new FTexture2DResourceMem object, representing the allocated memory, or NULL if the allocation failed.
	 */
	FTexture2DResourceMem* CreateTextureResourceMemory();

	// Called from the Renderthread
	void			RenderThreadInitResources(FRHICommandListBase& RHICmdList, int32 BufferIndex, FTexture2DResourceMem* ResourceMem );
	void			InitIndexBufferX(FRHICommandListBase& RHICmdList);
	void			InitIndexBufferY(FRHICommandListBase& RHICmdList);
	void			InitFlatIndexBuffer(FRHICommandListBase& RHICmdList);
	void			UpdateShaderParameters( int32 OctantID );
	static int32	ClassifyOctant( const FVector& LocalDirection );
	bool			ShouldSimulate() const;
	void			LockResources(FRHICommandListBase& RHICmdList);
	void			UnlockResources(FRHICommandListBase& RHICmdList);

	/* 
	 * Indicates whether culling should be reversed when rendering the fluid surface. 
	 * These are set on the simulation thread and read on the render thread when rendering the fluid surface.
	 */
	uint8					bReverseCulling[2];

	/** 
	 * If TRUE, packing will be done from front-to-back relative to the view direction.  
	 * If FALSE, packing will be done from back-to-front to avoid artifacts with translucency.
	 */
	uint8					bOpaqueMaterial:1;

	/* 
	 * Indicates whether the YFirstIndexBuffer should be used for rendering, or the XFirstIndexBuffer.
	 * These are set on the simulation thread and read on the render thread when rendering the fluid surface.
	 */
	uint8					bUseYFirstIndexBuffer[2];

	/* 
	 * Stores the view direction that the fluid surface was last rendered with, 
	 * used for packing the render data from back to front to behave correctly with translucency.
	 * These are set on the render thread and read from the simulation thread.
	 */
	FVector					LastViewDirection[2];

	FMatrix					FluidWorldToLocal;		// World-to-local matrix for the whole fluid
	FPlane					Plane;					// Representing the fluid plane in world space.
	FPlane					Edges[4];				// Outward planes for the four edges of the fluid, in world space.
	float*					HeightMap[2];			// Heights for each vertex. History of two simulation steps, plus one scratch.
	int32					HeightMapMemSize;		// Number of bytes in each height map.
	int32					CurrentHeightMap;		// Indexes the current height map, owned by the simulation thread.
	int32					NumCellsX;				// Number of simulation grid cells along the X-axis
	int32					NumCellsY;				// Number of simulation grid cells along the Y-axis
	int32					NumLowResCellsPerSideX; // Number of low res cells in each of the 4 border patches along the X axis
	int32					NumLowResCellsPerSideY; // Number of low res cells in each of the 4 border patches along the Y axis
	float					CellWidth;				// Width of a grid cell
	float					CellHeight;				// Height of a grid cell (normally the same as width)
	float					GridWidth;				// Width of the simulation grid, in localspace units
	float					GridHeight;				// Height of the simulation grid, in localspace units
	float					UpdateRate;
	float					TimeRollover;
	TArray<FVector>			DebugPositions;
	TArray<FVector>			DebugNormals;
	UFluidSurfaceComponent* Component;
	int32						NumVertices;			// Number of generated vertices this frame
	int32						NumIndices;				// Number of generated indices this frame
	uint8					bEnableCPUSimulation:1;	// Copy of FluidSurfaceComponent::EnableSimulation
	uint8					bEnableGPUSimulation:1;	// Copy of FluidSurfaceComponent::EnableDetail

	FIntPoint				PendingSimulationPos;	// Pending upper-left corner of the simulation grid (in cells), as queued up from the gamethread
	FIntPoint				SimulationPos[2];		// Upper-left corner of the simulation grid, for each of the two heightmaps
	FIntPoint				TotalSize;				// Total size of the fluid, including both simulation part and the surrounding flat border (in cells)
	float					TotalWidth;				// Total width of the fluid, including both simulation part and the surrounding flat border (in world-space units)
	float					TotalHeight;			// Total height of the fluid, including both simulation part and the surrounding flat border (in world-space units)

	// Used by the Renderthread
	FVector4				GridSize;				// Vertexshader parameter: X=GridWidth/2, Y=GridHeight/2, Z=1/NumCellsX, W=1/NumCellsY
	uint8					bResourcesLocked:1;		// Whether the GPU resources are locked or not
	float					SimulationActivity;		// Total absolute sum of the two latest heightmap simulations
	uint8					bShowSimulation:1;		// Whether to render the simulation geometry or just a flat quad.

	// Read by all threads
	const int32				GridPitch;				// Number of floats between each row in the simulation heightmap

	// Used by the worker (simulation) thread
	uint8					bWorkerThreadUpdateOnly:1;	// Whether the simulation thread should only retry UpdateRenderData()
	float					PrevSum;				// Total absolute sum of the previous heightmap simulation
	float					CurrentSum;				// Total absolute sum of the latest heightmap simulation
	uint8					bSimulationDirty:1;

#if ENABLE_XBOX_LEFTOVER
	// Xbox-specific variables
	FTextureRHIRef			HeightMapTextures[2];	// These textures use HeightMap[] as texture data.
	void*					TextureData;			// Currently locked texture data.
	uint16					TextureStride;
	int32					NumTessQuadsX;			// Number of tessellation quads along X-axis
	int32					NumTessQuadsY;			// Number of tessellation quads along Y-axis
	float					TessellationLevel;		// Number of sub-quads within each tessellation quad
	FVector4				TessellationParameters;	// Vertexshader parameter: X=TessellationLevel, Y=NumQuadsX, Z=NumQuadsY
	FVector4				TessellationFactors1;	// Vertexshader parameter: X=TessellationLevel, Y=NumQuadsX, Z=NumQuadsY
	FVector4				TessellationFactors2;	// Vertexshader parameter: X=TessellationLevel, Y=NumQuadsX, Z=NumQuadsY
	FVector4				TexcoordScaleBias;		// Vertexshader parameter: Converts from heightmap UV to fluid UV
	uint8					bReverseCullingXbox:1;	// Whether the triangle winding order is reversed due to back-to-front sorting
#endif

	TArray<FFluidForce>		FluidForces[2];
	FFluidVertex*			Vertices;
	FFluidVertex*			BorderVertices;
	float					DeltaTime;

    FRenderCommandFence					ReleaseResourcesFence;
	FFluidTessellationVertexFactory		VertexFactories[2];
	FFluidVertexFactory					FlatVertexFactories[2];
	FFluidVertexFactory					FlatQuadVertexFactory;
	FFluidVertexBuffer		VertexBuffers[2];
	FFluidVertexBuffer		FlatVertexBuffers[2];	// Vertex buffer for the surrounding flat area (dynamic double-buffered)
	FFluidVertexBuffer		FlatQuadVertexBuffer;	// Vertex buffer for rendering the simulation grid as a simple flat quad
	FRawGPUIndexBuffer		FlatIndexBuffer;		// Index buffer for the surrounding flat area (static).
	FRawGPUIndexBuffer		YFirstIndexBuffer;		// Index buffer packed iterating over Y first, then X
	FRawGPUIndexBuffer		XFirstIndexBuffer;		// Index buffer packed iterating over X first, then Y
	FRawGPUIndexBuffer		FlatQuadIndexBuffer;
	FFluidGPUResource		DetailGPUResource;

	/** 
	 * Used to double buffer data for thread-safe interactions between the rendering thread and the simulation thread.
	 * The simulation thread always writes to [SimulationIndex] and the rendering thread reads from [1 - SimulationIndex].
	 * SimulationIndex is flipped in RenderThreadTick(), after the rendering thread has blocked on the simulation thread.
	 */
	int32						SimulationIndex;

	/** Stores the value of SimulationPos at the time that rendering data (such as FlatVertexBuffers) were generated by the simulation thread.  Indexed by SimulationIndex. */
	FIntPoint				RenderDataPosition[2];

	volatile int32			SimulationRefCount;
	volatile int32			bSimulationBusy;
#if STATS
	int32					STAT_FluidSimulationValue;
	int32					STAT_FluidTessellationValue;
	int32					STAT_FluidSimulationCount;
#endif
	
private:
	void CalculateNormal(const float* Height, int32 X, int32 Y, float HeightScale, FVector2f& HeightDelta) const;

	friend class FFluidVertexFactory;
	friend class FFluidSurfaceSceneProxy;
	friend class FFluidMaterialRenderProxy;
	friend class FFluidSurfaceStaticLightingMesh;
	friend class FFluidVertexBuffer;
};

/*=============================================================================
	FFluidSimulation inline functions
=============================================================================*/

FORCEINLINE float FFluidSimulation::GetWidth() const
{
	return GridWidth;
}

FORCEINLINE float FFluidSimulation::GetHeight() const
{
	return GridHeight;
}

FORCEINLINE int32 FFluidSimulation::GetNumCellsX() const
{
	return NumCellsX;
}

FORCEINLINE int32 FFluidSimulation::GetNumCellsY() const
{
	return NumCellsY;
}

FORCEINLINE const FIntPoint& FFluidSimulation::GetTotalSize( ) const
{
	return TotalSize;
}

FORCEINLINE const FIntPoint& FFluidSimulation::GetSimulationPosition( ) const
{
	return SimulationPos[ SimulationIndex ];
}

FORCEINLINE const FMatrix& FFluidSimulation::GetWorldToLocal( ) const
{
	return FluidWorldToLocal;
}

FORCEINLINE const FVector4& FFluidSimulation::GetGridSize() const
{
	return GridSize;
}

#if ENABLE_XBOX_LEFTOVER
FORCEINLINE const FVector4& FFluidSimulation::GetTessellationParameters() const
{
	return TessellationParameters;
}

FORCEINLINE const FVector4& FFluidSimulation::GetTessellationFactors1() const
{
	return TessellationFactors1;
}

FORCEINLINE const FVector4& FFluidSimulation::GetTessellationFactors2() const
{
	return TessellationFactors2;
}

FORCEINLINE const FVector4& FFluidSimulation::GetTexcoordScaleBias() const
{
	return TexcoordScaleBias;
}
#endif

#endif	//FLUIDSURFACE_H

FluidSurface.cpp:

// By penguin21 and backported from UE3

#include "FluidSurface.h"
#include "FluidSurfaceComponent.h"
#include "FluidSurfaceModule.h"
#include "Engine/Canvas.h"
// #include "Kismet/KismetStringLibrary.h"

#define DISALLOW_32BIT_INDICES 0

// use 16-bit indices on platforms that cannot support 32-bit
#if DISALLOW_32BIT_INDICES
	#define PLATFORM_INDEX_TYPE uint16
#else
	#define PLATFORM_INDEX_TYPE uint32
#endif

#define SURFHEIGHT(Heightmap, X, Y)			( Heightmap[ (Y)*GridPitch + (X) ] )

/** When this define is set, then fluids are simulated in a separated thread. */
static bool GThreadedFluidSimulation = true;

DECLARE_CYCLE_STAT(TEXT("Fluid Simulation"),STAT_FluidSimulation,STATGROUP_Fluids);
DECLARE_CYCLE_STAT(TEXT("Fluid Tessellation"),STAT_FluidTessellation,STATGROUP_Fluids);
DECLARE_CYCLE_STAT(TEXT("Fluid Renderthread Blocked"),STAT_FluidRenderthreadBlocked,STATGROUP_Fluids);
DECLARE_MEMORY_STAT(TEXT("Fluid CPU Memory"),STAT_FluidCPUMemory,STATGROUP_Fluids);
DECLARE_MEMORY_STAT_POOL(TEXT("Fluid GPU Memory"),STAT_FluidGPUMemory,STATGROUP_Fluids, FPlatformMemory::MCR_GPU);

/*=============================================================================
	FFluidSimulation implementation
=============================================================================*/
FFluidSimulation::FFluidSimulation(UFluidSurfaceComponent* InComponent, bool bActive, int32 InSimulationQuadsX, int32 InSimulationQuadsY,
	float InCellWidth, float InCellHeight, int32 InTotalNumCellsX, int32 InTotalNumCellsY)
:	CurrentHeightMap( 0 )
,	NumCellsX( InSimulationQuadsX )
,	NumCellsY( InSimulationQuadsY )
,	CellWidth( InCellWidth )
,	CellHeight( InCellHeight )
,	UpdateRate( InComponent->FluidUpdateRate )
,	TimeRollover( 0.0f )
,	Component(InComponent)
,	NumVertices( 0 )
,	NumIndices( 0 )
,	SimulationActivity( 0.0f )
,	bShowSimulation( false )
,	GridPitch( Align<int32>(InSimulationQuadsX+1, GPUTESSELLATION) )
,	bWorkerThreadUpdateOnly( false )
,	PrevSum( 0.0f )
,	CurrentSum( 0.0f )
,	bSimulationDirty( false )
,	Vertices( nullptr )
,	BorderVertices( nullptr )
,	DeltaTime( 0.0f )
,	FlatQuadVertexFactory(InComponent->GetScene()->GetFeatureLevel())
,	YFirstIndexBuffer( InSimulationQuadsX * InSimulationQuadsY * 6, false, sizeof(PLATFORM_INDEX_TYPE) )
,	XFirstIndexBuffer( InSimulationQuadsX * InSimulationQuadsY * 6, false, sizeof(PLATFORM_INDEX_TYPE) )
,	SimulationIndex( 0 )
,	SimulationRefCount( false )
,	bSimulationBusy( false )
{
	for (int32 i = 0; i < 2; i++)
	{
		// Force init vertex factory data static array here!
		VertexFactories[i] = FFluidTessellationVertexFactory(InComponent->GetScene()->GetFeatureLevel());
		FlatVertexFactories[i] = FFluidVertexFactory(InComponent->GetScene()->GetFeatureLevel());
		
		bReverseCulling[i] = false;
		bUseYFirstIndexBuffer[i] = true;
		LastViewDirection[i] = FVector(1, 0, 0);
	}
	const UMaterial* FluidMaterial = InComponent->GetMaterial(0)->GetMaterial();
	bOpaqueMaterial = false;
	if ( FluidMaterial && (FluidMaterial->BlendMode == BLEND_Opaque || FluidMaterial->BlendMode == BLEND_Masked ) )
	{
		bOpaqueMaterial = true;
	}

#if STATS
	STAT_FluidSimulationValue = 0;
	STAT_FluidTessellationValue = 0;
	STAT_FluidSimulationCount = 0;
#endif

#if ENABLE_XBOX_LEFTOVER
	HeightMapTextures[0] = nullptr;
	HeightMapTextures[1] = nullptr;
	TextureData = nullptr;
	TextureStride = 0;
#endif

	bResourcesLocked = false;
	bEnableCPUSimulation = bActive ? InComponent->EnableSimulation : false;
	bEnableGPUSimulation = bActive ? InComponent->EnableDetail : false;

	GridWidth = NumCellsX * CellWidth;
	GridHeight = NumCellsY * CellHeight;
	TotalSize.X	= InTotalNumCellsX;
	TotalSize.Y	= InTotalNumCellsY;
	TotalWidth	= InTotalNumCellsX * CellWidth;
	TotalHeight	= InTotalNumCellsY * CellHeight;
	DetailGPUResource.SetSize(FMath::Clamp<int32>(Component->DetailResolution, 16, 2048), Component->DetailSize);

	HeightMapMemSize = GridPitch * (NumCellsY + 1) * sizeof(float);
	NumVertices		= (NumCellsX + 1) * (NumCellsY + 1);
	NumIndices		= YFirstIndexBuffer.GetNumIndices();
	HeightMap[0]	= static_cast<float*>(FMemory::Malloc(HeightMapMemSize));
	HeightMap[1]	= static_cast<float*>(FMemory::Malloc(HeightMapMemSize));
	FPlatformMemory::Memzero(HeightMap[0], HeightMapMemSize);
	FPlatformMemory::Memzero(HeightMap[1], HeightMapMemSize);

	RenderDataPosition[0].X = RenderDataPosition[1].X = SimulationPos[0].X = SimulationPos[1].X = PendingSimulationPos.X = (TotalSize.X - NumCellsX) / 2;
	RenderDataPosition[0].Y = RenderDataPosition[1].Y = SimulationPos[0].Y = SimulationPos[1].Y = PendingSimulationPos.Y = (TotalSize.Y - NumCellsY) / 2;

	// Don't use the ClampMap if vertex simulation is disabled.
	if ( bEnableCPUSimulation == false || DISABLE_CLAMPMAP )
	{
		Component->ClampMap.Empty(0);
	}
	else if (Component->ClampMap.Num() != NumVertices)
	{
		Component->ClampMap.Empty( NumVertices );
		Component->ClampMap.AddZeroed( NumVertices );
	}

	// Set up vertexshader parameters.
	UpdateShaderParameters( 0 );
	
	// Create buffers for the border geometry.
	VertexBuffers[0].Setup( this, NumVertices, FFluidVertexBuffer::BT_Simulation );
	VertexBuffers[1].Setup( this, NumVertices, FFluidVertexBuffer::BT_Simulation );
	
	// Create buffers for the geometry in the deactivated state.
	const int32 NumLowResCellsX = FMath::Max<int32>(FMath::TruncToInt(TotalWidth / InComponent->GridSpacingLowRes), 1);
	const int32 NumLowResCellsY = FMath::Max<int32>(FMath::TruncToInt(TotalHeight / InComponent->GridSpacingLowRes), 1);
	const int32 NumLowResVertices = (NumLowResCellsX + 1) * (NumLowResCellsY + 1);
	const int32 NumLowResIndices = NumLowResCellsX * NumLowResCellsY * 6;
	FlatQuadVertexBuffer.Setup( this, NumLowResVertices, FFluidVertexBuffer::BT_Quad, NumLowResCellsX, NumLowResCellsY );
	FlatQuadIndexBuffer.Setup( NumLowResIndices, false );

	// Give each of the 4 border patches 1/4 of the low res cells
	NumLowResCellsPerSideX = FMath::Max<int32>(NumLowResCellsX / 2, 1);
	NumLowResCellsPerSideY = FMath::Max<int32>(NumLowResCellsY / 2, 1);
	// Total number of vertices in the border vertex buffer
	const int32 NumLowResFlatVertices = (NumLowResCellsPerSideX + 1) * (NumLowResCellsPerSideY + 1) * 4;
	FlatVertexBuffers[0].Setup( this, NumLowResFlatVertices, FFluidVertexBuffer::BT_Border );
	FlatVertexBuffers[1].Setup( this, NumLowResFlatVertices, FFluidVertexBuffer::BT_Border );
	// Setup the border index buffer
	FlatIndexBuffer.Setup( 4 * NumLowResCellsPerSideX * NumLowResCellsPerSideY * 6, false );

	// Initialize all geometry buffers.
	InitResources();
	
	// Heightmaps
	INC_MEMORY_STAT_BY(STAT_FluidCPUMemory, 2*HeightMapMemSize);
	// Clampmap
	INC_MEMORY_STAT_BY(STAT_FluidCPUMemory, Component->ClampMap.Num()*sizeof(uint8));
	// Vertices
	INC_MEMORY_STAT_BY(STAT_FluidGPUMemory,  2*NumVertices*sizeof(FFluidVertex));
	// Indices
	INC_MEMORY_STAT_BY(STAT_FluidGPUMemory,  NumIndices*sizeof(uint32));
	// LowRes flat surface
	INC_MEMORY_STAT_BY(STAT_FluidGPUMemory, NumLowResVertices*sizeof(FFluidVertex) + NumLowResIndices*sizeof(uint32));

	if ( bEnableGPUSimulation )
	{
		INC_MEMORY_STAT_BY(STAT_FluidGPUMemory, DetailGPUResource.GetRenderTargetMemorySize());
	}

#if WITH_EDITOR
	const FString OwnerName = InComponent->GetOwner() ? InComponent->GetOwner()->GetName() : TEXT("None");
	UE_LOG(FluidSurface, Warning, TEXT("Component %s with Onwer %s: Created an Fluid Surface Simulation"), *InComponent->GetName(), *OwnerName);
#endif
}

// Destructor
FFluidSimulation::~FFluidSimulation()
{
	UE_LOG(FluidSurface, Warning, TEXT("Destroying Fluid simulation in component..."));
	// Heightmaps
	DEC_MEMORY_STAT_BY(STAT_FluidCPUMemory, 2*HeightMapMemSize);
	// Clampmap
	DEC_MEMORY_STAT_BY(STAT_FluidCPUMemory, Component->ClampMap.Num()*sizeof(uint8));
	// Vertices
	DEC_MEMORY_STAT_BY(STAT_FluidGPUMemory, 2*NumVertices*sizeof(FFluidVertex));
	// Indices
	DEC_MEMORY_STAT_BY(STAT_FluidGPUMemory, NumIndices*sizeof(uint32));

	// LowRes flat surface
	DEC_MEMORY_STAT_BY(STAT_FluidGPUMemory, FlatQuadVertexBuffer.GetMaxNumVertices()*sizeof(FFluidVertex) + FlatQuadIndexBuffer.GetNumIndices()*sizeof(uint32));
	if ( bEnableGPUSimulation )
	{
		DEC_MEMORY_STAT_BY(STAT_FluidGPUMemory, DetailGPUResource.GetRenderTargetMemorySize());
	}

	check( !GThreadedFluidSimulation || bSimulationBusy == false );
	check( SimulationRefCount == 0 );

	FMemory::Free(HeightMap[0]);
	FMemory::Free(HeightMap[1]);
}

void FFluidSimulation::DoWork()
{
	bool bUpdateSuccessful = true;
	if(!bWorkerThreadUpdateOnly)
	{
#if STATS
		const uint32 Timer = FPlatformTime::Cycles();
#endif
		const float StartTime = FPlatformTime::Seconds();
		constexpr float FluidSimulationTimeLimit = 30.f;
#if UE_BUILD_DEBUG
		constexpr float SimulationTimeLimit = FluidSimulationTimeLimit * 2.0 / 1000.0;
#else
		constexpr float SimulationTimeLimit = FluidSimulationTimeLimit / 1000.0;
#endif
		int32 NumIterations = 0;
		if ( !Component->bPause && bEnableCPUSimulation )
		{
			// Apply impulse (instantaneous) forces.
			for ( int32 ForceIndex=0; ForceIndex < FluidForces[SimulationIndex].Num(); ++ForceIndex )
			{
				FFluidForce& Force = FluidForces[SimulationIndex][ForceIndex];
				if ( Force.bImpulse )
				{
					ApplyForce( Force.LocalPos, Force.Strength, Force.Radius );
				}
			}

			const float TimeStep = 1.0f / UpdateRate;
			TimeRollover += DeltaTime;
			NumIterations = FMath::TruncToInt( TimeRollover / TimeStep );
			TimeRollover -= NumIterations * TimeStep;
			for (int32 Iteration=0; Iteration < NumIterations; ++Iteration)
			{
				for ( int32 ForceIndex=0; ForceIndex < FluidForces[SimulationIndex].Num(); ++ForceIndex )
				{
					FFluidForce& Force = FluidForces[SimulationIndex][ForceIndex];
					if ( !Force.bImpulse )
					{
						// Apply continuous forces.
						ApplyForce(Force.LocalPos, Force.Strength, Force.Radius);
					}
				}
				Simulate(TimeStep);
				CurrentHeightMap = 1 - CurrentHeightMap;
				// Limit the Simulation time to avoid spiraling into worsening framerates.
				const float TimeSpent = FPlatformTime::Seconds()-StartTime;
				if ( TimeSpent > SimulationTimeLimit )
				{
					NumIterations = Iteration + 1;
					break;
				}
			}
		}
#if STATS
		STAT_FluidSimulationCount += NumIterations;
		STAT_FluidSimulationValue += FPlatformTime::Cycles() - Timer;
#endif
	}
	
	{
#if STATS
		const uint32 Timer = FPlatformTime::Cycles();
#endif
		if (bEnableCPUSimulation)
		{
			bUpdateSuccessful = UpdateRenderData();
		}
#if STATS
		STAT_FluidTessellationValue += FPlatformTime::Cycles() - Timer;
#endif
	}
	
	if (bUpdateSuccessful)
	{
		// Setting this to FALSE makes Dispose() notify all other threads that we're done.
		bWorkerThreadUpdateOnly = false;
	}
	else
	{
		bWorkerThreadUpdateOnly = true;
		// Potentially yield the thread (letting other threads run on this core if necessary).
		FPlatformProcess::Sleep(0.f);
		// Re-add this work to the end of work pool (letting other works execute until we try again).
		GThreadPool->AddQueuedWork( this );
	}
}

/** Called from the worker thread requesting that we do work now. */
void FFluidSimulation::DoThreadedWork()
{
	DoWork();
	// Were we completely done with the simulation in DoWork? (Otherwise DoWork would've queued up another job.)
	if (bWorkerThreadUpdateOnly == false)
	{
		// Tell the other threads that the worker thread is done, using "release semantics".
		FGenericPlatformMisc::MemoryBarrier();
		FPlatformAtomics::InterlockedExchange(&bSimulationBusy,false);
	}
}

void FFluidSimulation::CalculateNormal(const float* Height, int32 X, int32 Y, float HeightScale, FVector2f& HeightDelta) const
{
	const float H0 = SURFHEIGHT( Height, X-1, Y-1 );
	const float H1 = SURFHEIGHT( Height, X,   Y-1 );
	const float H2 = SURFHEIGHT( Height, X+1, Y-1 );
	const float H3 = SURFHEIGHT( Height, X-1, Y   );
	const float H5 = SURFHEIGHT( Height, X+1, Y   );
	const float H6 = SURFHEIGHT( Height, X-1, Y+1 );
	const float H7 = SURFHEIGHT( Height, X,   Y+1 );
	const float H8 = SURFHEIGHT( Height, X+1, Y+1 );
	HeightDelta.X = (H8 - H0 + H2 - H6 + H5 - H3);
	HeightDelta.Y = (H8 - H0 + H6 - H2 + H7 - H1);
}

void FFluidSimulation::UpdateBorderGeometry(FFluidVertex* InVertices) const
{
	FFluidVertex Vertex;
	Vertex.Height		= 0.0f;
	Vertex.HeightDelta	= FVector2f( 0.0f, 0.0f );
	Vertex.UV			= FVector2f( 0.0f, 0.0f );

	// Calculate the UV-coords for the inner rectangle (which corresponds to the simulation grid).
	FVector2f UpperLeft, LowerRight;
	UpperLeft.X		= static_cast<float>(SimulationPos[CurrentHeightMap].X) / static_cast<float>(TotalSize.X);
	UpperLeft.Y		= static_cast<float>(SimulationPos[CurrentHeightMap].Y) / static_cast<float>(TotalSize.Y);
	LowerRight.X	= static_cast<float>(SimulationPos[CurrentHeightMap].X + NumCellsX) / static_cast<float>(TotalSize.X);
	LowerRight.Y	= static_cast<float>(SimulationPos[CurrentHeightMap].Y + NumCellsY) / static_cast<float>(TotalSize.Y);

	// Add a small offset to make it overlap the simulation grid a bit, to hide the seam.
	// This really only works for single pass, mostly opaque materials.
	static float OverlapOffset = 0.2f;
	FVector2f UpperLeftOffset, LowerRightOffset;
	UpperLeftOffset.X	= (SimulationPos[CurrentHeightMap].X + OverlapOffset) / static_cast<float>(TotalSize.X);
	UpperLeftOffset.Y	= (SimulationPos[CurrentHeightMap].Y + OverlapOffset) / static_cast<float>(TotalSize.Y);
	LowerRightOffset.X	= (SimulationPos[CurrentHeightMap].X + NumCellsX - OverlapOffset) / static_cast<float>(TotalSize.X);
	LowerRightOffset.Y	= (SimulationPos[CurrentHeightMap].Y + NumCellsY - OverlapOffset) / static_cast<float>(TotalSize.Y);

	const int32 NumLowResVertsPerSideX = NumLowResCellsPerSideX + 1;
	const int32 NumLowResVertsPerSideY = NumLowResCellsPerSideY + 1;

	// Upper left patch
	// Uses the offset boundary in the X direction to make it overlap with the simulation grid,
	// And the actual boundary in the Y direction to make other patches overlap with it.
	const float UpperLeftSectionScaleY = LowerRight.Y / static_cast<float>(NumLowResCellsPerSideY);
	for (int32 Y = 0; Y < NumLowResVertsPerSideY; Y++)
	{
		for (int32 X = 0; X < NumLowResVertsPerSideX; X++)
		{
			// Apply a pow(XCurve, 2) so that we will get somewhat better tessellation near the simulated grid
			const float XCurve = 1.0f - X / static_cast<float>(NumLowResCellsPerSideX);
			Vertex.UV.Set((1.0f - XCurve * XCurve) * UpperLeftOffset.X, Y * UpperLeftSectionScaleY);
			InVertices[Y * NumLowResVertsPerSideX + X] = Vertex;
		}
	}
	InVertices += NumLowResVertsPerSideY * NumLowResVertsPerSideX;

	// Lower left patch
	const float LowerLeftSectionScaleX = LowerRight.X / static_cast<float>(NumLowResCellsPerSideX);
	const float LowerLeftSectionTranslationY = LowerRightOffset.Y;
	for (int32 Y = 0; Y < NumLowResVertsPerSideY; Y++)
	{
		const float YCurve = Y / static_cast<float>(NumLowResCellsPerSideY);
		for (int32 X = 0; X < NumLowResVertsPerSideX; X++)
		{
			Vertex.UV.Set(X * LowerLeftSectionScaleX, LowerLeftSectionTranslationY + YCurve * YCurve * (1.0f - LowerRightOffset.Y));
			InVertices[Y * NumLowResVertsPerSideX + X] = Vertex;
		}
	}
	InVertices += NumLowResVertsPerSideY * NumLowResVertsPerSideX;
	
	// Lower right patch
	const float LowerRightSectionTranslationX = LowerRightOffset.X;
	const float LowerRightSectionTranslationY = UpperLeft.Y;
	const float LowerRightSectionScaleY = (1.0f - UpperLeft.Y) / static_cast<float>(NumLowResCellsPerSideY);
	for (int32 Y = 0; Y < NumLowResVertsPerSideY; Y++)
	{
		for (int32 X = 0; X < NumLowResVertsPerSideX; X++)
		{
			const float XCurve = X / static_cast<float>(NumLowResCellsPerSideX);
			Vertex.UV.Set(LowerRightSectionTranslationX + XCurve * XCurve * (1.0f - LowerRightOffset.X), LowerRightSectionTranslationY + Y * LowerRightSectionScaleY);
			InVertices[Y * NumLowResVertsPerSideX + X] = Vertex;
		}
	}
	InVertices += NumLowResVertsPerSideY * NumLowResVertsPerSideX;

	// Upper right patch
	const float UpperRightSectionTranslationX = UpperLeft.X;
	const float UpperRightSectionScaleX = (1.0f - UpperLeft.X) / static_cast<float>(NumLowResCellsPerSideX);
	for (int32 Y = 0; Y < NumLowResVertsPerSideY; Y++)
	{
		const float YCurve = 1.0f - Y / static_cast<float>(NumLowResCellsPerSideY);
		for (int32 X = 0; X < NumLowResVertsPerSideX; X++)
		{
			Vertex.UV.Set(UpperRightSectionTranslationX + X * UpperRightSectionScaleX, (1.0f - YCurve * YCurve) * UpperLeftOffset.Y);
			InVertices[Y * NumLowResVertsPerSideX + X] = Vertex;
		}
	}
}

bool FFluidSimulation::UpdateRenderData()
{
	UpdateBorderGeometry(BorderVertices);
	// Store the simulation position that was used to update render data for the rendering thread
	RenderDataPosition[SimulationIndex] = SimulationPos[CurrentHeightMap];

	// Update the vertices on the grid, iterating over the grid vertices.
	// Setup default packing order, starting at the negative limits of the grid and working toward the positive
	FVector2f UVOrigin( static_cast<float>(SimulationPos[CurrentHeightMap].X)/static_cast<float>(TotalSize.X), static_cast<float>(SimulationPos[CurrentHeightMap].Y)/static_cast<float>(TotalSize.Y) );
	FVector2f StepUV( 1.0f/TotalSize.X, 1.0f/TotalSize.Y );
	int32 StartY = 0;
	int32 EndY = NumCellsY;
	int32 IncY = 1;
	int32 StartX = 0;
	int32 EndX = NumCellsX;
	int32 IncX = 1;

	int32 NormalStartY = 1;
	int32 NormalEndY = NumCellsY;
	int32 NormalStartX = 1;
	int32 NormalEndX = NumCellsX;

	// Pack the render data from back-to-front based on the view direction if the material is translucent
	FVector EffectiveViewDirection = LastViewDirection[SimulationIndex];
	if ( bOpaqueMaterial )
	{
		// Reverse the view direction so that the render data will be packed from front-to-back for opaque materials
		EffectiveViewDirection = -LastViewDirection[SimulationIndex];
	}

	const bool bPositiveViewDirX = EffectiveViewDirection.X > 0.0f;
	// If the view direction's X component is positive, pack from the positive x limit to the negative x limit
	if (bPositiveViewDirX)
	{
		UVOrigin.X = static_cast<float>(SimulationPos[CurrentHeightMap].X + NumCellsX) / static_cast<float>(TotalSize.X);
		StepUV.X = -1.0f / TotalSize.X;
		StartX = NumCellsX;
		EndX = 0;
		IncX = -1;
		NormalStartX = NumCellsX - 1;
		NormalEndX = 1;
	}

	const bool bPositiveViewDirY = EffectiveViewDirection.Y > 0.0f;
	// If the view direction's Y component is positive, pack from the positive y limit to the negative y limit
	if (bPositiveViewDirY)
	{
		UVOrigin.Y = static_cast<float>(SimulationPos[CurrentHeightMap].Y + NumCellsY) / static_cast<float>(TotalSize.Y);
		StepUV.Y = -1.0f / TotalSize.Y;
		StartY = NumCellsY;
		EndY = 0;
		IncY = -1;
		NormalStartY = NumCellsY - 1;
		NormalEndY = 1;
	}

	// Culling needs to be reversed if either (but not both) of the packing directions were swapped.
	bReverseCulling[SimulationIndex] = bPositiveViewDirX != bPositiveViewDirY;

	int32 VertexIndex = 0;
	FFluidVertex Vertex;
	Vertex.HeightDelta = FVector2f( 0.0f, 0.0f );
	float* Height = HeightMap[CurrentHeightMap];
	const float MainScale = (Component->bShowFluidSimulation && bEnableCPUSimulation) ? Component->FluidHeightScale : 0.0f;

	const bool bIterateYFirst = FMath::Abs(EffectiveViewDirection.Y) > FMath::Abs(EffectiveViewDirection.X);
	// Iterate over Y first if the view direction is closer to the Y axis
	if (bIterateYFirst)
	{
		for ( int32 Y=StartY; Y >= 0 && Y <= NumCellsY; Y += IncY )
		{
			Vertex.UV	= UVOrigin;
			const int32 IndexY = Y * GridPitch;
			for ( int32 X=StartX; X >= 0 && X <= NumCellsX; X += IncX, ++VertexIndex )
			{
				const int32 Index			= IndexY + X;
				Vertex.Height			= Height[ Index ] * MainScale;
				Vertices[VertexIndex]	= Vertex;
				Vertex.UV.X				+= StepUV.X;
			}
			UVOrigin.Y	+= StepUV.Y;
		}
	}
	else
	{
		// Reverse culling if it is not reversed already
		bReverseCulling[SimulationIndex] = bReverseCulling[SimulationIndex] != 1;
		for ( int32 X=StartX; X >= 0 && X <= NumCellsX; X += IncX )
		{
			Vertex.UV	= UVOrigin;
			for ( int32 Y=StartY; Y >= 0 && Y <= NumCellsY; Y += IncY, ++VertexIndex )
			{
				const int32 Index			= Y * GridPitch + X;
				Vertex.Height			= Height[ Index ] * MainScale;
				Vertices[VertexIndex]	= Vertex;
				Vertex.UV.Y				+= StepUV.Y;
			}
			UVOrigin.X	+= StepUV.X;
		}
	}

	bool bShowNormals = Component->bShowSimulationNormals;
	if (bShowNormals && DebugPositions.Num() == 0)
	{
		DebugPositions.AddZeroed( NumVertices );
		DebugNormals.AddZeroed( NumVertices );
	}

	// Calculate surface normals
	float HeightScale = Component->LightingContrast * MainScale/CellWidth;

	// Iterate in the same order that vertex positions were packed
	if (bIterateYFirst)
	{
		bUseYFirstIndexBuffer[SimulationIndex] = true;
		// Start at [1, 1]
		VertexIndex = NumCellsX + 2;
		for ( int32 Y=NormalStartY; Y > 0 && Y < NumCellsY; Y += IncY )
		{
			for ( int32 X=NormalStartX; X > 0 && X < NumCellsX; X += IncX )
			{
				CalculateNormal( Height, X, Y, HeightScale, Vertices[VertexIndex].HeightDelta );

				if (bShowNormals)
				{
					FVector VX( 6.0f, 0.0f, Vertices[VertexIndex].HeightDelta.X*HeightScale );
					FVector VY( 0.0f, 6.0f, Vertices[VertexIndex].HeightDelta.Y*HeightScale );
					FVector Normal = (VX ^ VY).GetUnsafeNormal();
					FVector Position;
					Position.X = (Vertices[VertexIndex].UV.X - 0.5f) * TotalWidth;
					Position.Y = (Vertices[VertexIndex].UV.Y - 0.5f) * TotalHeight;
					Position.Z = SURFHEIGHT( Height, X, Y ) * MainScale;
					DebugPositions[VertexIndex] = Component->GetRenderMatrix().TransformPosition(Position);
					DebugNormals[VertexIndex] = Normal;
				}
				VertexIndex++;
			}
			// Skip over the last and first columns
			VertexIndex += 2;
		}
	}
	else
	{
		bUseYFirstIndexBuffer[SimulationIndex] = false;
		// Start at [1, 1]
		VertexIndex = NumCellsY + 2;
		for ( int32 X=NormalStartX; X > 0 && X < NumCellsX; X += IncX )
		{
			for ( int32 Y=NormalStartY; Y > 0 && Y < NumCellsY; Y += IncY )
			{
				CalculateNormal( Height, X, Y, HeightScale, Vertices[VertexIndex].HeightDelta );

				if (bShowNormals)
				{
					FVector VX( 6.0f, 0.0f, Vertices[VertexIndex].HeightDelta.X*HeightScale );
					FVector VY( 0.0f, 6.0f, Vertices[VertexIndex].HeightDelta.Y*HeightScale );
					FVector Normal = (VX ^ VY).GetUnsafeNormal();
					FVector Position;
					Position.X = (Vertices[VertexIndex].UV.X - 0.5f) * TotalWidth;
					Position.Y = (Vertices[VertexIndex].UV.Y - 0.5f) * TotalHeight;
					Position.Z = SURFHEIGHT( Height, X, Y ) * MainScale;
					DebugPositions[VertexIndex] = Component->GetRenderMatrix().TransformPosition(Position);
					DebugNormals[VertexIndex]  = Normal;
				}
				VertexIndex++;
			}
			// Skip over the last and first rows
			VertexIndex += 2;
		}
	}
	return true;
}

void FFluidSimulation::GameThreadTick(float InDeltaTime)
{
	FPlatformAtomics::InterlockedIncrement(&SimulationRefCount);
	
	ENQUEUE_RENDER_COMMAND(FluidTickSimulation)(
[this, InDeltaTime](FRHICommandListImmediate& RHICmdList)
	{
		RenderThreadTick(RHICmdList, InDeltaTime );
	});
}

/** @return TRUE if the linecheck passed (didn't hit anything) */
bool FFluidSimulation::LineCheck(FHitResult& Result, const FVector& End, const FVector& Start, const FVector& Extent) const
{
	const FVector Direction = End - Start;
	const FVector& Normal = Plane;

	if ( Extent.IsZero() )
	{
		// Calculate the intersection point32 (line vs plane).
		const float Denom = Normal | Direction;
		if ( FMath::Abs(Denom) < UE_KINDA_SMALL_NUMBER )	// Parallel to the fluid surface?
		{
			return true;
		}
		const float HitTime = (Normal | (Normal * Plane.W - Start)) / Denom;
		if ( HitTime < 0.0f || HitTime > 1.0f )	// Didn't intersect the fluid surface plane?
		{
			return true;
		}
		const FVector Intersection = Start + HitTime*Direction;

		// Check if the intersection point32 is inside the fluid (i.e. it's inside all of the edges).
		if ( Edges[0].PlaneDot(Intersection) <= 0 &&
			 Edges[1].PlaneDot(Intersection) <= 0 &&
			 Edges[2].PlaneDot(Intersection) <= 0 &&
			 Edges[3].PlaneDot(Intersection) <= 0 )
		{
			Result.Time = HitTime;
			Result.Normal = Normal;
			Result.Location = Intersection;
			return false;
		}
	}
	else
	{
		//@TODO optimize... All transforms are very expensive, especially TransformBy().
		const FVector LocalStart = FluidWorldToLocal.TransformPosition( Start );
		const FVector LocalEnd = FluidWorldToLocal.TransformPosition( End );
		FBox LocalBox( -Extent, Extent );
		LocalBox = LocalBox.TransformBy( FluidWorldToLocal );
		const FVector LocalExtent = LocalBox.GetExtent();
		const FBox Bbox( FVector(-TotalWidth*0.5f, -TotalHeight*0.5f, -10.0f), FVector(TotalWidth*0.5f, TotalHeight*0.5f, 10.0f) );
		FVector HitLocation, HitNormal;
		float HitTime;
		if ( FMath::LineExtentBoxIntersection( Bbox, LocalStart, LocalEnd, LocalExtent, HitLocation, HitNormal, HitTime ) )
		{
			Result.Time = HitTime;
			Result.Normal = Normal;
			Result.Location = Start + HitTime*Direction;
			return false;
		}
	}

	return true;
}

bool FFluidSimulation::PointCheck(FHitResult& Result, const FVector& Location, const FVector& Extent) const
{
	const FBox MyBbox( FVector(-TotalWidth*0.5f, -TotalHeight*0.5f, -10.0f), FVector(TotalWidth*0.5f, TotalHeight*0.5f, 10.0f) );
	FBox OtherBbox( Location - Extent, Location + Extent );
	OtherBbox = OtherBbox.TransformBy( FluidWorldToLocal );
	if ( MyBbox.Intersect( OtherBbox ) )
	{
		const float HeightDist = Plane.PlaneDot(Location);
		Result.Normal = Plane;
		Result.Location = Location + Result.Normal * FMath::Max<float>(20.0f - HeightDist, 0.0f);
		return false;
	}
	return true;
}

void FFluidSimulation::SetExtents(const FMatrix& InLocalToWorld, const FPlane& InPlane, const FPlane* InEdges)
{
	FluidWorldToLocal = InLocalToWorld.Inverse();
	Plane = InPlane;
	Edges[0] = InEdges[0];
	Edges[1] = InEdges[1];
	Edges[2] = InEdges[2];
	Edges[3] = InEdges[3];
}

bool FFluidSimulation::IsActive() const
{
	return (bEnableCPUSimulation || bEnableGPUSimulation);
}

void FFluidSimulation::AddForce(const FVector& LocalPos, float Strength, float LocalRadius, float bImpulse)
{
	if ( bEnableCPUSimulation || bEnableGPUSimulation )
	{
		const int32 Index = FluidForces[1 - SimulationIndex].AddDefaulted(1);
		FFluidForce& Force = FluidForces[1 - SimulationIndex][ Index ];
		Force.LocalPos	= LocalPos;
		Force.Strength	= bImpulse ? (Strength*40.0f) : (Strength/2.0f);
		Force.Radius	= LocalRadius;
		Force.bImpulse	= bImpulse;
	}
}

void FFluidSimulation::RenderThreadTick(FRHICommandList& RHICmdList, float InDeltaTime)
{
	SCOPE_CYCLE_COUNTER( STAT_FluidRenderthreadBlocked );
	// Resources aren't locked the first time, or if the device has been lost.
	if ( bResourcesLocked == false )
	{
		if ( ShouldSimulate() )
		{
			// Start the next simulation step.
			FPlatformAtomics::InterlockedExchange(&SimulationRefCount,true);
			DeltaTime = InDeltaTime;
			LockResources(RHICmdList);
			if ( GThreadedFluidSimulation )
			{
				GThreadPool->AddQueuedWork( this );
			}
		}
	}

	if ( GThreadedFluidSimulation )
	{
		// Wait until the current simulation is complete.
		BlockOnSimulation();
	}
	else if ( ShouldSimulate() )
	{
		DoWork();
	}

	UnlockResources(RHICmdList);
	// Render the full simulation geometry or a simple flat quad (should still show the simulation if bPause is TRUE).
	bShowSimulation = Component->bShowFluidSimulation && ShouldSimulate();
#if STATS
	{
		for(int32 i = 0;i<STAT_FluidSimulationValue;i++)
		{
			SCOPE_CYCLE_COUNTER(STAT_FluidSimulation);
		}
		STAT_FluidSimulationValue = 0;
		for(int32 i = 0;i<STAT_FluidSimulationCount;i++)
		{
			SCOPE_CYCLE_COUNTER(STAT_FluidSimulation);
		}
		STAT_FluidSimulationCount = 0;
	}
	{
		for(int32 i = 0;i<STAT_FluidTessellationValue;i++)
		{
			SCOPE_CYCLE_COUNTER(STAT_FluidTessellation);
		}
		STAT_FluidTessellationValue = 0;
	}
#endif
	
	// Run the GPU simulation of the detail grid
	if ( bEnableGPUSimulation && !Component->bPause )
	{
		DetailGPUResource.Tick( RHICmdList,
			DeltaTime, 
			FluidForces[SimulationIndex], 
			Component->DetailUpdateRate,
			Component->DetailDamping,
			Component->DetailTravelSpeed,
			Component->DetailTransfer,
			Component->DetailHeightScale,
			Component->bTiling
			);
	}
	
	// Transfer some results from the simulation to the renderthread.
	SimulationActivity = bSimulationDirty ? 100.0f : FMath::Abs<float>(CurrentSum + PrevSum);

	// Swap buffers.
	SimulationIndex = 1 - SimulationIndex;

	FluidForces[1 - SimulationIndex].Reset();
	DeltaTime = InDeltaTime;
	if ( ShouldSimulate() )
	{
		// Start the next simulation step.
		FPlatformAtomics::InterlockedExchange(&SimulationRefCount,true);
		FGenericPlatformMisc::MemoryBarrier();
		LockResources(RHICmdList);
		if ( GThreadedFluidSimulation )
		{
			GThreadPool->AddQueuedWork( this );
		}
	}
	FPlatformAtomics::InterlockedDecrement(&SimulationRefCount);
}

void FFluidSimulation::BlockOnSimulation()
{
	const uint32 IdleStart = FPlatformTime::Cycles();

	//@TODO: Implement with an OS call
	while ( GThreadedFluidSimulation && bSimulationBusy )
	{
		FPlatformProcess::Sleep(0.f);
	}

	for (int32 Index = 0; Index < ERenderThreadIdleTypes::Num; Index++)
	{
		GRenderThreadIdle[Index] += FPlatformTime::Cycles() - IdleStart;
	}
}

bool FFluidSimulation::IsClampedVertex(int32 X, int32 Y) const
{
	return (bEnableCPUSimulation == 1 && DISABLE_CLAMPMAP == 0) ? Component->ClampMap[ Y*(NumCellsX + 1) + X ] == 1 : false;
}

void FFluidSimulation::ApplyForce(const FVector& InLocalPos, float Strength, float LocalRadius)
{
	if ( Component->bPause || bEnableCPUSimulation == false )
	{
		return;
	}

	const int32 HeightMapIndex = 1 - CurrentHeightMap;
	float* Height = HeightMap[HeightMapIndex];

	FVector LocalPos( InLocalPos );
	LocalPos.X += TotalWidth*0.5f - SimulationPos[HeightMapIndex].X*CellWidth;
	LocalPos.Y += TotalHeight*0.5f - SimulationPos[HeightMapIndex].Y*CellHeight;
	const float LocalRadius2 = LocalRadius * LocalRadius;
	const float ForceFactor = /*CellWidth * */CellWidth / UE_PI;
	const float Force = ForceFactor * Strength / (UpdateRate * LocalRadius2);

	// Apply the force to the coarse grid.
	{
		const FIntRect ForceRect(
			FMath::Max<int32>( FMath::Floor((LocalPos.X - LocalRadius) / CellWidth), 1 ),
			FMath::Max<int32>( FMath::Floor((LocalPos.Y - LocalRadius) / CellHeight), 1 ),
			FMath::Min<int32>( FMath::CeilToInt((LocalPos.X + LocalRadius) / CellWidth), NumCellsX ),
			FMath::Min<int32>( FMath::CeilToInt((LocalPos.Y + LocalRadius) / CellHeight), NumCellsY )
			);
		FVector2D ForceOrigin( ForceRect.Min.X*CellWidth, ForceRect.Min.Y*CellHeight );
		for (int32 Y = ForceRect.Min.Y; Y < ForceRect.Max.Y; ++Y)
		{
			FVector2D Pos( ForceOrigin );
			for (int32 X = ForceRect.Min.X; X < ForceRect.Max.X; ++X)
			{
//				if ( !IsClampedVertex(X, Y) )
				{
					const float R2 = FMath::Square(Pos.X - LocalPos.X) + FMath::Square(Pos.Y - LocalPos.Y);
					if (R2 < LocalRadius2)
					{
						float R = LocalRadius2 - R2;
// 						if ( DetailRect.Contains(DetailPos) )
// 						{
// 							R *= (1.0f - DetailStrengthScale);
// 						}

						SURFHEIGHT(Height, X, Y) += R * Force;
						bSimulationDirty = true;
					}
				}
				Pos.X += CellWidth;
			}
			ForceOrigin.Y += CellHeight;
		}
	}
}

void FFluidSimulation::Simulate(float InDeltaTime)
{
	FIntPoint PrevPosition		= SimulationPos[1 - CurrentHeightMap];
	FIntPoint PrevPrevPosition	= SimulationPos[CurrentHeightMap];
	FIntPoint NewPosition		= PendingSimulationPos;
	FIntPoint PrevPrevOffset	= NewPosition - PrevPrevPosition;
	FIntPoint PrevOffset		= NewPosition - PrevPosition;
	float* NewHeights			= NULL;

	// Calculate the area in the new placement that was also covered by the two older placements.
	FIntPoint SimulationSize( NumCellsX, NumCellsY );
	FIntRect PrevPrevRect( PrevPrevPosition, PrevPrevPosition + SimulationSize );
	FIntRect PrevRect( PrevPosition, PrevPosition + SimulationSize );
	FIntRect NewRect( NewPosition, NewPosition + SimulationSize );
	FIntRect Rect( PrevPrevRect );
	Rect.Clip( PrevRect );
	Rect.Clip( NewRect );
	Rect -= NewPosition;
	if ( Rect.Width() == 0 )
	{
		Rect.Min.X = Rect.Max.X = 0;
	}
	if ( Rect.Height() == 0 )
	{
		Rect.Min.Y = Rect.Max.Y = 0;
	}

	// Simulate the fluid heightmap.
	NewHeights				= HeightMap[ CurrentHeightMap ];
	float* PrevPrevHeights	= HeightMap[ CurrentHeightMap ];
	float* PrevHeights		= HeightMap[ 1 - CurrentHeightMap ];
	const float DampFactor	= FMath::Clamp<float>(1.0f - (Component->FluidDamping / 30.0f), 0.0f, 1.0f);

	// Setup the iteration order to ensure we don't write to NewHeights before we've read from PrevPrevHeights.
	int32 StartX, EndX, StepX, StartY, EndY, StepY;
	if ( PrevPrevOffset.X >= 0 )
	{
		StartX		= FMath::Min<int32>(Rect.Min.X+1, Rect.Max.X);
		EndX		= Rect.Max.X;
		StepX		= 1;
	}
	else
	{
		StartX		= FMath::Max<int32>(Rect.Max.X-1, Rect.Min.X);
		EndX		= Rect.Min.X;
		StepX		= -1;
	}
	if ( PrevPrevOffset.Y >= 0 )
	{
		StartY		= FMath::Min<int32>(Rect.Min.Y+1, Rect.Max.Y);
		EndY		= Rect.Max.Y;
		StepY		= 1;
	}
	else
	{
		StartY		= FMath::Max<int32>(Rect.Max.Y-1, Rect.Min.Y);
		EndY		= Rect.Min.Y;
		StepY		= -1;
	}
	{
		int32 Y				= StartY;
		int32 PrevY			= Y + PrevOffset.Y;
		int32 PrevPrevY		= Y + PrevPrevOffset.Y;
		float TravelSpeed	= Component->FluidTravelSpeed;
		PrevSum				= CurrentSum;
		CurrentSum			= 0.0f;
		while ( Y != EndY )
		{
			int32 X			= StartX;
			int32 PrevX		= X + PrevOffset.X;
			int32 PrevPrevX	= X + PrevPrevOffset.X;
			while ( X != EndX )
			{
				//			if ( !IsClampedVertex(X, Y) )	// See if we are simulating this vertex.
				{
					const float Neighbors =
						SURFHEIGHT(PrevHeights, PrevX-1, PrevY) + 
						SURFHEIGHT(PrevHeights, PrevX+1, PrevY) +
						SURFHEIGHT(PrevHeights, PrevX, PrevY-1) + 
						SURFHEIGHT(PrevHeights, PrevX, PrevY+1);
					const float Current		= SURFHEIGHT(PrevHeights, PrevX, PrevY);
					const float Current4	= 4.0f * Current;
					const float Curve		= Current4 + TravelSpeed * (Neighbors - Current4);
					float NewHeight			= Curve * 0.5f - SURFHEIGHT(PrevPrevHeights, PrevPrevX, PrevPrevY);
					NewHeight				*= DampFactor;
					CurrentSum				+= FMath::Abs<float>(NewHeight);
					SURFHEIGHT(NewHeights, X, Y) = NewHeight;
				}
				X			+= StepX;
				PrevX		+= StepX;
				PrevPrevX	+= StepX;
			}
			Y				+= StepY;
			PrevY			+= StepY;
			PrevPrevY		+= StepY;
		}
	}

	SimulationPos[ CurrentHeightMap ] = NewPosition;
	bSimulationDirty = false;
	{
		// Zero out any regions that weren't touched by the simulation.
		for ( int32 Y=1; Y <= Rect.Min.Y; ++Y )
		{
			for ( int32 X=1; X <= NumCellsX; ++X )
			{
				SURFHEIGHT(NewHeights, X, Y) = 0.0f;
			}
		}
		if ( Rect.Min.X > 0 || Rect.Max.X < NumCellsX )
		{
			for ( int32 Y=Rect.Min.Y+1; Y < Rect.Max.Y; ++Y )
			{
				for ( int32 X=1; X <= Rect.Min.X; ++X )
				{
					SURFHEIGHT(NewHeights, X, Y) = 0.0f;
				}
				for ( int32 X=Rect.Max.X; X < NumCellsX; ++X )
				{
					SURFHEIGHT(NewHeights, X, Y) = 0.0f;
				}
			}
		}
		for ( int32 Y=Rect.Max.Y; Y < NumCellsY; ++Y )
		{
			for ( int32 X=1; X <= NumCellsX; ++X )
			{
				SURFHEIGHT(NewHeights, X, Y) = 0.0f;
			}
		}
	}
}

void FFluidSimulation::InitResources()
{
	BeginInitResource(&FlatIndexBuffer);
	BeginInitResource(&YFirstIndexBuffer);
	BeginInitResource(&XFirstIndexBuffer);
	BeginInitResource(&FlatQuadIndexBuffer);
	BeginInitResource(&FlatQuadVertexBuffer);
	for ( int32 BufferIndex=0; BufferIndex < 2; ++BufferIndex )
	{
		BeginInitResource( &VertexBuffers[BufferIndex] );
		BeginInitResource( &FlatVertexBuffers[BufferIndex] );

		// Allocate texture memory on the gamethread, so we can stream out high-res mips if we need to make room.
		FTexture2DResourceMem* ResourceMem = CreateTextureResourceMemory();

		ENQUEUE_RENDER_COMMAND(CreateHeightmapTexture)(
		[this, BufferIndex, ResourceMem](FRHICommandListImmediate& RHICmdList)
			{
				RenderThreadInitResources(RHICmdList, BufferIndex, ResourceMem);
			});

		BeginInitResource(&VertexFactories[BufferIndex]);
		BeginInitResource(&FlatVertexFactories[BufferIndex]);
		BeginInitResource(&FlatQuadVertexFactory);
	}
	
	// Don't try to lock the index buffer if we are running a commandlet
	if(!IsRunningCommandlet())
	{
		//@todo: Use indexed triangle strips to save on index buffer memory
		ENQUEUE_RENDER_COMMAND(InitYFirstIndexBuffer)(
		[this](FRHICommandListImmediate& RHICmdList)
		{
			InitIndexBufferX(RHICmdList);
			InitIndexBufferY(RHICmdList);
		});

		ENQUEUE_RENDER_COMMAND(InitFlatIndexBufferCommand)(
		[this](FRHICommandListImmediate& RHICmdList)
		{
			InitFlatIndexBuffer(RHICmdList);
		});
	}
	
	if ( bEnableGPUSimulation )
	{
		BeginInitResource(&DetailGPUResource);
	}
}

void FFluidSimulation::ReleaseResources(bool bBlockOnRelease)
{
	ENQUEUE_RENDER_COMMAND(StopFluidSimulation)(
	[this] (FRHICommandListBase&)
	{
		BlockOnSimulation();
	});

	BeginReleaseResource(&FlatIndexBuffer);
	BeginReleaseResource(&YFirstIndexBuffer);
	BeginReleaseResource(&XFirstIndexBuffer);
	BeginReleaseResource(&VertexBuffers[0]);
	BeginReleaseResource(&VertexBuffers[1]);
	BeginReleaseResource(&FlatVertexBuffers[0]);
	BeginReleaseResource(&FlatVertexBuffers[1]);
	BeginReleaseResource(&FlatQuadVertexBuffer);
	BeginReleaseResource(&FlatQuadIndexBuffer);
	BeginReleaseResource(&VertexFactories[0]);
	BeginReleaseResource(&VertexFactories[1]);
	BeginReleaseResource(&FlatVertexFactories[0]);
	BeginReleaseResource(&FlatVertexFactories[1]);
	BeginReleaseResource(&DetailGPUResource);
	BeginReleaseResource(&FlatQuadVertexFactory);
	
	ReleaseResourcesFence.BeginFence();
	if ( bBlockOnRelease )
	{
		ReleaseResourcesFence.Wait();
	}
}

bool FFluidSimulation::IsReleased() const
{
	const bool bStillSimulating = bSimulationBusy && GThreadedFluidSimulation;
	return ReleaseResourcesFence.IsFenceComplete() && !bStillSimulating && (SimulationRefCount == 0);
}

/**
 * Allocates texture memory on the gamethread. Will try to stream out high-res mip-levels if there's not enough room.
 * @return	A new FTexture2DResourceMem object, representing the allocated memory, or NULL if the allocation failed.
 */
FTexture2DResourceMem* FFluidSimulation::CreateTextureResourceMemory()
{
	return NULL;
}

void FFluidSimulation::RenderThreadInitResources(FRHICommandListBase& RHICmdList, int32 BufferIndex, FTexture2DResourceMem* ResourceMem)
{
	UE_LOG(FluidSurface, Warning, TEXT("Init Render Resource, BufferIndex: %i"), BufferIndex);
	VertexFactories[BufferIndex].InitResources(RHICmdList, VertexBuffers[BufferIndex], this );
	FlatVertexFactories[BufferIndex].InitResources(RHICmdList, FlatVertexBuffers[BufferIndex], this );
	FlatQuadVertexFactory.InitResources(RHICmdList, FlatQuadVertexBuffer, this );
	UE_LOG(FluidSurface, Warning, TEXT("End Init Render Resource, BufferIndex: %i"), BufferIndex);
}

void FFluidSimulation::InitIndexBufferX(FRHICommandListBase& RHICmdList)
{
	// Generate the indices on the grid, iterating over the grid cells.
	PLATFORM_INDEX_TYPE* Indices = static_cast<PLATFORM_INDEX_TYPE*>(YFirstIndexBuffer.Lock(RHICmdList));
	PLATFORM_INDEX_TYPE* CurrentIndices = Indices;
	PLATFORM_INDEX_TYPE IndexOrigin	= 0;
	const int32 NumX = GetNumCellsX();
	const int32 NumY = GetNumCellsY();

#if DISALLOW_32BIT_INDICES
	// validate data
	checkf((NumX + 1) * (NumY + 1) <= 65535, TEXT("FFluidSimulation::InitIndexBufferX: Fluid surface of size %d x %d is too big for this platform (must be less than 65535 verts)"), NumX, NumY);
#endif
	
	const uint32 Pitch = NumX + 1;
	for ( int32 Y=0; Y < NumY; ++Y )
	{
		bool bReverseTriangulation = false;
		PLATFORM_INDEX_TYPE Index = IndexOrigin;
		for ( int32 X=0; X < NumX; ++X )
		{
			if ( bReverseTriangulation )
			{
				CurrentIndices[0] = Index + 0;
				CurrentIndices[1] = Index + Pitch + 1;
				CurrentIndices[2] = Index + 1;
				CurrentIndices[3] = Index + 0;
				CurrentIndices[4] = Index + Pitch + 0;
				CurrentIndices[5] = Index + Pitch + 1;
			}
			else
			{
				CurrentIndices[0] = Index + 0;
				CurrentIndices[1] = Index + Pitch + 0;
				CurrentIndices[2] = Index + 1;
				CurrentIndices[3] = Index + Pitch + 0;
				CurrentIndices[4] = Index + Pitch + 1;
				CurrentIndices[5] = Index + 1;
			}
			CurrentIndices		+= 6;
			Index++;
			bReverseTriangulation = !bReverseTriangulation;
		}
		IndexOrigin		+= Pitch;
	}
	YFirstIndexBuffer.Unlock(RHICmdList);
}

void FFluidSimulation::InitIndexBufferY(FRHICommandListBase& RHICmdList)
{
	// Generate the indices on the grid, iterating over the grid cells.
	PLATFORM_INDEX_TYPE* Indices = static_cast<PLATFORM_INDEX_TYPE*>(XFirstIndexBuffer.Lock(RHICmdList));
	PLATFORM_INDEX_TYPE* CurrentIndices = Indices;
	const int32 NumX = GetNumCellsX();
	const int32 NumY = GetNumCellsY();

#if DISALLOW_32BIT_INDICES
	checkf((NumX + 1) * (NumY + 1) <= 65535, TEXT("FFluidSimulation::InitIndexBufferY: Fluid surface of size %d x %d is too big for this platform (must be less than 65535 verts)"), NumX, NumY);
#endif
	
	PLATFORM_INDEX_TYPE IndexOrigin	= 0;
	const uint32 Pitch = NumY + 1;
	for ( int32 X=0; X < NumX; ++X )
	{
		bool bReverseTriangulation = false;
		PLATFORM_INDEX_TYPE Index = IndexOrigin;
		for ( int32 Y=0; Y < NumY; ++Y )
		{
			if ( bReverseTriangulation )
			{
				CurrentIndices[0] = Index + 0;
				CurrentIndices[1] = Index + Pitch + 1;
				CurrentIndices[2] = Index + 1;
				CurrentIndices[3] = Index + 0;
				CurrentIndices[4] = Index + Pitch + 0;
				CurrentIndices[5] = Index + Pitch + 1;
			}
			else
			{
				CurrentIndices[0] = Index + 0;
				CurrentIndices[1] = Index + Pitch + 0;
				CurrentIndices[2] = Index + 1;
				CurrentIndices[3] = Index + Pitch + 0;
				CurrentIndices[4] = Index + Pitch + 1;
				CurrentIndices[5] = Index + 1;
			}
			CurrentIndices		+= 6;
			Index++;
			bReverseTriangulation = !bReverseTriangulation;
		}
		IndexOrigin		+= Pitch;
	}
	XFirstIndexBuffer.Unlock(RHICmdList);
}

void FFluidSimulation::InitFlatIndexBuffer(FRHICommandListBase& RHICmdList)
{
	// Populate the index buffer for the geometry in the deactivated state (vertices are stored row-by-row).
	{
		uint16* Indices = static_cast<uint16*>(FlatIndexBuffer.Lock(RHICmdList));
		const uint32 VertexPitch = NumLowResCellsPerSideX + 1;
		int32 LowResIndex = 0;
		// This index buffer contains the indices of all 4 border geometry patches
		for ( int32 QuadrantIndex=0; QuadrantIndex < 4; ++QuadrantIndex )
		{
			const int32 VertexOffset = QuadrantIndex * VertexPitch * (NumLowResCellsPerSideY + 1);
			for ( uint16 Y=0; Y < NumLowResCellsPerSideY; ++Y )
			{
				for ( uint16 X=0; X < NumLowResCellsPerSideX; ++X )
				{
					Indices[LowResIndex++] = (Y+0)*VertexPitch + (X+1) + VertexOffset;
					Indices[LowResIndex++] = (Y+0)*VertexPitch + (X+0) + VertexOffset;
					Indices[LowResIndex++] = (Y+1)*VertexPitch + (X+0) + VertexOffset;
					Indices[LowResIndex++] = (Y+0)*VertexPitch + (X+1) + VertexOffset;
					Indices[LowResIndex++] = (Y+1)*VertexPitch + (X+0) + VertexOffset;
					Indices[LowResIndex++] = (Y+1)*VertexPitch + (X+1) + VertexOffset;
				}
			}
			
		}
		FlatIndexBuffer.Unlock(RHICmdList);
	}

	// Populate the index buffer for the geometry in the deactivated state (vertices are stored row-by-row).
	{
		uint16* Indices = static_cast<uint16*>(FlatQuadIndexBuffer.Lock(RHICmdList));
		const int32 NumLowResQuadsX = FlatQuadVertexBuffer.GetNumQuadsX();
		const int32 NumLowResQuadsY = FlatQuadVertexBuffer.GetNumQuadsY();
		
		const int32 VertexPitch = NumLowResQuadsX + 1;
		int32 LowResIndex = 0;
		for ( uint16 Y=0; Y < NumLowResQuadsY; ++Y )
		{
			for ( uint16 X=0; X < NumLowResQuadsX; ++X )
			{
				Indices[LowResIndex++] = (Y+0)*VertexPitch + (X+1);
				Indices[LowResIndex++] = (Y+0)*VertexPitch + (X+0);
				Indices[LowResIndex++] = (Y+1)*VertexPitch + (X+0);
				Indices[LowResIndex++] = (Y+0)*VertexPitch + (X+1);
				Indices[LowResIndex++] = (Y+1)*VertexPitch + (X+0);
				Indices[LowResIndex++] = (Y+1)*VertexPitch + (X+1);
			}
		}
		FlatQuadIndexBuffer.Unlock(RHICmdList);
	}
}

void FFluidSimulation::UpdateShaderParameters(int32 OctantID)
{
	// Set up vertexshader tessellation parameters.
	const float HeightScale	= (bShowSimulation && bEnableCPUSimulation) ? Component->FluidHeightScale : 0.0f;
	const float TweakScale	= Component->LightingContrast * HeightScale/CellWidth;
	GridSize.Set(TotalWidth, TotalHeight, TweakScale, 0.0f);

#if ENABLE_XBOX_LEFTOVER
	// Back-to-front sorting is performed by remapping the x/y quad coordinates in the vertex shader:
	//
	// xy' = (a,b)*x + (c,d)*y + (e,f)
	//
	// (a,b) and (c,d) lets us swap x/y and choose direction (+/-). At the same time, they scale the
	// quad coordinates to texture space (0,1).
	//
	// (e,f) is used for inverting coordinates - i.e. 1-x or 1-y.
	//
	// TessellationValues1.xy holds (a,b)
	// TessellationValues2.xy holds (c,d)
	// TessellationValues2.zw holds (e,f)
		static const FVector4 TessellationValues1[8] =
	{
		FVector4(  0, -1, 0, 0 ),	// Tessellate Left, Up			(Viewdir Right, Down, 0-45 degrees)
		FVector4( -1,  0, 0, 0 ),	// Tessellate Up, left			(Viewdir Down, Right, 45-90 degrees)
		FVector4(  1,  0, 0, 0 ),	// Tessellate Up, right			(Viewdir Down, Left, 90-135 degrees)
		FVector4(  0, -1, 0, 0 ),	// Tessellate Right, up			(Viewdir Left, Down, 135-180 degrees)
		FVector4(  0,  1, 0, 0 ),	// Tessellate Right, down		(Viewdir Left, Up, 180-225 degrees)
		FVector4(  1,  0, 0, 0 ),	// Tessellate Down, right		(Viewdir Up, Left, 225-270 degrees)
		FVector4( -1,  0, 0, 0 ),	// Tessellate Down, left		(Viewdir Up, Right, 270-315 degrees)
		FVector4(  0,  1, 0, 0 ),	// Tessellate Left, Down		(Viewdir Right, Up, 315-360 degrees)
	};
	static const FVector4 TessellationValues2[8] =
	{
		FVector4( -1,  0, 1, 1 ),	// Tessellate Left, Up			(Viewdir Right, Down, 0-45 degrees)
		FVector4(  0, -1, 1, 1 ),	// Tessellate Up, left			(Viewdir Down, Right, 45-90 degrees)
		FVector4(  0, -1, 0, 1 ),	// Tessellate Up, right			(Viewdir Down, Left, 90-135 degrees)
		FVector4(  1,  0, 0, 1 ),	// Tessellate Right, up			(Viewdir Left, Down, 135-180 degrees)
		FVector4(  1,  0, 0, 0 ),	// Tessellate Right, down		(Viewdir Left, Up, 180-225 degrees)
		FVector4(  0,  1, 0, 0 ),	// Tessellate Down, right		(Viewdir Up, Left, 225-270 degrees)
		FVector4(  0,  1, 1, 0 ),	// Tessellate Down, left		(Viewdir Up, Right, 270-315 degrees)
		FVector4( -1,  0, 1, 0 ),	// Tessellate Left, Down		(Viewdir Right, Up, 315-360 degrees)
	};
	static const bool ShouldReverseCulling[8] =
	{
		false,
		true,
		false,
		true,
		false,
		true,
		false,
		true,
	};

	// Let the TessellationLevel ramp from 4.0 to 15.999 before changing number of tessellation quads,
	// and then ramp it all over again from 4.0 to 15.999. Etc.
	float QuadFactor		= FMath::Floor( FMath::Log2( Component->GPUTessellationFactor ) / FMath::Log2(static_cast<float>(GPUTESSELLATION) ) );
	QuadFactor				= FMath::Pow( GPUTESSELLATION, QuadFactor - 1.0f );
	TessellationLevel		= Component->GPUTessellationFactor / QuadFactor;
	NumTessQuadsX			= FMath::Max<int32>(FMath::CeilToInt( NumCellsX * QuadFactor ), 1);
	NumTessQuadsY			= FMath::Max<int32>(FMath::CeilToInt( NumCellsY * QuadFactor ), 1);

	TessellationFactors1	= TessellationValues1[ OctantID ];
	TessellationFactors2	= TessellationValues2[ OctantID ];
	bReverseCullingXbox		= ShouldReverseCulling[ OctantID ];
	TexcoordScaleBias[0]	= static_cast<float>(NumCellsX) / static_cast<float>(TotalSize.X);
	TexcoordScaleBias[1]	= static_cast<float>(NumCellsY) / static_cast<float>(TotalSize.Y);
	TexcoordScaleBias[2]	= static_cast<float>(RenderDataPosition[1 - SimulationIndex].X) / static_cast<float>(TotalSize.X);
	TexcoordScaleBias[3]	= static_cast<float>(RenderDataPosition[1 - SimulationIndex].Y) / static_cast<float>(TotalSize.Y);

	// Are we swapping X and Y coordinates?
	if ( FMath::Abs<float>(TessellationFactors1[0]) < 0.1f )
	{
		TessellationParameters.Set( HeightScale, NumTessQuadsY, 1.0f/NumTessQuadsY, 1.0f/NumTessQuadsX );
	}
	else
	{
		TessellationParameters.Set( HeightScale, NumTessQuadsX, 1.0f/NumTessQuadsX, 1.0f/NumTessQuadsY );
	}

	// Multiply (a,b) and (c,d) by (1/NumQuadsX, 1/NumQuadsY), to scale to texture space.
	TessellationFactors1[0]	/= NumTessQuadsX;
	TessellationFactors1[1]	/= NumTessQuadsY;
	TessellationFactors2[0]	/= NumTessQuadsX;
	TessellationFactors2[1]	/= NumTessQuadsY;
#endif
}

void FFluidSimulation::LockResources(FRHICommandListBase& RHICmdList)
{
	if ( bResourcesLocked == false && ShouldSimulate() )
	{
		Vertices			= VertexBuffers[SimulationIndex].Lock(RHICmdList);
		BorderVertices		= FlatVertexBuffers[SimulationIndex].Lock(RHICmdList);
		bResourcesLocked	= true;
	}
}

void FFluidSimulation::UnlockResources(FRHICommandListBase& RHICmdList)
{
	if ( bResourcesLocked == true )
	{
		VertexBuffers[SimulationIndex].Unlock(RHICmdList);
		FlatVertexBuffers[SimulationIndex].Unlock(RHICmdList);
		Vertices			= nullptr;
		BorderVertices		= nullptr;
		bResourcesLocked	= false;
	}
}

void FFluidSimulation::SetDetailPosition(FVector LocalPos)
{
	FVector ClampedPos;
	ClampedPos.X = FMath::Clamp<float>(LocalPos.X, -(TotalWidth - Component->DetailSize)*0.5f, (TotalWidth - Component->DetailSize)*0.5f);
	ClampedPos.Y = FMath::Clamp<float>(LocalPos.Y, -(TotalHeight - Component->DetailSize)*0.5f, (TotalHeight - Component->DetailSize)*0.5f);
	ClampedPos.Z = 0.0f;

	DetailGPUResource.SetDetailPosition(ClampedPos, bEnableGPUSimulation);
}

void FFluidSimulation::SetSimulationPosition(FVector LocalPos)
{
	if ( bEnableCPUSimulation )
	{
		const int32 CenterX = FMath::TruncToInt((LocalPos.X + TotalWidth*0.5f) / CellWidth);
		const int32 CenterY = FMath::TruncToInt((LocalPos.Y + TotalHeight*0.5f) / CellHeight);
		const int32 X = FMath::Max<int32>( CenterX - NumCellsX/2, 0 );
		const int32 Y = FMath::Max<int32>( CenterY - NumCellsY/2, 0 );
		PendingSimulationPos.X = FMath::Min<int32>( X, TotalSize.X - NumCellsX );
		PendingSimulationPos.Y = FMath::Min<int32>( Y, TotalSize.Y - NumCellsY );
	}
	else
	{
		// In this case, NumCellsX/Y and CellWidth/CellHeight has been faked to contain the entire fluid
		// (and SimulationPos[] isn't been updated), so we'll use the component settings as an approximation.
		const int32 TotalQuadsX = FMath::TruncToInt( TotalWidth / Component->GridSpacing );
		const int32 TotalQuadsY = FMath::TruncToInt( TotalHeight / Component->GridSpacing );
		const int32 SimQuadsX = FMath::Min<int32>(Component->SimulationQuadsX, TotalQuadsX);
		const int32 SimQuadsY =  FMath::Min<int32>(Component->SimulationQuadsY, TotalQuadsY);
		const int32 CenterX = FMath::TruncToInt((LocalPos.X + TotalWidth*0.5f) / Component->GridSpacing);
		const int32 CenterY = FMath::TruncToInt((LocalPos.Y + TotalHeight*0.5f) / Component->GridSpacing);
		const int32 X = FMath::Max<int32>( CenterX - SimQuadsX/2, 0 );
		const int32 Y = FMath::Max<int32>( CenterY - SimQuadsY/2, 0 );
		PendingSimulationPos.X = FMath::Min<int32>( X, TotalQuadsX - SimQuadsX );
		PendingSimulationPos.Y = FMath::Min<int32>( Y, TotalQuadsY - SimQuadsY );
	}

	if ( !bShowSimulation )
	{
		SimulationPos[0] = SimulationPos[1] = PendingSimulationPos;
	}
}

void FFluidSimulation::GetSimulationRect(FVector2D& TopLeft, FVector2D& BottomRight) const
{
	if ( bEnableCPUSimulation && bShowSimulation )
	{
		TopLeft.X = SimulationPos[CurrentHeightMap].X * CellWidth - 0.5f * TotalWidth;
		TopLeft.Y = SimulationPos[CurrentHeightMap].Y * CellHeight - 0.5f * TotalHeight;
		BottomRight.X = TopLeft.X + GridWidth;
		BottomRight.Y = TopLeft.Y + GridHeight;
	}
	else
	{
		// In this case, NumCellsX/Y and CellWidth/CellHeight has been faked to contain the entire fluid
		// (and SimulationPos[] isn't been updated), so we'll use the component settings as an approximation.
		const int32 TotalQuadsX = FMath::TruncToInt( TotalWidth / Component->GridSpacing );
		const int32 TotalQuadsY =  FMath::TruncToInt( TotalHeight / Component->GridSpacing );
		const int32 SimQuadsX = FMath::Min<int32>(Component->SimulationQuadsX, TotalQuadsX);
		const int32 SimQuadsY =  FMath::Min<int32>(Component->SimulationQuadsY, TotalQuadsY);
		TopLeft.X = SimulationPos[CurrentHeightMap].X * Component->GridSpacing - 0.5f * TotalWidth;
		TopLeft.Y = SimulationPos[CurrentHeightMap].Y * Component->GridSpacing - 0.5f * TotalHeight;
		BottomRight.X = TopLeft.X + SimQuadsX * Component->GridSpacing;
		BottomRight.Y = TopLeft.Y + SimQuadsY * Component->GridSpacing;
	}
}

/** Returns the rectangle of the detail grid, in fluid local space. */
void FFluidSimulation::GetDetailRect(FVector2D& TopLeft, FVector2D& BottomRight) const
{
	DetailGPUResource.GetDetailRect( TopLeft, BottomRight, bEnableGPUSimulation );
}

bool FFluidSimulation::IsWithinSimulationGrid(const FVector& LocalPos, float Radius) const
{
	FVector2D TopLeft, BottomRight;
	GetSimulationRect( TopLeft, BottomRight );
	return ((LocalPos.X - Radius) > TopLeft.X && (LocalPos.X + Radius) < BottomRight.X &&
			(LocalPos.Y - Radius) > TopLeft.Y && (LocalPos.Y + Radius) < BottomRight.Y);
}

bool FFluidSimulation::IsWithinDetailGrid(const FVector& LocalPos, float Radius) const
{
	FVector2D TopLeft, BottomRight;
	GetDetailRect( TopLeft, BottomRight );
	return ((LocalPos.X - Radius) > TopLeft.X && (LocalPos.X + Radius) < BottomRight.X &&
			(LocalPos.Y - Radius) > TopLeft.Y && (LocalPos.Y + Radius) < BottomRight.Y);
}

/**
 *	Octant 0 is [0..45) degrees
 *	Octant 1 is [45..90) degrees
 *	Octant 2 is [90..135) degrees
 *	Etc.
 */
int32 FFluidSimulation::ClassifyOctant(const FVector& LocalDirection)
{
	// appAtan2 returns a value [-PI..+PI]
	const float Angle = FMath::Atan2( LocalDirection.Y, LocalDirection.X );
	const int32 Octant = FMath::Floor( Angle / (UE_PI/4.0f) ) + 8;
	return (Octant % 8);
}

bool FFluidSimulation::ShouldSimulate() const
{
	static constexpr float ActivityLimit = 5.0f;
	// Disable in splitscreen
	if ( Component && GEngine->HasMultipleLocalPlayers(Component->GetWorld())|| !bEnableCPUSimulation || Component->bPause
		|| (SimulationActivity < ActivityLimit && FluidForces[SimulationIndex].Num() == 0) )
	{
		return false;
	}
	return true;
}

FluidSurfaceRender.cpp:

// By penguin21 and backported from UE3

/*=============================================================================
			FluidSurfaceRender.cpp: Fluid surface rendering.
=============================================================================*/

#include "FluidSurfaceComponent.h"
#include "FluidSurface.h"
#include "FluidSurfaceModule.h"
#include "LightMap.h"
#include "MaterialDomain.h"
#include "MeshDrawShaderBindings.h"
#include "MeshMaterialShader.h"
#include "Materials/MaterialRenderProxy.h"
#include "PhysicsEngine/BodySetup.h"
#include "VertexFactory.h"
#include "LocalVertexFactory.h"
#include "PrimitiveUniformShaderParametersBuilder.h"
#include "Engine/MapBuildDataRegistry.h"
#include "Rendering/StaticLightingSystemInterface.h"
#include "Runtime/Renderer/Private/LightSceneInfo.h"

/*=============================================================================
	FFluidVertex
=============================================================================*/
FFluidVertex::FFluidVertex()
	: Height(0)
	, UV(FVector2f::ZeroVector)
	, HeightDelta(FVector2f::ZeroVector)
{
}

FString FFluidVertex::ToString() const
{
	return FString::Printf(TEXT("Height: %f, UV:%s, HeightDelta: %s"), Height, *UV.ToString(), *HeightDelta.ToString());
}

/*=============================================================================
	FFluidVertexFactoryShaderParameters
=============================================================================*/
/**
 *	Vertexshader parameters for fluids.
 */
class FFluidVertexFactoryShaderParameters : public FVertexFactoryShaderParameters
{
	DECLARE_TYPE_LAYOUT(FFluidVertexFactoryShaderParameters, NonVirtual);
public:
	void Bind(const FShaderParameterMap& ParameterMap);
	void GetElementShaderBindings(
		const class FSceneInterface* Scene,
		const FSceneView* View,
		const class FMeshMaterialShader* Shader,
		const EVertexInputStreamType InputStreamType,
		ERHIFeatureLevel::Type FeatureLevel,
		const FVertexFactory* VertexFactory,
		const FMeshBatchElement& BatchElement,
		class FMeshDrawSingleShaderBindings& ShaderBindings,
		FVertexInputStreamArray& VertexStreams
	) const;
private:
	//LAYOUT_FIELD(FShaderParameter, LocalToWorldFluidParameter);
	//LAYOUT_FIELD(FShaderParameter, WorldToLocalFluidParameter);

	// Fluid Params
	LAYOUT_FIELD(FShaderParameter, GridSizeParameter);
	LAYOUT_FIELD(FShaderParameter, TessellationParameters);
	LAYOUT_FIELD(FShaderParameter, TessellationFactors1);
	LAYOUT_FIELD(FShaderParameter, TessellationFactors2);
	LAYOUT_FIELD(FShaderParameter, TexcoordScaleBias);
	LAYOUT_FIELD(FShaderParameter, SplineParameters);
	LAYOUT_FIELD(FShaderResourceParameter, HeightmapParameter);
	LAYOUT_FIELD(FShaderResourceParameter, HeightmapParameterSampler);
};
IMPLEMENT_TYPE_LAYOUT(FFluidVertexFactoryShaderParameters);

void FFluidVertexFactoryShaderParameters::Bind(const FShaderParameterMap& ParameterMap)
{
	//LocalToWorldFluidParameter.Bind( ParameterMap, TEXT("FluidLocalToWorld"), SPF_Optional ); // optional as instanced meshes don't need it
	//WorldToLocalFluidParameter.Bind( ParameterMap, TEXT("FluidWorldToLocal"), SPF_Optional );
	
	// Fluid Params
	GridSizeParameter.Bind( ParameterMap, TEXT("GridSize") );
	
	// Xbox-specific parameters.
	TessellationParameters.Bind( ParameterMap, TEXT("TessellationParameters"), SPF_Optional );
	TessellationFactors1.Bind( ParameterMap, TEXT("TessellationFactors1"), SPF_Optional );
	TessellationFactors2.Bind( ParameterMap, TEXT("TessellationFactors2"), SPF_Optional );
	HeightmapParameter.Bind( ParameterMap, TEXT("Heightmap"), SPF_Optional );
	HeightmapParameterSampler.Bind( ParameterMap, TEXT("HeightmapSampler"), SPF_Optional );
	TexcoordScaleBias.Bind( ParameterMap, TEXT("TexcoordScaleBias"), SPF_Optional );
	SplineParameters.Bind( ParameterMap, TEXT("SplineParameters"), SPF_Optional );
}

void FFluidVertexFactoryShaderParameters::GetElementShaderBindings(const FSceneInterface* Scene, const FSceneView* View,
                                                                   const FMeshMaterialShader* Shader, const EVertexInputStreamType InputStreamType, ERHIFeatureLevel::Type FeatureLevel,
                                                                   const FVertexFactory* VertexFactory, const FMeshBatchElement& BatchElement, FMeshDrawSingleShaderBindings& ShaderBindings,
                                                                   FVertexInputStreamArray& VertexStreams) const
{
	const FFluidVertexFactory* FluidVertexFactory = (FFluidVertexFactory*)VertexFactory;
	check(FluidVertexFactory);
	const FFluidSimulation* FluidSimulation = FluidVertexFactory->GetSimulation();
	check(FluidSimulation);
	const FVector4f GridSize = FVector4f(FluidSimulation->GetGridSize());
	
	ShaderBindings.Add(GridSizeParameter, GridSize);
#if ENABLE_XBOX_LEFTOVER
	if(FluidVertexFactory->UseGPUTessellation()){
		static float SplineMargin = 0.1f;
		const FVector4f SplineParameterValue( 0.5f - SplineMargin, 1.0f/SplineMargin, 0.0f, 0.0f );
		ShaderBindings.Add(TessellationParameters, FVector4f(FluidSimulation->GetTessellationParameters()));
		ShaderBindings.Add(TessellationFactors1, FVector4f(FluidSimulation->GetTessellationFactors1()));
		ShaderBindings.Add(TessellationFactors2, FVector4f(FluidSimulation->GetTessellationFactors2()));
		ShaderBindings.Add(TexcoordScaleBias, FVector4f(FluidSimulation->GetTexcoordScaleBias()));
		FRHISamplerState* SamplerStateLinear = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
		ShaderBindings.AddTexture(HeightmapParameter, HeightmapParameterSampler, SamplerStateLinear, FluidVertexFactory->GetHeightmapTexture());
		ShaderBindings.Add(SplineParameters, SplineParameterValue);
	}
#endif
}

/*=============================================================================
	FFluidVertexDeclaration
=============================================================================*/
/**
 * Fluid vertex declaration resource type.
 */
class FFluidVertexDeclaration : public FRenderResource
{
public:
	FVertexDeclarationRHIRef VertexDeclarationRHI;
	
	virtual void FillDeclElements(FVertexDeclarationElementList& Elements, int32& Offset)
	{
		//constexpr uint16 Stride = sizeof(FFluidVertex);
		
		/** The stream to read the texture coordinates from. */
		check( Offset == 0 );

		/** Vertex height */
		Elements.Add(FVertexElement(0,Offset,VET_Float1,0,0));
		Offset += sizeof(float);
		/** Texture coordinate */
		Elements.Add(FVertexElement(0,Offset,VET_Float2,1,0));
		Offset += sizeof(FVector2f);
		/** HeightDelta */
		Elements.Add(FVertexElement(0,Offset,VET_Float2,2,0));
		Offset += sizeof(FVector2f);
	}
	
	virtual void InitRHI(FRHICommandListBase& RHICmdList) override
	{
		FVertexDeclarationElementList Elements;
		int32 Offset = 0;
		
		FillDeclElements(Elements, Offset);

		// Create the vertex declaration for rendering the factory normally.
		VertexDeclarationRHI = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
	}

	virtual void ReleaseRHI() override
	{
		VertexDeclarationRHI.SafeRelease();
	}
};

/** The vertex declaration used by all fluidsurfaces. */
static TGlobalResource<FFluidVertexDeclaration> GFluidVertexDeclaration;

/*=============================================================================
	FFluidMaterialRenderProxy
=============================================================================*/
/**
 * A material render proxy that sets parameters needed by the fluid normal material node.
 */
class FFluidMaterialRenderProxy : public FMaterialRenderProxy
{
private:
	const FMaterialRenderProxy* const Parent;
	//FTexture FluidNormalTexture;
	FLinearColor DetailCoordOffset;
	FLinearColor DetailCoordScale;
public:
	/** Initialization constructor. */
	FFluidMaterialRenderProxy(const FMaterialRenderProxy* InParent, const FFluidSimulation* FluidSimulation)
		: FMaterialRenderProxy(InParent->GetMaterialName())
		, Parent(InParent)
	{
		FVector2D DetailMin;
		FVector2D DetailMax;
		FluidSimulation->DetailGPUResource.GetDetailRect(DetailMin, DetailMax, FluidSimulation->bEnableGPUSimulation);

		// Setup a scale to convert fluid texture coordinates in the range [0,1] over the entire low res grid to be [0,1] on the high res grid
		// These will be used to map the detail normal and attenuation textures onto the fluid surface
		DetailCoordScale = FLinearColor(
			FluidSimulation->TotalWidth / (DetailMax.X - DetailMin.X),
			FluidSimulation->TotalHeight / (DetailMax.Y - DetailMin.Y), 0);

		// Setup a texture coordinate offset
		DetailCoordOffset = FLinearColor(
			(DetailMin.X + .5f * FluidSimulation->TotalWidth) / FluidSimulation->TotalWidth,
			(DetailMin.Y + .5f * FluidSimulation->TotalHeight) / FluidSimulation->TotalHeight, 0);
		
		// Get the detail normal texture from the GPU simulation
		//FluidNormalTexture.TextureRHI = FluidSimulation->DetailGPUResource.GetNormalTexture();
		//FluidNormalTexture.SamplerStateRHI = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
	}

	// FMaterialRenderProxy interface.
	virtual const FMaterial* GetMaterialNoFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
	{
		check(Parent);
		return Parent->GetMaterialNoFallback(InFeatureLevel);
	}
	
	virtual const FMaterialRenderProxy* GetFallback(ERHIFeatureLevel::Type InFeatureLevel) const override
	{
		check(Parent);
		return Parent->GetFallback(InFeatureLevel);
	}

	virtual bool GetParameterValue(EMaterialParameterType Type, const FHashedMaterialParameterInfo& ParameterInfo, FMaterialParameterValue& OutValue,
	                               const FMaterialRenderContext& Context) const override
	{
		static FName DetailCoordOffsetParam = FName(TEXT("DetailCoordOffset"));
		static FName DetailCoordScaleParam = FName(TEXT("DetailCoordScale"));
		//static FName FluidDetailNormalParam = FName(TEXT("FluidDetailNormal"));
		switch (Type)
		{
		case EMaterialParameterType::Vector:
			if (ParameterInfo.Name == DetailCoordOffsetParam)
			{
				OutValue = DetailCoordOffset;
				return true;
			}
			else if (ParameterInfo.Name == DetailCoordScaleParam)
			{
				OutValue = DetailCoordScale;
				return true;
			}
			break;
		/*case EMaterialParameterType::Texture:
			if (ParameterInfo.Name == FluidDetailNormalParam)
			{
				OutValue = FluidNormalTexture;
				return true;
			}
			break;*/
		default:
			break;
		}
		return Parent->GetParameterValue(Type, ParameterInfo, OutValue, Context);
	}
};

/** Implementation of the LCI for fluid surfaces */
class FFluidSurfaceLCI : public FLightCacheInterface
{
public:
    /** Initialization constructor. */
    FFluidSurfaceLCI(const UFluidSurfaceComponent* InComponent) :
        Component(InComponent)
    {
    	if (!Component)
    	{
    		return;
    	}

    	if (Component->GetLightmapType() == ELightmapType::ForceVolumetric)
    	{
    		SetGlobalVolumeLightmap(true);
    	}
#if WITH_EDITOR
    	else if (Component && FStaticLightingSystemInterface::GetPrimitiveMeshMapBuildData(Component, 0))
    	{
		    if (const FMeshMapBuildData* MeshMapBuildData = FStaticLightingSystemInterface::GetPrimitiveMeshMapBuildData(Component, 0))
    		{
    			SetLightMap(MeshMapBuildData->LightMap);
    			SetShadowMap(MeshMapBuildData->ShadowMap);
    			SetResourceCluster(MeshMapBuildData->ResourceCluster);
    			bCanUsePrecomputedLightingParametersFromGPUScene = true;
    			IrrelevantLights = MeshMapBuildData->IrrelevantLights;
    		}
    	}
#endif
    }
	
    // FLightCacheInterface
    virtual FLightInteraction GetInteraction(const class FLightSceneProxy* LightSceneProxy) const override
    {
    	// ask base class
	    const ELightInteractionType LightInteraction = GetStaticInteraction(LightSceneProxy, IrrelevantLights);

    	if (LightInteraction != LIT_MAX)
    	{
    		return FLightInteraction(LightInteraction);
    	}

        return FLightInteraction::Dynamic();
    }

private:
	TArray<FGuid> IrrelevantLights;
    const UFluidSurfaceComponent* const Component;
};

/*=============================================================================
	FFluidVertexBuffer implementation
=============================================================================*/
FFluidVertexBuffer::FFluidVertexBuffer()
	: Owner(NULL)
	, NumVerts(0)
	, MaxNumVertices(0)
	, bIsLocked(false)
	, BufferType(BT_Simulation)
	, bBorderGeometry(0)
	, NumQuadsX(0)
	, NumQuadsY(0)
{
}

void FFluidVertexBuffer::Setup( FFluidSimulation* InOwner, uint32 InMaxNumVertices, EBufferType InBufferType, int32 InNumQuadsX/*=0*/, int32 InNumQuadsY/*=0*/ )
{
	Owner = InOwner;
	MaxNumVertices = InMaxNumVertices;
	BufferType = InBufferType;
	NumQuadsX = InNumQuadsX;
	NumQuadsY = InNumQuadsY;
}

FFluidVertex* FFluidVertexBuffer::Lock(FRHICommandListBase& RHICmdList)
{
	const uint32 TotalSize = MaxNumVertices * sizeof(FFluidVertex);
	FFluidVertex* Vertices = static_cast<FFluidVertex*>(RHICmdList.LockBuffer(VertexBufferRHI, 0, TotalSize, RLM_WriteOnly));
	
	bIsLocked = true;
	return Vertices;
}

void FFluidVertexBuffer::Unlock(FRHICommandListBase& RHICmdList)
{
	if ( bIsLocked )
	{
		RHICmdList.UnlockBuffer(VertexBufferRHI);
		bIsLocked = false;
	}
}

bool FFluidVertexBuffer::IsLocked() const
{
	return bIsLocked;
}

uint32 FFluidVertexBuffer::GetMaxNumVertices() const
{
	return MaxNumVertices;
}

int32 FFluidVertexBuffer::GetNumQuadsX() const
{
	return NumQuadsX;
}

int32 FFluidVertexBuffer::GetNumQuadsY() const
{
	return NumQuadsY;
}

void FFluidVertexBuffer::InitRHI(FRHICommandListBase& RHICmdList)
{
	const uint32 NumVertsPerSide = MaxNumVertices + 1;
	NumVerts = NumVertsPerSide * NumVertsPerSide;

	const uint32 VertsCurrent = MaxNumVertices > 0 ? MaxNumVertices : NumVerts;
	FRHIResourceCreateInfo CreateInfo(TEXT("FluidVertexBufferResource"));
	EBufferUsageFlags Flags = BUF_Static | BUF_ShaderResource;
	if ( BufferType == BT_Border )
	{
		// Create the vertices for the flat border geometry.
		uint32 TotalVBSize = VertsCurrent * sizeof(FFluidVertex);
		// TODO: Determine if this should be set to Dynamic all the time, or if Static is correct.
		// If Static is correct, make sure this buffer isn't locked at a later time.
#if PLATFORM_MAC
		VertexBufferRHI = RHICmdList.CreateVertexBuffer( TotalVBSize, BUF_Dynamic|Flags, CreateInfo);
#else
		VertexBufferRHI = RHICmdList.CreateVertexBuffer( TotalVBSize, Flags, CreateInfo);
#endif
		FFluidVertex* Vertices = Lock(RHICmdList);
		if(Owner)
			Owner->UpdateBorderGeometry( Vertices );
		Unlock(RHICmdList);
	}
	else if ( BufferType == BT_Simulation )
	{
		// create a dynamic vertex buffer
		uint32 TotalVBSize = VertsCurrent * sizeof(FFluidVertex);
		VertexBufferRHI = RHICmdList.CreateVertexBuffer( TotalVBSize, BUF_Dynamic, CreateInfo);
		if(Owner)
		{
			int32 NumCellsX = Owner->GetNumCellsX();
			int32 NumCellsY = Owner->GetNumCellsY();
			const FIntPoint& TotalSize = Owner->GetTotalSize();
			const FIntPoint& SimulationPos = Owner->GetSimulationPosition();

			FFluidVertex* Vertices = Lock(RHICmdList);
			FFluidVertex Vertex = FFluidVertex();
			float CellSize = Owner->GetWidth() / static_cast<float>(NumCellsX);
			FVector2f UVOrigin( static_cast<float>(SimulationPos.X)/static_cast<float>(TotalSize.X), static_cast<float>(SimulationPos.Y)/static_cast<float>(TotalSize.Y) );
			FVector2f StepUV( 1.0f/TotalSize.X, 1.0f/TotalSize.Y );
			Vertex.Height		= 0.0f;
			Vertex.HeightDelta	= FVector2f( 0.0f, 0.0f );

			for ( int32 Y=0, VertexIndex=0; Y <= NumCellsY; ++Y )
			{
				Vertex.UV		= UVOrigin;
				for ( int32 X=0; X <= NumCellsX; ++X, ++VertexIndex )
				{
					Vertices[VertexIndex]	= Vertex;
					Vertex.UV.X				+= StepUV.X;
				}
				UVOrigin.Y	+= StepUV.Y;
			}
		}
		Unlock(RHICmdList);
	}
	else if ( BufferType == BT_Quad )
	{
		check( VertsCurrent == ((NumQuadsX+1)*(NumQuadsY+1)) );
		uint32 TotalVBSize	= VertsCurrent * sizeof(FFluidVertex);
		VertexBufferRHI		= RHICmdList.CreateVertexBuffer( TotalVBSize, Flags, CreateInfo);
		FFluidVertex* Vertices = Lock(RHICmdList);
		FFluidVertex Vertex = FFluidVertex();
		FVector2f UVOrigin( 0.0f, 0.0f );
		FVector2f StepUV( 1.0f/NumQuadsX, 1.0f/NumQuadsY );
		Vertex.Height		= 0.0f;
		Vertex.HeightDelta	= FVector2f( 0.0f, 0.0f );

		for ( int32 Y=0, VertexIndex=0; Y <= NumQuadsY; ++Y )
		{
			Vertex.UV = UVOrigin;
			for ( int32 X=0; X <= NumQuadsX; ++X, ++VertexIndex )
			{
				Vertices[VertexIndex] = Vertex;
				Vertex.UV.X += StepUV.X;
			}
			UVOrigin.Y += StepUV.Y;
		}
		Unlock(RHICmdList);
	}
}

void FFluidVertexBuffer::ReleaseRHI()
{
	if(Owner){
		Owner->BlockOnSimulation();
		Owner->UnlockResources(FRHICommandListImmediate::Get());
	}
	VertexBufferRHI.SafeRelease();
	bIsLocked = false;
}

TGlobalResource<FFluidVertexBuffer> GFluidVertexBuffer;

/*=============================================================================
	FFluidVertexFactory implementation
=============================================================================*/
FFluidVertexFactory::FFluidVertexFactory(ERHIFeatureLevel::Type InFeatureLevel)	: FVertexFactory(InFeatureLevel)
, bUseGPUTessellation(false)
{
}

FFluidSimulation* FFluidVertexFactory::GetSimulation() const
{
	return FluidSimulation;
}

#if ENABLE_XBOX_LEFTOVER
FTextureRHIRef& FFluidVertexFactory::GetHeightmapTexture() const
{
	return FluidSimulation->HeightMapTextures[1 - FluidSimulation->SimulationIndex];
}
#endif

void FFluidVertexFactory::InitResources(FRHICommandListBase& RHICmdList, const FFluidVertexBuffer& VertexBuffer, FFluidSimulation* InFluidSimulation)
{
	FluidSimulation = InFluidSimulation;

	// Height stream
	Height = FVertexStreamComponent(&VertexBuffer,STRUCT_OFFSET(FFluidVertex,Height),sizeof(FFluidVertex),VET_Float1);
	// UV stream
	TexCoord = FVertexStreamComponent(&VertexBuffer,STRUCT_OFFSET(FFluidVertex,UV),sizeof(FFluidVertex),VET_Float2);
	// Tangents stream
	HeightDelta = FVertexStreamComponent(&VertexBuffer,STRUCT_OFFSET(FFluidVertex,HeightDelta),sizeof(FFluidVertex),VET_Float2);
	
	UpdateRHI(RHICmdList);
	UE_LOG(FluidSurface, Warning, TEXT("We inited this vertex factory."));
}

void FFluidVertexFactory::InitRHI(FRHICommandListBase& RHICmdList)
{
	check(HasValidFeatureLevel());
	
	// Dummy call, just to add a stream source.
	AccessStreamComponent( Height, 0 );
	
	SetDeclaration(GFluidVertexDeclaration.VertexDeclarationRHI);
}

FString FFluidVertexFactory::GetFriendlyName() const
{
	return FString( TEXT("FluidsVertexFactory") );
}

/**
 * Can be overridden by FVertexFactory subclasses to modify their compile environment just before compilation occurs.
 */
void FFluidVertexFactory::ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
#if ENABLE_XBOX_LEFTOVER
	FString ValueXbox = TEXT("1");
#else
	FString ValueXbox = TEXT("0");
#endif
	//FLocalVertexFactory::ModifyCompilationEnvironment(Parameters, OutEnvironment);
	OutEnvironment.SetDefine( TEXT( "USE_FLUIDSURFACE" ), TEXT( "1" ) );
	OutEnvironment.SetDefine( TEXT( "ENABLE_XBOX_LEFTOVER" ), ValueXbox );
}

/**
 * Should we cache the material's shadertype on this platform with this vertex factory? 
 */
bool FFluidVertexFactory::ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters)
{
	if(Parameters.MaterialParameters.bIsUsedWithWater || Parameters.MaterialParameters.bIsSpecialEngineMaterial && Parameters.MaterialParameters.MaterialDomain != EMaterialDomain::MD_DeferredDecal || Parameters.MaterialParameters.bIsDefaultMaterial)
	{
		if(!FString(Parameters.ShaderType->GetShaderFilename()).Contains(TEXT("VelocityShader")))
			return true;
	}
	return false;
}

void FFluidVertexFactory::GetPSOPrecacheVertexFetchElements(EVertexInputStreamType VertexInputStreamType, FVertexDeclarationElementList& Elements)
{
	GFluidVertexDeclaration.VertexDeclarationRHI->GetInitializer(Elements);
}

/** Returns whether the vertex shader should generate vertices (TRUE) or if it should use a vertex buffer (FALSE). */
bool FFluidVertexFactory::UseGPUTessellation() const
{
	return bUseGPUTessellation;
}

/*=============================================================================
	FFluidTessellationVertexFactory
=============================================================================*/
FFluidTessellationVertexFactory::FFluidTessellationVertexFactory(ERHIFeatureLevel::Type InFeatureLevel)	: FFluidVertexFactory(InFeatureLevel )
{
	bUseGPUTessellation = true;
}

// Shader Vertex Factory Implementations
IMPLEMENT_VERTEX_FACTORY_TYPE(FFluidVertexFactory, "/FluidSurfaceShader/FluidVertexFactory.ush",
	EVertexFactoryFlags::UsedWithMaterials |
	/*EVertexFactoryFlags::SupportsStaticLighting |*/
	EVertexFactoryFlags::SupportsDynamicLighting |
	EVertexFactoryFlags::SupportsPSOPrecaching
);
IMPLEMENT_VERTEX_FACTORY_TYPE(FFluidTessellationVertexFactory,"/FluidSurfaceShader/FluidVertexFactory.ush",
	EVertexFactoryFlags::UsedWithMaterials |
	/*EVertexFactoryFlags::SupportsStaticLighting |*/
	EVertexFactoryFlags::SupportsDynamicLighting |
	EVertexFactoryFlags::SupportsPSOPrecaching
);
// IMPLEMENTATION OF VERTEX FACTORY TYPE
IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FFluidVertexFactory, SF_Vertex, FFluidVertexFactoryShaderParameters);
IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FFluidTessellationVertexFactory, SF_Vertex, FFluidVertexFactoryShaderParameters);

/** A logical exclusive or function. */
inline bool UE3_XOR(uint8 A, uint8 B)
{
	return (A && !B) || (!A && B);
}

/*=============================================================================
	FFluidSurfaceSceneProxy
=============================================================================*/
/**
 * A fluid surface component scene proxy.
 */
class FFluidSurfaceSceneProxy : public FPrimitiveSceneProxy
{
public:
	/* Constructor */
	FFluidSurfaceSceneProxy( const UFluidSurfaceComponent* InComponent )
		: FPrimitiveSceneProxy(InComponent)
		, FluidSimulation(InComponent->GetFluidSimulation())
		, Component(InComponent)
		, MaterialViewRelevance(InComponent->GetMaterialViewRelevance(GetScene().GetFeatureLevel()))
		, MaterialLowResViewRelevance(InComponent->GetLowResMaterialViewRelevance(GetScene().GetFeatureLevel()))
		, MaterialFarResViewRelevance(InComponent->GetFarMaterialViewRelevance(GetScene().GetFeatureLevel()))
		, Material(InComponent->GetMaterialFluid())
		, LCI(InComponent)
		, HasLowResMat(InComponent->LowResFluidMaterial != nullptr)
		, HasFarResMat(InComponent->FarFluidMaterial != nullptr)
		, FarMaterialDist(InComponent->FarMaterialDist)
		, FarFluidMaterialForLowDetailMode(InComponent->FarFluidMaterialForLowDetailMode)
		, BoundsExtension(InComponent->BoundsExtension)
	{
		MaterialProxy = Material->GetRenderProxy();
		MaterialProxyLowRes = InComponent->GetLowResMaterial()->GetRenderProxy();
		MaterialProxyFarRes = InComponent->GetFarMaterial()->GetRenderProxy();
	}

	/* Destructor */
	virtual ~FFluidSurfaceSceneProxy() override {}
	
	virtual bool HasCustomOcclusionBounds() const override
	{
		return BoundsExtension != 0.f;
	}
	
	virtual FBoxSphereBounds GetCustomOcclusionBounds() const override
	{
		FBoxSphereBounds Result = GetBounds();
		Result.SphereRadius += BoundsExtension;
		return Result;
	}

	/*virtual void DrawStaticElements(FStaticPrimitiveDrawInterface* PDI) override
	{
		if(FluidSimulation == nullptr) return;
		if(Component == nullptr) return;
		
		checkSlow(IsInParallelRenderingThread());
		if (!HasViewDependentDPG())
		{
			const bool bIsSimulation = FluidSimulation->bShowSimulation;
			const ESceneDepthPriorityGroup PrimitiveDPG = GetStaticDepthPriorityGroup();

			FMeshBatch Mesh;
			Mesh.MaterialRenderProxy = MaterialProxy;
			Mesh.DepthPriorityGroup = PrimitiveDPG;
			Mesh.MeshIdInPrimitive = 0;
			Mesh.SegmentIndex = 0;
			Mesh.LODIndex = 0;
			
			FMeshBatchElement& BatchElement = Mesh.Elements[0];
			BatchElement.PrimitiveUniformBuffer = GetUniformBuffer();
			BatchElement.FirstIndex				= 0;
			BatchElement.MinVertexIndex			= 0;
			BatchElement.MaxVertexIndex			= FluidSimulation->NumVertices - 1;
			// Override all geometry with a flat quad if we can.
			if ( bIsSimulation == false )
			{
				const int32 NumLowResQuadsX		= FluidSimulation->FlatQuadVertexBuffer.GetNumQuadsX();
				const int32 NumLowResQuadsY		= FluidSimulation->FlatQuadVertexBuffer.GetNumQuadsY();
				Mesh.Type						= PT_TriangleList;
				Mesh.VertexFactory				= &FluidSimulation->FlatQuadVertexFactory;
				BatchElement.IndexBuffer		= &FluidSimulation->FlatQuadIndexBuffer;
				BatchElement.MaxVertexIndex		= (NumLowResQuadsX + 1) * (NumLowResQuadsY + 1) - 1;
				BatchElement.NumPrimitives		= NumLowResQuadsX * NumLowResQuadsY * 2;
				Mesh.ReverseCulling				= IsLocalToWorldDeterminantNegative();
			}
			else
			{
				if (FluidSimulation->bUseYFirstIndexBuffer[1 - FluidSimulation->SimulationIndex])
				{
					BatchElement.IndexBuffer = &FluidSimulation->YFirstIndexBuffer;
				}
				else
				{
					BatchElement.IndexBuffer = &FluidSimulation->XFirstIndexBuffer;
				}
				Mesh.ReverseCulling = UE3_XOR(IsLocalToWorldDeterminantNegative(), FluidSimulation->bReverseCulling[1 - FluidSimulation->SimulationIndex]);
				Mesh.Type = PT_TriangleList;
				BatchElement.NumPrimitives	= FluidSimulation->NumIndices / 3;
				Mesh.VertexFactory = &FluidSimulation->VertexFactories[1 - FluidSimulation->SimulationIndex];
			}
			BatchElement.PrimitiveIdMode = PrimID_ForceZero;

			// Draw Static Mesh
			PDI->DrawMesh(Mesh, FLT_MAX);
		}
	}*/

	virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
	{
		const FEngineShowFlags& EngineShowFlags = ViewFamily.EngineShowFlags;
		// Get WireFrame Material
		FColoredMaterialRenderProxy* WireframeMaterialInstance = new FColoredMaterialRenderProxy(
			GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy() : nullptr, FLinearColor(0.7f, 0.005f, 0.f));
		Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance);
		
		// Main Render
		for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
		{
			if (VisibilityMap & (1 << ViewIndex))
			{
				const FSceneView* View = Views[ViewIndex];
				GetDynamicMeshElementsForView(View, ViewIndex, Collector, EngineShowFlags, WireframeMaterialInstance);
			}
		}
		
		// Debug Render
		for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
		{
			if (VisibilityMap & (1 << ViewIndex))
			{
				const FSceneView* View = Views[ViewIndex];
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
				DrawDebugMeshes(View, ViewIndex, Collector, EngineShowFlags);

				RenderBounds(Collector.GetPDI(ViewIndex), ViewFamily.EngineShowFlags, GetBounds(), IsSelected());
				if (HasCustomOcclusionBounds())
					RenderBounds(Collector.GetPDI(ViewIndex), ViewFamily.EngineShowFlags, GetCustomOcclusionBounds(), IsSelected());
#endif
				if (FluidSimulation && !EngineShowFlags.HitProxies && !EngineShowFlags.Wireframe)
				{
					// Store the view direction that was last used for rendering
					//@todo - calculate this in PreRenderViews so that it only happens once per frame, and multiple views don't conflict
					FVector WorldViewDirection = View->GetViewDirection();
					FMatrix RotationOnlyWorldToLocal = FluidSimulation->GetWorldToLocal();
					RotationOnlyWorldToLocal.RemoveScaling();
					FluidSimulation->LastViewDirection[1 - FluidSimulation->SimulationIndex] = RotationOnlyWorldToLocal.TransformPosition(WorldViewDirection);
				}
			}
		}
	}

	void GetDynamicMeshElementsForView(const FSceneView* View, int32 ViewIndex, FMeshElementCollector& Collector, const FEngineShowFlags& EngineShowFlags, FColoredMaterialRenderProxy* WireframeMaterialInstance) const
	{
		if(FluidSimulation == nullptr) return;
		if(Component == nullptr) return;
		const bool bIsSimulation = FluidSimulation->bShowSimulation;
		const bool bIsWireframeView = EngineShowFlags.Wireframe;
		if ( GIsEditor 
				&& !View->Family->bRealtimeUpdate
				&& FluidSimulation->bEnableGPUSimulation
				&& Component->bShowFluidDetail
				&& !(EngineShowFlags.HitProxies)
				&& !(bIsWireframeView) )
		{
			// Initialize render targets when starting up the editor in a non-realtime viewport
			// Note: this will potentially cause the rest of the first frame to render incorrectly due to overridden render state
			FluidSimulation->DetailGPUResource.InitializeRenderTargetContents(Collector.GetRHICommandList());
		}
		bool bShouldWireframeMode = false;
		bool bShouldCanApplyViewModeOverrides = true;
		const FMaterialRenderProxy* CurrentMat = MaterialProxy;
		// Set material from far
		if((HasLowResMat || HasFarResMat) && Component->ShouldUseFarMaterial(View->ViewLocation))
		{
			if(HasFarResMat)
				CurrentMat = MaterialProxyFarRes;
			else if(HasLowResMat)
				CurrentMat = MaterialProxyLowRes;
		}
		
		if(bIsWireframeView){
			CurrentMat = WireframeMaterialInstance;
			bShouldWireframeMode = true;
			bShouldCanApplyViewModeOverrides = false;
		}
		//FBoxSphereBounds PreSkinnedLocalBounds;
		//GetPreSkinnedLocalBounds(PreSkinnedLocalBounds);
		const FMatrix& WorldToLocal	= FluidSimulation->GetWorldToLocal();
		const FMatrix& LocalToWorldComponent = Component->GetRenderMatrix();

		const FVector WorldViewDirection	= View->GetViewDirection();
		const FVector LocalViewDirection	= WorldToLocal.TransformPosition( WorldViewDirection );
		FVector LocalViewPosition	= WorldToLocal.TransformPosition( View->ViewLocation );
		const int32 OctantID		= FFluidSimulation::ClassifyOctant( LocalViewDirection );
		const ESceneDepthPriorityGroup DPG = GetDepthPriorityGroup(View);
		FMeshBatch& Mesh = Collector.AllocateMesh();
		FMeshBatchElement& BatchElement = Mesh.Elements[0];
		Mesh.LCI					= &LCI;
		Mesh.CastShadow				= false;
		Mesh.DepthPriorityGroup		= DPG;
		BatchElement.PrimitiveUniformBuffer = GetUniformBuffer();
		BatchElement.FirstIndex				= 0;
		BatchElement.MinVertexIndex			= 0;
		BatchElement.MaxVertexIndex			= FluidSimulation->NumVertices - 1;
		Mesh.bCanApplyViewModeOverrides = bShouldCanApplyViewModeOverrides;
		
		FluidSimulation->UpdateShaderParameters( OctantID );
		
		// Override all geometry with a flat quad if we can.
		if ( bIsSimulation == false )
		{
			const int32 NumLowResQuadsX		= FluidSimulation->FlatQuadVertexBuffer.GetNumQuadsX();
			const int32 NumLowResQuadsY		= FluidSimulation->FlatQuadVertexBuffer.GetNumQuadsY();
			Mesh.Type						= PT_TriangleList;
			Mesh.VertexFactory				= &FluidSimulation->FlatQuadVertexFactory;
			BatchElement.IndexBuffer		= &FluidSimulation->FlatQuadIndexBuffer;
			BatchElement.MaxVertexIndex		= (NumLowResQuadsX + 1) * (NumLowResQuadsY + 1) - 1;
			BatchElement.NumPrimitives		= NumLowResQuadsX * NumLowResQuadsY * 2;
			Mesh.ReverseCulling				= IsLocalToWorldDeterminantNegative();
		}
		else
		{
			if (FluidSimulation->bUseYFirstIndexBuffer[1 - FluidSimulation->SimulationIndex])
			{
				BatchElement.IndexBuffer = &FluidSimulation->YFirstIndexBuffer;
			}
			else
			{
				BatchElement.IndexBuffer = &FluidSimulation->XFirstIndexBuffer;
			}
			Mesh.ReverseCulling = UE3_XOR(IsLocalToWorldDeterminantNegative(), FluidSimulation->bReverseCulling[1 - FluidSimulation->SimulationIndex]);
			Mesh.Type = PT_TriangleList;
			BatchElement.NumPrimitives	= FluidSimulation->NumIndices / 3;
			Mesh.VertexFactory = &FluidSimulation->VertexFactories[1 - FluidSimulation->SimulationIndex];
		}
		BatchElement.PrimitiveIdMode = PrimID_ForceZero;
		
		// Set Material Proxy
		Mesh.MaterialRenderProxy = CurrentMat;
		Mesh.bWireframe				= bShouldWireframeMode;
		Mesh.LODIndex = 0;
		Mesh.bUseWireframeSelectionColoring = IsSelected();
		if(bIsSimulation)
		{
			// Setup the flat border geometry.
			FMeshBatch& BorderMesh = Collector.AllocateMesh();
			FMeshBatchElement& BorderBatchElement = BorderMesh.Elements[0];
			BorderBatchElement.IndexBuffer = &FluidSimulation->FlatIndexBuffer;
			BorderMesh.LCI					= &LCI;
			BorderMesh.CastShadow			= false;
			BorderMesh.DepthPriorityGroup	= DPG;
			BorderMesh.bCanApplyViewModeOverrides = bShouldCanApplyViewModeOverrides;
			BorderMesh.VertexFactory = &FluidSimulation->FlatVertexFactories[1 - FluidSimulation->SimulationIndex];
			BorderMesh.MaterialRenderProxy = CurrentMat;
			BorderBatchElement.PrimitiveIdMode = PrimID_ForceZero;
			BorderBatchElement.PrimitiveUniformBuffer = GetUniformBuffer();
			
			BorderBatchElement.FirstIndex			= 0;
			BorderBatchElement.MinVertexIndex		= 0;
			BorderBatchElement.MaxVertexIndex		= FluidSimulation->FlatVertexBuffers[1 - FluidSimulation->SimulationIndex].GetMaxNumVertices() - 1;
			BorderMesh.ReverseCulling				= IsLocalToWorldDeterminantNegative();
			BorderMesh.Type							= PT_TriangleList;
			BorderBatchElement.NumPrimitives		= FluidSimulation->FlatIndexBuffer.GetNumIndices() / 3;
			BorderMesh.bWireframe					= bShouldWireframeMode;
			
			BorderMesh.LODIndex = 0;
			Collector.AddMesh(ViewIndex, BorderMesh);
		}
		Collector.AddMesh(ViewIndex, Mesh);
	}

#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	void DrawDebugMeshes(const FSceneView* View, int32 ViewIndex, FMeshElementCollector& Collector, const FEngineShowFlags& EngineShowFlags) const
	{
		if(FluidSimulation == nullptr) return;
		if(Component == nullptr) return;
		FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
		check(PDI);
		// Debug Meshes
		if ( Component->bShowSimulationNormals && FluidSimulation->DebugPositions.Num() > 0 )
		{
			for (int32 Y = 1; Y < FluidSimulation->NumCellsY; Y++ )
			{
				for ( int32 X = 1; X < FluidSimulation->NumCellsX; X++ )
				{
					const int32 CurrentIndex = Y * (FluidSimulation->NumCellsX + 1) + X;
					PDI->DrawLine(FluidSimulation->DebugPositions[CurrentIndex], FluidSimulation->DebugPositions[CurrentIndex] + Component->NormalLength *
						FluidSimulation->DebugNormals[CurrentIndex], FLinearColor::White, SDPG_World);
				}
			}
		}

		if ( Component->bShowDetailPosition && Component->EnableDetail )
		{
			FVector2D DetailMin;
			FVector2D DetailMax;
			FluidSimulation->GetDetailRect(DetailMin, DetailMax);
			FVector Corner0(DetailMin.X, DetailMin.Y, 1.0f);
			FVector Corner1(DetailMin.X, DetailMax.Y, 1.0f);
			FVector Corner2(DetailMax.X, DetailMax.Y, 1.0f);
			FVector Corner3(DetailMax.X, DetailMin.Y, 1.0f);
			Corner0 = Component->GetRenderMatrix().TransformPosition(Corner0);
			Corner1 = Component->GetRenderMatrix().TransformPosition(Corner1);
			Corner2 = Component->GetRenderMatrix().TransformPosition(Corner2);
			Corner3 = Component->GetRenderMatrix().TransformPosition(Corner3);
			PDI->DrawLine(Corner0, Corner1, FLinearColor::White, SDPG_World);
			PDI->DrawLine(Corner1, Corner2, FLinearColor::White, SDPG_World);
			PDI->DrawLine(Corner2, Corner3, FLinearColor::White, SDPG_World);
			PDI->DrawLine(Corner3, Corner0, FLinearColor::White, SDPG_World);
		}

		if ( Component->bShowSimulationPosition && Component->EnableSimulation )
		{
			FVector2D SimMin;
			FVector2D SimMax;
			FluidSimulation->GetSimulationRect(SimMin, SimMax);
			FVector Corner0(SimMin.X, SimMin.Y, 1.0f);
			FVector Corner1(SimMin.X, SimMax.Y, 1.0f);
			FVector Corner2(SimMax.X, SimMax.Y, 1.0f);
			FVector Corner3(SimMax.X, SimMin.Y, 1.0f);
			Corner0 = Component->GetRenderMatrix().TransformPosition(Corner0);
			Corner1 = Component->GetRenderMatrix().TransformPosition(Corner1);
			Corner2 = Component->GetRenderMatrix().TransformPosition(Corner2);
			Corner3 = Component->GetRenderMatrix().TransformPosition(Corner3);
			constexpr FLinearColor Color(1,1,0);
			PDI->DrawLine(Corner0, Corner1, Color, SDPG_World);
			PDI->DrawLine(Corner1, Corner2, Color, SDPG_World);
			PDI->DrawLine(Corner2, Corner3, Color, SDPG_World);
			PDI->DrawLine(Corner3, Corner0, Color, SDPG_World);
		}

		if(Component->bShowDetailNormals
			&& FluidSimulation->bEnableGPUSimulation
			&& !EngineShowFlags.HitProxies
			&& !EngineShowFlags.Wireframe)
		{
			// Render a visualization of the detail simulation
			FluidSimulation->DetailGPUResource.Visualize(Collector.GetRHICommandList(), View);
		}
	}
#endif

	virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
	{
		FPrimitiveViewRelevance Result = FPrimitiveViewRelevance();
		const bool bIsShown = IsShown(View);
		Result.bDrawRelevance = bIsShown;
		Result.bShadowRelevance = IsShadowCast( View );
		Result.bDynamicRelevance = true;
		Result.bStaticRelevance = false;
		/*if (
#if !(UE_BUILD_SHIPPING) || WITH_EDITOR
			IsRichView(*View->Family) ||
			View->Family->EngineShowFlags.Collision ||
			View->Family->EngineShowFlags.Bounds ||
			View->Family->EngineShowFlags.VisualizeInstanceUpdates ||
#endif
#if WITH_EDITOR
			(IsSelected() && View->Family->EngineShowFlags.VertexColors) ||
			(IsSelected() && View->Family->EngineShowFlags.PhysicalMaterialMasks) ||
#endif
			// Force down dynamic rendering path if invalid lightmap settings, so we can apply an error material in DrawRichMesh
			(HasStaticLighting() && !HasValidSettingsForStaticLighting()) ||
				HasViewDependentDPG())
			{
				Result.bDynamicRelevance = true;
			}
			else
			{
				Result.bStaticRelevance = true;

#if WITH_EDITOR
			//only check these in the editor
			Result.bEditorVisualizeLevelInstanceRelevance = IsEditingLevelInstanceChild();
			Result.bEditorStaticSelectionRelevance = (IsSelected() || IsHovered());
#endif
		}*/
		Result.bRenderCustomDepth = ShouldRenderCustomDepth();
		Result.bRenderInMainPass = ShouldRenderInMainPass();
		Result.bTranslucentSelfShadow = bCastVolumetricTranslucentShadow;
		Result.bUsesLightingChannels = GetLightingChannelMask() != GetDefaultLightingChannelMask();
		// Set material from far
		if((HasLowResMat || HasFarResMat) && Component && Component->ShouldUseFarMaterial(View->ViewLocation))
		{
			if(HasFarResMat)
				MaterialFarResViewRelevance.SetPrimitiveViewRelevance(Result);
			else if(HasLowResMat)
				MaterialLowResViewRelevance.SetPrimitiveViewRelevance(Result);
		}else{
			MaterialViewRelevance.SetPrimitiveViewRelevance(Result);
		}
		Result.bVelocityRelevance = DrawsVelocity() && Result.bOpaque && Result.bRenderInMainPass;
		return Result;
	}

	virtual SIZE_T GetTypeHash() const override
	{
		static size_t UniquePointer;
		return reinterpret_cast<size_t>(&UniquePointer);
	}
	
	virtual uint32 GetMemoryFootprint( void ) const override { return( sizeof( *this ) + GetAllocatedSize() ); }
	// ReSharper disable once CppHidingFunction
	uint32 GetAllocatedSize( void ) const { return( FPrimitiveSceneProxy::GetAllocatedSize() ); }

	void SetFluidSimulation(FFluidSimulation* InFluidSimulation) { FluidSimulation = InFluidSimulation;}
private:
	FFluidSimulation* FluidSimulation;
	const UFluidSurfaceComponent* Component;
	FMaterialRelevance MaterialViewRelevance;
	FMaterialRelevance MaterialLowResViewRelevance;
	FMaterialRelevance MaterialFarResViewRelevance;
	UMaterialInterface* Material;

	FMaterialRenderProxy* MaterialProxy;
	FMaterialRenderProxy* MaterialProxyLowRes;
	FMaterialRenderProxy* MaterialProxyFarRes;
	FFluidSurfaceLCI LCI;
	uint8 HasLowResMat:1;
	uint8 HasFarResMat:1;
	float FarMaterialDist;
	uint8 FarFluidMaterialForLowDetailMode:1;
	float BoundsExtension;
};

/*=============================================================================
	UFluidSurfaceComponent scene proxy creation
=============================================================================*/
FPrimitiveSceneProxy* UFluidSurfaceComponent::CreateSceneProxy()
{
	return new FFluidSurfaceSceneProxy( this );
}

void UFluidSurfaceComponent::UpdateFluidSimulationProxy() const
{
	if(!SceneProxy) return;
	FFluidSurfaceSceneProxy* FluidSurfaceSceneProxy = static_cast<FFluidSurfaceSceneProxy*>(SceneProxy);
	if(!FluidSurfaceSceneProxy) return;
	FluidSurfaceSceneProxy->SetFluidSimulation(FluidSimulation.Get());
}

/*void UFluidSurfaceComponent::SendRenderDynamicData_Concurrent()
{
	Super::SendRenderDynamicData_Concurrent();
	if(SceneProxy && FluidSimulation)
	{
		FFluidSurfaceComponentDynamicData* NewData = CreateDynamicData();

		FFluidSurfaceSceneProxy* FluidSurfaceSceneProxy = static_cast<FFluidSurfaceSceneProxy*>(SceneProxy);
		ENQUEUE_RENDER_COMMAND(FSendFluidSurfaceDynamicRenderData)(
			[FluidSurfaceSceneProxy, NewData](FRHICommandListBase&)
		{
			FluidSurfaceSceneProxy->SetDynamicData_RenderThread(NewData);
		});
	}
}*/

The other code is inside of FluidSurface.zip, someone can help me?

Project:

RawGPUIndexBuffer.h:

// By penguin21 and backported from UE3

#pragma once

#include "CoreMinimal.h"

/**
 *	FRawGPUIndexBuffer represents a basic index buffer GPU resource with
 *	no CPU-side data.
 *	The member functions are meant to be called from the renderthread only.
 */
class FRawGPUIndexBuffer : public FIndexBuffer
{
public:
	/**
	 *	Default constructor
	 */
	FRawGPUIndexBuffer();

	/**
	 *	Setup constructor
	 *	@param InNumIndices		- Number of indices to allocate space for
	 *	@param InIsDynamic		- TRUE if the index buffer should be dynamic
	 *	@param InStride			- Number of bytes per index
	 */
	FRawGPUIndexBuffer(uint32 InNumIndices, bool InIsDynamic=false, uint32 InStride=sizeof(uint16));

	/**
	 *	Sets up the index buffer, if the default constructor was used.
	 *	@param InNumIndices		- Number of indices to allocate space for
	 *	@param InIsDynamic		- TRUE if the index buffer should be dynamic
	 *	@param InStride			- Number of bytes per index
	 */
	void Setup(uint32 InNumIndices, bool InIsDynamic=false, uint32 InStride=sizeof(uint16));

	/**
	 *	Returns TRUE if the index buffer hasn't been filled in with data yet,
	 *	or if it's a dynamic resource that has been re-created due to Device Lost.
	 *	Calling Lock + Unlock will make the index buffer non-empty again.
	 */
	bool IsEmpty() const
	{
		return bIsEmpty;
	}

	/**
	 *	Returns the number of indices that are allocated in the buffer.
	 */
	int GetNumIndices() const
	{
		return NumIndices;
	}

	/**
	 *	Create an empty index buffer RHI resource.
	 */
	virtual void InitRHI(FRHICommandListBase& RHICmdList) override;

	/**
	 *	Releases a index buffer RHI resource.
	 *	Called when the resource is released, or when reseting all RHI resources.
	 */
	virtual void ReleaseRHI() override;

	/**
	 *	Locks the index buffer and returns a pointer to the first index in the locked region.
	 *
	 *	@param FirstIndex		- First index in the locked region. Defaults to the first index in the buffer.
	 *	@param NumIndices		- Number of indices to lock. Defaults to the remainder of the buffer.
	 */
	void* Lock(FRHICommandListBase& RHICmdList, uint32 FirstIndex=0, uint32 NumIndices=0);

	/**
	 *	Unlocks the index buffer.
	 */
	void Unlock(FRHICommandListBase& RHICmdList);

protected:
	uint32	NumIndices;
	uint32	Stride;
	uint8	bIsDynamic:1;
	uint8	bIsEmpty:1;
};

RawGPUIndexBuffer.cpp:

// By penguin21 and backported from UE3

#include "RawGPUIndexBuffer.h"

/*-----------------------------------------------------------------------------
	FRawGPUIndexBuffer
-----------------------------------------------------------------------------*/

/**
 *	Default constructor
 */
FRawGPUIndexBuffer::FRawGPUIndexBuffer()
	: NumIndices(0)
	, Stride(sizeof(uint16))
	, bIsDynamic(false)
	, bIsEmpty(true)
{
}

/**
 *	Setup constructor
 *	@param InNumIndices		- Number of indices to allocate space for
 *	@param InIsDynamic		- TRUE if the index buffer should be dynamic
 *	@param InStride			- Number of bytes per index
 */
FRawGPUIndexBuffer::FRawGPUIndexBuffer(uint32 InNumIndices, bool InIsDynamic, uint32 InStride)
:	NumIndices(InNumIndices)
,	Stride(InStride)
,	bIsDynamic(InIsDynamic)
,	bIsEmpty(true)
{
}

/**
 *	Sets up the index buffer, if the default constructor was used.
 *	@param InNumIndices		- Number of indices to allocate space for
 *	@param InIsDynamic		- TRUE if the index buffer should be dynamic
 *	@param InStride			- Number of bytes per index
 */
void FRawGPUIndexBuffer::Setup(uint32 InNumIndices, bool InIsDynamic, uint32 InStride)
{
	check( bIsEmpty );
	NumIndices	= InNumIndices;
	Stride		= InStride;
	bIsDynamic	= InIsDynamic;
	bIsEmpty	= true;
}

/**
 *	Renderthread API.
 *	Create the index buffer RHI resource and initialize its data.
 */
void FRawGPUIndexBuffer::InitRHI(FRHICommandListBase& RHICmdList)
{
	FRHIResourceCreateInfo CreateInfo(TEXT("FRawGPUIndexBuffer"));
	IndexBufferRHI = RHICmdList.CreateIndexBuffer(Stride, NumIndices * Stride, bIsDynamic ? BUF_Dynamic : BUF_Static, CreateInfo);
	bIsEmpty = true;
}

/**
 *	Renderthread API.
 *	Releases a index buffer RHI resource.
 *	Called when the resource is released, or when reseting all RHI resources.
 */
void FRawGPUIndexBuffer::ReleaseRHI()
{
	IndexBufferRHI.SafeRelease();
	bIsEmpty = true;
}

/**
 *	Renderthread API.
 *	Locks the index buffer and returns a pointer to the first index in the locked region.
 *
 *	@param FirstIndex		- First index in the locked region. Defaults to the first index in the buffer.
 *	@param InNumIndices		- Number of indices to lock. Defaults to the remainder of the buffer.
 */
void* FRawGPUIndexBuffer::Lock(FRHICommandListBase& RHICmdList, uint32 FirstIndex, uint32 InNumIndices)
{
	if ( InNumIndices == 0 )
	{
		InNumIndices = NumIndices - FirstIndex;
	}
	return RHICmdList.LockBuffer(IndexBufferRHI, FirstIndex * Stride, InNumIndices * Stride, RLM_WriteOnly);
}

/**
 *	Renderthread API.
 *	Unlocks the index buffer.
 */
void FRawGPUIndexBuffer::Unlock(FRHICommandListBase& RHICmdList)
{
	RHICmdList.UnlockBuffer(IndexBufferRHI);
	bIsEmpty = false;
}

Here is the ShaderCode:
FluidVertexFactory.ush:

/*=============================================================================
	FluidVertexFactory.ush: Local vertex factory shader code.
=============================================================================*/

#include "/Engine/Private/Common.ush"
#include "/Engine/Private/VertexFactoryCommon.ush"

#define USE_FLUIDSURFACE 1
#define ENABLE_XBOX_LEFTOVER 0

//float4x4	FluidLocalToWorld;
//float3x3	FluidWorldToLocal;
float4		GridSize;				// X=TotalWidth, Y=TotalHeight, Z=TweakScale (for tangents)
float4		LightmapCoordinateScaleBias;
float4		ShadowmapCoordinateScaleBias;

#if ENABLE_XBOX_LEFTOVER
Texture2D	Heightmap;
SamplerState HeightmapSampler;

float4 TessellationParameters;	// X = HeightScale (for vertex height)
// Y = NumQuadsX (number of quads along X in the control mesh)
// Z = 1/NumQuadsX
// W = 1/NumQuadsY

// The following are used to tessellate the grid in 8 different direction,
// for back-to-front sorting purposes.
float4		TessellationFactors1;
float4		TessellationFactors2;
float4		TexcoordScaleBias;		// Scale and bias to convert from heightmap UV to fluid UV
float4		SplineParameters;

static const float HeightScale		= TessellationParameters.x;
static const float NumQuadsX		= TessellationParameters.y;
static const float InvNumQuadsX		= TessellationParameters.z;
#endif // ENABLE_XBOX_LEFTOVER

#define FluidSize GridSize.xy
#define TweakScale GridSize.z

struct FVertexFactoryInput
{
	float	Height		: ATTRIBUTE0;
	float2	TexCoord	: ATTRIBUTE1;
	float2	HeightDelta : ATTRIBUTE2;
	
	VF_INSTANCED_STEREO_DECLARE_INPUT_BLOCK()
};

struct FVertexFactoryInterpolantsVSToPS
{
	TANGENTTOWORLD_INTERPOLATOR_BLOCK
	
	float4	TexCoord	: TEXCOORD0;
	float3	TangentX	: TEXCOORD1;
	float3	TangentY	: TANGENTTOWORLD0;
	float3	TangentZ	: TANGENTTOWORLD2;
	
#if INSTANCED_STEREO
	nointerpolation uint EyeIndex : PACKED_EYE_INDEX;
#endif
};

struct FVertexFactoryIntermediates
{
	float Dummy;

	//float3 InvNonUniformScale;
	//float DeterminantSign;

	FDFMatrix LocalToWorld;
	FDFInverseMatrix WorldToLocal;
	FDFMatrix PrevLocalToWorld;
	
	/** Cached primitive and instance data */
	FSceneDataIntermediates SceneData;
};

FSceneDataIntermediates GetSceneDataIntermediates(FVertexFactoryIntermediates Intermediates)
{
	return Intermediates.SceneData;
}

uint VertexFactoryGetViewIndex(FVertexFactoryIntermediates Intermediates)
{
	return GetSceneDataIntermediates(Intermediates).ViewIndex;
}

uint VertexFactoryGetInstanceIdLoadIndex(FVertexFactoryIntermediates Intermediates)
{
	return GetSceneDataIntermediates(Intermediates).InstanceIdLoadIndex;
}

FInstanceSceneData GetInstanceData(FVertexFactoryIntermediates Intermediates)
{
	return Intermediates.SceneData.InstanceData;
}

FPrimitiveSceneData GetPrimitiveData(FVertexFactoryIntermediates Intermediates)
{
	return Intermediates.SceneData.Primitive;
}

uint GetPrimitiveId(FVertexFactoryInterpolantsVSToPS Interpolants)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
	return Interpolants.PrimitiveId;
#else
	return 0;
#endif
}

void SetPrimitiveId(inout FVertexFactoryInterpolantsVSToPS Interpolants, uint PrimitiveId)
{
#if VF_USE_PRIMITIVE_SCENE_DATA
	Interpolants.PrimitiveId = PrimitiveId;
#endif
}

#if NEEDS_LIGHTMAP_COORDINATE
float2 GetLightMapCoordinate(FVertexFactoryInterpolantsVSToPS Interpolants)
{
	return Interpolants.TexCoord.zw;
}
#endif

/** Converts from vertex factory specific interpolants to a FMaterialPixelParameters, which is used by material inputs. */
FMaterialPixelParameters GetMaterialPixelParameters(FVertexFactoryInterpolantsVSToPS Interpolants, float4 SvPosition)
{
	// GetMaterialPixelParameters is responsible for fully initializing the result
	FMaterialPixelParameters Result = MakeInitializedMaterialPixelParameters();
#if NUM_MATERIAL_TEXCOORDS
	UNROLL
	for (int CoordinateIndex=0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS; CoordinateIndex++)
	{
		Result.TexCoords[CoordinateIndex] = Interpolants.TexCoord.xy;
	}
#endif
	Result.VertexColor = 1;

	half3 TangentY = Interpolants.TangentY;
	half3 TangentZ = Interpolants.TangentZ;
	Result.TangentToWorld = transpose(float3x3(Interpolants.TangentX, TangentY, TangentZ));
	Result.UnMirrored = 1;
	
#if LIGHTMAP_UV_ACCESS
	#if NEEDS_LIGHTMAP_COORDINATE	//TEXTURE_LIGHTMAP || SIMPLE_TEXTURE_LIGHTMAP
		Result.LightmapUVs = GetLightMapCoordinate(Interpolants);
	#else
		Result.LightmapUVs = float2(0, 0);
	#endif
#endif

	Result.TwoSidedSign = 1;
	Result.PrimitiveId = GetPrimitiveId(Interpolants);
	return Result;
}

float4 GetLocalPosFluid(FVertexFactoryInput Vertex)
{
	float4 LocalPos;
	LocalPos.xy = (Vertex.TexCoord.xy - 0.5f) * FluidSize;
	LocalPos.zw	= float2( Vertex.Height, 1.0f );
	return LocalPos;
}

float4 CalcWorldPosition( FVertexFactoryInput Vertex, FVertexFactoryIntermediates Intermediates )
{
	return mul( DFHackToFloat(Intermediates.LocalToWorld), GetLocalPosFluid(Vertex) );
}

float4 VertexFactoryGetRasterizedWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float4 InWorldPosition)
{
	return InWorldPosition;
}

float3 VertexFactoryGetPositionForVertexLighting(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, float3 TranslatedWorldPosition)
{
	return TranslatedWorldPosition;
}

FVertexFactoryIntermediates GetVertexFactoryIntermediates(FVertexFactoryInput Input)
{
	FVertexFactoryIntermediates Intermediates = (FVertexFactoryIntermediates)0;
	Intermediates.SceneData = VF_GPUSCENE_GET_INTERMEDIATES(Input);
	Intermediates.Dummy = 0;

	// Pre calculate
	//Intermediates.InvNonUniformScale	= GetInstanceData(Intermediates).InvNonUniformScale;
	//Intermediates.DeterminantSign		= GetInstanceData(Intermediates).DeterminantSign;
	Intermediates.LocalToWorld			= GetInstanceData(Intermediates).LocalToWorld;
	Intermediates.WorldToLocal			= GetInstanceData(Intermediates).WorldToLocal;
	Intermediates.PrevLocalToWorld		= GetInstanceData(Intermediates).PrevLocalToWorld;
	return Intermediates;
}

// @return translated world position
float4 VertexFactoryGetWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
	return CalcWorldPosition(Input, Intermediates);
}

FVertexFactoryInterpolantsVSToPS VertexFactoryGetInterpolantsVSToPS(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates, FMaterialVertexParameters VertexParameters)
{
	// Initialize the whole struct to 0
	// Really only the last two components of the packed UVs have the opportunity to be uninitialized
	FVertexFactoryInterpolantsVSToPS Interpolants = (FVertexFactoryInterpolantsVSToPS)0;
	
	Interpolants.TexCoord.xy = Input.TexCoord;
	Interpolants.TexCoord.zw = 0;
	
	float3 VX				= float3( 6.0f, 0.0f, Input.HeightDelta.x*TweakScale );
	float3 VY				= float3( 0.0f, 6.0f, Input.HeightDelta.y*TweakScale );
	float3 Normal			= cross( VX, VY );
	Interpolants.TangentX	= normalize(VX);
	Interpolants.TangentZ	= normalize(Normal);
	Interpolants.TangentY	= cross(Interpolants.TangentZ, Interpolants.TangentX);

	SetPrimitiveId(Interpolants, Intermediates.SceneData.PrimitiveId);
	
	return Interpolants;
}

float4 TransformTranslatedWorldToLocal(float4 TranslatedWorldPosition, FDFInverseMatrix WorldToLocal)
{
	float4x4 TranslatedWorldToLocal = DFFastToTranslatedWorld(WorldToLocal, ResolvedView.PreViewTranslation);
	float4 LocalPosition = mul(TranslatedWorldPosition, TranslatedWorldToLocal);
	return LocalPosition;
}

// local position relative to instance
float3 VertexFactoryGetInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
	float4 PosPrimSpace = TransformTranslatedWorldToLocal(VertexFactoryGetWorldPosition(Input, Intermediates), Intermediates.WorldToLocal);
	return PosPrimSpace.xyz; // No support for instancing, so instance == primitive
}

// local position relative to instance
float3 VertexFactoryGetPreviousInstanceSpacePosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
	return VertexFactoryGetInstanceSpacePosition(Input, Intermediates);
}

float4 VertexFactoryGetTranslatedPrimitiveVolumeBounds(FVertexFactoryInterpolantsVSToPS Interpolants)
{
	FPrimitiveSceneData PrimitiveData = GetPrimitiveData(GetPrimitiveId(Interpolants));
	return float4(DFFastToTranslatedWorld(PrimitiveData.ObjectWorldPosition, ResolvedView.PreViewTranslation), PrimitiveData.ObjectRadius);
}

// @return previous translated world position
float4 VertexFactoryGetPreviousWorldPosition(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
	return VertexFactoryGetWorldPosition(Input, Intermediates);
}

/**
* Get the 3x3 tangent basis vectors for this vertex factory
* this vertex factory will calculate the binormal on-the-fly
*
* @param Input - vertex input stream structure
* @return 3x3 matrix
*/
half3x3 VertexFactoryGetTangentToLocal( FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates )
{
	float3x3 Basis;
	const half3 VX		= half3( 6.0f, 0.0f, Input.HeightDelta.x*TweakScale );
	const half3 VY		= half3( 0.0f, 6.0f, Input.HeightDelta.y*TweakScale );
	const half3 Normal	= cross( VX, VY );
	Basis[0]		= normalize( VX );
	Basis[2]		= normalize( Normal );
	Basis[1]		= cross( Basis[2], Basis[0] );
	return Basis;
}

/** Converts from vertex factory specific input to a FMaterialVertexParameters, which is used by vertex shader material inputs. */
FMaterialVertexParameters GetMaterialVertexParameters(
	FVertexFactoryInput Input, 
	FVertexFactoryIntermediates Intermediates, 
	float3 WorldPosition, 
	half3x3 TangentToLocal,
	bool bIsPreviousFrame = false)
{
	FMaterialVertexParameters Result = MakeInitializedMaterialVertexParameters();
	Result.SceneData = Intermediates.SceneData;
	Result.PrimitiveId = Intermediates.SceneData.PrimitiveId;
	
	Result.WorldPosition = WorldPosition;
	Result.VertexColor = 1;
	
	if (bIsPreviousFrame)
	{
		Result.PositionInstanceSpace = VertexFactoryGetPreviousInstanceSpacePosition(Input, Intermediates);
	}
	else
	{
		Result.PositionInstanceSpace = VertexFactoryGetInstanceSpacePosition(Input, Intermediates);		
	}
	Result.PositionPrimitiveSpace = Result.PositionInstanceSpace; // No support for instancing, so instance == primitive
	float3x3 TangentToWorld = mul(transpose((float3x3)DFHackToFloat(Intermediates.LocalToWorld)), TangentToLocal);
	
	// Previous frame not handled deliberately. Lacks necessary information and
	// primitives using this VF are usually transparent and hence don't output velocity
	Result.TangentToWorld = TangentToWorld;
	Result.PreSkinnedPosition = GetLocalPosFluid(Input); //WorldPosition - DFHackToFloat(ResolvedView.PreViewTranslation).xyz; // Relative Pos
	Result.PreSkinnedNormal = float3(0,0,1); // Relative Pos Normal
	Result.PrevFrameLocalToWorld = Intermediates.PrevLocalToWorld;
#if NUM_MATERIAL_TEXCOORDS_VERTEX
	for(int CoordinateIndex = 0; CoordinateIndex < NUM_MATERIAL_TEXCOORDS_VERTEX; CoordinateIndex++)
	{
		// TexCoords for the vertex shader for this VertexFactory are not implemented.
		Result.TexCoords[CoordinateIndex] = 0.0f;
	}
#endif
	Result.LWCData = MakeMaterialLWCData(Result);
	
	return Result;
}

float3 VertexFactoryGetWorldNormal(FVertexFactoryInput Input, FVertexFactoryIntermediates Intermediates)
{
	const float3 VX		= float3( 6.0f, 0.0f, Input.HeightDelta.x*TweakScale );
	const float3 VY		= float3( 0.0f, 6.0f, Input.HeightDelta.y*TweakScale );
	const float3 TangentZ	= cross( VX, VY );
	float3 WorldNormal = mul( DFHackToFloat(GetPrimitiveData(Intermediates).LocalToWorld),TangentZ );
	return WorldNormal.xyz;
}