CelticCraft/Plugins/VoxelFree/Source/Voxel/Private/VoxelShaders/VoxelErosion.cpp

269 lines
No EOL
7 KiB
C++

// Copyright 2020 Phyronnaz
#include "VoxelShaders/VoxelErosion.h"
#include "VoxelShaders/VoxelErosionShader.h"
#include "VoxelUtilities/VoxelMathUtilities.h"
#include "VoxelMessages.h"
#include "Engine/Texture2D.h"
#include "Logging/MessageLog.h"
#include "Logging/TokenizedMessage.h"
void UVoxelErosion::Initialize()
{
if (bIsInit)
{
FVoxelMessages::Error("Erosion is already initialized!");
return;
}
RealSize = FMath::Max(32, FMath::CeilToInt(Size / 32.f) * 32);;
ENQUEUE_RENDER_COMMAND(Step)(
[ThisPtr = this](FRHICommandList& RHICmdList)
{
ThisPtr->Init_RenderThread();
});
FlushRenderingCommands();
if (RainMapInit.Texture.GetSizeX() == RealSize &&
RainMapInit.Texture.GetSizeY() == RealSize)
{
CopyTextureToRHI(RainMapInit.Texture, RainMap);
}
else
{
FVoxelMessages::Error(
FString::Printf(
TEXT("Voxel Erosion Init: RainMapInit has size (%d, %d), but should have size (%d, %d)"),
RainMapInit.Texture.GetSizeX(),
RainMapInit.Texture.GetSizeY(),
RealSize,
RealSize),
this);
}
if (HeightmapInit.Texture.GetSizeX() == RealSize &&
HeightmapInit.Texture.GetSizeY() == RealSize)
{
CopyTextureToRHI(HeightmapInit.Texture, TerrainHeight);
}
else
{
FVoxelMessages::Error(
FString::Printf(
TEXT("Voxel Erosion Init: HeightmapInit has size (%d, %d), but should have size (%d, %d)"),
HeightmapInit.Texture.GetSizeX(),
HeightmapInit.Texture.GetSizeY(),
RealSize,
RealSize),
this);
}
bIsInit = true;
}
bool UVoxelErosion::IsInitialized() const
{
return bIsInit;
}
void UVoxelErosion::Step(int32 Count)
{
if (!bIsInit)
{
FVoxelMessages::Error("Erosion is not initialized!");
return;
}
FVoxelErosionParameters Parameters;
Parameters.size = RealSize;
Parameters.dt = DeltaTime;
Parameters.l = Scale;
Parameters.g = Gravity;
Parameters.Kc = SedimentCapacity;
Parameters.Ks = SedimentDissolving;
Parameters.Kd = SedimentDeposition;
Parameters.Kr = RainStrength;
Parameters.Ke = Evaporation;
ENQUEUE_RENDER_COMMAND(Step)(
[Parameters, Count, ThisPtr = this](FRHICommandList& RHICmdList)
{
ThisPtr->Step_RenderThread(Parameters, Count);
});
}
FVoxelFloatTexture UVoxelErosion::GetTerrainHeightTexture()
{
if (!bIsInit)
{
FVoxelMessages::Error("Erosion is not initialized!");
return {};
}
auto Texture = MakeVoxelShared<TVoxelTexture<float>::FTextureData>();
CopyRHIToTexture(TerrainHeight, Texture);
return { TVoxelTexture<float>(Texture) };
}
FVoxelFloatTexture UVoxelErosion::GetWaterHeightTexture()
{
if (!bIsInit)
{
FVoxelMessages::Error("Erosion is not initialized!");
return {};
}
auto Texture = MakeVoxelShared<TVoxelTexture<float>::FTextureData>();
CopyRHIToTexture(WaterHeight, Texture);
return { TVoxelTexture<float>(Texture) };
}
FVoxelFloatTexture UVoxelErosion::GetSedimentTexture()
{
if (!bIsInit)
{
FVoxelMessages::Error("Erosion is not initialized!");
return {};
}
auto Texture = MakeVoxelShared<TVoxelTexture<float>::FTextureData>();
CopyRHIToTexture(Sediment, Texture);
return { TVoxelTexture<float>(Texture) };
}
template<typename T>
void UVoxelErosion::RunShader(const FVoxelErosionParameters& Parameters)
{
FRHICommandListImmediate& RHICmdList = GRHICommandList.GetImmediateCommandList();
TShaderMapRef<T> ComputeShader(GetGlobalShaderMap(ERHIFeatureLevel::SM5));
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
ComputeShader->SetSurfaces(
RHICmdList,
RainMapUAV,
TerrainHeightUAV,
TerrainHeight1UAV,
WaterHeightUAV,
WaterHeight1UAV,
WaterHeight2UAV,
SedimentUAV,
Sediment1UAV,
OutflowUAV,
VelocityUAV);
ComputeShader->SetUniformBuffers(RHICmdList, Parameters);
RHICmdList.DispatchComputeShader(RealSize / VOXEL_EROSION_NUM_THREADS_CS, RealSize / VOXEL_EROSION_NUM_THREADS_CS, 1);
ComputeShader->UnbindBuffers(RHICmdList);
}
void UVoxelErosion::CopyTextureToRHI(const TVoxelTexture<float>& Texture, const FTexture2DRHIRef& RHITexture)
{
ENQUEUE_RENDER_COMMAND(CopyTextureToRHI)([Texture, RHITexture, ThisPtr = this](FRHICommandList& RHICmdList)
{
ThisPtr->CopyTextureToRHI_RenderThread(Texture, RHITexture);
});
FlushRenderingCommands();
}
void UVoxelErosion::CopyRHIToTexture(const FTexture2DRHIRef& RHITexture, TVoxelSharedRef<TVoxelTexture<float>::FTextureData>& Texture)
{
ENQUEUE_RENDER_COMMAND(CopyRHIToTexture)(
[RHITexture, Texture, ThisPtr = this](FRHICommandList& RHICmdList)
{
ThisPtr->CopyRHIToTexture_RenderThread(RHITexture, *Texture);
});
FlushRenderingCommands();
}
void UVoxelErosion::CopyTextureToRHI_RenderThread(const TVoxelTexture<float>& Texture, const FTexture2DRHIRef& RHITexture)
{
check(IsInRenderingThread());
const int32 Size = RHITexture->GetSizeX();
if (!ensureAlways(RHITexture->GetSizeY() == Size)) return;
if (!ensureAlways(Texture.GetSizeX() == Size)) return;
if (!ensureAlways(Texture.GetSizeY() == Size)) return;
uint32 MappedStride = 0;
float* const RHIData = static_cast<float*>(RHILockTexture2D(RHITexture, 0, RLM_WriteOnly, MappedStride, false));
if (!ensureAlways(RHIData)) return;
check(Texture.GetTextureData().Num() == Size * Size);
FMemory::Memcpy(RHIData, Texture.GetTextureData().GetData(), Size * Size * sizeof(float));
RHIUnlockTexture2D(RHITexture, 0, false);
}
void UVoxelErosion::CopyRHIToTexture_RenderThread(const FTexture2DRHIRef& RHITexture, TVoxelTexture<float>::FTextureData& Texture)
{
check(IsInRenderingThread());
const int32 Size = RHITexture->GetSizeX();
if (!ensureAlways(RHITexture->GetSizeY() == Size)) return;
uint32 MappedStride = 0;
const float* RESTRICT const RHIData = static_cast<float*>(RHILockTexture2D(RHITexture, 0, RLM_ReadOnly, MappedStride, false));
if (!ensureAlways(RHIData)) return;
Texture.SetSize(Size, Size);
for (int32 Index = 0; Index < Size * Size; Index++)
{
Texture.SetValue(Index, RHIData[Index]);
}
RHIUnlockTexture2D(RHITexture, 0, false);
}
void UVoxelErosion::Init_RenderThread()
{
check(IsInRenderingThread());
FRHIResourceCreateInfo CreateInfo(TEXT("Name"));
const ETextureCreateFlags Flags = TexCreate_ShaderResource | TexCreate_UAV;
#define CREATE_TEXTURE(Name, SizeX) \
Name = RHICreateTexture2D(SizeX * RealSize, RealSize, PF_R32_FLOAT, 1, 1, Flags, CreateInfo); \
Name##UAV = RHICreateUnorderedAccessView(Name); \
CREATE_TEXTURE(RainMap, 1);
CREATE_TEXTURE(TerrainHeight, 1);
CREATE_TEXTURE(TerrainHeight1, 1);
CREATE_TEXTURE(WaterHeight, 1);
CREATE_TEXTURE(WaterHeight1, 1);
CREATE_TEXTURE(WaterHeight2, 1);
CREATE_TEXTURE(Sediment, 1);
CREATE_TEXTURE(Sediment1, 1);
CREATE_TEXTURE(Outflow, 4);
CREATE_TEXTURE(Velocity, 2);
#undef CREATE_TEXTURE
}
void UVoxelErosion::Step_RenderThread(const FVoxelErosionParameters& Parameters, int32 Count)
{
check(IsInRenderingThread());
for (int32 Index = 0; Index < Count; Index++)
{
RunShader<FVoxelErosionWaterIncrementCS>(Parameters);
RunShader<FVoxelErosionFlowSimulationCS>(Parameters);
RunShader<FVoxelErosionErosionDepositionCS>(Parameters);
RunShader<FVoxelErosionSedimentTransportationCS>(Parameters);
RunShader<FVoxelErosionEvaporationCS>(Parameters);
}
}