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: 0 715 0.0 %
Date: 2022-03-08 13:03:03 Functions: 0 24 0.0 %

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

Generated by: LCOV version 1.13