LCOV - code coverage report
Current view: top level - source/renderer - WaterManager.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 627 0.0 %
Date: 2021-09-24 14:46:47 Functions: 0 16 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             : /*
      19             :  * Water settings (speed, height) and texture management
      20             :  */
      21             : 
      22             : #include "precompiled.h"
      23             : 
      24             : #include "graphics/Terrain.h"
      25             : #include "graphics/TextureManager.h"
      26             : #include "graphics/ShaderManager.h"
      27             : #include "graphics/ShaderProgram.h"
      28             : #include "lib/bits.h"
      29             : #include "lib/timer.h"
      30             : #include "lib/tex/tex.h"
      31             : #include "lib/res/graphics/ogl_tex.h"
      32             : #include "maths/MathUtil.h"
      33             : #include "maths/Vector2D.h"
      34             : #include "ps/CLogger.h"
      35             : #include "ps/CStrInternStatic.h"
      36             : #include "ps/Game.h"
      37             : #include "ps/World.h"
      38             : #include "renderer/WaterManager.h"
      39             : #include "renderer/Renderer.h"
      40             : #include "renderer/RenderingOptions.h"
      41             : #include "simulation2/Simulation2.h"
      42             : #include "simulation2/components/ICmpWaterManager.h"
      43             : #include "simulation2/components/ICmpRangeManager.h"
      44             : 
      45             : 
      46             : struct CoastalPoint
      47             : {
      48           0 :     CoastalPoint(int idx, CVector2D pos) : index(idx), position(pos) {};
      49             :     int index;
      50             :     CVector2D position;
      51             : };
      52             : 
      53           0 : struct SWavesVertex {
      54             :     // vertex position
      55             :     CVector3D m_BasePosition;
      56             :     CVector3D m_ApexPosition;
      57             :     CVector3D m_SplashPosition;
      58             :     CVector3D m_RetreatPosition;
      59             : 
      60             :     CVector2D m_PerpVect;
      61             :     u8 m_UV[3];
      62             : 
      63             :     // pad to a power of two
      64             :     u8 m_Padding[5];
      65             : };
      66             : cassert(sizeof(SWavesVertex) == 64);
      67             : 
      68           0 : struct WaveObject
      69             : {
      70             :     CVertexBuffer::VBChunk* m_VBvertices;
      71             :     CBoundingBoxAligned m_AABB;
      72             :     size_t m_Width;
      73             :     float m_TimeDiff;
      74             : };
      75             : 
      76           0 : WaterManager::WaterManager()
      77             : {
      78             :     // water
      79           0 :     m_RenderWater = false; // disabled until textures are successfully loaded
      80           0 :     m_WaterHeight = 5.0f;
      81             : 
      82           0 :     m_WaterCurrentTex = 0;
      83             : 
      84           0 :     m_ReflectionTexture = 0;
      85           0 :     m_RefractionTexture = 0;
      86           0 :     m_RefTextureSize = 0;
      87             : 
      88           0 :     m_ReflectionFbo = 0;
      89           0 :     m_RefractionFbo = 0;
      90           0 :     m_FancyEffectsFBO = 0;
      91             : 
      92           0 :     m_WaterTexTimer = 0.0;
      93             : 
      94           0 :     m_WindAngle = 0.0f;
      95           0 :     m_Waviness = 8.0f;
      96           0 :     m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f);
      97           0 :     m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f);
      98           0 :     m_Murkiness = 0.45f;
      99           0 :     m_RepeatPeriod = 16.0f;
     100             : 
     101           0 :     m_DistanceHeightmap = NULL;
     102           0 :     m_BlurredNormalMap = NULL;
     103           0 :     m_WindStrength = NULL;
     104             : 
     105           0 :     m_ShoreWaves_VBIndices = NULL;
     106             : 
     107           0 :     m_WaterEffects = true;
     108           0 :     m_WaterFancyEffects = false;
     109           0 :     m_WaterRealDepth = false;
     110           0 :     m_WaterRefraction = false;
     111           0 :     m_WaterReflection = false;
     112           0 :     m_WaterType = L"ocean";
     113             : 
     114           0 :     m_NeedsReloading = false;
     115           0 :     m_NeedInfoUpdate = true;
     116             : 
     117           0 :     m_FancyTexture = 0;
     118           0 :     m_FancyTextureDepth = 0;
     119           0 :     m_ReflFboDepthTexture = 0;
     120           0 :     m_RefrFboDepthTexture = 0;
     121             : 
     122           0 :     m_MapSize = 0;
     123             : 
     124           0 :     m_updatei0 = 0;
     125           0 :     m_updatej0 = 0;
     126           0 :     m_updatei1 = 0;
     127           0 :     m_updatej1 = 0;
     128           0 : }
     129             : 
     130           0 : WaterManager::~WaterManager()
     131             : {
     132             :     // Cleanup if the caller messed up
     133           0 :     UnloadWaterTextures();
     134             : 
     135           0 :     for (WaveObject* const& obj : m_ShoreWaves)
     136             :     {
     137           0 :         if (obj->m_VBvertices)
     138           0 :             g_VBMan.Release(obj->m_VBvertices);
     139           0 :         delete obj;
     140             :     }
     141             : 
     142           0 :     if (m_ShoreWaves_VBIndices)
     143           0 :         g_VBMan.Release(m_ShoreWaves_VBIndices);
     144             : 
     145           0 :     delete[] m_DistanceHeightmap;
     146           0 :     delete[] m_BlurredNormalMap;
     147           0 :     delete[] m_WindStrength;
     148             : 
     149           0 :     if (!g_Renderer.GetCapabilities().m_PrettyWater)
     150           0 :         return;
     151             : 
     152           0 :     glDeleteTextures(1, &m_FancyTexture);
     153           0 :     glDeleteTextures(1, &m_FancyTextureDepth);
     154           0 :     glDeleteTextures(1, &m_ReflFboDepthTexture);
     155           0 :     glDeleteTextures(1, &m_RefrFboDepthTexture);
     156             : 
     157           0 :     pglDeleteFramebuffersEXT(1, &m_FancyEffectsFBO);
     158           0 :     pglDeleteFramebuffersEXT(1, &m_RefractionFbo);
     159           0 :     pglDeleteFramebuffersEXT(1, &m_ReflectionFbo);
     160           0 : }
     161             : 
     162             : 
     163             : ///////////////////////////////////////////////////////////////////
     164             : // Progressive load of water textures
     165           0 : int WaterManager::LoadWaterTextures()
     166             : {
     167             :     // TODO: this doesn't need to be progressive-loading any more
     168             :     // (since texture loading is async now)
     169             : 
     170           0 :     wchar_t pathname[PATH_MAX];
     171             : 
     172             :     // Load diffuse grayscale images (for non-fancy water)
     173           0 :     for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); ++i)
     174             :     {
     175           0 :         swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/default/diffuse%02d.dds", (int)i+1);
     176           0 :         CTextureProperties textureProps(pathname);
     177           0 :         textureProps.SetWrap(GL_REPEAT);
     178             : 
     179           0 :         CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
     180           0 :         texture->Prefetch();
     181           0 :         m_WaterTexture[i] = texture;
     182             :     }
     183             : 
     184           0 :     if (!g_Renderer.GetCapabilities().m_PrettyWater)
     185             :     {
     186             :         // Enable rendering, now that we've succeeded this far
     187           0 :         m_RenderWater = true;
     188           0 :         return 0;
     189             :     }
     190             : 
     191             : #if CONFIG2_GLES
     192             : #warning Fix WaterManager::LoadWaterTextures on GLES
     193             : #else
     194             :     // Load normalmaps (for fancy water)
     195           0 :     ReloadWaterNormalTextures();
     196             : 
     197             :     // Load CoastalWaves
     198           0 :     {
     199           0 :         CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png");
     200           0 :         textureProps.SetWrap(GL_REPEAT);
     201           0 :         CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
     202           0 :         texture->Prefetch();
     203           0 :         m_WaveTex = texture;
     204             :     }
     205             : 
     206             :     // Load Foam
     207           0 :     {
     208           0 :         CTextureProperties textureProps(L"art/textures/terrain/types/water/foam.png");
     209           0 :         textureProps.SetWrap(GL_REPEAT);
     210           0 :         CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
     211           0 :         texture->Prefetch();
     212           0 :         m_FoamTex = texture;
     213             :     }
     214             : 
     215             :     // Use screen-sized textures for minimum artifacts.
     216           0 :     m_RefTextureSize = g_Renderer.GetHeight();
     217             : 
     218           0 :     m_RefTextureSize = round_up_to_pow2(m_RefTextureSize);
     219             : 
     220             :     // Create reflection texture
     221           0 :     glGenTextures(1, &m_ReflectionTexture);
     222           0 :     glBindTexture(GL_TEXTURE_2D, m_ReflectionTexture);
     223           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     224           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     225           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
     226           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
     227           0 :     glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0,  GL_RGBA, GL_UNSIGNED_BYTE, 0);
     228             : 
     229             :     // Create refraction texture
     230           0 :     glGenTextures(1, &m_RefractionTexture);
     231           0 :     glBindTexture(GL_TEXTURE_2D, m_RefractionTexture);
     232           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     233           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     234           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
     235           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
     236           0 :     glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0,  GL_RGBA, GL_UNSIGNED_BYTE, 0);
     237             : 
     238             :     // Create depth textures
     239           0 :     glGenTextures(1, &m_ReflFboDepthTexture);
     240           0 :     glBindTexture(GL_TEXTURE_2D, m_ReflFboDepthTexture);
     241           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     242           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     243           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     244           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     245           0 :     glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0,  GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
     246             : 
     247           0 :     glGenTextures(1, &m_RefrFboDepthTexture);
     248           0 :     glBindTexture(GL_TEXTURE_2D, m_RefrFboDepthTexture);
     249           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     250           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     251           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     252           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     253           0 :     glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)m_RefTextureSize, (GLsizei)m_RefTextureSize, 0,  GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
     254             : 
     255             :     // Create the Fancy Effects texture
     256           0 :     glGenTextures(1, &m_FancyTexture);
     257           0 :     glBindTexture(GL_TEXTURE_2D, m_FancyTexture);
     258           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     259           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     260           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     261           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     262             : 
     263           0 :     glGenTextures(1, &m_FancyTextureDepth);
     264           0 :     glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth);
     265           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     266           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     267           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     268           0 :     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     269             : 
     270           0 :     glBindTexture(GL_TEXTURE_2D, 0);
     271             : 
     272           0 :     Resize();
     273             : 
     274             :     // Create the water framebuffers
     275             : 
     276           0 :     GLint currentFbo;
     277           0 :     glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &currentFbo);
     278             : 
     279           0 :     m_ReflectionFbo = 0;
     280           0 :     pglGenFramebuffersEXT(1, &m_ReflectionFbo);
     281           0 :     pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_ReflectionFbo);
     282           0 :     pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_ReflectionTexture, 0);
     283           0 :     pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_ReflFboDepthTexture, 0);
     284             : 
     285           0 :     ogl_WarnIfError();
     286             : 
     287           0 :     GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
     288           0 :     if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
     289             :     {
     290           0 :         LOGWARNING("Reflection framebuffer object incomplete: 0x%04X", status);
     291           0 :         g_RenderingOptions.SetWaterReflection(false);
     292           0 :         UpdateQuality();
     293             :     }
     294             : 
     295           0 :     m_RefractionFbo = 0;
     296           0 :     pglGenFramebuffersEXT(1, &m_RefractionFbo);
     297           0 :     pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_RefractionFbo);
     298           0 :     pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_RefractionTexture, 0);
     299           0 :     pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_RefrFboDepthTexture, 0);
     300             : 
     301           0 :     ogl_WarnIfError();
     302             : 
     303           0 :     status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
     304           0 :     if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
     305             :     {
     306           0 :         LOGWARNING("Refraction framebuffer object incomplete: 0x%04X", status);
     307           0 :         g_RenderingOptions.SetWaterRefraction(false);
     308           0 :         UpdateQuality();
     309             :     }
     310             : 
     311           0 :     pglGenFramebuffersEXT(1, &m_FancyEffectsFBO);
     312           0 :     pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO);
     313           0 :     pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_FancyTexture, 0);
     314           0 :     pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_FancyTextureDepth, 0);
     315             : 
     316           0 :     ogl_WarnIfError();
     317             : 
     318           0 :     status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
     319           0 :     if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
     320             :     {
     321           0 :         LOGWARNING("Fancy Effects framebuffer object incomplete: 0x%04X", status);
     322           0 :         g_RenderingOptions.SetWaterRefraction(false);
     323           0 :         UpdateQuality();
     324             :     }
     325             : 
     326           0 :     pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, currentFbo);
     327             : 
     328             :     // Enable rendering, now that we've succeeded this far
     329           0 :     m_RenderWater = true;
     330             : #endif
     331           0 :     return 0;
     332             : }
     333             : 
     334             : 
     335             : ///////////////////////////////////////////////////////////////////
     336             : // Resize: Updates the fancy water textures.
     337           0 : void WaterManager::Resize()
     338             : {
     339           0 :     glBindTexture(GL_TEXTURE_2D, m_FancyTexture);
     340           0 :     glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0,  GL_RGBA, GL_UNSIGNED_SHORT, NULL);
     341             : 
     342           0 :     glBindTexture(GL_TEXTURE_2D, m_FancyTextureDepth);
     343           0 :     glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, (GLsizei)g_Renderer.GetWidth(), (GLsizei)g_Renderer.GetHeight(), 0,  GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
     344             : 
     345           0 :     glBindTexture(GL_TEXTURE_2D, 0);
     346           0 : }
     347             : 
     348           0 : void WaterManager::ReloadWaterNormalTextures()
     349             : {
     350           0 :     wchar_t pathname[PATH_MAX];
     351           0 :     for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i)
     352             :     {
     353           0 :         swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), static_cast<int>(i) + 1);
     354           0 :         CTextureProperties textureProps(pathname);
     355           0 :         textureProps.SetWrap(GL_REPEAT);
     356           0 :         textureProps.SetMaxAnisotropy(4);
     357             : 
     358           0 :         CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
     359           0 :         texture->Prefetch();
     360           0 :         m_NormalMap[i] = texture;
     361             :     }
     362           0 : }
     363             : 
     364             : ///////////////////////////////////////////////////////////////////
     365             : // Unload water textures
     366           0 : void WaterManager::UnloadWaterTextures()
     367             : {
     368           0 :     for(size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
     369           0 :         m_WaterTexture[i].reset();
     370             : 
     371           0 :     if (!g_Renderer.GetCapabilities().m_PrettyWater)
     372             :         return;
     373             : 
     374           0 :     for(size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++)
     375           0 :         m_NormalMap[i].reset();
     376             : 
     377           0 :     glDeleteTextures(1, &m_ReflectionTexture);
     378           0 :     glDeleteTextures(1, &m_RefractionTexture);
     379           0 :     pglDeleteFramebuffersEXT(1, &m_RefractionFbo);
     380           0 :     pglDeleteFramebuffersEXT(1, &m_ReflectionFbo);
     381             : }
     382             : 
     383             : template<bool Transpose>
     384           0 : static inline void ComputeDirection(float* distanceMap, const u16* heightmap, float waterHeight, size_t SideSize, size_t maxLevel)
     385             : {
     386             : #define ABOVEWATER(x, z) (HEIGHT_SCALE * heightmap[z*SideSize + x] >= waterHeight)
     387             : #define UPDATELOOKAHEAD \
     388             :     for (; lookahead <= id2+maxLevel && lookahead < SideSize && \
     389             :            ((!Transpose && !ABOVEWATER(lookahead, id1)) || (Transpose && !ABOVEWATER(id1, lookahead))); ++lookahead)
     390             :     // Algorithm:
     391             :     // We want to know the distance to the closest shore point. Go through each line/column,
     392             :     // keep track of when we encountered the last shore point and how far ahead the next one is.
     393           0 :     for (size_t id1 = 0; id1 < SideSize; ++id1)
     394             :     {
     395           0 :         size_t id2 = 0;
     396           0 :         const size_t& x = Transpose ? id1 : id2;
     397           0 :         const size_t& z = Transpose ? id2 : id1;
     398             : 
     399           0 :         size_t level = ABOVEWATER(x, z) ? 0 : maxLevel;
     400           0 :         size_t lookahead = (size_t)(level > 0);
     401             : 
     402           0 :         UPDATELOOKAHEAD;
     403             : 
     404             :         // start moving
     405           0 :         for (; id2 < SideSize; ++id2)
     406             :         {
     407             :             // update current level
     408           0 :             if (ABOVEWATER(x, z))
     409           0 :                 level = 0;
     410             :             else
     411           0 :                 level = std::min(level+1, maxLevel);
     412             : 
     413             :             // move lookahead
     414           0 :             if (lookahead == id2)
     415           0 :                 ++lookahead;
     416           0 :             UPDATELOOKAHEAD;
     417             : 
     418             :             // This is the important bit: set the distance to either:
     419             :             // - the distance to the previous shore point (level)
     420             :             // - the distance to the next shore point (lookahead-id2)
     421           0 :             distanceMap[z*SideSize + x] = std::min(distanceMap[z*SideSize + x], (float)std::min(lookahead-id2, level));
     422             :         }
     423             :     }
     424             : #undef ABOVEWATER
     425             : #undef UPDATELOOKAHEAD
     426           0 : }
     427           0 : 
     428             : ///////////////////////////////////////////////////////////////////
     429             : // Calculate our binary heightmap from the terrain heightmap.
     430             : void WaterManager::RecomputeDistanceHeightmap()
     431             : {
     432             :     CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     433             :     if (!terrain || !terrain->GetHeightMap())
     434             :         return;
     435             : 
     436           0 :     size_t SideSize = m_MapSize;
     437             : 
     438           0 :     // we want to look ahead some distance, but not too much (less efficient and not interesting). This is our lookahead.
     439           0 :     const size_t maxLevel = 5;
     440           0 : 
     441             :     if (m_DistanceHeightmap == NULL)
     442           0 :     {
     443           0 :         m_DistanceHeightmap = new float[SideSize*SideSize];
     444             :         std::fill(m_DistanceHeightmap, m_DistanceHeightmap + SideSize*SideSize, (float)maxLevel);
     445           0 :     }
     446             : 
     447             :     // Create a manhattan-distance heightmap.
     448           0 :     // This could be refined to only be done near the coast itself, but it's probably not necessary.
     449             : 
     450             :     u16* heightmap = terrain->GetHeightMap();
     451           0 : 
     452           0 :     ComputeDirection<false>(m_DistanceHeightmap, heightmap, m_WaterHeight, SideSize, maxLevel);
     453             :     ComputeDirection<true>(m_DistanceHeightmap, heightmap, m_WaterHeight, SideSize, maxLevel);
     454           0 : }
     455             : 
     456             : // This requires m_DistanceHeightmap to be defined properly.
     457           0 : void WaterManager::CreateWaveMeshes()
     458           0 : {
     459           0 :     if (m_MapSize == 0)
     460             :         return;
     461             : 
     462             :     CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     463             :     if (!terrain || !terrain->GetHeightMap())
     464           0 :         return;
     465             : 
     466             :     for (WaveObject* const& obj : m_ShoreWaves)
     467             :     {
     468             :         if (obj->m_VBvertices)
     469           0 :             g_VBMan.Release(obj->m_VBvertices);
     470           0 :         delete obj;
     471             :     }
     472             :     m_ShoreWaves.clear();
     473             : 
     474             :     if (m_ShoreWaves_VBIndices)
     475             :     {
     476             :         g_VBMan.Release(m_ShoreWaves_VBIndices);
     477             :         m_ShoreWaves_VBIndices = NULL;
     478             :     }
     479           0 : 
     480             :     if (m_Waviness < 5.0f && m_WaterType != L"ocean")
     481           0 :         return;
     482           0 : 
     483           0 :     size_t SideSize = m_MapSize;
     484             : 
     485           0 :     // First step: get the points near the coast.
     486           0 :     std::set<int> CoastalPointsSet;
     487             :     for (size_t z = 1; z < SideSize-1; ++z)
     488           0 :         for (size_t x = 1; x < SideSize-1; ++x)
     489             :             // get the points not on the shore but near it, ocean-side
     490             :             if (m_DistanceHeightmap[z*m_MapSize + x] > 0.5f && m_DistanceHeightmap[z*m_MapSize + x] < 1.5f)
     491           0 :                 CoastalPointsSet.insert((z)*SideSize + x);
     492             : 
     493             :     // Second step: create chains out of those coastal points.
     494           0 :     static const int around[8][2] = { { -1,-1 }, { -1,0 }, { -1,1 }, { 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 } };
     495           0 : 
     496             :     std::vector<std::deque<CoastalPoint> > CoastalPointsChains;
     497           0 :     while (!CoastalPointsSet.empty())
     498             :     {
     499             :         int index = *(CoastalPointsSet.begin());
     500           0 :         int x = index % SideSize;
     501           0 :         int y = (index - x ) / SideSize;
     502           0 : 
     503             :         std::deque<CoastalPoint> Chain;
     504             : 
     505             :         Chain.push_front(CoastalPoint(index,CVector2D(x*4,y*4)));
     506             : 
     507           0 :         // Erase us.
     508             :         CoastalPointsSet.erase(CoastalPointsSet.begin());
     509             : 
     510             :         // We're our starter points. At most we can have 2 points close to us.
     511             :         // We'll pick the first one and look for its neighbors (he can only have one new)
     512           0 :         // Up until we either reach the end of the chain, or ourselves.
     513             :         // Then go down the other direction if there is any.
     514             :         int neighbours[2] = { -1, -1 };
     515             :         int nbNeighb = 0;
     516           0 :         for (int i = 0; i < 8; ++i)
     517             :         {
     518           0 :             if (CoastalPointsSet.count(x + around[i][0] + (y + around[i][1])*SideSize))
     519           0 :             {
     520             :                 if (nbNeighb < 2)
     521             :                     neighbours[nbNeighb] = x + around[i][0] + (y + around[i][1])*SideSize;
     522           0 :                 ++nbNeighb;
     523             :             }
     524             :         }
     525           0 :         if (nbNeighb > 2)
     526             :             continue;
     527           0 : 
     528             :         for (int i = 0; i < 2; ++i)
     529           0 :         {
     530           0 :             if (neighbours[i] == -1)
     531             :                 continue;
     532             :             // Move to our neighboring point
     533             :             int xx = neighbours[i] % SideSize;
     534             :             int yy = (neighbours[i] - xx ) / SideSize;
     535             :             int indexx = xx + yy*SideSize;
     536           0 :             int endedChain = false;
     537             : 
     538           0 :             if (i == 0)
     539           0 :                 Chain.push_back(CoastalPoint(indexx,CVector2D(xx*4,yy*4)));
     540             :             else
     541             :                 Chain.push_front(CoastalPoint(indexx,CVector2D(xx*4,yy*4)));
     542             : 
     543           0 :             // If there's a loop we'll be the "other" neighboring point already so check for that.
     544             :             // We'll readd at the end/front the other one to have full squares.
     545           0 :             if (CoastalPointsSet.count(indexx) == 0)
     546           0 :                 break;
     547             : 
     548           0 :             CoastalPointsSet.erase(indexx);
     549           0 : 
     550             :             // Start checking from there.
     551             :             while(!endedChain)
     552           0 :             {
     553             :                 bool found = false;
     554           0 :                 nbNeighb = 0;
     555           0 :                 for (int p = 0; p < 8; ++p)
     556           0 :                 {
     557             :                     if (CoastalPointsSet.count(xx+around[p][0] + (yy + around[p][1])*SideSize))
     558           0 :                     {
     559             :                         if (nbNeighb >= 2)
     560           0 :                         {
     561             :                             CoastalPointsSet.erase(xx + yy*SideSize);
     562           0 :                             continue;
     563           0 :                         }
     564             :                         ++nbNeighb;
     565             :                         // We've found a new point around us.
     566           0 :                         // Move there
     567             :                         xx = xx + around[p][0];
     568             :                         yy = yy + around[p][1];
     569           0 :                         indexx = xx + yy*SideSize;
     570             :                         if (i == 0)
     571             :                             Chain.push_back(CoastalPoint(indexx,CVector2D(xx*4,yy*4)));
     572           0 :                         else
     573           0 :                             Chain.push_front(CoastalPoint(indexx,CVector2D(xx*4,yy*4)));
     574           0 :                         CoastalPointsSet.erase(xx + yy*SideSize);
     575             :                         found = true;
     576           0 :                         break;
     577           0 :                     }
     578             :                 }
     579             :                 if (!found)
     580           0 :                     endedChain = true;
     581             :             }
     582           0 :         }
     583           0 :         if (Chain.size() > 10)
     584             :             CoastalPointsChains.push_back(Chain);
     585           0 :     }
     586           0 : 
     587           0 :     // (optional) third step: Smooth chains out.
     588             :     // This is also really dumb.
     589           0 :     for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
     590             :     {
     591           0 :         // Bump 1 for smoother.
     592             :         for (int p = 0; p < 3; ++p)
     593             :         {
     594           0 :             for (size_t j = 1; j < CoastalPointsChains[i].size()-1; ++j)
     595             :             {
     596             :                 CVector2D realPos = CoastalPointsChains[i][j-1].position + CoastalPointsChains[i][j+1].position;
     597             : 
     598             :                 CoastalPointsChains[i][j].position = (CoastalPointsChains[i][j].position + realPos/2.0f)/2.0f;
     599             :             }
     600           0 :         }
     601           0 :     }
     602           0 : 
     603             :     // Fourth step: create waves themselves, using those chains. We basically create subchains.
     604           0 :     GLushort waveSizes = 14;    // maximal size in width.
     605             : 
     606           0 :     // Construct indices buffer (we can afford one for all of them)
     607           0 :     std::vector<GLushort> water_indices;
     608           0 :     for (GLushort a = 0; a < waveSizes - 1; ++a)
     609             :     {
     610             :         for (GLushort rect = 0; rect < 7; ++rect)
     611           0 :         {
     612           0 :             water_indices.push_back(a * 9 + rect);
     613             :             water_indices.push_back(a * 9 + 9 + rect);
     614           0 :             water_indices.push_back(a * 9 + 1 + rect);
     615             :             water_indices.push_back(a * 9 + 9 + rect);
     616           0 :             water_indices.push_back(a * 9 + 10 + rect);
     617           0 :             water_indices.push_back(a * 9 + 1 + rect);
     618             :         }
     619           0 :     }
     620           0 :     // Generic indexes, max-length
     621           0 :     m_ShoreWaves_VBIndices = g_VBMan.Allocate(sizeof(GLushort), water_indices.size(), GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER);
     622           0 :     m_ShoreWaves_VBIndices->m_Owner->UpdateChunkVertices(m_ShoreWaves_VBIndices, &water_indices[0]);
     623             : 
     624           0 :     float diff = (rand() % 50) / 5.0f;
     625           0 : 
     626             :     for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
     627           0 :     {
     628             :         for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j)
     629             :         {
     630             :             if (CoastalPointsChains[i].size()- 1 - j < waveSizes)
     631           0 :                 break;
     632             : 
     633             :             GLushort width = waveSizes;
     634           0 : 
     635             :             // First pass to get some parameters out.
     636             :             float outmost = 0.0f;   // how far to move on the shore.
     637           0 :             float avgDepth = 0.0f;
     638             :             int sign = 1;
     639             :             CVector2D firstPerp(0,0), perp(0,0), lastPerp(0,0);
     640             :             for (GLushort a = 0; a < waveSizes;++a)
     641           0 :             {
     642             :                 lastPerp = perp;
     643           0 :                 perp = CVector2D(0,0);
     644             :                 int nb = 0;
     645           0 :                 CVector2D pos = CoastalPointsChains[i][j+a].position;
     646             :                 CVector2D posPlus;
     647             :                 CVector2D posMinus;
     648             :                 if (a > 0)
     649             :                 {
     650           0 :                     ++nb;
     651             :                     posMinus = CoastalPointsChains[i][j+a-1].position;
     652             :                     perp += pos-posMinus;
     653           0 :                 }
     654           0 :                 if (a < waveSizes-1)
     655           0 :                 {
     656           0 :                     ++nb;
     657           0 :                     posPlus = CoastalPointsChains[i][j+a+1].position;
     658             :                     perp += posPlus-pos;
     659           0 :                 }
     660           0 :                 perp /= nb;
     661           0 :                 perp = CVector2D(-perp.Y,perp.X).Normalized();
     662           0 : 
     663             :                 if (a == 0)
     664             :                     firstPerp = perp;
     665           0 : 
     666           0 :                 if ( a > 1 && perp.Dot(lastPerp) < 0.90f && perp.Dot(firstPerp) < 0.70f)
     667             :                 {
     668             :                     width = a+1;
     669           0 :                     break;
     670           0 :                 }
     671             : 
     672             :                 if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterHeight)
     673             :                     sign = -1;
     674             : 
     675           0 :                 avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterHeight;
     676             : 
     677             :                 float localOutmost = -2.0f;
     678           0 :                 while (localOutmost < 0.0f)
     679             :                 {
     680           0 :                     float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - m_WaterHeight;
     681             :                     if (depth < 0.0f || depth > 0.6f)
     682           0 :                         localOutmost += 0.2f;
     683             :                     else
     684           0 :                         break;
     685             :                 }
     686             : 
     687             :                 outmost += localOutmost;
     688             :             }
     689             :             if (width < 5)
     690           0 :             {
     691             :                 j += 6;
     692             :                 continue;
     693           0 :             }
     694           0 : 
     695             :             outmost /= width;
     696           0 : 
     697             :             if (outmost > -0.5f)
     698           0 :             {
     699           0 :                 j += 3;
     700           0 :                 continue;
     701           0 :             }
     702           0 :             outmost = -2.5f + outmost * m_Waviness/10.0f;
     703           0 : 
     704             :             avgDepth /= width;
     705             : 
     706             :             if (avgDepth > -1.3f)
     707           0 :             {
     708           0 :                 j += 3;
     709             :                 continue;
     710           0 :             }
     711             :             // we passed the checks, we can create a wave of size "width".
     712           0 : 
     713             :             WaveObject* shoreWave = new WaveObject;
     714           0 :             std::vector<SWavesVertex> vertices;
     715             :             vertices.reserve(9*width);
     716           0 : 
     717             :             shoreWave->m_Width = width;
     718             :             shoreWave->m_TimeDiff = diff;
     719             :             diff += (rand() % 100) / 25.0f + 4.0f;
     720             : 
     721             :             for (GLushort a = 0; a < width;++a)
     722             :             {
     723             :                 perp = CVector2D(0,0);
     724             :                 int nb = 0;
     725             :                 CVector2D pos = CoastalPointsChains[i][j+a].position;
     726           0 :                 CVector2D posPlus;
     727             :                 CVector2D posMinus;
     728           0 :                 if (a > 0)
     729           0 :                 {
     730           0 :                     ++nb;
     731           0 :                     posMinus = CoastalPointsChains[i][j+a-1].position;
     732           0 :                     perp += pos-posMinus;
     733           0 :                 }
     734           0 :                 if (a < waveSizes-1)
     735             :                 {
     736           0 :                     ++nb;
     737           0 :                     posPlus = CoastalPointsChains[i][j+a+1].position;
     738           0 :                     perp += posPlus-pos;
     739             :                 }
     740           0 :                 perp /= nb;
     741             :                 perp = CVector2D(-perp.Y,perp.X).Normalized();
     742           0 : 
     743           0 :                 SWavesVertex point[9];
     744           0 : 
     745             :                 float baseHeight = 0.04f;
     746           0 : 
     747           0 :                 float halfWidth = (width-1.0f)/2.0f;
     748             :                 float sideNess = sqrtf(Clamp( (halfWidth - fabsf(a - halfWidth)) / 3.0f, 0.0f, 1.0f));
     749           0 : 
     750           0 :                 point[0].m_UV[0] = a; point[0].m_UV[1] = 8;
     751             :                 point[1].m_UV[0] = a; point[1].m_UV[1] = 7;
     752           0 :                 point[2].m_UV[0] = a; point[2].m_UV[1] = 6;
     753             :                 point[3].m_UV[0] = a; point[3].m_UV[1] = 5;
     754           0 :                 point[4].m_UV[0] = a; point[4].m_UV[1] = 4;
     755           0 :                 point[5].m_UV[0] = a; point[5].m_UV[1] = 3;
     756             :                 point[6].m_UV[0] = a; point[6].m_UV[1] = 2;
     757             :                 point[7].m_UV[0] = a; point[7].m_UV[1] = 1;
     758           0 :                 point[8].m_UV[0] = a; point[8].m_UV[1] = 0;
     759           0 : 
     760             :                 point[0].m_PerpVect = perp;
     761           0 :                 point[1].m_PerpVect = perp;
     762             :                 point[2].m_PerpVect = perp;
     763           0 :                 point[3].m_PerpVect = perp;
     764           0 :                 point[4].m_PerpVect = perp;
     765             :                 point[5].m_PerpVect = perp;
     766           0 :                 point[6].m_PerpVect = perp;
     767           0 :                 point[7].m_PerpVect = perp;
     768           0 :                 point[8].m_PerpVect = perp;
     769             : 
     770             :                 static const float perpT1[9] = { 6.0f, 6.05f, 6.1f, 6.2f, 6.3f, 6.4f, 6.5f, 6.6f, 9.7f };
     771             :                 static const float perpT2[9] = { 2.0f, 2.1f,  2.2f, 2.3f, 2.4f, 3.0f, 3.3f, 3.6f, 9.5f };
     772             :                 static const float perpT3[9] = { 1.1f, 0.7f, -0.2f, 0.0f, 0.6f, 1.3f, 2.2f, 3.6f, 9.0f };
     773           0 :                 static const float perpT4[9] = { 2.0f, 2.1f,  1.2f, 1.5f, 1.7f, 1.9f, 2.7f, 3.8f, 9.0f };
     774             : 
     775           0 :                 static const float heightT1[9] = { 0.0f, 0.2f, 0.5f, 0.8f, 0.9f, 0.85f, 0.6f, 0.2f, 0.0 };
     776             :                 static const float heightT2[9] = { -0.8f, -0.4f, 0.0f, 0.1f, 0.1f, 0.03f, 0.0f, 0.0f, 0.0 };
     777           0 :                 static const float heightT3[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0 };
     778           0 : 
     779             :                 for (size_t t = 0; t < 9; ++t)
     780             :                 {
     781           0 :                     float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost),
     782             :                                                                             pos.Y+sign*perp.Y*(perpT1[t]+outmost));
     783           0 :                     point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight),
     784             :                                                         pos.Y+sign*perp.Y*(perpT1[t]+outmost));
     785           0 :                 }
     786           0 :                 for (size_t t = 0; t < 9; ++t)
     787             :                 {
     788           0 :                     float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost),
     789             :                                                                             pos.Y+sign*perp.Y*(perpT2[t]+outmost));
     790           0 :                     point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight),
     791             :                                                         pos.Y+sign*perp.Y*(perpT2[t]+outmost));
     792           0 :                 }
     793             :                 for (size_t t = 0; t < 9; ++t)
     794           0 :                 {
     795           0 :                     float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess),
     796             :                                                                             pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess));
     797             :                     point[t].m_SplashPosition = CVector3D(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), baseHeight + heightT2[t]*sideNess + std::max(m_WaterHeight,terrHeight), pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess));
     798             :                 }
     799           0 :                 for (size_t t = 0; t < 9; ++t)
     800           0 :                 {
     801           0 :                     float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost),
     802             :                                                                             pos.Y+sign*perp.Y*(perpT4[t]+outmost));
     803           0 :                     point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterHeight,terrHeight),
     804           0 :                                                            pos.Y+sign*perp.Y*(perpT4[t]+outmost));
     805           0 :                 }
     806             : 
     807           0 :                 vertices.push_back(point[8]);
     808             :                 vertices.push_back(point[7]);
     809           0 :                 vertices.push_back(point[6]);
     810           0 :                 vertices.push_back(point[5]);
     811           0 :                 vertices.push_back(point[4]);
     812           0 :                 vertices.push_back(point[3]);
     813           0 :                 vertices.push_back(point[2]);
     814           0 :                 vertices.push_back(point[1]);
     815             :                 vertices.push_back(point[0]);
     816           0 : 
     817           0 :                 shoreWave->m_AABB += point[8].m_SplashPosition;
     818           0 :                 shoreWave->m_AABB += point[8].m_BasePosition;
     819             :                 shoreWave->m_AABB += point[0].m_SplashPosition;
     820           0 :                 shoreWave->m_AABB += point[0].m_BasePosition;
     821             :                 shoreWave->m_AABB += point[4].m_ApexPosition;
     822           0 :             }
     823           0 : 
     824           0 :             if (sign == 1)
     825             :             {
     826           0 :                 // Let's do some fancy reversing.
     827           0 :                 std::vector<SWavesVertex> reversed;
     828             :                 reversed.reserve(vertices.size());
     829           0 :                 for (int a = width-1; a >= 0; --a)
     830             :                 {
     831           0 :                     for (size_t t = 0; t < 9; ++t)
     832             :                         reversed.push_back(vertices[a*9+t]);
     833           0 :                 }
     834           0 :                 vertices = reversed;
     835             :             }
     836           0 :             j += width/2-1;
     837           0 : 
     838           0 :             shoreWave->m_VBvertices = g_VBMan.Allocate(sizeof(SWavesVertex), vertices.size(), GL_STATIC_DRAW, GL_ARRAY_BUFFER);
     839           0 :             shoreWave->m_VBvertices->m_Owner->UpdateChunkVertices(shoreWave->m_VBvertices, &vertices[0]);
     840           0 : 
     841           0 :             m_ShoreWaves.push_back(shoreWave);
     842           0 :         }
     843           0 :     }
     844           0 : }
     845             : 
     846           0 : void WaterManager::RenderWaves(const CFrustum& frustrum)
     847           0 : {
     848           0 : #if CONFIG2_GLES
     849           0 : #warning Fix WaterManager::RenderWaves on GLES
     850           0 : #else
     851           0 :     if (g_Renderer.DoSkipSubmit() || !m_WaterFancyEffects)
     852           0 :         return;
     853           0 : 
     854           0 :     pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FancyEffectsFBO);
     855             : 
     856           0 :     GLuint attachments[1] = { GL_COLOR_ATTACHMENT0_EXT };
     857           0 :     pglDrawBuffers(1, attachments);
     858           0 : 
     859           0 :     glClearColor(0.0f,0.0f, 0.0f,0.0f);
     860             :     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     861           0 : 
     862           0 :     glEnable(GL_BLEND);
     863           0 :     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     864             :     glEnable(GL_DEPTH_TEST);
     865           0 :     glDepthFunc(GL_ALWAYS);
     866             : 
     867           0 :     CShaderDefines none;
     868           0 :     CShaderProgramPtr shader = g_Renderer.GetShaderManager().LoadProgram("glsl/waves", none);
     869           0 : 
     870             :     shader->Bind();
     871             : 
     872           0 :     shader->BindTexture(str_waveTex, m_WaveTex);
     873             :     shader->BindTexture(str_foamTex, m_FoamTex);
     874           0 : 
     875           0 :     shader->Uniform(str_time, (float)m_WaterTexTimer);
     876           0 :     shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
     877             : 
     878             :     for (size_t a = 0; a < m_ShoreWaves.size(); ++a)
     879           0 :     {
     880             :         if (!frustrum.IsBoxVisible(m_ShoreWaves[a]->m_AABB))
     881           0 :             continue;
     882           0 : 
     883           0 :         CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBvertices;
     884             :         SWavesVertex* base = (SWavesVertex*)VBchunk->m_Owner->Bind();
     885           0 : 
     886             :         // setup data pointers
     887           0 :         GLsizei stride = sizeof(SWavesVertex);
     888           0 :         shader->VertexPointer(3, GL_FLOAT, stride, &base[VBchunk->m_Index].m_BasePosition);
     889           0 :         shader->TexCoordPointer(GL_TEXTURE0, 2, GL_UNSIGNED_BYTE, stride, &base[VBchunk->m_Index].m_UV);
     890             :         //  NormalPointer(gl_FLOAT, stride, &base[m_VBWater->m_Index].m_UV)
     891             :         pglVertexAttribPointerARB(2, 2, GL_FLOAT, GL_FALSE, stride, &base[VBchunk->m_Index].m_PerpVect); // replaces commented above because my normal is vec2
     892             :         shader->VertexAttribPointer(str_a_apexPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_ApexPosition);
     893           0 :         shader->VertexAttribPointer(str_a_splashPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_SplashPosition);
     894           0 :         shader->VertexAttribPointer(str_a_retreatPosition, 3, GL_FLOAT, false, stride, &base[VBchunk->m_Index].m_RetreatPosition);
     895           0 : 
     896           0 :         shader->AssertPointersBound();
     897           0 : 
     898           0 :         shader->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff);
     899           0 :         shader->Uniform(str_width, (int)m_ShoreWaves[a]->m_Width);
     900           0 : 
     901           0 :         u8* indexBase = m_ShoreWaves_VBIndices->m_Owner->Bind();
     902             :         glDrawElements(GL_TRIANGLES, (GLsizei) (m_ShoreWaves[a]->m_Width-1)*(7*6),
     903           0 :                        GL_UNSIGNED_SHORT, indexBase + sizeof(u16)*(m_ShoreWaves_VBIndices->m_Index));
     904           0 : 
     905           0 :         shader->Uniform(str_translation, m_ShoreWaves[a]->m_TimeDiff + 6.0f);
     906           0 : 
     907           0 :         // TODO: figure out why this doesn't work.
     908             :         //g_Renderer.m_Stats.m_DrawCalls++;
     909             :         //g_Renderer.m_Stats.m_WaterTris += m_ShoreWaves_VBIndices->m_Count / 3;
     910           0 : 
     911             :         CVertexBuffer::Unbind();
     912             :     }
     913           0 :     shader->Unbind();
     914           0 :     pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
     915           0 : 
     916             :     glDisable(GL_BLEND);
     917           0 :     glDepthFunc(GL_LEQUAL);
     918           0 : #endif
     919             : }
     920           0 : 
     921             : void WaterManager::RecomputeWaterData()
     922           0 : {
     923             :     if (!m_MapSize)
     924           0 :         return;
     925           0 : 
     926             :     RecomputeDistanceHeightmap();
     927           0 :     RecomputeWindStrength();
     928             :     CreateWaveMeshes();
     929             : }
     930             : 
     931             : ///////////////////////////////////////////////////////////////////
     932           0 : // Calculate the strength of the wind at a given point on the map.
     933             : void WaterManager::RecomputeWindStrength()
     934             : {
     935             :     if (m_MapSize <= 0)
     936             :         return;
     937           0 : 
     938           0 :     if (m_WindStrength == nullptr)
     939             :         m_WindStrength = new float[m_MapSize*m_MapSize];
     940           0 : 
     941             :     CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     942           0 :     if (!terrain || !terrain->GetHeightMap())
     943           0 :         return;
     944             : 
     945           0 :     CVector2D windDir = CVector2D(cos(m_WindAngle), sin(m_WindAngle));
     946           0 : 
     947             :     int stepSize = 10;
     948           0 :     ssize_t windX = -round(stepSize * windDir.X);
     949           0 :     ssize_t windY = -round(stepSize * windDir.Y);
     950           0 : 
     951           0 :     struct SWindPoint {
     952             :         SWindPoint(size_t x, size_t y, float strength) : X(x), Y(y), windStrength(strength) {}
     953           0 :         ssize_t X;
     954           0 :         ssize_t Y;
     955             :         float windStrength;
     956           0 :     };
     957             : 
     958           0 :     std::vector<SWindPoint> startingPoints;
     959           0 :     std::vector<std::pair<int, int>> movement; // Every increment, move each starting point by all of these.
     960             : 
     961           0 :     // Compute starting points (one or two edges of the map) and how much to move each computation increment.
     962           0 :     if (fabs(windDir.X) < 0.01f)
     963             :     {
     964           0 :         movement.emplace_back(0, windY > 0.f ? 1 : -1);
     965             :         startingPoints.reserve(m_MapSize);
     966           0 :         size_t start = windY > 0 ? 0 : m_MapSize - 1;
     967             :         for (size_t x = 0; x < m_MapSize; ++x)
     968             :             startingPoints.emplace_back(x, start, 0.f);
     969           0 :     }
     970           0 :     else if (fabs(windDir.Y) < 0.01f)
     971             :     {
     972             :         movement.emplace_back(windX > 0.f ? 1 : - 1, 0);
     973           0 :         startingPoints.reserve(m_MapSize);
     974           0 :         size_t start = windX > 0 ? 0 : m_MapSize - 1;
     975           0 :         for (size_t z = 0; z < m_MapSize; ++z)
     976             :             startingPoints.emplace_back(start, z, 0.f);
     977           0 :     }
     978           0 :     else
     979           0 :     {
     980           0 :         startingPoints.reserve(m_MapSize * 2);
     981             :         // Points along X.
     982           0 :         size_t start = windY > 0 ? 0 : m_MapSize - 1;
     983             :         for (size_t x = 0; x < m_MapSize; ++x)
     984           0 :             startingPoints.emplace_back(x, start, 0.f);
     985           0 :         // Points along Z, avoid repeating the corner point.
     986             :         start = windX > 0 ? 0 : m_MapSize - 1;
     987           0 :         if (windY > 0)
     988           0 :             for (size_t z = 1; z < m_MapSize; ++z)
     989           0 :                 startingPoints.emplace_back(start, z, 0.f);
     990             :         else
     991           0 :             for (size_t z = 0; z < m_MapSize-1; ++z)
     992             :                 startingPoints.emplace_back(start, z, 0.f);
     993             : 
     994             :         // Compute movement array.
     995             :         movement.reserve(std::max(std::abs(windX),std::abs(windY)));
     996             :         while (windX != 0 || windY != 0)
     997           0 :         {
     998             :             std::pair<ssize_t, ssize_t> move = {
     999           0 :                 windX == 0 ? 0 : windX > 0 ? +1 : -1,
    1000           0 :                 windY == 0 ? 0 : windY > 0 ? +1 : -1
    1001             :             };
    1002           0 :             windX -= move.first;
    1003           0 :             windY -= move.second;
    1004             :             movement.push_back(move);
    1005             :         }
    1006             :     }
    1007           0 : 
    1008             :     // We have all starting points ready, move them all until the map is covered.
    1009           0 :     for (SWindPoint& point : startingPoints)
    1010             :     {
    1011             :         // Starting velocity is 1.0 unless in shallow water.
    1012           0 :         m_WindStrength[point.Y * m_MapSize + point.X] = 1.f;
    1013           0 :         float depth = m_WaterHeight - terrain->GetVertexGroundLevel(point.X, point.Y);
    1014           0 :         if (depth > 0.f && depth < 2.f)
    1015             :             m_WindStrength[point.Y * m_MapSize + point.X] = depth / 2.f;
    1016             :         point.windStrength = m_WindStrength[point.Y * m_MapSize + point.X];
    1017             : 
    1018             :         bool onMap = true;
    1019           0 :         while (onMap)
    1020             :             for (size_t step = 0; step < movement.size(); ++step)
    1021           0 :             {
    1022           0 :                 // Move wind speed towards the mean.
    1023             :                 point.windStrength = 0.15f + point.windStrength * 0.85f;
    1024           0 : 
    1025           0 :                 // Adjust speed based on height difference, a positive height difference slowly increases speed (simulate venturi effect)
    1026             :                 // and a lower height reduces speed (wind protection from hills/...)
    1027           0 :                 float heightDiff = std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X + movement[step].first, point.Y + movement[step].second)) -
    1028           0 :                     std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X, point.Y));
    1029             :                 if (heightDiff > 0.f)
    1030             :                     point.windStrength = std::min(2.f, point.windStrength + std::min(4.f, heightDiff) / 40.f);
    1031           0 :                 else
    1032             :                     point.windStrength = std::max(0.f, point.windStrength + std::max(-4.f, heightDiff) / 5.f);
    1033           0 : 
    1034           0 :                 point.X += movement[step].first;
    1035           0 :                 point.Y += movement[step].second;
    1036             : 
    1037           0 :                 if (point.X < 0 || point.X >= static_cast<ssize_t>(m_MapSize) || point.Y < 0 || point.Y >= static_cast<ssize_t>(m_MapSize))
    1038           0 :                 {
    1039             :                     onMap = false;
    1040             :                     break;
    1041             :                 }
    1042             :                 m_WindStrength[point.Y * m_MapSize + point.X] = point.windStrength;
    1043             :             }
    1044           0 :     }
    1045           0 :     // TODO: should perhaps blur a little, or change the above code to incorporate neighboring tiles a bit.
    1046             : }
    1047             : 
    1048           0 : ////////////////////////////////////////////////////////////////////////
    1049             : // TODO: This will always recalculate for now
    1050           0 : void WaterManager::SetMapSize(size_t size)
    1051           0 : {
    1052           0 :     // TODO: Im' blindly trusting the user here.
    1053           0 :     m_MapSize = size;
    1054           0 :     m_NeedInfoUpdate = true;
    1055             :     m_updatei0 = 0;
    1056           0 :     m_updatei1 = size;
    1057             :     m_updatej0 = 0;
    1058           0 :     m_updatej1 = size;
    1059           0 : 
    1060           0 :     SAFE_ARRAY_DELETE(m_DistanceHeightmap);
    1061           0 :     SAFE_ARRAY_DELETE(m_BlurredNormalMap);
    1062           0 :     SAFE_ARRAY_DELETE(m_WindStrength);
    1063             : }
    1064             : 
    1065             : ////////////////////////////////////////////////////////////////////////
    1066           0 : // This will set the bools properly
    1067             : void WaterManager::UpdateQuality()
    1068           0 : {
    1069           0 :     if (g_RenderingOptions.GetWaterEffects() != m_WaterEffects)
    1070           0 :     {
    1071             :         m_WaterEffects = g_RenderingOptions.GetWaterEffects();
    1072           0 :         m_NeedsReloading = true;
    1073           0 :     }
    1074           0 :     if (g_RenderingOptions.GetWaterFancyEffects() != m_WaterFancyEffects)
    1075           0 :     {
    1076             :         m_WaterFancyEffects = g_RenderingOptions.GetWaterFancyEffects();
    1077           0 :         m_NeedsReloading = true;
    1078           0 :     }
    1079             :     if (g_RenderingOptions.GetWaterRealDepth() != m_WaterRealDepth)
    1080             :     {
    1081           0 :         m_WaterRealDepth = g_RenderingOptions.GetWaterRealDepth();
    1082           0 :         m_NeedsReloading = true;
    1083             :     }
    1084           0 :     if (g_RenderingOptions.GetWaterRefraction() != m_WaterRefraction)
    1085           0 :     {
    1086           0 :         m_WaterRefraction = g_RenderingOptions.GetWaterRefraction();
    1087           0 :         m_NeedsReloading = true;
    1088           0 :     }
    1089           0 :     if (g_RenderingOptions.GetWaterReflection() != m_WaterReflection)
    1090           0 :     {
    1091             :         m_WaterReflection = g_RenderingOptions.GetWaterReflection();
    1092             :         m_NeedsReloading = true;
    1093             :     }
    1094             : }
    1095           0 : 
    1096             : bool WaterManager::WillRenderFancyWater()
    1097             : {
    1098           0 :     return m_RenderWater && g_RenderingOptions.GetWaterEffects() && g_Renderer.GetCapabilities().m_PrettyWater;
    1099           0 : }

Generated by: LCOV version 1.13