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 686 0.0 %
Date: 2021-09-24 14:46:47 Functions: 0 23 0.0 %

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

Generated by: LCOV version 1.13