1
0
forked from 0ad/0ad
0ad/source/renderer/TerrainRenderer.cpp

791 lines
27 KiB
C++

/* Copyright (C) 2023 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "renderer/TerrainRenderer.h"
#include "graphics/Camera.h"
#include "graphics/Canvas2D.h"
#include "graphics/Decal.h"
#include "graphics/GameView.h"
#include "graphics/LightEnv.h"
#include "graphics/LOSTexture.h"
#include "graphics/Patch.h"
#include "graphics/Model.h"
#include "graphics/ShaderManager.h"
#include "graphics/TerritoryTexture.h"
#include "graphics/TextRenderer.h"
#include "graphics/TextureManager.h"
#include "maths/MathUtil.h"
#include "maths/Vector2D.h"
#include "ps/CLogger.h"
#include "ps/CStrInternStatic.h"
#include "ps/Filesystem.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/World.h"
#include "renderer/backend/IDevice.h"
#include "renderer/backend/PipelineState.h"
#include "renderer/DecalRData.h"
#include "renderer/PatchRData.h"
#include "renderer/Renderer.h"
#include "renderer/RenderingOptions.h"
#include "renderer/SceneRenderer.h"
#include "renderer/ShadowMap.h"
#include "renderer/SkyManager.h"
#include "renderer/VertexArray.h"
#include "renderer/WaterManager.h"
#include <memory>
/**
* TerrainRenderer keeps track of which phase it is in, to detect
* when Submit, PrepareForRendering etc. are called in the wrong order.
*/
enum Phase
{
Phase_Submit,
Phase_Render
};
/**
* Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class.
*/
struct TerrainRendererInternals
{
/// Which phase (submitting or rendering patches) are we in right now?
Phase phase;
/// Patches that were submitted for this frame
std::vector<CPatchRData*> visiblePatches[CSceneRenderer::CULL_MAX];
/// Decals that were submitted for this frame
std::vector<CDecalRData*> visibleDecals[CSceneRenderer::CULL_MAX];
/// Fancy water shader
CShaderTechniquePtr fancyWaterTech;
CShaderTechniquePtr shaderTechniqueSolid, shaderTechniqueSolidDepthTest;
Renderer::Backend::IVertexInputLayout* overlayVertexInputLayout = nullptr;
Renderer::Backend::IVertexInputLayout* decalsVertexInputLayout = nullptr;
Renderer::Backend::IVertexInputLayout* baseVertexInputLayout = nullptr;
Renderer::Backend::IVertexInputLayout* blendVertexInputLayout = nullptr;
Renderer::Backend::IVertexInputLayout* streamVertexInputLayout = nullptr;
Renderer::Backend::IVertexInputLayout* streamWithPositionAsTexCoordVertexInputLayout = nullptr;
Renderer::Backend::IVertexInputLayout* sideVertexInputLayout = nullptr;
Renderer::Backend::IVertexInputLayout* waterSurfaceVertexInputLayout = nullptr;
Renderer::Backend::IVertexInputLayout* waterSurfaceWithDataVertexInputLayout = nullptr;
Renderer::Backend::IVertexInputLayout* waterShoreVertexInputLayout = nullptr;
CSimulation2* simulation;
};
///////////////////////////////////////////////////////////////////
// Construction/Destruction
TerrainRenderer::TerrainRenderer()
{
m = new TerrainRendererInternals();
m->phase = Phase_Submit;
}
TerrainRenderer::~TerrainRenderer()
{
delete m;
}
void TerrainRenderer::Initialize()
{
const std::array<Renderer::Backend::SVertexAttributeFormat, 2> overlayAttributes{{
{Renderer::Backend::VertexAttributeStream::POSITION,
Renderer::Backend::Format::R32G32B32_SFLOAT, 0, sizeof(float) * 3,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
{Renderer::Backend::VertexAttributeStream::UV0,
Renderer::Backend::Format::R32G32B32_SFLOAT, 0, sizeof(float) * 3,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
}};
m->overlayVertexInputLayout = g_Renderer.GetVertexInputLayout(overlayAttributes);
m->decalsVertexInputLayout = CDecalRData::GetVertexInputLayout();
m->baseVertexInputLayout = CPatchRData::GetBaseVertexInputLayout();
m->blendVertexInputLayout = CPatchRData::GetBlendVertexInputLayout();
m->streamVertexInputLayout = CPatchRData::GetStreamVertexInputLayout(false);
m->streamWithPositionAsTexCoordVertexInputLayout =
CPatchRData::GetStreamVertexInputLayout(true);
m->sideVertexInputLayout = CPatchRData::GetSideVertexInputLayout();
m->waterSurfaceVertexInputLayout = CPatchRData::GetWaterSurfaceVertexInputLayout(false);
m->waterSurfaceWithDataVertexInputLayout = CPatchRData::GetWaterSurfaceVertexInputLayout(true);
m->waterShoreVertexInputLayout = CPatchRData::GetWaterShoreVertexInputLayout();
}
void TerrainRenderer::SetSimulation(CSimulation2* simulation)
{
m->simulation = simulation;
}
///////////////////////////////////////////////////////////////////
// Submit a patch for rendering
void TerrainRenderer::Submit(int cullGroup, CPatch* patch)
{
ENSURE(m->phase == Phase_Submit);
CPatchRData* data = (CPatchRData*)patch->GetRenderData();
if (data == 0)
{
// no renderdata for patch, create it now
data = new CPatchRData(patch, m->simulation);
patch->SetRenderData(data);
}
data->Update(m->simulation);
m->visiblePatches[cullGroup].push_back(data);
}
///////////////////////////////////////////////////////////////////
// Submit a decal for rendering
void TerrainRenderer::Submit(int cullGroup, CModelDecal* decal)
{
ENSURE(m->phase == Phase_Submit);
CDecalRData* data = (CDecalRData*)decal->GetRenderData();
if (data == 0)
{
// no renderdata for decal, create it now
data = new CDecalRData(decal, m->simulation);
decal->SetRenderData(data);
}
data->Update(m->simulation);
m->visibleDecals[cullGroup].push_back(data);
}
///////////////////////////////////////////////////////////////////
// Prepare for rendering
void TerrainRenderer::PrepareForRendering()
{
ENSURE(m->phase == Phase_Submit);
m->phase = Phase_Render;
}
///////////////////////////////////////////////////////////////////
// Clear submissions lists
void TerrainRenderer::EndFrame()
{
ENSURE(m->phase == Phase_Render || m->phase == Phase_Submit);
for (int i = 0; i < CSceneRenderer::CULL_MAX; ++i)
{
m->visiblePatches[i].clear();
m->visibleDecals[i].clear();
}
m->phase = Phase_Submit;
}
void TerrainRenderer::RenderTerrainOverlayTexture(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
int cullGroup, const CVector2D& textureTransform,
Renderer::Backend::ITexture* texture)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
CShaderTechniquePtr debugOverlayTech =
g_Renderer.GetShaderManager().LoadEffect(str_debug_overlay);
deviceCommandContext->SetGraphicsPipelineState(
debugOverlayTech->GetGraphicsPipelineState());
deviceCommandContext->BeginPass();
Renderer::Backend::IShaderProgram* debugOverlayShader = debugOverlayTech->GetShader();
deviceCommandContext->SetTexture(
debugOverlayShader->GetBindingSlot(str_baseTex), texture);
const CMatrix3D transform =
g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
deviceCommandContext->SetUniform(
debugOverlayShader->GetBindingSlot(str_transform), transform.AsFloatArray());
deviceCommandContext->SetUniform(
debugOverlayShader->GetBindingSlot(str_textureTransform), textureTransform.AsFloatArray());
CPatchRData::RenderStreams(
deviceCommandContext, m->streamWithPositionAsTexCoordVertexInputLayout, visiblePatches);
// To make the overlay visible over water, render an additional map-sized
// water-height patch.
CBoundingBoxAligned waterBounds;
for (CPatchRData* data : visiblePatches)
waterBounds += data->GetWaterBounds();
if (!waterBounds.IsEmpty())
{
// Add a delta to avoid z-fighting.
const float height = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight + 0.05f;
const float waterPos[] =
{
waterBounds[0].X, height, waterBounds[0].Z,
waterBounds[1].X, height, waterBounds[0].Z,
waterBounds[1].X, height, waterBounds[1].Z,
waterBounds[0].X, height, waterBounds[0].Z,
waterBounds[1].X, height, waterBounds[1].Z,
waterBounds[0].X, height, waterBounds[1].Z
};
deviceCommandContext->SetVertexInputLayout(m->overlayVertexInputLayout);
deviceCommandContext->SetVertexBufferData(
0, waterPos, std::size(waterPos) * sizeof(waterPos[0]));
deviceCommandContext->Draw(0, 6);
}
deviceCommandContext->EndPass();
}
///////////////////////////////////////////////////////////////////
/**
* Set up all the uniforms for a shader pass.
*/
void TerrainRenderer::PrepareShader(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
Renderer::Backend::IShaderProgram* shader, ShadowMap* shadow)
{
CSceneRenderer& sceneRenderer = g_Renderer.GetSceneRenderer();
const CMatrix3D transform = sceneRenderer.GetViewCamera().GetViewProjection();
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_transform), transform.AsFloatArray());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_cameraPos),
sceneRenderer.GetViewCamera().GetOrientation().GetTranslation().AsFloatArray());
const CLightEnv& lightEnv = sceneRenderer.GetLightEnv();
if (shadow)
shadow->BindTo(deviceCommandContext, shader);
CLOSTexture& los = sceneRenderer.GetScene().GetLOSTexture();
deviceCommandContext->SetTexture(
shader->GetBindingSlot(str_losTex), los.GetTextureSmooth());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_losTransform),
los.GetTextureMatrix()[0], los.GetTextureMatrix()[12]);
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_ambient),
lightEnv.m_AmbientColor.AsFloatArray());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_sunColor),
lightEnv.m_SunColor.AsFloatArray());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_sunDir),
lightEnv.GetSunDir().AsFloatArray());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_fogColor),
lightEnv.m_FogColor.AsFloatArray());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_fogParams),
lightEnv.m_FogFactor, lightEnv.m_FogMax);
}
void TerrainRenderer::RenderTerrainShader(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
std::vector<CDecalRData*>& visibleDecals = m->visibleDecals[cullGroup];
if (visiblePatches.empty() && visibleDecals.empty())
return;
if (!m->shaderTechniqueSolid)
{
m->shaderTechniqueSolid = g_Renderer.GetShaderManager().LoadEffect(
str_solid, {},
[](Renderer::Backend::SGraphicsPipelineStateDesc& pipelineStateDesc)
{
pipelineStateDesc.rasterizationState.cullMode = Renderer::Backend::CullMode::NONE;
});
}
// render the solid black sides of the map first
deviceCommandContext->SetGraphicsPipelineState(
m->shaderTechniqueSolid->GetGraphicsPipelineState());
deviceCommandContext->BeginPass();
Renderer::Backend::IShaderProgram* shaderSolid = m->shaderTechniqueSolid->GetShader();
const CMatrix3D transform =
g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
deviceCommandContext->SetUniform(
shaderSolid->GetBindingSlot(str_transform), transform.AsFloatArray());
deviceCommandContext->SetUniform(
shaderSolid->GetBindingSlot(str_color), 0.0f, 0.0f, 0.0f, 1.0f);
CPatchRData::RenderSides(
deviceCommandContext, m->sideVertexInputLayout, visiblePatches);
deviceCommandContext->EndPass();
CPatchRData::RenderBases(
deviceCommandContext, m->baseVertexInputLayout, visiblePatches, context, shadow);
// render blend passes for each patch
CPatchRData::RenderBlends(
deviceCommandContext, m->blendVertexInputLayout, visiblePatches, context, shadow);
CDecalRData::RenderDecals(
deviceCommandContext, m->decalsVertexInputLayout, visibleDecals, context, shadow);
}
///////////////////////////////////////////////////////////////////
// Render un-textured patches as polygons
void TerrainRenderer::RenderPatches(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
int cullGroup, const CShaderDefines& defines, const CColor& color)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
if (visiblePatches.empty())
return;
GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain patches");
CShaderTechniquePtr solidTech = g_Renderer.GetShaderManager().LoadEffect(str_terrain_solid, defines);
deviceCommandContext->SetGraphicsPipelineState(
solidTech->GetGraphicsPipelineState());
deviceCommandContext->BeginPass();
Renderer::Backend::IShaderProgram* solidShader = solidTech->GetShader();
const CMatrix3D transform =
g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
deviceCommandContext->SetUniform(
solidShader->GetBindingSlot(str_transform), transform.AsFloatArray());
deviceCommandContext->SetUniform(
solidShader->GetBindingSlot(str_color), color.AsFloatArray());
CPatchRData::RenderStreams(
deviceCommandContext, m->streamVertexInputLayout, visiblePatches);
deviceCommandContext->EndPass();
}
///////////////////////////////////////////////////////////////////
// Render outlines of submitted patches as lines
void TerrainRenderer::RenderOutlines(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
int cullGroup)
{
ENSURE(m->phase == Phase_Render);
std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
if (visiblePatches.empty())
return;
GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain outlines");
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderOutline();
}
///////////////////////////////////////////////////////////////////
// Scissor rectangle of water patches
CBoundingBoxAligned TerrainRenderer::ScissorWater(int cullGroup, const CCamera& camera)
{
CBoundingBoxAligned scissor;
for (const CPatchRData* data : m->visiblePatches[cullGroup])
{
const CBoundingBoxAligned& waterBounds = data->GetWaterBounds();
if (waterBounds.IsEmpty())
continue;
const CBoundingBoxAligned waterBoundsInViewPort =
camera.GetBoundsInViewPort(waterBounds);
if (!waterBoundsInViewPort.IsEmpty())
scissor += waterBoundsInViewPort;
}
if (scissor.IsEmpty())
return scissor;
return CBoundingBoxAligned(
CVector3D(Clamp(scissor[0].X, -1.0f, 1.0f), Clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f),
CVector3D(Clamp(scissor[1].X, -1.0f, 1.0f), Clamp(scissor[1].Y, -1.0f, 1.0f), 1.0f));
}
// Render fancy water
bool TerrainRenderer::RenderFancyWater(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
{
PROFILE3_GPU("fancy water");
GPU_SCOPED_LABEL(deviceCommandContext, "Render fancy water");
CSceneRenderer& sceneRenderer = g_Renderer.GetSceneRenderer();
WaterManager& waterManager = sceneRenderer.GetWaterManager();
CShaderDefines defines = context;
// If we're using fancy water, make sure its shader is loaded
if (!m->fancyWaterTech || waterManager.m_NeedsReloading)
{
if (waterManager.m_WaterRealDepth)
defines.Add(str_USE_REAL_DEPTH, str_1);
if (waterManager.m_WaterFancyEffects)
defines.Add(str_USE_FANCY_EFFECTS, str_1);
if (waterManager.m_WaterRefraction)
defines.Add(str_USE_REFRACTION, str_1);
if (waterManager.m_WaterReflection)
defines.Add(str_USE_REFLECTION, str_1);
m->fancyWaterTech = g_Renderer.GetShaderManager().LoadEffect(str_water_high, defines);
if (!m->fancyWaterTech)
{
LOGERROR("Failed to load water shader. Falling back to a simple water.\n");
waterManager.m_RenderWater = false;
return false;
}
waterManager.m_NeedsReloading = false;
}
CLOSTexture& losTexture = sceneRenderer.GetScene().GetLOSTexture();
// Calculating the advanced informations about Foam and all if the quality calls for it.
/*if (WaterMgr->m_NeedInfoUpdate && (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves))
{
WaterMgr->m_NeedInfoUpdate = false;
WaterMgr->CreateSuperfancyInfo();
}*/
const double time = waterManager.m_WaterTexTimer;
const float repeatPeriod = waterManager.m_RepeatPeriod;
deviceCommandContext->SetGraphicsPipelineState(
m->fancyWaterTech->GetGraphicsPipelineState());
deviceCommandContext->BeginPass();
Renderer::Backend::IShaderProgram* fancyWaterShader = m->fancyWaterTech->GetShader();
const CCamera& camera = g_Renderer.GetSceneRenderer().GetViewCamera();
const double period = 8.0;
// TODO: move uploading to a prepare function during loading.
const CTexturePtr& currentNormalTexture = waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)];
const CTexturePtr& nextNormalTexture = waterManager.m_NormalMap[waterManager.GetNextTextureIndex(period)];
currentNormalTexture->UploadBackendTextureIfNeeded(deviceCommandContext);
nextNormalTexture->UploadBackendTextureIfNeeded(deviceCommandContext);
deviceCommandContext->SetTexture(
fancyWaterShader->GetBindingSlot(str_normalMap),
currentNormalTexture->GetBackendTexture());
deviceCommandContext->SetTexture(
fancyWaterShader->GetBindingSlot(str_normalMap2),
nextNormalTexture->GetBackendTexture());
if (waterManager.m_WaterFancyEffects)
{
deviceCommandContext->SetTexture(
fancyWaterShader->GetBindingSlot(str_waterEffectsTex),
waterManager.m_FancyTexture.get());
}
if (waterManager.m_WaterRefraction && waterManager.m_WaterRealDepth)
{
deviceCommandContext->SetTexture(
fancyWaterShader->GetBindingSlot(str_depthTex),
waterManager.m_RefrFboDepthTexture.get());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_projInvTransform),
waterManager.m_RefractionProjInvMatrix.AsFloatArray());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_viewInvTransform),
waterManager.m_RefractionViewInvMatrix.AsFloatArray());
}
if (waterManager.m_WaterRefraction)
{
deviceCommandContext->SetTexture(
fancyWaterShader->GetBindingSlot(str_refractionMap),
waterManager.m_RefractionTexture.get());
}
if (waterManager.m_WaterReflection)
{
deviceCommandContext->SetTexture(
fancyWaterShader->GetBindingSlot(str_reflectionMap),
waterManager.m_ReflectionTexture.get());
}
deviceCommandContext->SetTexture(
fancyWaterShader->GetBindingSlot(str_losTex), losTexture.GetTextureSmooth());
const CLightEnv& lightEnv = sceneRenderer.GetLightEnv();
const CMatrix3D transform = sceneRenderer.GetViewCamera().GetViewProjection();
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_transform), transform.AsFloatArray());
deviceCommandContext->SetTexture(
fancyWaterShader->GetBindingSlot(str_skyCube),
sceneRenderer.GetSkyManager().GetSkyCube());
// TODO: check that this rotates in the right direction.
CMatrix3D skyBoxRotation;
skyBoxRotation.SetIdentity();
skyBoxRotation.RotateY(M_PI + lightEnv.GetRotation());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_skyBoxRot),
skyBoxRotation.AsFloatArray());
if (waterManager.m_WaterRefraction)
{
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_refractionMatrix),
waterManager.m_RefractionMatrix.AsFloatArray());
}
if (waterManager.m_WaterReflection)
{
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_reflectionMatrix),
waterManager.m_ReflectionMatrix.AsFloatArray());
}
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_ambient), lightEnv.m_AmbientColor.AsFloatArray());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_sunDir), lightEnv.GetSunDir().AsFloatArray());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_sunColor), lightEnv.m_SunColor.AsFloatArray());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_color), waterManager.m_WaterColor.AsFloatArray());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_tint), waterManager.m_WaterTint.AsFloatArray());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_waviness), waterManager.m_Waviness);
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_murkiness), waterManager.m_Murkiness);
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_windAngle), waterManager.m_WindAngle);
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_repeatScale), 1.0f / repeatPeriod);
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_losTransform),
losTexture.GetTextureMatrix()[0], losTexture.GetTextureMatrix()[12]);
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_cameraPos),
camera.GetOrientation().GetTranslation().AsFloatArray());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_fogColor),
lightEnv.m_FogColor.AsFloatArray());
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_fogParams),
lightEnv.m_FogFactor, lightEnv.m_FogMax);
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_time), static_cast<float>(time));
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_screenSize),
static_cast<float>(g_Renderer.GetWidth()),
static_cast<float>(g_Renderer.GetHeight()));
if (waterManager.m_WaterType == L"clap")
{
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_waveParams1),
30.0f, 1.5f, 20.0f, 0.03f);
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_waveParams2),
0.5f, 0.0f, 0.0f, 0.0f);
}
else if (waterManager.m_WaterType == L"lake")
{
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_waveParams1),
8.5f, 1.5f, 15.0f, 0.03f);
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_waveParams2),
0.2f, 0.0f, 0.0f, 0.07f);
}
else
{
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_waveParams1),
15.0f, 0.8f, 10.0f, 0.1f);
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_waveParams2),
0.3f, 0.0f, 0.1f, 0.3f);
}
if (shadow)
shadow->BindTo(deviceCommandContext, fancyWaterShader);
for (CPatchRData* data : m->visiblePatches[cullGroup])
{
data->RenderWaterSurface(
deviceCommandContext, m->waterSurfaceWithDataVertexInputLayout);
if (waterManager.m_WaterFancyEffects)
data->RenderWaterShore(deviceCommandContext, m->waterShoreVertexInputLayout);
}
deviceCommandContext->EndPass();
return true;
}
void TerrainRenderer::RenderSimpleWater(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
int cullGroup)
{
PROFILE3_GPU("simple water");
GPU_SCOPED_LABEL(deviceCommandContext, "Render Simple Water");
const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager();
CLOSTexture& losTexture = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture();
const double time = waterManager.m_WaterTexTimer;
CShaderDefines context;
if (g_Renderer.GetSceneRenderer().GetWaterRenderMode() == WIREFRAME)
context.Add(str_MODE_WIREFRAME, str_1);
CShaderTechniquePtr waterSimpleTech =
g_Renderer.GetShaderManager().LoadEffect(str_water_simple, context);
deviceCommandContext->SetGraphicsPipelineState(
waterSimpleTech->GetGraphicsPipelineState());
deviceCommandContext->BeginPass();
Renderer::Backend::IShaderProgram* waterSimpleShader = waterSimpleTech->GetShader();
const CTexturePtr& waterTexture = waterManager.m_WaterTexture[waterManager.GetCurrentTextureIndex(1.6)];
waterTexture->UploadBackendTextureIfNeeded(deviceCommandContext);
deviceCommandContext->SetTexture(
waterSimpleShader->GetBindingSlot(str_baseTex), waterTexture->GetBackendTexture());
deviceCommandContext->SetTexture(
waterSimpleShader->GetBindingSlot(str_losTex), losTexture.GetTextureSmooth());
const CMatrix3D transform =
g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
deviceCommandContext->SetUniform(
waterSimpleShader->GetBindingSlot(str_transform), transform.AsFloatArray());
deviceCommandContext->SetUniform(
waterSimpleShader->GetBindingSlot(str_losTransform),
losTexture.GetTextureMatrix()[0], losTexture.GetTextureMatrix()[12]);
deviceCommandContext->SetUniform(
waterSimpleShader->GetBindingSlot(str_time), static_cast<float>(time));
deviceCommandContext->SetUniform(
waterSimpleShader->GetBindingSlot(str_color), waterManager.m_WaterColor.AsFloatArray());
for (CPatchRData* data : m->visiblePatches[cullGroup])
{
data->RenderWaterSurface(
deviceCommandContext, m->waterSurfaceVertexInputLayout);
}
deviceCommandContext->EndPass();
}
///////////////////////////////////////////////////////////////////
// Render water that is part of the terrain
void TerrainRenderer::RenderWater(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
{
const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager();
if (!waterManager.WillRenderFancyWater())
RenderSimpleWater(deviceCommandContext, cullGroup);
else
RenderFancyWater(deviceCommandContext, context, cullGroup, shadow);
}
void TerrainRenderer::RenderWaterFoamOccluders(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
int cullGroup)
{
CSceneRenderer& sceneRenderer = g_Renderer.GetSceneRenderer();
const WaterManager& waterManager = sceneRenderer.GetWaterManager();
if (!waterManager.WillRenderFancyWater())
return;
if (!m->shaderTechniqueSolidDepthTest)
{
m->shaderTechniqueSolidDepthTest = g_Renderer.GetShaderManager().LoadEffect(
str_solid, {},
[](Renderer::Backend::SGraphicsPipelineStateDesc& pipelineStateDesc)
{
pipelineStateDesc.depthStencilState.depthTestEnabled = true;
pipelineStateDesc.rasterizationState.cullMode = Renderer::Backend::CullMode::NONE;
});
}
GPU_SCOPED_LABEL(deviceCommandContext, "Render water foam occluders");
Renderer::Backend::IFramebuffer* framebuffer =
waterManager.m_FancyEffectsOccludersFramebuffer.get();
deviceCommandContext->BeginFramebufferPass(framebuffer);
Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
viewportRect.width = framebuffer->GetWidth();
viewportRect.height = framebuffer->GetHeight();
deviceCommandContext->SetViewports(1, &viewportRect);
// Overwrite waves that would be behind the ground.
deviceCommandContext->SetGraphicsPipelineState(
m->shaderTechniqueSolidDepthTest->GetGraphicsPipelineState());
deviceCommandContext->BeginPass();
Renderer::Backend::IShaderProgram* dummyShader = m->shaderTechniqueSolidDepthTest->GetShader();
const CMatrix3D transform = sceneRenderer.GetViewCamera().GetViewProjection();
deviceCommandContext->SetUniform(
dummyShader->GetBindingSlot(str_transform), transform.AsFloatArray());
deviceCommandContext->SetUniform(
dummyShader->GetBindingSlot(str_color), 0.0f, 0.0f, 0.0f, 0.0f);
for (CPatchRData* data : m->visiblePatches[cullGroup])
data->RenderWaterShore(deviceCommandContext, m->waterShoreVertexInputLayout);
deviceCommandContext->EndPass();
deviceCommandContext->EndFramebufferPass();
}
void TerrainRenderer::RenderPriorities(CCanvas2D& canvas, int cullGroup)
{
PROFILE("priorities");
ENSURE(m->phase == Phase_Render);
CTextRenderer textRenderer;
textRenderer.SetCurrentFont(CStrIntern("mono-stroke-10"));
textRenderer.SetCurrentColor(CColor(1.0f, 1.0f, 0.0f, 1.0f));
std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderPriorities(textRenderer);
canvas.DrawText(textRenderer);
}