Index: ps/trunk/source/graphics/LOSTexture.cpp
===================================================================
--- ps/trunk/source/graphics/LOSTexture.cpp (revision 27040)
+++ ps/trunk/source/graphics/LOSTexture.cpp (revision 27041)
@@ -1,472 +1,472 @@
/* Copyright (C) 2022 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 .
*/
#include "precompiled.h"
#include "LOSTexture.h"
#include "graphics/ShaderManager.h"
#include "lib/bits.h"
#include "lib/config2.h"
#include "lib/timer.h"
#include "maths/MathUtil.h"
#include "ps/CLogger.h"
#include "ps/CStrInternStatic.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "renderer/backend/IDevice.h"
#include "renderer/Renderer.h"
#include "renderer/RenderingOptions.h"
#include "renderer/TimeManager.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/helpers/Los.h"
/*
The LOS bitmap is computed with one value per LOS vertex, based on
CCmpRangeManager's visibility information.
The bitmap is then blurred using an NxN filter (in particular a
7-tap Binomial filter as an efficient integral approximation of a Gaussian).
To implement the blur efficiently without using extra memory for a second copy
of the bitmap, we generate the bitmap with (N-1)/2 pixels of padding on each side,
then the blur shifts the image back into the corner.
The blurred bitmap is then uploaded into a GL texture for use by the renderer.
*/
// Blur with a NxN filter, where N = g_BlurSize must be an odd number.
// Keep it in relation to the number of impassable tiles in MAP_EDGE_TILES.
static const size_t g_BlurSize = 7;
// Alignment (in bytes) of the pixel data passed into texture uploading.
// This must be a multiple of GL_UNPACK_ALIGNMENT, which ought to be 1 (since
// that's what we set it to) but in some weird cases appears to have a different
// value. (See Trac #2594). Multiples of 4 are possibly good for performance anyway.
static const size_t g_SubTextureAlignment = 4;
CLOSTexture::CLOSTexture(CSimulation2& simulation)
: m_Simulation(simulation)
{
if (CRenderer::IsInitialised() && g_RenderingOptions.GetSmoothLOS())
CreateShader();
}
CLOSTexture::~CLOSTexture()
{
m_SmoothFramebuffers[0].reset();
m_SmoothFramebuffers[1].reset();
if (m_Texture)
DeleteTexture();
}
// Create the LOS texture engine. Should be ran only once.
bool CLOSTexture::CreateShader()
{
m_SmoothTech = g_Renderer.GetShaderManager().LoadEffect(str_los_interp);
m_ShaderInitialized = m_SmoothTech && m_SmoothTech->GetShader();
if (!m_ShaderInitialized)
{
LOGERROR("Failed to load SmoothLOS shader, disabling.");
g_RenderingOptions.SetSmoothLOS(false);
return false;
}
return true;
}
void CLOSTexture::DeleteTexture()
{
m_Texture.reset();
m_SmoothTextures[0].reset();
m_SmoothTextures[1].reset();
}
void CLOSTexture::MakeDirty()
{
m_Dirty = true;
}
Renderer::Backend::ITexture* CLOSTexture::GetTextureSmooth()
{
if (CRenderer::IsInitialised() && !g_RenderingOptions.GetSmoothLOS())
return GetTexture();
else
return m_SmoothTextures[m_WhichTexture].get();
}
void CLOSTexture::InterpolateLOS(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
{
const bool skipSmoothLOS = CRenderer::IsInitialised() && !g_RenderingOptions.GetSmoothLOS();
if (!skipSmoothLOS && !m_ShaderInitialized)
{
if (!CreateShader())
return;
// RecomputeTexture will not cause the ConstructTexture to run.
// Force the textures to be created.
DeleteTexture();
ConstructTexture(deviceCommandContext);
m_Dirty = true;
}
if (m_Dirty)
{
RecomputeTexture(deviceCommandContext);
// We need to subtract the frame time because without it we have the
// same output images for the current and previous frames.
m_LastTextureRecomputeTime = timer_Time() - g_Renderer.GetTimeManager().GetFrameDelta();
m_WhichTexture = 1u - m_WhichTexture;
m_Dirty = false;
}
if (skipSmoothLOS)
return;
GPU_SCOPED_LABEL(deviceCommandContext, "Render LOS texture");
deviceCommandContext->SetFramebuffer(m_SmoothFramebuffers[m_WhichTexture].get());
deviceCommandContext->SetGraphicsPipelineState(
m_SmoothTech->GetGraphicsPipelineStateDesc());
deviceCommandContext->BeginPass();
Renderer::Backend::IShaderProgram* shader = m_SmoothTech->GetShader();
deviceCommandContext->SetTexture(
shader->GetBindingSlot(str_losTex1), m_Texture.get());
deviceCommandContext->SetTexture(
shader->GetBindingSlot(str_losTex2), m_SmoothTextures[1u - m_WhichTexture].get());
const float delta = Clamp(
(timer_Time() - m_LastTextureRecomputeTime) * 2.0f, 0.0f, 1.0f);
deviceCommandContext->SetUniform(shader->GetBindingSlot(str_delta), delta);
const SViewPort oldVp = g_Renderer.GetViewport();
const SViewPort vp =
{
0, 0,
static_cast(m_Texture->GetWidth()),
static_cast(m_Texture->GetHeight())
};
g_Renderer.SetViewport(vp);
float quadVerts[] =
{
1.0f, 1.0f,
-1.0f, 1.0f,
-1.0f, -1.0f,
-1.0f, -1.0f,
1.0f, -1.0f,
1.0f, 1.0f
};
float quadTex[] =
{
1.0f, 1.0f,
0.0f, 1.0f,
0.0f, 0.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::POSITION,
Renderer::Backend::Format::R32G32_SFLOAT, 0, 0,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0);
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::UV0,
Renderer::Backend::Format::R32G32_SFLOAT, 0, 0,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 1);
deviceCommandContext->SetVertexBufferData(
0, quadVerts, std::size(quadVerts) * sizeof(quadVerts[0]));
deviceCommandContext->SetVertexBufferData(
1, quadTex, std::size(quadTex) * sizeof(quadTex[0]));
deviceCommandContext->Draw(0, 6);
g_Renderer.SetViewport(oldVp);
deviceCommandContext->EndPass();
deviceCommandContext->SetFramebuffer(
deviceCommandContext->GetDevice()->GetCurrentBackbuffer());
}
Renderer::Backend::ITexture* CLOSTexture::GetTexture()
{
ENSURE(!m_Dirty);
return m_Texture.get();
}
const CMatrix3D& CLOSTexture::GetTextureMatrix()
{
ENSURE(!m_Dirty);
return m_TextureMatrix;
}
const CMatrix3D& CLOSTexture::GetMinimapTextureMatrix()
{
ENSURE(!m_Dirty);
return m_MinimapTextureMatrix;
}
void CLOSTexture::ConstructTexture(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
{
CmpPtr cmpRangeManager(m_Simulation, SYSTEM_ENTITY);
if (!cmpRangeManager)
return;
m_MapSize = cmpRangeManager->GetVerticesPerSide();
const size_t textureSize = round_up_to_pow2(round_up((size_t)m_MapSize + g_BlurSize - 1, g_SubTextureAlignment));
Renderer::Backend::IDevice* backendDevice = deviceCommandContext->GetDevice();
const Renderer::Backend::Sampler::Desc defaultSamplerDesc =
Renderer::Backend::Sampler::MakeDefaultSampler(
Renderer::Backend::Sampler::Filter::LINEAR,
Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE);
if (backendDevice->IsFramebufferFormatSupported(Renderer::Backend::Format::R8_UNORM))
{
m_TextureFormat = Renderer::Backend::Format::R8_UNORM;
m_TextureFormatStride = 1;
}
else
{
m_TextureFormat = Renderer::Backend::Format::R8G8B8A8_UNORM;
m_TextureFormatStride = 4;
}
m_Texture = backendDevice->CreateTexture2D("LOSTexture",
m_TextureFormat, textureSize, textureSize, defaultSamplerDesc);
// Initialise texture with SoD color, for the areas we don't
// overwrite with uploading later.
const size_t textureDataSize = textureSize * textureSize * m_TextureFormatStride;
std::unique_ptr texData = std::make_unique(textureDataSize);
memset(texData.get(), 0x00, textureDataSize);
if (CRenderer::IsInitialised() && g_RenderingOptions.GetSmoothLOS())
{
m_SmoothTextures[0] = backendDevice->CreateTexture2D("LOSSmoothTexture0",
m_TextureFormat, textureSize, textureSize, defaultSamplerDesc);
m_SmoothTextures[1] = backendDevice->CreateTexture2D("LOSSmoothTexture1",
m_TextureFormat, textureSize, textureSize, defaultSamplerDesc);
m_SmoothFramebuffers[0] = backendDevice->CreateFramebuffer(
"LOSSmoothFramebuffer0", m_SmoothTextures[0].get(), nullptr);
m_SmoothFramebuffers[1] = backendDevice->CreateFramebuffer(
"LOSSmoothFramebuffer1", m_SmoothTextures[1].get(), nullptr);
if (!m_SmoothFramebuffers[0] || !m_SmoothFramebuffers[1])
{
LOGERROR("Failed to create LOS framebuffers");
g_RenderingOptions.SetSmoothLOS(false);
}
deviceCommandContext->UploadTexture(
m_SmoothTextures[0].get(), m_TextureFormat, texData.get(), textureDataSize);
deviceCommandContext->UploadTexture(
m_SmoothTextures[1].get(), m_TextureFormat, texData.get(), textureDataSize);
}
deviceCommandContext->UploadTexture(
m_Texture.get(), m_TextureFormat, texData.get(), textureDataSize);
texData.reset();
{
// Texture matrix: We want to map
// world pos (0, y, 0) (i.e. first vertex)
// onto texcoord (0.5/texsize, 0.5/texsize) (i.e. middle of first texel);
// world pos ((mapsize-1)*cellsize, y, (mapsize-1)*cellsize) (i.e. last vertex)
// onto texcoord ((mapsize-0.5) / texsize, (mapsize-0.5) / texsize) (i.e. middle of last texel)
float s = (m_MapSize-1) / static_cast(textureSize * (m_MapSize-1) * LOS_TILE_SIZE);
float t = 0.5f / textureSize;
m_TextureMatrix.SetZero();
m_TextureMatrix._11 = s;
m_TextureMatrix._23 = s;
m_TextureMatrix._14 = t;
m_TextureMatrix._24 = t;
m_TextureMatrix._44 = 1;
}
{
// Minimap matrix: We want to map UV (0,0)-(1,1) onto (0,0)-(mapsize/texsize, mapsize/texsize)
float s = m_MapSize / (float)textureSize;
m_MinimapTextureMatrix.SetZero();
m_MinimapTextureMatrix._11 = s;
m_MinimapTextureMatrix._22 = s;
m_MinimapTextureMatrix._44 = 1;
}
}
void CLOSTexture::RecomputeTexture(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
{
// If the map was resized, delete and regenerate the texture
if (m_Texture)
{
CmpPtr cmpRangeManager(m_Simulation, SYSTEM_ENTITY);
if (!cmpRangeManager || m_MapSize != cmpRangeManager->GetVerticesPerSide())
DeleteTexture();
}
bool recreated = false;
if (!m_Texture)
{
ConstructTexture(deviceCommandContext);
recreated = true;
}
PROFILE("recompute LOS texture");
CmpPtr cmpRangeManager(m_Simulation, SYSTEM_ENTITY);
if (!cmpRangeManager)
return;
size_t pitch;
const size_t dataSize = GetBitmapSize(m_MapSize, m_MapSize, &pitch);
ENSURE(pitch * m_MapSize <= dataSize);
std::unique_ptr losData = std::make_unique(
dataSize * m_TextureFormatStride);
- CLosQuerier los(cmpRangeManager->GetLosQuerier(g_Game->GetSimulation2()->GetSimContext().GetCurrentDisplayedPlayer()));
+ CLosQuerier los(cmpRangeManager->GetLosQuerier(m_Simulation.GetSimContext().GetCurrentDisplayedPlayer()));
GenerateBitmap(los, &losData[0], m_MapSize, m_MapSize, pitch);
// GenerateBitmap writes data tightly packed and we need to offset it to fit
// into the texture format properly.
const size_t textureDataPitch = pitch * m_TextureFormatStride;
if (m_TextureFormatStride > 1)
{
// We skip the last byte because it will be first in our order and we
// don't need to move it.
for (size_t index = 0; index + 1 < dataSize; ++index)
{
const size_t oldAddress = dataSize - 1 - index;
const size_t newAddress = oldAddress * m_TextureFormatStride;
losData[newAddress] = losData[oldAddress];
losData[oldAddress] = 0;
}
}
if (CRenderer::IsInitialised() && g_RenderingOptions.GetSmoothLOS() && recreated)
{
deviceCommandContext->UploadTextureRegion(
m_SmoothTextures[0].get(), m_TextureFormat, losData.get(),
textureDataPitch * m_MapSize, 0, 0, pitch, m_MapSize);
deviceCommandContext->UploadTextureRegion(
m_SmoothTextures[1].get(), m_TextureFormat, losData.get(),
textureDataPitch * m_MapSize, 0, 0, pitch, m_MapSize);
}
deviceCommandContext->UploadTextureRegion(
m_Texture.get(), m_TextureFormat, losData.get(),
textureDataPitch * m_MapSize, 0, 0, pitch, m_MapSize);
}
size_t CLOSTexture::GetBitmapSize(size_t w, size_t h, size_t* pitch)
{
*pitch = round_up(w + g_BlurSize - 1, g_SubTextureAlignment);
return *pitch * (h + g_BlurSize - 1);
}
void CLOSTexture::GenerateBitmap(const CLosQuerier& los, u8* losData, size_t w, size_t h, size_t pitch)
{
u8 *dataPtr = losData;
// Initialise the top padding
for (size_t j = 0; j < g_BlurSize/2; ++j)
for (size_t i = 0; i < pitch; ++i)
*dataPtr++ = 0;
for (size_t j = 0; j < h; ++j)
{
// Initialise the left padding
for (size_t i = 0; i < g_BlurSize/2; ++i)
*dataPtr++ = 0;
// Fill in the visibility data
for (size_t i = 0; i < w; ++i)
{
if (los.IsVisible_UncheckedRange(i, j))
*dataPtr++ = 255;
else if (los.IsExplored_UncheckedRange(i, j))
*dataPtr++ = 127;
else
*dataPtr++ = 0;
}
// Initialise the right padding
for (size_t i = 0; i < pitch - w - g_BlurSize/2; ++i)
*dataPtr++ = 0;
}
// Initialise the bottom padding
for (size_t j = 0; j < g_BlurSize/2; ++j)
for (size_t i = 0; i < pitch; ++i)
*dataPtr++ = 0;
// Horizontal blur:
for (size_t j = g_BlurSize/2; j < h + g_BlurSize/2; ++j)
{
for (size_t i = 0; i < w; ++i)
{
u8* d = &losData[i+j*pitch];
*d = (
1*d[0] +
6*d[1] +
15*d[2] +
20*d[3] +
15*d[4] +
6*d[5] +
1*d[6]
) / 64;
}
}
// Vertical blur:
for (size_t j = 0; j < h; ++j)
{
for (size_t i = 0; i < w; ++i)
{
u8* d = &losData[i+j*pitch];
*d = (
1*d[0*pitch] +
6*d[1*pitch] +
15*d[2*pitch] +
20*d[3*pitch] +
15*d[4*pitch] +
6*d[5*pitch] +
1*d[6*pitch]
) / 64;
}
}
}
Index: ps/trunk/source/graphics/MiniMapTexture.cpp
===================================================================
--- ps/trunk/source/graphics/MiniMapTexture.cpp (revision 27040)
+++ ps/trunk/source/graphics/MiniMapTexture.cpp (revision 27041)
@@ -1,813 +1,814 @@
/* Copyright (C) 2022 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 .
*/
#include "precompiled.h"
#include "MiniMapTexture.h"
#include "graphics/GameView.h"
#include "graphics/LOSTexture.h"
#include "graphics/MiniPatch.h"
#include "graphics/ShaderManager.h"
#include "graphics/ShaderProgramPtr.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureEntry.h"
#include "graphics/TerrainTextureManager.h"
#include "graphics/TerritoryTexture.h"
#include "graphics/TextureManager.h"
#include "lib/bits.h"
#include "lib/hash.h"
#include "lib/timer.h"
#include "maths/MathUtil.h"
#include "maths/Vector2D.h"
#include "ps/ConfigDB.h"
#include "ps/CStrInternStatic.h"
#include "ps/Filesystem.h"
#include "ps/Game.h"
#include "ps/Profile.h"
#include "ps/VideoMode.h"
#include "ps/World.h"
#include "ps/XML/Xeromyces.h"
#include "renderer/backend/IDevice.h"
#include "renderer/Renderer.h"
#include "renderer/RenderingOptions.h"
#include "renderer/SceneRenderer.h"
#include "renderer/WaterManager.h"
#include "scriptinterface/Object.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpMinimap.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/system/ParamNode.h"
#include
#include
#include
namespace
{
// Set max drawn entities to 64K / 4 for now, which is more than enough.
// 4 is the number of vertices per entity.
// TODO: we should be cleverer about drawing them to reduce clutter,
// f.e. use instancing.
constexpr size_t MAX_ENTITIES_DRAWN = 65536 / 4;
constexpr size_t MAX_ICON_COUNT = 256;
constexpr size_t MAX_UNIQUE_ICON_COUNT = 64;
constexpr size_t ICON_COMBINING_GRID_SIZE = 10;
constexpr size_t FINAL_TEXTURE_SIZE = 512;
unsigned int ScaleColor(unsigned int color, float x)
{
unsigned int r = unsigned(float(color & 0xff) * x);
unsigned int g = unsigned(float((color >> 8) & 0xff) * x);
unsigned int b = unsigned(float((color >> 16) & 0xff) * x);
return (0xff000000 | b | g << 8 | r << 16);
}
void DrawTexture(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
{
const float quadUVs[] =
{
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
1.0f, 1.0f,
0.0f, 1.0f,
0.0f, 0.0f
};
const float quadVertices[] =
{
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f
};
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::POSITION,
Renderer::Backend::Format::R32G32B32_SFLOAT, 0, 0,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0);
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::UV0,
Renderer::Backend::Format::R32G32_SFLOAT, 0, 0,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 1);
deviceCommandContext->SetVertexBufferData(
0, quadVertices, std::size(quadVertices) * sizeof(quadVertices[0]));
deviceCommandContext->SetVertexBufferData(
1, quadUVs, std::size(quadUVs) * sizeof(quadUVs[0]));
deviceCommandContext->Draw(0, 6);
}
struct MinimapUnitVertex
{
// This struct is copyable for convenience and because to move is to copy for primitives.
u8 r, g, b, a;
CVector2D position;
};
// Adds a vertex to the passed VertexArray
inline void AddEntity(const MinimapUnitVertex& v,
VertexArrayIterator& attrColor,
VertexArrayIterator& attrPos,
const float entityRadius,
const bool useInstancing)
{
if (useInstancing)
{
(*attrColor)[0] = v.r;
(*attrColor)[1] = v.g;
(*attrColor)[2] = v.b;
(*attrColor)[3] = v.a;
++attrColor;
(*attrPos)[0] = v.position.X;
(*attrPos)[1] = v.position.Y;
++attrPos;
return;
}
const CVector2D offsets[4] =
{
{-entityRadius, 0.0f},
{0.0f, -entityRadius},
{entityRadius, 0.0f},
{0.0f, entityRadius}
};
for (const CVector2D& offset : offsets)
{
(*attrColor)[0] = v.r;
(*attrColor)[1] = v.g;
(*attrColor)[2] = v.b;
(*attrColor)[3] = v.a;
++attrColor;
(*attrPos)[0] = v.position.X + offset.X;
(*attrPos)[1] = v.position.Y + offset.Y;
++attrPos;
}
}
} // anonymous namespace
size_t CMiniMapTexture::CellIconKeyHash::operator()(
const CellIconKey& key) const
{
size_t seed = 0;
hash_combine(seed, key.path);
hash_combine(seed, key.r);
hash_combine(seed, key.g);
hash_combine(seed, key.b);
return seed;
}
bool CMiniMapTexture::CellIconKeyEqual::operator()(
const CellIconKey& lhs, const CellIconKey& rhs) const
{
return
lhs.path == rhs.path &&
lhs.r == rhs.r &&
lhs.g == rhs.g &&
lhs.b == rhs.b;
}
CMiniMapTexture::CMiniMapTexture(CSimulation2& simulation)
: m_Simulation(simulation), m_IndexArray(false),
m_VertexArray(Renderer::Backend::IBuffer::Type::VERTEX, true),
m_InstanceVertexArray(Renderer::Backend::IBuffer::Type::VERTEX, false)
{
// Register Relax NG validator.
CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng");
m_ShallowPassageHeight = GetShallowPassageHeight();
double blinkDuration = 1.0;
// Tests won't have config initialised
if (CConfigDB::IsInitialised())
{
CFG_GET_VAL("gui.session.minimap.blinkduration", blinkDuration);
CFG_GET_VAL("gui.session.minimap.pingduration", m_PingDuration);
}
m_HalfBlinkDuration = blinkDuration / 2.0;
m_AttributePos.format = Renderer::Backend::Format::R32G32_SFLOAT;
m_VertexArray.AddAttribute(&m_AttributePos);
m_AttributeColor.format = Renderer::Backend::Format::R8G8B8A8_UNORM;
m_VertexArray.AddAttribute(&m_AttributeColor);
m_VertexArray.SetNumberOfVertices(MAX_ENTITIES_DRAWN * 4);
m_VertexArray.Layout();
m_IndexArray.SetNumberOfVertices(MAX_ENTITIES_DRAWN * 6);
m_IndexArray.Layout();
VertexArrayIterator index = m_IndexArray.GetIterator();
for (size_t i = 0; i < m_IndexArray.GetNumberOfVertices(); ++i)
*index++ = 0;
m_IndexArray.Upload();
VertexArrayIterator attrPos = m_AttributePos.GetIterator();
VertexArrayIterator attrColor = m_AttributeColor.GetIterator();
for (size_t i = 0; i < m_VertexArray.GetNumberOfVertices(); ++i)
{
(*attrColor)[0] = 0;
(*attrColor)[1] = 0;
(*attrColor)[2] = 0;
(*attrColor)[3] = 0;
++attrColor;
(*attrPos)[0] = -10000.0f;
(*attrPos)[1] = -10000.0f;
++attrPos;
}
m_VertexArray.Upload();
if (g_VideoMode.GetBackendDevice()->GetCapabilities().instancing)
{
m_UseInstancing = true;
const size_t numberOfCircleSegments = 8;
m_InstanceAttributePosition.format = Renderer::Backend::Format::R32G32_SFLOAT;
m_InstanceVertexArray.AddAttribute(&m_InstanceAttributePosition);
m_InstanceVertexArray.SetNumberOfVertices(numberOfCircleSegments * 3);
m_InstanceVertexArray.Layout();
VertexArrayIterator attributePosition =
m_InstanceAttributePosition.GetIterator();
for (size_t segment = 0; segment < numberOfCircleSegments; ++segment)
{
const float currentAngle = static_cast(segment) / numberOfCircleSegments * 2.0f * M_PI;
const float nextAngle = static_cast(segment + 1) / numberOfCircleSegments * 2.0f * M_PI;
(*attributePosition)[0] = 0.0f;
(*attributePosition)[1] = 0.0f;
++attributePosition;
(*attributePosition)[0] = std::cos(currentAngle);
(*attributePosition)[1] = std::sin(currentAngle);
++attributePosition;
(*attributePosition)[0] = std::cos(nextAngle);
(*attributePosition)[1] = std::sin(nextAngle);
++attributePosition;
}
m_InstanceVertexArray.Upload();
m_InstanceVertexArray.FreeBackingStore();
}
}
CMiniMapTexture::~CMiniMapTexture()
{
DestroyTextures();
}
void CMiniMapTexture::Update(const float UNUSED(deltaRealTime))
{
if (m_WaterHeight != g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight)
{
m_TerrainTextureDirty = true;
m_FinalTextureDirty = true;
}
}
void CMiniMapTexture::Render(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
{
const CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
if (!terrain)
return;
if (!m_TerrainTexture)
CreateTextures(deviceCommandContext, terrain);
if (m_TerrainTextureDirty)
RebuildTerrainTexture(deviceCommandContext, terrain);
RenderFinalTexture(deviceCommandContext);
}
void CMiniMapTexture::CreateTextures(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext, const CTerrain* terrain)
{
DestroyTextures();
m_MapSize = terrain->GetVerticesPerSide();
const size_t textureSize = round_up_to_pow2(static_cast(m_MapSize));
const Renderer::Backend::Sampler::Desc defaultSamplerDesc =
Renderer::Backend::Sampler::MakeDefaultSampler(
Renderer::Backend::Sampler::Filter::LINEAR,
Renderer::Backend::Sampler::AddressMode::CLAMP_TO_EDGE);
Renderer::Backend::IDevice* backendDevice = deviceCommandContext->GetDevice();
// Create terrain texture
m_TerrainTexture = backendDevice->CreateTexture2D("MiniMapTerrainTexture",
Renderer::Backend::Format::R8G8B8A8_UNORM, textureSize, textureSize, defaultSamplerDesc);
// Initialise texture with solid black, for the areas we don't
// overwrite with uploading later.
std::unique_ptr texData = std::make_unique(textureSize * textureSize);
for (size_t i = 0; i < textureSize * textureSize; ++i)
texData[i] = 0xFF000000;
deviceCommandContext->UploadTexture(
m_TerrainTexture.get(), Renderer::Backend::Format::R8G8B8A8_UNORM,
texData.get(), textureSize * textureSize * 4);
texData.reset();
m_TerrainData = std::make_unique((m_MapSize - 1) * (m_MapSize - 1));
m_FinalTexture = backendDevice->CreateTexture2D("MiniMapFinalTexture",
Renderer::Backend::Format::R8G8B8A8_UNORM, FINAL_TEXTURE_SIZE, FINAL_TEXTURE_SIZE, defaultSamplerDesc);
m_FinalTextureFramebuffer = backendDevice->CreateFramebuffer("MiniMapFinalFramebuffer",
m_FinalTexture.get(), nullptr);
ENSURE(m_FinalTextureFramebuffer);
}
void CMiniMapTexture::DestroyTextures()
{
m_TerrainTexture.reset();
m_FinalTexture.reset();
m_TerrainData.reset();
}
void CMiniMapTexture::RebuildTerrainTexture(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
const CTerrain* terrain)
{
const u32 x = 0;
const u32 y = 0;
const u32 width = m_MapSize - 1;
const u32 height = m_MapSize - 1;
m_WaterHeight = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight;
m_TerrainTextureDirty = false;
for (u32 j = 0; j < height; ++j)
{
u32* dataPtr = m_TerrainData.get() + ((y + j) * width) + x;
for (u32 i = 0; i < width; ++i)
{
const float avgHeight = ( terrain->GetVertexGroundLevel((int)i, (int)j)
+ terrain->GetVertexGroundLevel((int)i+1, (int)j)
+ terrain->GetVertexGroundLevel((int)i, (int)j+1)
+ terrain->GetVertexGroundLevel((int)i+1, (int)j+1)
) / 4.0f;
if (avgHeight < m_WaterHeight && avgHeight > m_WaterHeight - m_ShallowPassageHeight)
{
// shallow water
*dataPtr++ = 0xffc09870;
}
else if (avgHeight < m_WaterHeight)
{
// Set water as constant color for consistency on different maps
*dataPtr++ = 0xffa07850;
}
else
{
int hmap = ((int)terrain->GetHeightMap()[(y + j) * m_MapSize + x + i]) >> 8;
int val = (hmap / 3) + 170;
u32 color = 0xFFFFFFFF;
CMiniPatch* mp = terrain->GetTile(x + i, y + j);
if (mp)
{
CTerrainTextureEntry* tex = mp->GetTextureEntry();
if (tex)
{
// If the texture can't be loaded yet, set the dirty flags
// so we'll try regenerating the terrain texture again soon
if (!tex->GetTexture()->TryLoad())
m_TerrainTextureDirty = true;
color = tex->GetBaseColor();
}
}
*dataPtr++ = ScaleColor(color, float(val) / 255.0f);
}
}
}
// Upload the texture
deviceCommandContext->UploadTextureRegion(
m_TerrainTexture.get(), Renderer::Backend::Format::R8G8B8A8_UNORM,
m_TerrainData.get(), width * height * 4, 0, 0, width, height);
}
void CMiniMapTexture::RenderFinalTexture(
Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
{
// only update 2x / second
// (note: since units only move a few pixels per second on the minimap,
// we can get away with infrequent updates; this is slow)
// TODO: Update all but camera at same speed as simulation
const double currentTime = timer_Time();
const bool doUpdate = (currentTime - m_LastFinalTextureUpdate > 0.5) || m_FinalTextureDirty;
if (doUpdate)
m_LastFinalTextureUpdate = currentTime;
else
return;
m_FinalTextureDirty = false;
PROFILE3("Render minimap texture");
GPU_SCOPED_LABEL(deviceCommandContext, "Render minimap texture");
deviceCommandContext->SetFramebuffer(m_FinalTextureFramebuffer.get());
const SViewPort oldViewPort = g_Renderer.GetViewport();
const SViewPort viewPort = { 0, 0, FINAL_TEXTURE_SIZE, FINAL_TEXTURE_SIZE };
g_Renderer.SetViewport(viewPort);
CmpPtr cmpRangeManager(m_Simulation, SYSTEM_ENTITY);
ENSURE(cmpRangeManager);
- CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
+ CLOSTexture& losTexture = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture();
const float invTileMapSize = 1.0f / static_cast(TERRAIN_TILE_SIZE * m_MapSize);
const float texCoordMax = m_TerrainTexture ? static_cast(m_MapSize - 1) / m_TerrainTexture->GetWidth() : 1.0f;
Renderer::Backend::IShaderProgram* shader = nullptr;
CShaderTechniquePtr tech;
CShaderDefines baseDefines;
baseDefines.Add(str_MINIMAP_BASE, str_1);
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, baseDefines);
Renderer::Backend::GraphicsPipelineStateDesc pipelineStateDesc =
tech->GetGraphicsPipelineStateDesc();
deviceCommandContext->SetGraphicsPipelineState(pipelineStateDesc);
deviceCommandContext->BeginPass();
shader = tech->GetShader();
if (m_TerrainTexture)
{
deviceCommandContext->SetTexture(
shader->GetBindingSlot(str_baseTex), m_TerrainTexture.get());
}
CMatrix3D baseTransform;
baseTransform.SetIdentity();
CMatrix3D baseTextureTransform;
baseTextureTransform.SetIdentity();
CMatrix3D terrainTransform;
terrainTransform.SetIdentity();
terrainTransform.Scale(texCoordMax, texCoordMax, 1.0f);
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_transform), baseTransform.AsFloatArray());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_textureTransform), terrainTransform.AsFloatArray());
if (m_TerrainTexture)
DrawTexture(deviceCommandContext);
deviceCommandContext->EndPass();
pipelineStateDesc.blendState.enabled = true;
pipelineStateDesc.blendState.srcColorBlendFactor = pipelineStateDesc.blendState.srcAlphaBlendFactor =
Renderer::Backend::BlendFactor::SRC_ALPHA;
pipelineStateDesc.blendState.dstColorBlendFactor = pipelineStateDesc.blendState.dstAlphaBlendFactor =
Renderer::Backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
pipelineStateDesc.blendState.colorBlendOp = pipelineStateDesc.blendState.alphaBlendOp =
Renderer::Backend::BlendOp::ADD;
pipelineStateDesc.blendState.colorWriteMask =
Renderer::Backend::ColorWriteMask::RED |
Renderer::Backend::ColorWriteMask::GREEN |
Renderer::Backend::ColorWriteMask::BLUE;
deviceCommandContext->SetGraphicsPipelineState(pipelineStateDesc);
deviceCommandContext->BeginPass();
// Draw territory boundaries
- CTerritoryTexture& territoryTexture = g_Game->GetView()->GetTerritoryTexture();
+ CTerritoryTexture& territoryTexture =
+ g_Renderer.GetSceneRenderer().GetScene().GetTerritoryTexture();
deviceCommandContext->SetTexture(
shader->GetBindingSlot(str_baseTex), territoryTexture.GetTexture());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_transform), baseTransform.AsFloatArray());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_textureTransform),
territoryTexture.GetMinimapTextureMatrix().AsFloatArray());
DrawTexture(deviceCommandContext);
deviceCommandContext->EndPass();
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap_los, CShaderDefines());
deviceCommandContext->SetGraphicsPipelineState(
tech->GetGraphicsPipelineStateDesc());
deviceCommandContext->BeginPass();
shader = tech->GetShader();
deviceCommandContext->SetTexture(
shader->GetBindingSlot(str_baseTex), losTexture.GetTexture());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_transform), baseTransform.AsFloatArray());
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_textureTransform),
losTexture.GetMinimapTextureMatrix().AsFloatArray());
DrawTexture(deviceCommandContext);
deviceCommandContext->EndPass();
// We might scale entities properly in the vertex shader but it requires
// additional space in the vertex buffer. So we assume that we don't need
// to change an entity size so often.
// Radius with instancing is lower because an entity has a more round shape.
const float entityRadius = static_cast(m_MapSize) / 128.0f * (m_UseInstancing ? 5.0 : 6.0f);
if (doUpdate)
{
m_Icons.clear();
m_IconsCache.clear();
CSimulation2::InterfaceList ents = m_Simulation.GetEntitiesWithInterface(IID_Minimap);
VertexArrayIterator attrPos = m_AttributePos.GetIterator();
VertexArrayIterator attrColor = m_AttributeColor.GetIterator();
m_EntitiesDrawn = 0;
MinimapUnitVertex v;
std::vector pingingVertices;
pingingVertices.reserve(MAX_ENTITIES_DRAWN / 2);
if (currentTime > m_NextBlinkTime)
{
m_BlinkState = !m_BlinkState;
m_NextBlinkTime = currentTime + m_HalfBlinkDuration;
}
bool iconsEnabled = false;
CFG_GET_VAL("gui.session.minimap.icons.enabled", iconsEnabled);
float iconsOpacity = 1.0f;
CFG_GET_VAL("gui.session.minimap.icons.opacity", iconsOpacity);
float iconsSizeScale = 1.0f;
CFG_GET_VAL("gui.session.minimap.icons.sizescale", iconsSizeScale);
bool iconsCountOverflow = false;
entity_pos_t posX, posZ;
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
{
ICmpMinimap* cmpMinimap = static_cast(it->second);
if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ))
{
LosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, m_Simulation.GetSimContext().GetCurrentDisplayedPlayer());
if (vis != LosVisibility::HIDDEN)
{
v.a = 255;
v.position.X = posX.ToFloat();
v.position.Y = posZ.ToFloat();
// Check minimap pinging to indicate something
if (m_BlinkState && cmpMinimap->CheckPing(currentTime, m_PingDuration))
{
v.r = 255; // ping color is white
v.g = 255;
v.b = 255;
pingingVertices.push_back(v);
}
else
{
AddEntity(v, attrColor, attrPos, entityRadius, m_UseInstancing);
++m_EntitiesDrawn;
}
if (!iconsEnabled || !cmpMinimap->HasIcon())
continue;
const CellIconKey key{
cmpMinimap->GetIconPath(), v.r, v.g, v.b};
const u16 gridX = Clamp(
(v.position.X * invTileMapSize) * ICON_COMBINING_GRID_SIZE, 0, ICON_COMBINING_GRID_SIZE - 1);
const u16 gridY = Clamp(
(v.position.Y * invTileMapSize) * ICON_COMBINING_GRID_SIZE, 0, ICON_COMBINING_GRID_SIZE - 1);
CellIcon icon{
gridX, gridY, cmpMinimap->GetIconSize() * iconsSizeScale * 0.5f, v.position};
if (m_IconsCache.find(key) == m_IconsCache.end() && m_IconsCache.size() >= MAX_UNIQUE_ICON_COUNT)
{
iconsCountOverflow = true;
}
else
{
m_IconsCache[key].emplace_back(std::move(icon));
}
}
}
}
// We need to combine too close icons with the same path, we use a grid for
// that. But to save some allocations and space we store only the current
// row.
struct Cell
{
u32 count;
float maxHalfSize;
CVector2D averagePosition;
};
std::array gridRow;
for (auto& [key, icons] : m_IconsCache)
{
CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(
CTextureProperties(key.path));
const CColor color(key.r / 255.0f, key.g / 255.0f, key.b / 255.0f, iconsOpacity);
std::sort(icons.begin(), icons.end(),
[](const CellIcon& lhs, const CellIcon& rhs) -> bool
{
if (lhs.gridY != rhs.gridY)
return lhs.gridY < rhs.gridY;
return lhs.gridX < rhs.gridX;
});
for (auto beginIt = icons.begin(); beginIt != icons.end();)
{
auto endIt = std::next(beginIt);
while (endIt != icons.end() && beginIt->gridY == endIt->gridY)
++endIt;
gridRow.fill({0, 0.0f, {}});
for (; beginIt != endIt; ++beginIt)
{
Cell& cell = gridRow[beginIt->gridX];
const float previousPositionWeight = static_cast(cell.count) / (cell.count + 1);
cell.averagePosition = cell.averagePosition * previousPositionWeight + beginIt->worldPosition / static_cast(cell.count + 1);
cell.maxHalfSize = std::max(cell.maxHalfSize, beginIt->halfSize);
++cell.count;
}
for (const Cell& cell : gridRow)
{
if (cell.count == 0)
continue;
if (m_Icons.size() < MAX_ICON_COUNT)
{
m_Icons.emplace_back(Icon{
texture, color, cell.averagePosition, cell.maxHalfSize});
}
else
iconsCountOverflow = true;
}
}
}
if (iconsCountOverflow)
LOGWARNING("Too many minimap icons to draw.");
// Add the pinged vertices at the end, so they are drawn on top
for (const MinimapUnitVertex& vertex : pingingVertices)
{
AddEntity(vertex, attrColor, attrPos, entityRadius, m_UseInstancing);
++m_EntitiesDrawn;
}
ENSURE(m_EntitiesDrawn < MAX_ENTITIES_DRAWN);
if (!m_UseInstancing)
{
VertexArrayIterator index = m_IndexArray.GetIterator();
for (size_t entityIndex = 0; entityIndex < m_EntitiesDrawn; ++entityIndex)
{
index[entityIndex * 6 + 0] = static_cast(entityIndex * 4 + 0);
index[entityIndex * 6 + 1] = static_cast(entityIndex * 4 + 1);
index[entityIndex * 6 + 2] = static_cast(entityIndex * 4 + 2);
index[entityIndex * 6 + 3] = static_cast(entityIndex * 4 + 0);
index[entityIndex * 6 + 4] = static_cast(entityIndex * 4 + 2);
index[entityIndex * 6 + 5] = static_cast(entityIndex * 4 + 3);
}
m_IndexArray.Upload();
}
m_VertexArray.Upload();
}
m_VertexArray.PrepareForRendering();
if (m_EntitiesDrawn > 0)
{
CShaderDefines pointDefines;
pointDefines.Add(str_MINIMAP_POINT, str_1);
if (m_UseInstancing)
pointDefines.Add(str_USE_GPU_INSTANCING, str_1);
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, pointDefines);
deviceCommandContext->SetGraphicsPipelineState(
tech->GetGraphicsPipelineStateDesc());
deviceCommandContext->BeginPass();
shader = tech->GetShader();
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_transform), baseTransform.AsFloatArray());
CMatrix3D unitMatrix;
unitMatrix.SetIdentity();
// Convert world space coordinates into [0, 2].
const float unitScale = invTileMapSize;
unitMatrix.Scale(unitScale * 2.0f, unitScale * 2.0f, 1.0f);
// Offset the coordinates to [-1, 1].
unitMatrix.Translate(CVector3D(-1.0f, -1.0f, 0.0f));
deviceCommandContext->SetUniform(
shader->GetBindingSlot(str_transform), unitMatrix.AsFloatArray());
Renderer::Backend::IDeviceCommandContext::Rect scissorRect;
scissorRect.x = scissorRect.y = 1;
scissorRect.width = scissorRect.height = FINAL_TEXTURE_SIZE - 2;
deviceCommandContext->SetScissors(1, &scissorRect);
m_VertexArray.UploadIfNeeded(deviceCommandContext);
const uint32_t stride = m_VertexArray.GetStride();
const uint32_t firstVertexOffset = m_VertexArray.GetOffset() * stride;
if (m_UseInstancing)
{
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::POSITION,
m_AttributePos.format,
m_InstanceVertexArray.GetOffset() + m_InstanceAttributePosition.offset,
m_InstanceVertexArray.GetStride(),
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0);
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::UV1,
m_AttributePos.format, firstVertexOffset + m_AttributePos.offset, stride,
Renderer::Backend::VertexAttributeRate::PER_INSTANCE, 1);
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::COLOR,
m_AttributeColor.format, firstVertexOffset + m_AttributeColor.offset, stride,
Renderer::Backend::VertexAttributeRate::PER_INSTANCE, 1);
deviceCommandContext->SetVertexBuffer(0, m_InstanceVertexArray.GetBuffer());
deviceCommandContext->SetVertexBuffer(1, m_VertexArray.GetBuffer());
deviceCommandContext->SetUniform(shader->GetBindingSlot(str_width), entityRadius);
deviceCommandContext->DrawInstanced(0, m_InstanceVertexArray.GetNumberOfVertices(), 0, m_EntitiesDrawn);
}
else
{
m_IndexArray.UploadIfNeeded(deviceCommandContext);
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::POSITION,
m_AttributePos.format, firstVertexOffset + m_AttributePos.offset, stride,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0);
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::COLOR,
m_AttributeColor.format, firstVertexOffset + m_AttributeColor.offset, stride,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0);
deviceCommandContext->SetVertexBuffer(0, m_VertexArray.GetBuffer());
deviceCommandContext->SetIndexBuffer(m_IndexArray.GetBuffer());
deviceCommandContext->DrawIndexed(m_IndexArray.GetOffset(), m_EntitiesDrawn * 6, 0);
}
g_Renderer.GetStats().m_DrawCalls++;
deviceCommandContext->SetScissors(0, nullptr);
deviceCommandContext->EndPass();
}
deviceCommandContext->SetFramebuffer(
deviceCommandContext->GetDevice()->GetCurrentBackbuffer());
g_Renderer.SetViewport(oldViewPort);
}
// static
float CMiniMapTexture::GetShallowPassageHeight()
{
float shallowPassageHeight = 0.0f;
CParamNode externalParamNode;
CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder");
const CParamNode pathingSettings = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses");
if (pathingSettings.GetChild("default").IsOk() && pathingSettings.GetChild("default").GetChild("MaxWaterDepth").IsOk())
shallowPassageHeight = pathingSettings.GetChild("default").GetChild("MaxWaterDepth").ToFloat();
return shallowPassageHeight;
}
Index: ps/trunk/source/renderer/TerrainRenderer.cpp
===================================================================
--- ps/trunk/source/renderer/TerrainRenderer.cpp (revision 27040)
+++ ps/trunk/source/renderer/TerrainRenderer.cpp (revision 27041)
@@ -1,728 +1,728 @@
/* Copyright (C) 2022 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 .
*/
#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 "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/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"
/**
* 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 visiblePatches[CSceneRenderer::CULL_MAX];
/// Decals that were submitted for this frame
std::vector visibleDecals[CSceneRenderer::CULL_MAX];
/// Fancy water shader
CShaderTechniquePtr fancyWaterTech;
CSimulation2* simulation;
};
///////////////////////////////////////////////////////////////////
// Construction/Destruction
TerrainRenderer::TerrainRenderer()
{
m = new TerrainRendererInternals();
m->phase = Phase_Submit;
}
TerrainRenderer::~TerrainRenderer()
{
delete m;
}
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, CMatrix3D& textureMatrix,
Renderer::Backend::ITexture* texture)
{
ENSURE(m->phase == Phase_Render);
std::vector& visiblePatches = m->visiblePatches[cullGroup];
CShaderTechniquePtr debugOverlayTech =
g_Renderer.GetShaderManager().LoadEffect(str_debug_overlay);
deviceCommandContext->SetGraphicsPipelineState(
debugOverlayTech->GetGraphicsPipelineStateDesc());
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), textureMatrix.AsFloatArray());
CPatchRData::RenderStreams(deviceCommandContext, visiblePatches, true);
// 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->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::POSITION,
Renderer::Backend::Format::R32G32B32_SFLOAT, 0, 0,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0);
deviceCommandContext->SetVertexAttributeFormat(
Renderer::Backend::VertexAttributeStream::UV0,
Renderer::Backend::Format::R32G32B32_SFLOAT, 0, 0,
Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0);
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& visiblePatches = m->visiblePatches[cullGroup];
std::vector& visibleDecals = m->visibleDecals[cullGroup];
if (visiblePatches.empty() && visibleDecals.empty())
return;
// render the solid black sides of the map first
CShaderTechniquePtr techSolid = g_Renderer.GetShaderManager().LoadEffect(str_solid);
Renderer::Backend::GraphicsPipelineStateDesc solidPipelineStateDesc =
techSolid->GetGraphicsPipelineStateDesc();
solidPipelineStateDesc.rasterizationState.cullMode = Renderer::Backend::CullMode::NONE;
deviceCommandContext->SetGraphicsPipelineState(solidPipelineStateDesc);
deviceCommandContext->BeginPass();
Renderer::Backend::IShaderProgram* shaderSolid = techSolid->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, visiblePatches);
deviceCommandContext->EndPass();
CPatchRData::RenderBases(deviceCommandContext, visiblePatches, context, shadow);
// render blend passes for each patch
CPatchRData::RenderBlends(deviceCommandContext, visiblePatches, context, shadow);
CDecalRData::RenderDecals(deviceCommandContext, 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& 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->GetGraphicsPipelineStateDesc());
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, visiblePatches, false);
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& 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->GetGraphicsPipelineStateDesc());
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(time));
deviceCommandContext->SetUniform(
fancyWaterShader->GetBindingSlot(str_screenSize),
static_cast(g_Renderer.GetWidth()),
static_cast(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, true);
if (waterManager.m_WaterFancyEffects)
data->RenderWaterShore(deviceCommandContext);
}
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_Game->GetView()->GetLOSTexture();
+ 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->GetGraphicsPipelineStateDesc());
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(time));
deviceCommandContext->SetUniform(
waterSimpleShader->GetBindingSlot(str_color), waterManager.m_WaterColor.AsFloatArray());
std::vector& visiblePatches = m->visiblePatches[cullGroup];
for (size_t i = 0; i < visiblePatches.size(); ++i)
{
CPatchRData* data = visiblePatches[i];
data->RenderWaterSurface(deviceCommandContext, false);
}
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;
GPU_SCOPED_LABEL(deviceCommandContext, "Render water foam occluders");
// Render normals and foam to a framebuffer if we're using fancy effects.
deviceCommandContext->SetFramebuffer(waterManager.m_FancyEffectsFramebuffer.get());
// Overwrite waves that would be behind the ground.
CShaderTechniquePtr dummyTech = g_Renderer.GetShaderManager().LoadEffect(str_solid);
Renderer::Backend::GraphicsPipelineStateDesc pipelineStateDesc =
dummyTech->GetGraphicsPipelineStateDesc();
pipelineStateDesc.depthStencilState.depthTestEnabled = true;
pipelineStateDesc.rasterizationState.cullMode = Renderer::Backend::CullMode::NONE;
deviceCommandContext->SetGraphicsPipelineState(pipelineStateDesc);
deviceCommandContext->BeginPass();
Renderer::Backend::IShaderProgram* dummyShader = dummyTech->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);
deviceCommandContext->EndPass();
deviceCommandContext->SetFramebuffer(
deviceCommandContext->GetDevice()->GetCurrentBackbuffer());
}
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& visiblePatches = m->visiblePatches[cullGroup];
for (size_t i = 0; i < visiblePatches.size(); ++i)
visiblePatches[i]->RenderPriorities(textRenderer);
canvas.DrawText(textRenderer);
}
|