LCOV - code coverage report
Current view: top level - source/renderer - PatchRData.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 31 732 4.2 %
Date: 2023-01-19 00:18:29 Functions: 8 47 17.0 %

          Line data    Source code
       1             : /* Copyright (C) 2023 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "renderer/PatchRData.h"
      21             : 
      22             : #include "graphics/GameView.h"
      23             : #include "graphics/LightEnv.h"
      24             : #include "graphics/LOSTexture.h"
      25             : #include "graphics/Patch.h"
      26             : #include "graphics/ShaderManager.h"
      27             : #include "graphics/Terrain.h"
      28             : #include "graphics/TerrainTextureEntry.h"
      29             : #include "graphics/TextRenderer.h"
      30             : #include "graphics/TextureManager.h"
      31             : #include "lib/allocators/DynamicArena.h"
      32             : #include "lib/allocators/STLAllocators.h"
      33             : #include "maths/MathUtil.h"
      34             : #include "ps/CLogger.h"
      35             : #include "ps/CStrInternStatic.h"
      36             : #include "ps/Game.h"
      37             : #include "ps/GameSetup/Config.h"
      38             : #include "ps/Profile.h"
      39             : #include "ps/Pyrogenesis.h"
      40             : #include "ps/VideoMode.h"
      41             : #include "ps/World.h"
      42             : #include "renderer/AlphaMapCalculator.h"
      43             : #include "renderer/DebugRenderer.h"
      44             : #include "renderer/Renderer.h"
      45             : #include "renderer/SceneRenderer.h"
      46             : #include "renderer/TerrainRenderer.h"
      47             : #include "renderer/WaterManager.h"
      48             : #include "simulation2/components/ICmpWaterManager.h"
      49             : #include "simulation2/Simulation2.h"
      50             : 
      51             : #include <algorithm>
      52             : #include <numeric>
      53             : #include <set>
      54             : 
      55             : const ssize_t BlendOffsets[9][2] = {
      56             :     {  0, -1 },
      57             :     { -1, -1 },
      58             :     { -1,  0 },
      59             :     { -1,  1 },
      60             :     {  0,  1 },
      61             :     {  1,  1 },
      62             :     {  1,  0 },
      63             :     {  1, -1 },
      64             :     {  0,  0 }
      65             : };
      66             : 
      67             : // static
      68           6 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetBaseVertexInputLayout()
      69             : {
      70           6 :     const uint32_t stride = sizeof(SBaseVertex);
      71           6 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 3> attributes{{
      72             :         {Renderer::Backend::VertexAttributeStream::POSITION,
      73             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
      74             :             offsetof(SBaseVertex, m_Position), stride,
      75             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
      76             :         {Renderer::Backend::VertexAttributeStream::NORMAL,
      77             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
      78             :             offsetof(SBaseVertex, m_Normal), stride,
      79             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
      80             :         {Renderer::Backend::VertexAttributeStream::UV0,
      81             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
      82             :             offsetof(SBaseVertex, m_Position), stride,
      83             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
      84             :     }};
      85           6 :     return g_Renderer.GetVertexInputLayout(attributes);
      86             : }
      87             : 
      88             : // static
      89           6 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetBlendVertexInputLayout()
      90             : {
      91           6 :     const uint32_t stride = sizeof(SBlendVertex);
      92           6 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 4> attributes{{
      93             :         {Renderer::Backend::VertexAttributeStream::POSITION,
      94             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
      95             :             offsetof(SBlendVertex, m_Position), stride,
      96             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
      97             :         {Renderer::Backend::VertexAttributeStream::NORMAL,
      98             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
      99             :             offsetof(SBlendVertex, m_Normal), stride,
     100             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     101             :         {Renderer::Backend::VertexAttributeStream::UV0,
     102             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
     103             :             offsetof(SBlendVertex, m_Position), stride,
     104             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     105             :         {Renderer::Backend::VertexAttributeStream::UV1,
     106             :             Renderer::Backend::Format::R32G32_SFLOAT,
     107             :             offsetof(SBlendVertex, m_AlphaUVs), stride,
     108             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     109             :     }};
     110           6 :     return g_Renderer.GetVertexInputLayout(attributes);
     111             : }
     112             : 
     113             : // static
     114          12 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetStreamVertexInputLayout(
     115             :     const bool bindPositionAsTexCoord)
     116             : {
     117          12 :     const uint32_t stride = sizeof(SBaseVertex);
     118          12 :     if (bindPositionAsTexCoord)
     119             :     {
     120           6 :         const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
     121             :             {Renderer::Backend::VertexAttributeStream::POSITION,
     122             :                 Renderer::Backend::Format::R32G32B32_SFLOAT,
     123             :                 offsetof(SBaseVertex, m_Position), stride,
     124             :                 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     125             :             {Renderer::Backend::VertexAttributeStream::UV0,
     126             :                 Renderer::Backend::Format::R32G32B32_SFLOAT,
     127             :                 offsetof(SBaseVertex, m_Position), stride,
     128             :                 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     129             :         }};
     130           6 :         return g_Renderer.GetVertexInputLayout(attributes);
     131             :     }
     132             :     else
     133             :     {
     134           6 :         const std::array<Renderer::Backend::SVertexAttributeFormat, 1> attributes{{
     135             :             {Renderer::Backend::VertexAttributeStream::POSITION,
     136             :                 Renderer::Backend::Format::R32G32B32_SFLOAT,
     137             :                 offsetof(SBaseVertex, m_Position), stride,
     138             :                 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     139             :         }};
     140           6 :         return g_Renderer.GetVertexInputLayout(attributes);
     141             :     }
     142             : }
     143             : 
     144             : // static
     145           6 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetSideVertexInputLayout()
     146             : {
     147           6 :     const uint32_t stride = sizeof(SSideVertex);
     148           6 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 1> attributes{{
     149             :         {Renderer::Backend::VertexAttributeStream::POSITION,
     150             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
     151             :             offsetof(SSideVertex, m_Position), stride,
     152             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     153             :     }};
     154           6 :     return g_Renderer.GetVertexInputLayout(attributes);
     155             : }
     156             : 
     157             : // static
     158          12 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetWaterSurfaceVertexInputLayout(
     159             :     const bool bindWaterData)
     160             : {
     161          12 :     const uint32_t stride = sizeof(SWaterVertex);
     162          12 :     if (bindWaterData)
     163             :     {
     164           6 :         const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
     165             :             {Renderer::Backend::VertexAttributeStream::POSITION,
     166             :                 Renderer::Backend::Format::R32G32B32_SFLOAT,
     167             :                 offsetof(SWaterVertex, m_Position), stride,
     168             :                 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     169             :             // UV1 will be used only in case of bindWaterData.
     170             :             {Renderer::Backend::VertexAttributeStream::UV1,
     171             :                 Renderer::Backend::Format::R32G32_SFLOAT,
     172             :                 offsetof(SWaterVertex, m_WaterData), stride,
     173             :                 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     174             :         }};
     175           6 :         return g_Renderer.GetVertexInputLayout(attributes);
     176             :     }
     177             :     else
     178             :     {
     179           6 :         const std::array<Renderer::Backend::SVertexAttributeFormat, 1> attributes{{
     180             :             {Renderer::Backend::VertexAttributeStream::POSITION,
     181             :                 Renderer::Backend::Format::R32G32B32_SFLOAT,
     182             :                 offsetof(SWaterVertex, m_Position), stride,
     183             :                 Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     184             :         }};
     185           6 :         return g_Renderer.GetVertexInputLayout(attributes);
     186             :     }
     187             : }
     188             : 
     189             : // static
     190           6 : Renderer::Backend::IVertexInputLayout* CPatchRData::GetWaterShoreVertexInputLayout()
     191             : {
     192           6 :     const uint32_t stride = sizeof(SWaterVertex);
     193           6 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 2> attributes{{
     194             :         {Renderer::Backend::VertexAttributeStream::POSITION,
     195             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
     196             :             offsetof(SWaterVertex, m_Position), stride,
     197             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     198             :         {Renderer::Backend::VertexAttributeStream::UV1,
     199             :             Renderer::Backend::Format::R32G32_SFLOAT,
     200             :             offsetof(SWaterVertex, m_WaterData), stride,
     201             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     202             :     }};
     203           6 :     return g_Renderer.GetVertexInputLayout(attributes);
     204             : }
     205             : 
     206           0 : CPatchRData::CPatchRData(CPatch* patch, CSimulation2* simulation) :
     207           0 :     m_Patch(patch), m_Simulation(simulation)
     208             : {
     209           0 :     ENSURE(patch);
     210           0 :     Build();
     211           0 : }
     212             : 
     213             : CPatchRData::~CPatchRData() = default;
     214             : 
     215             : /**
     216             :  * Represents a blend for a single tile, texture and shape.
     217             :  */
     218             : struct STileBlend
     219             : {
     220             :     CTerrainTextureEntry* m_Texture;
     221             :     int m_Priority;
     222             :     u16 m_TileMask; // bit n set if this blend contains neighbour tile BlendOffsets[n]
     223             : 
     224             :     struct DecreasingPriority
     225             :     {
     226           0 :         bool operator()(const STileBlend& a, const STileBlend& b) const
     227             :         {
     228           0 :             if (a.m_Priority > b.m_Priority)
     229           0 :                 return true;
     230           0 :             if (a.m_Priority < b.m_Priority)
     231           0 :                 return false;
     232           0 :             if (a.m_Texture && b.m_Texture)
     233           0 :                 return a.m_Texture->GetTag() > b.m_Texture->GetTag();
     234           0 :             return false;
     235             :         }
     236             :     };
     237             : 
     238             :     struct CurrentTile
     239             :     {
     240           0 :         bool operator()(const STileBlend& a) const
     241             :         {
     242           0 :             return (a.m_TileMask & (1 << 8)) != 0;
     243             :         }
     244             :     };
     245             : };
     246             : 
     247             : /**
     248             :  * Represents the ordered collection of blends drawn on a particular tile.
     249             :  */
     250           0 : struct STileBlendStack
     251             : {
     252             :     u8 i, j;
     253             :     std::vector<STileBlend> blends; // back of vector is lowest-priority texture
     254             : };
     255             : 
     256             : /**
     257             :  * Represents a batched collection of blends using the same texture.
     258             :  */
     259           0 : struct SBlendLayer
     260             : {
     261             :     struct Tile
     262             :     {
     263             :         u8 i, j;
     264             :         u8 shape;
     265             :     };
     266             : 
     267             :     CTerrainTextureEntry* m_Texture;
     268             :     std::vector<Tile> m_Tiles;
     269             : };
     270             : 
     271           0 : void CPatchRData::BuildBlends()
     272             : {
     273           0 :     PROFILE3("build blends");
     274             : 
     275           0 :     m_BlendSplats.clear();
     276             : 
     277           0 :     std::vector<SBlendVertex> blendVertices;
     278           0 :     std::vector<u16> blendIndices;
     279             : 
     280           0 :     CTerrain* terrain = m_Patch->m_Parent;
     281             : 
     282           0 :     std::vector<STileBlendStack> blendStacks;
     283           0 :     blendStacks.reserve(PATCH_SIZE*PATCH_SIZE);
     284             : 
     285           0 :     std::vector<STileBlend> blends;
     286           0 :     blends.reserve(9);
     287             : 
     288             :     // For each tile in patch ..
     289           0 :     for (ssize_t j = 0; j < PATCH_SIZE; ++j)
     290             :     {
     291           0 :         for (ssize_t i = 0; i < PATCH_SIZE; ++i)
     292             :         {
     293           0 :             ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
     294           0 :             ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
     295             : 
     296           0 :             blends.clear();
     297             : 
     298             :             // Compute a blend for every tile in the 3x3 square around this tile
     299           0 :             for (size_t n = 0; n < 9; ++n)
     300             :             {
     301           0 :                 ssize_t ox = gx + BlendOffsets[n][1];
     302           0 :                 ssize_t oz = gz + BlendOffsets[n][0];
     303             : 
     304           0 :                 CMiniPatch* nmp = terrain->GetTile(ox, oz);
     305           0 :                 if (!nmp)
     306           0 :                     continue;
     307             : 
     308             :                 STileBlend blend;
     309           0 :                 blend.m_Texture = nmp->GetTextureEntry();
     310           0 :                 blend.m_Priority = nmp->GetPriority();
     311           0 :                 blend.m_TileMask = 1 << n;
     312           0 :                 blends.push_back(blend);
     313             :             }
     314             : 
     315             :             // Sort the blends, highest priority first
     316           0 :             std::sort(blends.begin(), blends.end(), STileBlend::DecreasingPriority());
     317             : 
     318           0 :             STileBlendStack blendStack;
     319           0 :             blendStack.i = i;
     320           0 :             blendStack.j = j;
     321             : 
     322             :             // Put the blends into the tile's stack, merging any adjacent blends with the same texture
     323           0 :             for (size_t k = 0; k < blends.size(); ++k)
     324             :             {
     325           0 :                 if (!blendStack.blends.empty() && blendStack.blends.back().m_Texture == blends[k].m_Texture)
     326           0 :                     blendStack.blends.back().m_TileMask |= blends[k].m_TileMask;
     327             :                 else
     328           0 :                     blendStack.blends.push_back(blends[k]);
     329             :             }
     330             : 
     331             :             // Remove blends that are after (i.e. lower priority than) the current tile
     332             :             // (including the current tile), since we don't want to render them on top of
     333             :             // the tile's base texture
     334           0 :             blendStack.blends.erase(
     335           0 :                 std::find_if(blendStack.blends.begin(), blendStack.blends.end(), STileBlend::CurrentTile()),
     336           0 :                 blendStack.blends.end());
     337             : 
     338           0 :             blendStacks.push_back(blendStack);
     339             :         }
     340             :     }
     341             : 
     342             :     // Given the blend stack per tile, we want to batch together as many blends as possible.
     343             :     // Group them into a series of layers (each of which has a single texture):
     344             :     // (This is effectively a topological sort / linearisation of the partial order induced
     345             :     // by the per-tile stacks, preferring to make tiles with equal textures adjacent.)
     346             : 
     347           0 :     std::vector<SBlendLayer> blendLayers;
     348             : 
     349             :     while (true)
     350             :     {
     351           0 :         if (!blendLayers.empty())
     352             :         {
     353             :             // Try to grab as many tiles as possible that match our current layer,
     354             :             // from off the blend stacks of all the tiles
     355             : 
     356           0 :             CTerrainTextureEntry* tex = blendLayers.back().m_Texture;
     357             : 
     358           0 :             for (size_t k = 0; k < blendStacks.size(); ++k)
     359             :             {
     360           0 :                 if (!blendStacks[k].blends.empty() && blendStacks[k].blends.back().m_Texture == tex)
     361             :                 {
     362           0 :                     SBlendLayer::Tile t = { blendStacks[k].i, blendStacks[k].j, (u8)blendStacks[k].blends.back().m_TileMask };
     363           0 :                     blendLayers.back().m_Tiles.push_back(t);
     364           0 :                     blendStacks[k].blends.pop_back();
     365             :                 }
     366             :                 // (We've already merged adjacent entries of the same texture in each stack,
     367             :                 // so we don't need to bother looping to check the next entry in this stack again)
     368             :             }
     369             :         }
     370             : 
     371             :         // We've grabbed as many tiles as possible; now we need to start a new layer.
     372             :         // The new layer's texture could come from the back of any non-empty stack;
     373             :         // choose the longest stack as a heuristic to reduce the number of layers
     374           0 :         CTerrainTextureEntry* bestTex = NULL;
     375           0 :         size_t bestStackSize = 0;
     376             : 
     377           0 :         for (size_t k = 0; k < blendStacks.size(); ++k)
     378             :         {
     379           0 :             if (blendStacks[k].blends.size() > bestStackSize)
     380             :             {
     381           0 :                 bestStackSize = blendStacks[k].blends.size();
     382           0 :                 bestTex = blendStacks[k].blends.back().m_Texture;
     383             :             }
     384             :         }
     385             : 
     386             :         // If all our stacks were empty, we're done
     387           0 :         if (bestStackSize == 0)
     388           0 :             break;
     389             : 
     390             :         // Otherwise add the new layer, then loop back and start filling it in
     391             : 
     392           0 :         SBlendLayer layer;
     393           0 :         layer.m_Texture = bestTex;
     394           0 :         blendLayers.push_back(layer);
     395           0 :     }
     396             : 
     397             :     // Now build outgoing splats
     398           0 :     m_BlendSplats.resize(blendLayers.size());
     399             : 
     400           0 :     for (size_t k = 0; k < blendLayers.size(); ++k)
     401             :     {
     402           0 :         SSplat& splat = m_BlendSplats[k];
     403           0 :         splat.m_IndexStart = blendIndices.size();
     404           0 :         splat.m_Texture = blendLayers[k].m_Texture;
     405             : 
     406           0 :         for (size_t t = 0; t < blendLayers[k].m_Tiles.size(); ++t)
     407             :         {
     408           0 :             SBlendLayer::Tile& tile = blendLayers[k].m_Tiles[t];
     409           0 :             AddBlend(blendVertices, blendIndices, tile.i, tile.j, tile.shape, splat.m_Texture);
     410             :         }
     411             : 
     412           0 :         splat.m_IndexCount = blendIndices.size() - splat.m_IndexStart;
     413             :     }
     414             : 
     415             :     // Release existing vertex buffer chunks
     416           0 :     m_VBBlends.Reset();
     417           0 :     m_VBBlendIndices.Reset();
     418             : 
     419           0 :     if (blendVertices.size())
     420             :     {
     421             :         // Construct vertex buffer
     422             : 
     423           0 :         m_VBBlends = g_VBMan.AllocateChunk(
     424             :             sizeof(SBlendVertex), blendVertices.size(),
     425             :             Renderer::Backend::IBuffer::Type::VERTEX, false,
     426           0 :             nullptr, CVertexBufferManager::Group::TERRAIN);
     427           0 :         m_VBBlends->m_Owner->UpdateChunkVertices(m_VBBlends.Get(), &blendVertices[0]);
     428             : 
     429             :         // Update the indices to include the base offset of the vertex data
     430           0 :         for (size_t k = 0; k < blendIndices.size(); ++k)
     431           0 :             blendIndices[k] += static_cast<u16>(m_VBBlends->m_Index);
     432             : 
     433           0 :         m_VBBlendIndices = g_VBMan.AllocateChunk(
     434             :             sizeof(u16), blendIndices.size(),
     435             :             Renderer::Backend::IBuffer::Type::INDEX, false,
     436           0 :             nullptr, CVertexBufferManager::Group::TERRAIN);
     437           0 :         m_VBBlendIndices->m_Owner->UpdateChunkVertices(m_VBBlendIndices.Get(), &blendIndices[0]);
     438             :     }
     439           0 : }
     440             : 
     441           0 : void CPatchRData::AddBlend(std::vector<SBlendVertex>& blendVertices, std::vector<u16>& blendIndices,
     442             :                u16 i, u16 j, u8 shape, CTerrainTextureEntry* texture)
     443             : {
     444           0 :     CTerrain* terrain = m_Patch->m_Parent;
     445             : 
     446           0 :     ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
     447           0 :     ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
     448             : 
     449             :     // uses the current neighbour texture
     450           0 :     BlendShape8 shape8;
     451           0 :     for (size_t m = 0; m < 8; ++m)
     452           0 :         shape8[m] = (shape & (1 << m)) ? 0 : 1;
     453             : 
     454             :     // calculate the required alphamap and the required rotation of the alphamap from blendshape
     455             :     unsigned int alphamapflags;
     456           0 :     int alphamap = CAlphaMapCalculator::Calculate(shape8, alphamapflags);
     457             : 
     458             :     // now actually render the blend tile (if we need one)
     459           0 :     if (alphamap == -1)
     460           0 :         return;
     461             : 
     462           0 :     float u0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u0;
     463           0 :     float u1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].u1;
     464           0 :     float v0 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v0;
     465           0 :     float v1 = texture->m_TerrainAlpha->second.m_AlphaMapCoords[alphamap].v1;
     466             : 
     467           0 :     if (alphamapflags & BLENDMAP_FLIPU)
     468           0 :         std::swap(u0, u1);
     469             : 
     470           0 :     if (alphamapflags & BLENDMAP_FLIPV)
     471           0 :         std::swap(v0, v1);
     472             : 
     473           0 :     int base = 0;
     474           0 :     if (alphamapflags & BLENDMAP_ROTATE90)
     475           0 :         base = 1;
     476           0 :     else if (alphamapflags & BLENDMAP_ROTATE180)
     477           0 :         base = 2;
     478           0 :     else if (alphamapflags & BLENDMAP_ROTATE270)
     479           0 :         base = 3;
     480             : 
     481           0 :     SBlendVertex vtx[4];
     482           0 :     vtx[(base + 0) % 4].m_AlphaUVs[0] = u0;
     483           0 :     vtx[(base + 0) % 4].m_AlphaUVs[1] = v0;
     484           0 :     vtx[(base + 1) % 4].m_AlphaUVs[0] = u1;
     485           0 :     vtx[(base + 1) % 4].m_AlphaUVs[1] = v0;
     486           0 :     vtx[(base + 2) % 4].m_AlphaUVs[0] = u1;
     487           0 :     vtx[(base + 2) % 4].m_AlphaUVs[1] = v1;
     488           0 :     vtx[(base + 3) % 4].m_AlphaUVs[0] = u0;
     489           0 :     vtx[(base + 3) % 4].m_AlphaUVs[1] = v1;
     490             : 
     491           0 :     SBlendVertex dst;
     492             : 
     493           0 :     CVector3D normal;
     494             : 
     495           0 :     u16 index = static_cast<u16>(blendVertices.size());
     496             : 
     497           0 :     terrain->CalcPosition(gx, gz, dst.m_Position);
     498           0 :     terrain->CalcNormal(gx, gz, normal);
     499           0 :     dst.m_Normal = normal;
     500           0 :     dst.m_AlphaUVs[0] = vtx[0].m_AlphaUVs[0];
     501           0 :     dst.m_AlphaUVs[1] = vtx[0].m_AlphaUVs[1];
     502           0 :     blendVertices.push_back(dst);
     503             : 
     504           0 :     terrain->CalcPosition(gx + 1, gz, dst.m_Position);
     505           0 :     terrain->CalcNormal(gx + 1, gz, normal);
     506           0 :     dst.m_Normal = normal;
     507           0 :     dst.m_AlphaUVs[0] = vtx[1].m_AlphaUVs[0];
     508           0 :     dst.m_AlphaUVs[1] = vtx[1].m_AlphaUVs[1];
     509           0 :     blendVertices.push_back(dst);
     510             : 
     511           0 :     terrain->CalcPosition(gx + 1, gz + 1, dst.m_Position);
     512           0 :     terrain->CalcNormal(gx + 1, gz + 1, normal);
     513           0 :     dst.m_Normal = normal;
     514           0 :     dst.m_AlphaUVs[0] = vtx[2].m_AlphaUVs[0];
     515           0 :     dst.m_AlphaUVs[1] = vtx[2].m_AlphaUVs[1];
     516           0 :     blendVertices.push_back(dst);
     517             : 
     518           0 :     terrain->CalcPosition(gx, gz + 1, dst.m_Position);
     519           0 :     terrain->CalcNormal(gx, gz + 1, normal);
     520           0 :     dst.m_Normal = normal;
     521           0 :     dst.m_AlphaUVs[0] = vtx[3].m_AlphaUVs[0];
     522           0 :     dst.m_AlphaUVs[1] = vtx[3].m_AlphaUVs[1];
     523           0 :     blendVertices.push_back(dst);
     524             : 
     525           0 :     bool dir = terrain->GetTriangulationDir(gx, gz);
     526           0 :     if (dir)
     527             :     {
     528           0 :         blendIndices.push_back(index+0);
     529           0 :         blendIndices.push_back(index+1);
     530           0 :         blendIndices.push_back(index+3);
     531             : 
     532           0 :         blendIndices.push_back(index+1);
     533           0 :         blendIndices.push_back(index+2);
     534           0 :         blendIndices.push_back(index+3);
     535             :     }
     536             :     else
     537             :     {
     538           0 :         blendIndices.push_back(index+0);
     539           0 :         blendIndices.push_back(index+1);
     540           0 :         blendIndices.push_back(index+2);
     541             : 
     542           0 :         blendIndices.push_back(index+2);
     543           0 :         blendIndices.push_back(index+3);
     544           0 :         blendIndices.push_back(index+0);
     545             :     }
     546             : }
     547             : 
     548           0 : void CPatchRData::BuildIndices()
     549             : {
     550           0 :     PROFILE3("build indices");
     551             : 
     552           0 :     CTerrain* terrain = m_Patch->m_Parent;
     553             : 
     554           0 :     ssize_t px = m_Patch->m_X * PATCH_SIZE;
     555           0 :     ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
     556             : 
     557             :     // must have allocated some vertices before trying to build corresponding indices
     558           0 :     ENSURE(m_VBBase);
     559             : 
     560             :     // number of vertices in each direction in each patch
     561           0 :     ssize_t vsize=PATCH_SIZE+1;
     562             : 
     563             :     // PATCH_SIZE must be 2^8-2 or less to not overflow u16 indices buffer. Thankfully this is always true.
     564           0 :     ENSURE(vsize*vsize < 65536);
     565             : 
     566           0 :     std::vector<unsigned short> indices;
     567           0 :     indices.reserve(PATCH_SIZE * PATCH_SIZE * 4);
     568             : 
     569             :     // release existing splats
     570           0 :     m_Splats.clear();
     571             : 
     572             :     // build grid of textures on this patch
     573           0 :     std::vector<CTerrainTextureEntry*> textures;
     574             :     CTerrainTextureEntry* texgrid[PATCH_SIZE][PATCH_SIZE];
     575           0 :     for (ssize_t j=0;j<PATCH_SIZE;j++) {
     576           0 :         for (ssize_t i=0;i<PATCH_SIZE;i++) {
     577           0 :             CTerrainTextureEntry* tex=m_Patch->m_MiniPatches[j][i].GetTextureEntry();
     578           0 :             texgrid[j][i]=tex;
     579           0 :             if (std::find(textures.begin(),textures.end(),tex)==textures.end()) {
     580           0 :                 textures.push_back(tex);
     581             :             }
     582             :         }
     583             :     }
     584             : 
     585             :     // now build base splats from interior textures
     586           0 :     m_Splats.resize(textures.size());
     587             :     // build indices for base splats
     588           0 :     size_t base=m_VBBase->m_Index;
     589             : 
     590           0 :     for (size_t k = 0; k < m_Splats.size(); ++k)
     591             :     {
     592           0 :         CTerrainTextureEntry* tex = textures[k];
     593             : 
     594           0 :         SSplat& splat=m_Splats[k];
     595           0 :         splat.m_Texture=tex;
     596           0 :         splat.m_IndexStart=indices.size();
     597             : 
     598           0 :         for (ssize_t j = 0; j < PATCH_SIZE; j++)
     599             :         {
     600           0 :             for (ssize_t i = 0; i < PATCH_SIZE; i++)
     601             :             {
     602           0 :                 if (texgrid[j][i] == tex)
     603             :                 {
     604           0 :                     bool dir = terrain->GetTriangulationDir(px+i, pz+j);
     605           0 :                     if (dir)
     606             :                     {
     607           0 :                         indices.push_back(u16(((j+0)*vsize+(i+0))+base));
     608           0 :                         indices.push_back(u16(((j+0)*vsize+(i+1))+base));
     609           0 :                         indices.push_back(u16(((j+1)*vsize+(i+0))+base));
     610             : 
     611           0 :                         indices.push_back(u16(((j+0)*vsize+(i+1))+base));
     612           0 :                         indices.push_back(u16(((j+1)*vsize+(i+1))+base));
     613           0 :                         indices.push_back(u16(((j+1)*vsize+(i+0))+base));
     614             :                     }
     615             :                     else
     616             :                     {
     617           0 :                         indices.push_back(u16(((j+0)*vsize+(i+0))+base));
     618           0 :                         indices.push_back(u16(((j+0)*vsize+(i+1))+base));
     619           0 :                         indices.push_back(u16(((j+1)*vsize+(i+1))+base));
     620             : 
     621           0 :                         indices.push_back(u16(((j+1)*vsize+(i+1))+base));
     622           0 :                         indices.push_back(u16(((j+1)*vsize+(i+0))+base));
     623           0 :                         indices.push_back(u16(((j+0)*vsize+(i+0))+base));
     624             :                     }
     625             :                 }
     626             :             }
     627             :         }
     628           0 :         splat.m_IndexCount=indices.size()-splat.m_IndexStart;
     629             :     }
     630             : 
     631             :     // Release existing vertex buffer chunk
     632           0 :     m_VBBaseIndices.Reset();
     633             : 
     634           0 :     ENSURE(indices.size());
     635             : 
     636             :     // Construct vertex buffer
     637           0 :     m_VBBaseIndices = g_VBMan.AllocateChunk(
     638             :         sizeof(u16), indices.size(),
     639           0 :         Renderer::Backend::IBuffer::Type::INDEX, false, nullptr, CVertexBufferManager::Group::TERRAIN);
     640           0 :     m_VBBaseIndices->m_Owner->UpdateChunkVertices(m_VBBaseIndices.Get(), &indices[0]);
     641           0 : }
     642             : 
     643             : 
     644           0 : void CPatchRData::BuildVertices()
     645             : {
     646           0 :     PROFILE3("build vertices");
     647             : 
     648             :     // create both vertices and lighting colors
     649             : 
     650             :     // number of vertices in each direction in each patch
     651           0 :     ssize_t vsize = PATCH_SIZE + 1;
     652             : 
     653           0 :     std::vector<SBaseVertex> vertices;
     654           0 :     vertices.resize(vsize * vsize);
     655             : 
     656             :     // get index of this patch
     657           0 :     ssize_t px = m_Patch->m_X;
     658           0 :     ssize_t pz = m_Patch->m_Z;
     659             : 
     660           0 :     CTerrain* terrain = m_Patch->m_Parent;
     661             : 
     662             :     // build vertices
     663           0 :     for (ssize_t j = 0; j < vsize; ++j)
     664             :     {
     665           0 :         for (ssize_t i = 0; i < vsize; ++i)
     666             :         {
     667           0 :             ssize_t ix = px * PATCH_SIZE + i;
     668           0 :             ssize_t iz = pz * PATCH_SIZE + j;
     669           0 :             ssize_t v = j * vsize + i;
     670             : 
     671             :             // calculate vertex data
     672           0 :             terrain->CalcPosition(ix, iz, vertices[v].m_Position);
     673             : 
     674           0 :             CVector3D normal;
     675           0 :             terrain->CalcNormal(ix, iz, normal);
     676           0 :             vertices[v].m_Normal = normal;
     677             :         }
     678             :     }
     679             : 
     680             :     // upload to vertex buffer
     681           0 :     if (!m_VBBase)
     682             :     {
     683           0 :         m_VBBase = g_VBMan.AllocateChunk(
     684           0 :             sizeof(SBaseVertex), vsize * vsize,
     685             :             Renderer::Backend::IBuffer::Type::VERTEX, false,
     686           0 :             nullptr, CVertexBufferManager::Group::TERRAIN);
     687             :     }
     688             : 
     689           0 :     m_VBBase->m_Owner->UpdateChunkVertices(m_VBBase.Get(), &vertices[0]);
     690           0 : }
     691             : 
     692           0 : void CPatchRData::BuildSide(std::vector<SSideVertex>& vertices, CPatchSideFlags side)
     693             : {
     694           0 :     ssize_t vsize = PATCH_SIZE + 1;
     695           0 :     CTerrain* terrain = m_Patch->m_Parent;
     696           0 :     CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
     697             : 
     698           0 :     for (ssize_t k = 0; k < vsize; k++)
     699             :     {
     700           0 :         ssize_t gx = m_Patch->m_X * PATCH_SIZE;
     701           0 :         ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
     702           0 :         switch (side)
     703             :         {
     704           0 :         case CPATCH_SIDE_NEGX: gz += k; break;
     705           0 :         case CPATCH_SIDE_POSX: gx += PATCH_SIZE; gz += PATCH_SIZE-k; break;
     706           0 :         case CPATCH_SIDE_NEGZ: gx += PATCH_SIZE-k; break;
     707           0 :         case CPATCH_SIDE_POSZ: gz += PATCH_SIZE; gx += k; break;
     708             :         }
     709             : 
     710           0 :         CVector3D pos;
     711           0 :         terrain->CalcPosition(gx, gz, pos);
     712             : 
     713             :         // Clamp the height to the water level
     714           0 :         float waterHeight = 0.f;
     715           0 :         if (cmpWaterManager)
     716           0 :             waterHeight = cmpWaterManager->GetExactWaterLevel(pos.X, pos.Z);
     717           0 :         pos.Y = std::max(pos.Y, waterHeight);
     718             : 
     719           0 :         SSideVertex v0, v1;
     720           0 :         v0.m_Position = pos;
     721           0 :         v1.m_Position = pos;
     722           0 :         v1.m_Position.Y = 0;
     723             : 
     724           0 :         if (k == 0)
     725             :         {
     726           0 :             vertices.emplace_back(v1);
     727           0 :             vertices.emplace_back(v0);
     728             :         }
     729           0 :         if (k > 0)
     730             :         {
     731           0 :             const size_t lastIndex = vertices.size() - 1;
     732           0 :             vertices.emplace_back(v1);
     733           0 :             vertices.emplace_back(vertices[lastIndex]);
     734           0 :             vertices.emplace_back(v0);
     735           0 :             vertices.emplace_back(v1);
     736           0 :             if (k + 1 < vsize)
     737             :             {
     738           0 :                 vertices.emplace_back(v1);
     739           0 :                 vertices.emplace_back(v0);
     740             :             }
     741             :         }
     742             :     }
     743           0 : }
     744             : 
     745           0 : void CPatchRData::BuildSides()
     746             : {
     747           0 :     PROFILE3("build sides");
     748             : 
     749           0 :     std::vector<SSideVertex> sideVertices;
     750             : 
     751           0 :     int sideFlags = m_Patch->GetSideFlags();
     752             : 
     753             :     // If no sides are enabled, we don't need to do anything
     754           0 :     if (!sideFlags)
     755           0 :         return;
     756             : 
     757             :     // For each side, generate a tristrip by adding a vertex at ground/water
     758             :     // level and a vertex underneath at height 0.
     759             : 
     760           0 :     if (sideFlags & CPATCH_SIDE_NEGX)
     761           0 :         BuildSide(sideVertices, CPATCH_SIDE_NEGX);
     762             : 
     763           0 :     if (sideFlags & CPATCH_SIDE_POSX)
     764           0 :         BuildSide(sideVertices, CPATCH_SIDE_POSX);
     765             : 
     766           0 :     if (sideFlags & CPATCH_SIDE_NEGZ)
     767           0 :         BuildSide(sideVertices, CPATCH_SIDE_NEGZ);
     768             : 
     769           0 :     if (sideFlags & CPATCH_SIDE_POSZ)
     770           0 :         BuildSide(sideVertices, CPATCH_SIDE_POSZ);
     771             : 
     772           0 :     if (sideVertices.empty())
     773           0 :         return;
     774             : 
     775           0 :     if (!m_VBSides)
     776             :     {
     777           0 :         m_VBSides = g_VBMan.AllocateChunk(
     778             :             sizeof(SSideVertex), sideVertices.size(),
     779             :             Renderer::Backend::IBuffer::Type::VERTEX, false,
     780           0 :             nullptr, CVertexBufferManager::Group::DEFAULT);
     781             :     }
     782           0 :     m_VBSides->m_Owner->UpdateChunkVertices(m_VBSides.Get(), &sideVertices[0]);
     783             : }
     784             : 
     785           0 : void CPatchRData::Build()
     786             : {
     787           0 :     BuildVertices();
     788           0 :     BuildSides();
     789           0 :     BuildIndices();
     790           0 :     BuildBlends();
     791           0 :     BuildWater();
     792           0 : }
     793             : 
     794           0 : void CPatchRData::Update(CSimulation2* simulation)
     795             : {
     796           0 :     m_Simulation = simulation;
     797           0 :     if (m_UpdateFlags!=0) {
     798             :         // TODO,RC 11/04/04 - need to only rebuild necessary bits of renderdata rather
     799             :         // than everything; it's complicated slightly because the blends are dependent
     800             :         // on both vertex and index data
     801           0 :         BuildVertices();
     802           0 :         BuildSides();
     803           0 :         BuildIndices();
     804           0 :         BuildBlends();
     805           0 :         BuildWater();
     806             : 
     807           0 :         m_UpdateFlags=0;
     808             :     }
     809           0 : }
     810             : 
     811             : // To minimise the cost of memory allocations, everything used for computing
     812             : // batches uses a arena allocator. (All allocations are short-lived so we can
     813             : // just throw away the whole arena at the end of each frame.)
     814             : 
     815             : using Arena = Allocators::DynamicArena<1 * MiB>;
     816             : 
     817             : // std::map types with appropriate arena allocators and default comparison operator
     818             : template<class Key, class Value>
     819             : using PooledBatchMap = std::map<Key, Value, std::less<Key>, ProxyAllocator<std::pair<Key const, Value>, Arena>>;
     820             : 
     821             : // Equivalent to "m[k]", when it returns a arena-allocated std::map (since we can't
     822             : // use the default constructor in that case)
     823             : template<typename M>
     824           0 : typename M::mapped_type& PooledMapGet(M& m, const typename M::key_type& k, Arena& arena)
     825             : {
     826           0 :     return m.insert(std::make_pair(k,
     827             :         typename M::mapped_type(typename M::mapped_type::key_compare(), typename M::mapped_type::allocator_type(arena))
     828           0 :     )).first->second;
     829             : }
     830             : 
     831             : // Equivalent to "m[k]", when it returns a std::pair of arena-allocated std::vectors
     832             : template<typename M>
     833           0 : typename M::mapped_type& PooledPairGet(M& m, const typename M::key_type& k, Arena& arena)
     834             : {
     835           0 :     return m.insert(std::make_pair(k, std::make_pair(
     836             :             typename M::mapped_type::first_type(typename M::mapped_type::first_type::allocator_type(arena)),
     837             :             typename M::mapped_type::second_type(typename M::mapped_type::second_type::allocator_type(arena))
     838           0 :     ))).first->second;
     839             : }
     840             : 
     841             : // Each multidraw batch has a list of index counts, and a list of pointers-to-first-indexes
     842             : using BatchElements = std::pair<std::vector<u32, ProxyAllocator<u32, Arena>>, std::vector<u32, ProxyAllocator<u32, Arena>>>;
     843             : 
     844             : // Group batches by index buffer
     845             : using IndexBufferBatches = PooledBatchMap<CVertexBuffer*, BatchElements>;
     846             : 
     847             : // Group batches by vertex buffer
     848             : using VertexBufferBatches = PooledBatchMap<CVertexBuffer*, IndexBufferBatches>;
     849             : 
     850             : // Group batches by texture
     851             : using TextureBatches = PooledBatchMap<CTerrainTextureEntry*, VertexBufferBatches>;
     852             : 
     853             : // Group batches by shaders.
     854             : using ShaderTechniqueBatches = PooledBatchMap<std::pair<CStrIntern, CShaderDefines>, TextureBatches>;
     855             : 
     856           0 : void CPatchRData::RenderBases(
     857             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     858             :     Renderer::Backend::IVertexInputLayout* vertexInputLayout,
     859             :     const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
     860             : {
     861           0 :     PROFILE3("render terrain bases");
     862           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain bases");
     863             : 
     864           0 :     Arena arena;
     865             : 
     866           0 :     ShaderTechniqueBatches batches(ShaderTechniqueBatches::key_compare(), (ShaderTechniqueBatches::allocator_type(arena)));
     867             : 
     868           0 :     PROFILE_START("compute batches");
     869             : 
     870             :     // Collect all the patches' base splats into their appropriate batches
     871           0 :     for (size_t i = 0; i < patches.size(); ++i)
     872             :     {
     873           0 :         CPatchRData* patch = patches[i];
     874           0 :         for (size_t j = 0; j < patch->m_Splats.size(); ++j)
     875             :         {
     876           0 :             SSplat& splat = patch->m_Splats[j];
     877           0 :             const CMaterial& material = splat.m_Texture->GetMaterial();
     878           0 :             if (material.GetShaderEffect().empty())
     879             :             {
     880           0 :                 LOGERROR("Terrain renderer failed to load shader effect.\n");
     881           0 :                 continue;
     882             :             }
     883             : 
     884             :             BatchElements& batch = PooledPairGet(
     885             :                 PooledMapGet(
     886             :                     PooledMapGet(
     887           0 :                         PooledMapGet(batches, std::make_pair(material.GetShaderEffect(), material.GetShaderDefines()), arena),
     888             :                         splat.m_Texture, arena
     889           0 :                     ),
     890           0 :                     patch->m_VBBase->m_Owner, arena
     891           0 :                 ),
     892           0 :                 patch->m_VBBaseIndices->m_Owner, arena
     893           0 :             );
     894             : 
     895           0 :             batch.first.push_back(splat.m_IndexCount);
     896             : 
     897           0 :             batch.second.push_back(patch->m_VBBaseIndices->m_Index + splat.m_IndexStart);
     898             :         }
     899             :     }
     900             : 
     901             :     PROFILE_END("compute batches");
     902             : 
     903             :     // Render each batch
     904           0 :     for (ShaderTechniqueBatches::iterator itTech = batches.begin(); itTech != batches.end(); ++itTech)
     905             :     {
     906           0 :         CShaderDefines defines = context;
     907           0 :         defines.SetMany(itTech->first.second);
     908           0 :         CShaderTechniquePtr techBase = g_Renderer.GetShaderManager().LoadEffect(
     909           0 :             itTech->first.first, defines);
     910             : 
     911           0 :         const int numPasses = techBase->GetNumPasses();
     912           0 :         for (int pass = 0; pass < numPasses; ++pass)
     913             :         {
     914           0 :             deviceCommandContext->SetGraphicsPipelineState(
     915           0 :                 techBase->GetGraphicsPipelineState(pass));
     916           0 :             deviceCommandContext->BeginPass();
     917           0 :             Renderer::Backend::IShaderProgram* shader = techBase->GetShader(pass);
     918           0 :             TerrainRenderer::PrepareShader(deviceCommandContext, shader, shadow);
     919             : 
     920             :             const int32_t baseTexBindingSlot =
     921           0 :                 shader->GetBindingSlot(str_baseTex);
     922             :             const int32_t textureTransformBindingSlot =
     923           0 :                 shader->GetBindingSlot(str_textureTransform);
     924             : 
     925           0 :             TextureBatches& textureBatches = itTech->second;
     926           0 :             for (TextureBatches::iterator itt = textureBatches.begin(); itt != textureBatches.end(); ++itt)
     927             :             {
     928           0 :                 if (!itt->first->GetMaterial().GetSamplers().empty())
     929             :                 {
     930             :                     const CMaterial::SamplersVector& samplers =
     931           0 :                         itt->first->GetMaterial().GetSamplers();
     932           0 :                     for(const CMaterial::TextureSampler& samp : samplers)
     933           0 :                         samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
     934           0 :                     for(const CMaterial::TextureSampler& samp : samplers)
     935             :                     {
     936           0 :                         deviceCommandContext->SetTexture(
     937           0 :                             shader->GetBindingSlot(samp.Name),
     938           0 :                             samp.Sampler->GetBackendTexture());
     939             :                     }
     940             : 
     941           0 :                     itt->first->GetMaterial().GetStaticUniforms().BindUniforms(
     942             :                         deviceCommandContext, shader);
     943             : 
     944           0 :                     float c = itt->first->GetTextureMatrix()[0];
     945           0 :                     float ms = itt->first->GetTextureMatrix()[8];
     946           0 :                     deviceCommandContext->SetUniform(
     947           0 :                         textureTransformBindingSlot, c, ms);
     948             :                 }
     949             :                 else
     950             :                 {
     951           0 :                     deviceCommandContext->SetTexture(
     952             :                         baseTexBindingSlot,
     953           0 :                         g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture());
     954             :                 }
     955             : 
     956           0 :                 for (VertexBufferBatches::iterator itv = itt->second.begin(); itv != itt->second.end(); ++itv)
     957             :                 {
     958           0 :                     ENSURE(!itv->first->GetBuffer()->IsDynamic());
     959             : 
     960           0 :                     deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
     961             : 
     962           0 :                     deviceCommandContext->SetVertexBuffer(0, itv->first->GetBuffer(), 0);
     963             : 
     964           0 :                     for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
     965             :                     {
     966           0 :                         ENSURE(!it->first->GetBuffer()->IsDynamic());
     967           0 :                         deviceCommandContext->SetIndexBuffer(it->first->GetBuffer());
     968             : 
     969           0 :                         BatchElements& batch = it->second;
     970             : 
     971           0 :                         for (size_t i = 0; i < batch.first.size(); ++i)
     972           0 :                             deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
     973             : 
     974           0 :                         g_Renderer.m_Stats.m_DrawCalls++;
     975           0 :                         g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
     976             :                     }
     977             :                 }
     978             :             }
     979           0 :             deviceCommandContext->EndPass();
     980             :         }
     981             :     }
     982           0 : }
     983             : 
     984             : /**
     985             :  * Helper structure for RenderBlends.
     986             :  */
     987           0 : struct SBlendBatch
     988             : {
     989           0 :     SBlendBatch(Arena& arena) :
     990           0 :         m_Batches(VertexBufferBatches::key_compare(), VertexBufferBatches::allocator_type(arena))
     991             :     {
     992           0 :     }
     993             : 
     994             :     CTerrainTextureEntry* m_Texture;
     995             :     CShaderTechniquePtr m_ShaderTech;
     996             :     VertexBufferBatches m_Batches;
     997             : };
     998             : 
     999             : /**
    1000             :  * Helper structure for RenderBlends.
    1001             :  */
    1002           0 : struct SBlendStackItem
    1003             : {
    1004           0 :     SBlendStackItem(CVertexBuffer::VBChunk* v, CVertexBuffer::VBChunk* i,
    1005           0 :             const std::vector<CPatchRData::SSplat>& s, Arena& arena) :
    1006           0 :         vertices(v), indices(i), splats(s.begin(), s.end(), SplatStack::allocator_type(arena))
    1007             :     {
    1008           0 :     }
    1009             : 
    1010             :     using SplatStack = std::vector<CPatchRData::SSplat, ProxyAllocator<CPatchRData::SSplat, Arena>>;
    1011             :     CVertexBuffer::VBChunk* vertices;
    1012             :     CVertexBuffer::VBChunk* indices;
    1013             :     SplatStack splats;
    1014             : };
    1015             : 
    1016           0 : void CPatchRData::RenderBlends(
    1017             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
    1018             :     Renderer::Backend::IVertexInputLayout* vertexInputLayout,
    1019             :     const std::vector<CPatchRData*>& patches, const CShaderDefines& context, ShadowMap* shadow)
    1020             : {
    1021           0 :     PROFILE3("render terrain blends");
    1022           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain blends");
    1023             : 
    1024           0 :     Arena arena;
    1025             : 
    1026             :     using BatchesStack = std::vector<SBlendBatch, ProxyAllocator<SBlendBatch, Arena>>;
    1027           0 :     BatchesStack batches((BatchesStack::allocator_type(arena)));
    1028             : 
    1029           0 :     CShaderDefines contextBlend = context;
    1030           0 :     contextBlend.Add(str_BLEND, str_1);
    1031             : 
    1032           0 :     PROFILE_START("compute batches");
    1033             : 
    1034             :     // Reserve an arbitrary size that's probably big enough in most cases,
    1035             :     // to avoid heavy reallocations
    1036           0 :     batches.reserve(256);
    1037             : 
    1038             :     using BlendStacks = std::vector<SBlendStackItem, ProxyAllocator<SBlendStackItem, Arena>>;
    1039           0 :     BlendStacks blendStacks((BlendStacks::allocator_type(arena)));
    1040           0 :     blendStacks.reserve(patches.size());
    1041             : 
    1042             :     // Extract all the blend splats from each patch
    1043           0 :     for (size_t i = 0; i < patches.size(); ++i)
    1044             :     {
    1045           0 :         CPatchRData* patch = patches[i];
    1046           0 :         if (!patch->m_BlendSplats.empty())
    1047             :         {
    1048             : 
    1049           0 :             blendStacks.push_back(SBlendStackItem(patch->m_VBBlends.Get(), patch->m_VBBlendIndices.Get(), patch->m_BlendSplats, arena));
    1050             :             // Reverse the splats so the first to be rendered is at the back of the list
    1051           0 :             std::reverse(blendStacks.back().splats.begin(), blendStacks.back().splats.end());
    1052             :         }
    1053             :     }
    1054             : 
    1055             :     // Rearrange the collection of splats to be grouped by texture, preserving
    1056             :     // order of splats within each patch:
    1057             :     // (This is exactly the same algorithm used in CPatchRData::BuildBlends,
    1058             :     // but applied to patch-sized splats rather than to tile-sized splats;
    1059             :     // see that function for comments on the algorithm.)
    1060             :     while (true)
    1061             :     {
    1062           0 :         if (!batches.empty())
    1063             :         {
    1064           0 :             CTerrainTextureEntry* tex = batches.back().m_Texture;
    1065             : 
    1066           0 :             for (size_t k = 0; k < blendStacks.size(); ++k)
    1067             :             {
    1068           0 :                 SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
    1069           0 :                 if (!splats.empty() && splats.back().m_Texture == tex)
    1070             :                 {
    1071           0 :                     CVertexBuffer::VBChunk* vertices = blendStacks[k].vertices;
    1072           0 :                     CVertexBuffer::VBChunk* indices = blendStacks[k].indices;
    1073             : 
    1074           0 :                     BatchElements& batch = PooledPairGet(PooledMapGet(batches.back().m_Batches, vertices->m_Owner, arena), indices->m_Owner, arena);
    1075           0 :                     batch.first.push_back(splats.back().m_IndexCount);
    1076             : 
    1077           0 :                     batch.second.push_back(indices->m_Index + splats.back().m_IndexStart);
    1078             : 
    1079           0 :                     splats.pop_back();
    1080             :                 }
    1081             :             }
    1082             :         }
    1083             : 
    1084           0 :         CTerrainTextureEntry* bestTex = NULL;
    1085           0 :         size_t bestStackSize = 0;
    1086             : 
    1087           0 :         for (size_t k = 0; k < blendStacks.size(); ++k)
    1088             :         {
    1089           0 :             SBlendStackItem::SplatStack& splats = blendStacks[k].splats;
    1090           0 :             if (splats.size() > bestStackSize)
    1091             :             {
    1092           0 :                 bestStackSize = splats.size();
    1093           0 :                 bestTex = splats.back().m_Texture;
    1094             :             }
    1095             :         }
    1096             : 
    1097           0 :         if (bestStackSize == 0)
    1098           0 :             break;
    1099             : 
    1100           0 :         SBlendBatch layer(arena);
    1101           0 :         layer.m_Texture = bestTex;
    1102           0 :         if (!bestTex->GetMaterial().GetSamplers().empty())
    1103             :         {
    1104           0 :             CShaderDefines defines = contextBlend;
    1105           0 :             defines.SetMany(bestTex->GetMaterial().GetShaderDefines());
    1106             :             // TODO: move enabling blend to XML.
    1107           0 :             const CStrIntern shaderEffect = bestTex->GetMaterial().GetShaderEffect();
    1108           0 :             if (shaderEffect != str_terrain_base)
    1109           0 :                 ONCE(LOGWARNING("Shader effect '%s' doesn't support semi-transparent terrain rendering.", shaderEffect.c_str()));
    1110           0 :             layer.m_ShaderTech = g_Renderer.GetShaderManager().LoadEffect(
    1111           0 :                 shaderEffect == str_terrain_base ? str_terrain_blend : shaderEffect, defines);
    1112             :         }
    1113           0 :         batches.push_back(layer);
    1114           0 :     }
    1115             : 
    1116             :     PROFILE_END("compute batches");
    1117             : 
    1118           0 :     CVertexBuffer* lastVB = nullptr;
    1119           0 :     Renderer::Backend::IShaderProgram* previousShader = nullptr;
    1120           0 :     for (BatchesStack::iterator itTechBegin = batches.begin(), itTechEnd = batches.begin(); itTechBegin != batches.end(); itTechBegin = itTechEnd)
    1121             :     {
    1122           0 :         while (itTechEnd != batches.end() && itTechEnd->m_ShaderTech == itTechBegin->m_ShaderTech)
    1123           0 :             ++itTechEnd;
    1124             : 
    1125           0 :         const CShaderTechniquePtr& techBase = itTechBegin->m_ShaderTech;
    1126           0 :         const int numPasses = techBase->GetNumPasses();
    1127           0 :         for (int pass = 0; pass < numPasses; ++pass)
    1128             :         {
    1129           0 :             deviceCommandContext->SetGraphicsPipelineState(
    1130           0 :                 techBase->GetGraphicsPipelineState(pass));
    1131           0 :             deviceCommandContext->BeginPass();
    1132             : 
    1133           0 :             Renderer::Backend::IShaderProgram* shader = techBase->GetShader(pass);
    1134           0 :             TerrainRenderer::PrepareShader(deviceCommandContext, shader, shadow);
    1135             : 
    1136           0 :             Renderer::Backend::ITexture* lastBlendTex = nullptr;
    1137             : 
    1138             :             const int32_t baseTexBindingSlot =
    1139           0 :                 shader->GetBindingSlot(str_baseTex);
    1140             :             const int32_t blendTexBindingSlot =
    1141           0 :                 shader->GetBindingSlot(str_blendTex);
    1142             :             const int32_t textureTransformBindingSlot =
    1143           0 :                 shader->GetBindingSlot(str_textureTransform);
    1144             : 
    1145           0 :             for (BatchesStack::iterator itt = itTechBegin; itt != itTechEnd; ++itt)
    1146             :             {
    1147           0 :                 if (itt->m_Texture->GetMaterial().GetSamplers().empty())
    1148           0 :                     continue;
    1149             : 
    1150           0 :                 if (itt->m_Texture)
    1151             :                 {
    1152           0 :                     const CMaterial::SamplersVector& samplers = itt->m_Texture->GetMaterial().GetSamplers();
    1153           0 :                     for (const CMaterial::TextureSampler& samp : samplers)
    1154           0 :                         samp.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
    1155           0 :                     for (const CMaterial::TextureSampler& samp : samplers)
    1156             :                     {
    1157           0 :                         deviceCommandContext->SetTexture(
    1158           0 :                             shader->GetBindingSlot(samp.Name),
    1159           0 :                             samp.Sampler->GetBackendTexture());
    1160             :                     }
    1161             : 
    1162           0 :                     Renderer::Backend::ITexture* currentBlendTex = itt->m_Texture->m_TerrainAlpha->second.m_CompositeAlphaMap.get();
    1163           0 :                     if (currentBlendTex != lastBlendTex)
    1164             :                     {
    1165           0 :                         deviceCommandContext->SetTexture(
    1166           0 :                             blendTexBindingSlot, currentBlendTex);
    1167           0 :                         lastBlendTex = currentBlendTex;
    1168             :                     }
    1169             : 
    1170           0 :                     itt->m_Texture->GetMaterial().GetStaticUniforms().BindUniforms(deviceCommandContext, shader);
    1171             : 
    1172           0 :                     float c = itt->m_Texture->GetTextureMatrix()[0];
    1173           0 :                     float ms = itt->m_Texture->GetTextureMatrix()[8];
    1174           0 :                     deviceCommandContext->SetUniform(
    1175           0 :                         textureTransformBindingSlot, c, ms);
    1176             :                 }
    1177             :                 else
    1178             :                 {
    1179           0 :                     deviceCommandContext->SetTexture(
    1180           0 :                         baseTexBindingSlot, g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture());
    1181             :                 }
    1182             : 
    1183           0 :                 for (VertexBufferBatches::iterator itv = itt->m_Batches.begin(); itv != itt->m_Batches.end(); ++itv)
    1184             :                 {
    1185             :                     // Rebind the VB only if it changed since the last batch
    1186           0 :                     if (itv->first != lastVB || shader != previousShader)
    1187             :                     {
    1188           0 :                         lastVB = itv->first;
    1189           0 :                         previousShader = shader;
    1190             : 
    1191           0 :                         ENSURE(!itv->first->GetBuffer()->IsDynamic());
    1192             : 
    1193           0 :                         deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
    1194             : 
    1195           0 :                         deviceCommandContext->SetVertexBuffer(0, itv->first->GetBuffer(), 0);
    1196             :                     }
    1197             : 
    1198           0 :                     for (IndexBufferBatches::iterator it = itv->second.begin(); it != itv->second.end(); ++it)
    1199             :                     {
    1200           0 :                         ENSURE(!it->first->GetBuffer()->IsDynamic());
    1201           0 :                         deviceCommandContext->SetIndexBuffer(it->first->GetBuffer());
    1202             : 
    1203           0 :                         BatchElements& batch = it->second;
    1204             : 
    1205           0 :                         for (size_t i = 0; i < batch.first.size(); ++i)
    1206           0 :                             deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
    1207             : 
    1208           0 :                         g_Renderer.m_Stats.m_DrawCalls++;
    1209           0 :                         g_Renderer.m_Stats.m_BlendSplats++;
    1210           0 :                         g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
    1211             :                     }
    1212             :                 }
    1213             :             }
    1214           0 :             deviceCommandContext->EndPass();
    1215             :         }
    1216             :     }
    1217           0 : }
    1218             : 
    1219           0 : void CPatchRData::RenderStreams(
    1220             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
    1221             :     Renderer::Backend::IVertexInputLayout* vertexInputLayout,
    1222             :     const std::vector<CPatchRData*>& patches)
    1223             : {
    1224           0 :     PROFILE3("render terrain streams");
    1225             : 
    1226             :     // Each batch has a list of index counts, and a list of pointers-to-first-indexes
    1227             :     using StreamBatchElements = std::pair<std::vector<u32>, std::vector<u32>>;
    1228             : 
    1229             :     // Group batches by index buffer
    1230             :     using StreamIndexBufferBatches = std::map<CVertexBuffer*, StreamBatchElements>;
    1231             : 
    1232             :     // Group batches by vertex buffer
    1233             :     using StreamVertexBufferBatches = std::map<CVertexBuffer*, StreamIndexBufferBatches>;
    1234             : 
    1235           0 :     StreamVertexBufferBatches batches;
    1236             : 
    1237           0 :     PROFILE_START("compute batches");
    1238             : 
    1239             :     // Collect all the patches into their appropriate batches
    1240           0 :     for (const CPatchRData* patch : patches)
    1241             :     {
    1242           0 :         StreamBatchElements& batch = batches[patch->m_VBBase->m_Owner][patch->m_VBBaseIndices->m_Owner];
    1243             : 
    1244           0 :         batch.first.push_back(patch->m_VBBaseIndices->m_Count);
    1245             : 
    1246           0 :         batch.second.push_back(patch->m_VBBaseIndices->m_Index);
    1247             :     }
    1248             : 
    1249             :     PROFILE_END("compute batches");
    1250             : 
    1251           0 :     deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
    1252             : 
    1253             :     // Render each batch
    1254           0 :     for (const std::pair<CVertexBuffer* const, StreamIndexBufferBatches>& streamBatch : batches)
    1255             :     {
    1256           0 :         ENSURE(!streamBatch.first->GetBuffer()->IsDynamic());
    1257             : 
    1258           0 :         deviceCommandContext->SetVertexBuffer(0, streamBatch.first->GetBuffer(), 0);
    1259             : 
    1260           0 :         for (const std::pair<CVertexBuffer* const, StreamBatchElements>& batchIndexBuffer : streamBatch.second)
    1261             :         {
    1262           0 :             ENSURE(!batchIndexBuffer.first->GetBuffer()->IsDynamic());
    1263           0 :             deviceCommandContext->SetIndexBuffer(batchIndexBuffer.first->GetBuffer());
    1264             : 
    1265           0 :             const StreamBatchElements& batch = batchIndexBuffer.second;
    1266             : 
    1267           0 :             for (size_t i = 0; i < batch.first.size(); ++i)
    1268           0 :                 deviceCommandContext->DrawIndexed(batch.second[i], batch.first[i], 0);
    1269             : 
    1270           0 :             g_Renderer.m_Stats.m_DrawCalls++;
    1271           0 :             g_Renderer.m_Stats.m_TerrainTris += std::accumulate(batch.first.begin(), batch.first.end(), 0) / 3;
    1272             :         }
    1273             :     }
    1274           0 : }
    1275             : 
    1276           0 : void CPatchRData::RenderOutline()
    1277             : {
    1278           0 :     CTerrain* terrain = m_Patch->m_Parent;
    1279           0 :     ssize_t gx = m_Patch->m_X * PATCH_SIZE;
    1280           0 :     ssize_t gz = m_Patch->m_Z * PATCH_SIZE;
    1281             : 
    1282           0 :     CVector3D pos;
    1283           0 :     std::vector<CVector3D> line;
    1284           0 :     for (ssize_t i = 0, j = 0; i <= PATCH_SIZE; ++i)
    1285             :     {
    1286           0 :         terrain->CalcPosition(gx + i, gz + j, pos);
    1287           0 :         line.push_back(pos);
    1288             :     }
    1289           0 :     for (ssize_t i = PATCH_SIZE, j = 1; j <= PATCH_SIZE; ++j)
    1290             :     {
    1291           0 :         terrain->CalcPosition(gx + i, gz + j, pos);
    1292           0 :         line.push_back(pos);
    1293             :     }
    1294           0 :     for (ssize_t i = PATCH_SIZE-1, j = PATCH_SIZE; i >= 0; --i)
    1295             :     {
    1296           0 :         terrain->CalcPosition(gx + i, gz + j, pos);
    1297           0 :         line.push_back(pos);
    1298             :     }
    1299           0 :     for (ssize_t i = 0, j = PATCH_SIZE-1; j >= 0; --j)
    1300             :     {
    1301           0 :         terrain->CalcPosition(gx + i, gz + j, pos);
    1302           0 :         line.push_back(pos);
    1303             :     }
    1304             : 
    1305           0 :     g_Renderer.GetDebugRenderer().DrawLine(line, CColor(0.0f, 0.0f, 1.0f, 1.0f), 0.1f);
    1306           0 : }
    1307             : 
    1308           0 : void CPatchRData::RenderSides(
    1309             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
    1310             :     Renderer::Backend::IVertexInputLayout* vertexInputLayout,
    1311             :     const std::vector<CPatchRData*>& patches)
    1312             : {
    1313           0 :     PROFILE3("render terrain sides");
    1314           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain sides");
    1315             : 
    1316           0 :     if (patches.empty())
    1317           0 :         return;
    1318             : 
    1319           0 :     deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
    1320             : 
    1321           0 :     CVertexBuffer* lastVB = nullptr;
    1322           0 :     for (CPatchRData* patch : patches)
    1323             :     {
    1324           0 :         ENSURE(patch->m_UpdateFlags == 0);
    1325           0 :         if (!patch->m_VBSides)
    1326           0 :             continue;
    1327           0 :         if (lastVB != patch->m_VBSides->m_Owner)
    1328             :         {
    1329           0 :             lastVB = patch->m_VBSides->m_Owner;
    1330           0 :             ENSURE(!lastVB->GetBuffer()->IsDynamic());
    1331           0 :             deviceCommandContext->SetVertexBuffer(0, patch->m_VBSides->m_Owner->GetBuffer(), 0);
    1332             :         }
    1333             : 
    1334           0 :         deviceCommandContext->Draw(patch->m_VBSides->m_Index, patch->m_VBSides->m_Count);
    1335             : 
    1336             :         // bump stats
    1337           0 :         g_Renderer.m_Stats.m_DrawCalls++;
    1338           0 :         g_Renderer.m_Stats.m_TerrainTris += patch->m_VBSides->m_Count / 3;
    1339             :     }
    1340             : }
    1341             : 
    1342           0 : void CPatchRData::RenderPriorities(CTextRenderer& textRenderer)
    1343             : {
    1344           0 :     CTerrain* terrain = m_Patch->m_Parent;
    1345           0 :     const CCamera& camera = *(g_Game->GetView()->GetCamera());
    1346             : 
    1347           0 :     for (ssize_t j = 0; j < PATCH_SIZE; ++j)
    1348             :     {
    1349           0 :         for (ssize_t i = 0; i < PATCH_SIZE; ++i)
    1350             :         {
    1351           0 :             ssize_t gx = m_Patch->m_X * PATCH_SIZE + i;
    1352           0 :             ssize_t gz = m_Patch->m_Z * PATCH_SIZE + j;
    1353             : 
    1354           0 :             CVector3D pos;
    1355           0 :             terrain->CalcPosition(gx, gz, pos);
    1356             : 
    1357             :             // Move a bit towards the center of the tile
    1358           0 :             pos.X += TERRAIN_TILE_SIZE/4.f;
    1359           0 :             pos.Z += TERRAIN_TILE_SIZE/4.f;
    1360             : 
    1361             :             float x, y;
    1362           0 :             camera.GetScreenCoordinates(pos, x, y);
    1363             : 
    1364           0 :             textRenderer.PrintfAt(x, y, L"%d", m_Patch->m_MiniPatches[j][i].Priority);
    1365             :         }
    1366             :     }
    1367           0 : }
    1368             : 
    1369             : //
    1370             : // Water build and rendering
    1371             : //
    1372             : 
    1373             : // Build vertex buffer for water vertices over our patch
    1374           0 : void CPatchRData::BuildWater()
    1375             : {
    1376           0 :     PROFILE3("build water");
    1377             : 
    1378             :     // Number of vertices in each direction in each patch
    1379             :     ENSURE(PATCH_SIZE % water_cell_size == 0);
    1380             : 
    1381           0 :     m_VBWater.Reset();
    1382           0 :     m_VBWaterIndices.Reset();
    1383           0 :     m_VBWaterShore.Reset();
    1384           0 :     m_VBWaterIndicesShore.Reset();
    1385             : 
    1386           0 :     m_WaterBounds.SetEmpty();
    1387             : 
    1388             :     // We need to use this to access the water manager or we may not have the
    1389             :     // actual values but some compiled-in defaults
    1390           0 :     CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
    1391           0 :     if (!cmpWaterManager)
    1392           0 :         return;
    1393             : 
    1394             :     // Build data for water
    1395           0 :     std::vector<SWaterVertex> water_vertex_data;
    1396           0 :     std::vector<u16> water_indices;
    1397             :     u16 water_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
    1398           0 :     memset(water_index_map, 0xFF, sizeof(water_index_map));
    1399             : 
    1400             :     // Build data for shore
    1401           0 :     std::vector<SWaterVertex> water_vertex_data_shore;
    1402           0 :     std::vector<u16> water_indices_shore;
    1403             :     u16 water_shore_index_map[PATCH_SIZE+1][PATCH_SIZE+1];
    1404           0 :     memset(water_shore_index_map, 0xFF, sizeof(water_shore_index_map));
    1405             : 
    1406           0 :     const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager();
    1407             : 
    1408           0 :     CPatch* patch = m_Patch;
    1409           0 :     CTerrain* terrain = patch->m_Parent;
    1410             : 
    1411           0 :     ssize_t mapSize = terrain->GetVerticesPerSide();
    1412             : 
    1413             :     // Top-left coordinates of our patch.
    1414           0 :     ssize_t px = m_Patch->m_X * PATCH_SIZE;
    1415           0 :     ssize_t pz = m_Patch->m_Z * PATCH_SIZE;
    1416             : 
    1417             :     // To whoever implements different water heights, this is a TODO: water height)
    1418           0 :     float waterHeight = cmpWaterManager->GetExactWaterLevel(0.0f,0.0f);
    1419             : 
    1420             :     // The 4 points making a water tile.
    1421           0 :     int moves[4][2] = {
    1422             :         {0, 0},
    1423             :         {water_cell_size, 0},
    1424             :         {0, water_cell_size},
    1425             :         {water_cell_size, water_cell_size}
    1426             :     };
    1427             :     // Where to look for when checking for water for shore tiles.
    1428           0 :     int check[10][2] = {
    1429             :         {0, 0},
    1430             :         {water_cell_size, 0},
    1431             :         {water_cell_size*2, 0},
    1432             :         {0, water_cell_size},
    1433             :         {0, water_cell_size*2},
    1434             :         {water_cell_size, water_cell_size},
    1435             :         {water_cell_size*2, water_cell_size*2},
    1436             :         {-water_cell_size, 0},
    1437             :         {0, -water_cell_size},
    1438             :         {-water_cell_size, -water_cell_size}
    1439             :     };
    1440             : 
    1441             :     // build vertices, uv, and shader varying
    1442           0 :     for (ssize_t z = 0; z < PATCH_SIZE; z += water_cell_size)
    1443             :     {
    1444           0 :         for (ssize_t x = 0; x < PATCH_SIZE; x += water_cell_size)
    1445             :         {
    1446             :             // Check that this tile is close to water
    1447           0 :             bool nearWater = false;
    1448           0 :             for (size_t test = 0; test < 10; ++test)
    1449           0 :                 if (terrain->GetVertexGroundLevel(x + px + check[test][0], z + pz + check[test][1]) < waterHeight)
    1450           0 :                     nearWater = true;
    1451           0 :             if (!nearWater)
    1452           0 :                 continue;
    1453             : 
    1454             :             // This is actually lying and I should call CcmpTerrain
    1455             :             /*if (!terrain->IsOnMap(x+x1, z+z1)
    1456             :              && !terrain->IsOnMap(x+x1, z+z1 + water_cell_size)
    1457             :              && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1)
    1458             :              && !terrain->IsOnMap(x+x1 + water_cell_size, z+z1 + water_cell_size))
    1459             :              continue;*/
    1460             : 
    1461           0 :             for (int i = 0; i < 4; ++i)
    1462             :             {
    1463           0 :                 if (water_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
    1464           0 :                     continue;
    1465             : 
    1466           0 :                 ssize_t xx = x + px + moves[i][0];
    1467           0 :                 ssize_t zz = z + pz + moves[i][1];
    1468             : 
    1469           0 :                 SWaterVertex vertex;
    1470           0 :                 terrain->CalcPosition(xx,zz, vertex.m_Position);
    1471           0 :                 float depth = waterHeight - vertex.m_Position.Y;
    1472             : 
    1473           0 :                 vertex.m_Position.Y = waterHeight;
    1474             : 
    1475           0 :                 m_WaterBounds += vertex.m_Position;
    1476             : 
    1477           0 :                 vertex.m_WaterData = CVector2D(waterManager.m_WindStrength[xx + zz*mapSize], depth);
    1478             : 
    1479           0 :                 water_index_map[z+moves[i][1]][x+moves[i][0]] = static_cast<u16>(water_vertex_data.size());
    1480           0 :                 water_vertex_data.push_back(vertex);
    1481             :             }
    1482           0 :             water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
    1483           0 :             water_indices.push_back(water_index_map[z + moves[0][1]][x + moves[0][0]]);
    1484           0 :             water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
    1485           0 :             water_indices.push_back(water_index_map[z + moves[1][1]][x + moves[1][0]]);
    1486           0 :             water_indices.push_back(water_index_map[z + moves[3][1]][x + moves[3][0]]);
    1487           0 :             water_indices.push_back(water_index_map[z + moves[2][1]][x + moves[2][0]]);
    1488             : 
    1489             :             // Check id this tile is partly over land.
    1490             :             // If so add a square over the terrain. This is necessary to render waves that go on shore.
    1491           0 :             if (terrain->GetVertexGroundLevel(x+px, z+pz) < waterHeight &&
    1492           0 :                 terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz) < waterHeight &&
    1493           0 :                 terrain->GetVertexGroundLevel(x+px, z+pz+water_cell_size) < waterHeight &&
    1494           0 :                 terrain->GetVertexGroundLevel(x+px + water_cell_size, z+pz+water_cell_size) < waterHeight)
    1495           0 :                 continue;
    1496             : 
    1497           0 :             for (int i = 0; i < 4; ++i)
    1498             :             {
    1499           0 :                 if (water_shore_index_map[z+moves[i][1]][x+moves[i][0]] != 0xFFFF)
    1500           0 :                     continue;
    1501           0 :                 ssize_t xx = x + px + moves[i][0];
    1502           0 :                 ssize_t zz = z + pz + moves[i][1];
    1503             : 
    1504           0 :                 SWaterVertex vertex;
    1505           0 :                 terrain->CalcPosition(xx,zz, vertex.m_Position);
    1506             : 
    1507           0 :                 vertex.m_Position.Y += 0.02f;
    1508           0 :                 m_WaterBounds += vertex.m_Position;
    1509             : 
    1510           0 :                 vertex.m_WaterData = CVector2D(0.0f, -5.0f);
    1511             : 
    1512           0 :                 water_shore_index_map[z+moves[i][1]][x+moves[i][0]] = static_cast<u16>(water_vertex_data_shore.size());
    1513           0 :                 water_vertex_data_shore.push_back(vertex);
    1514             :             }
    1515           0 :             if (terrain->GetTriangulationDir(x + px, z + pz))
    1516             :             {
    1517           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
    1518           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
    1519           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
    1520           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
    1521           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
    1522           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
    1523             :             }
    1524             :             else
    1525             :             {
    1526           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
    1527           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[2][1]][x + moves[2][0]]);
    1528           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
    1529           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[3][1]][x + moves[3][0]]);
    1530           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[0][1]][x + moves[0][0]]);
    1531           0 :                 water_indices_shore.push_back(water_shore_index_map[z + moves[1][1]][x + moves[1][0]]);
    1532             :             }
    1533             :         }
    1534             :     }
    1535             : 
    1536             :     // No vertex buffers if no data generated
    1537           0 :     if (!water_indices.empty())
    1538             :     {
    1539           0 :         m_VBWater = g_VBMan.AllocateChunk(
    1540             :             sizeof(SWaterVertex), water_vertex_data.size(),
    1541             :             Renderer::Backend::IBuffer::Type::VERTEX, false,
    1542           0 :             nullptr, CVertexBufferManager::Group::WATER);
    1543           0 :         m_VBWater->m_Owner->UpdateChunkVertices(m_VBWater.Get(), &water_vertex_data[0]);
    1544             : 
    1545           0 :         m_VBWaterIndices = g_VBMan.AllocateChunk(
    1546             :             sizeof(u16), water_indices.size(),
    1547             :             Renderer::Backend::IBuffer::Type::INDEX, false,
    1548           0 :             nullptr, CVertexBufferManager::Group::WATER);
    1549           0 :         m_VBWaterIndices->m_Owner->UpdateChunkVertices(m_VBWaterIndices.Get(), &water_indices[0]);
    1550             :     }
    1551             : 
    1552           0 :     if (!water_indices_shore.empty())
    1553             :     {
    1554           0 :         m_VBWaterShore = g_VBMan.AllocateChunk(
    1555             :             sizeof(SWaterVertex), water_vertex_data_shore.size(),
    1556             :             Renderer::Backend::IBuffer::Type::VERTEX, false,
    1557           0 :             nullptr, CVertexBufferManager::Group::WATER);
    1558           0 :         m_VBWaterShore->m_Owner->UpdateChunkVertices(m_VBWaterShore.Get(), &water_vertex_data_shore[0]);
    1559             : 
    1560             :         // Construct indices buffer
    1561           0 :         m_VBWaterIndicesShore = g_VBMan.AllocateChunk(
    1562             :             sizeof(u16), water_indices_shore.size(),
    1563             :             Renderer::Backend::IBuffer::Type::INDEX, false,
    1564           0 :             nullptr, CVertexBufferManager::Group::WATER);
    1565           0 :         m_VBWaterIndicesShore->m_Owner->UpdateChunkVertices(m_VBWaterIndicesShore.Get(), &water_indices_shore[0]);
    1566             :     }
    1567             : }
    1568             : 
    1569           0 : void CPatchRData::RenderWaterSurface(
    1570             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
    1571             :     Renderer::Backend::IVertexInputLayout* vertexInputLayout)
    1572             : {
    1573           0 :     ASSERT(m_UpdateFlags == 0);
    1574             : 
    1575           0 :     if (!m_VBWater)
    1576           0 :         return;
    1577             : 
    1578           0 :     ENSURE(!m_VBWater->m_Owner->GetBuffer()->IsDynamic());
    1579           0 :     ENSURE(!m_VBWaterIndices->m_Owner->GetBuffer()->IsDynamic());
    1580             : 
    1581           0 :     const uint32_t stride = sizeof(SWaterVertex);
    1582           0 :     const uint32_t firstVertexOffset = m_VBWater->m_Index * stride;
    1583             : 
    1584           0 :     deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
    1585             : 
    1586           0 :     deviceCommandContext->SetVertexBuffer(
    1587           0 :         0, m_VBWater->m_Owner->GetBuffer(), firstVertexOffset);
    1588           0 :     deviceCommandContext->SetIndexBuffer(m_VBWaterIndices->m_Owner->GetBuffer());
    1589             : 
    1590           0 :     deviceCommandContext->DrawIndexed(m_VBWaterIndices->m_Index, m_VBWaterIndices->m_Count, 0);
    1591             : 
    1592           0 :     g_Renderer.m_Stats.m_DrawCalls++;
    1593           0 :     g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndices->m_Count / 3;
    1594             : }
    1595             : 
    1596           0 : void CPatchRData::RenderWaterShore(
    1597             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
    1598             :     Renderer::Backend::IVertexInputLayout* vertexInputLayout)
    1599             : {
    1600           0 :     ASSERT(m_UpdateFlags == 0);
    1601             : 
    1602           0 :     if (!m_VBWaterShore)
    1603           0 :         return;
    1604             : 
    1605           0 :     ENSURE(!m_VBWaterShore->m_Owner->GetBuffer()->IsDynamic());
    1606           0 :     ENSURE(!m_VBWaterIndicesShore->m_Owner->GetBuffer()->IsDynamic());
    1607             : 
    1608           0 :     const uint32_t stride = sizeof(SWaterVertex);
    1609           0 :     const uint32_t firstVertexOffset = m_VBWaterShore->m_Index * stride;
    1610             : 
    1611           0 :     deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
    1612             : 
    1613           0 :     deviceCommandContext->SetVertexBuffer(
    1614           0 :         0, m_VBWaterShore->m_Owner->GetBuffer(), firstVertexOffset);
    1615           0 :     deviceCommandContext->SetIndexBuffer(m_VBWaterIndicesShore->m_Owner->GetBuffer());
    1616             : 
    1617           0 :     deviceCommandContext->DrawIndexed(m_VBWaterIndicesShore->m_Index, m_VBWaterIndicesShore->m_Count, 0);
    1618             : 
    1619           0 :     g_Renderer.m_Stats.m_DrawCalls++;
    1620           0 :     g_Renderer.m_Stats.m_WaterTris += m_VBWaterIndicesShore->m_Count / 3;
    1621           3 : }

Generated by: LCOV version 1.13