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: 56 611 9.2 %
Date: 2023-01-19 00:18:29 Functions: 6 26 23.1 %

          Line data    Source code
       1             : /* Copyright (C) 2023 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "graphics/Terrain.h"
      21             : #include "graphics/TextureManager.h"
      22             : #include "graphics/ShaderManager.h"
      23             : #include "graphics/ShaderProgram.h"
      24             : #include "lib/bits.h"
      25             : #include "lib/timer.h"
      26             : #include "maths/MathUtil.h"
      27             : #include "maths/Vector2D.h"
      28             : #include "ps/CLogger.h"
      29             : #include "ps/CStrInternStatic.h"
      30             : #include "ps/Game.h"
      31             : #include "ps/VideoMode.h"
      32             : #include "ps/World.h"
      33             : #include "renderer/backend/IDevice.h"
      34             : #include "renderer/Renderer.h"
      35             : #include "renderer/RenderingOptions.h"
      36             : #include "renderer/SceneRenderer.h"
      37             : #include "renderer/WaterManager.h"
      38             : #include "simulation2/Simulation2.h"
      39             : #include "simulation2/components/ICmpWaterManager.h"
      40             : #include "simulation2/components/ICmpRangeManager.h"
      41             : 
      42             : #include <algorithm>
      43             : 
      44             : struct CoastalPoint
      45             : {
      46           0 :     CoastalPoint(int idx, CVector2D pos) : index(idx), position(pos) {};
      47             :     int index;
      48             :     CVector2D position;
      49             : };
      50             : 
      51           0 : struct SWavesVertex
      52             : {
      53             :     // vertex position
      54             :     CVector3D m_BasePosition;
      55             :     CVector3D m_ApexPosition;
      56             :     CVector3D m_SplashPosition;
      57             :     CVector3D m_RetreatPosition;
      58             : 
      59             :     CVector2D m_PerpVect;
      60             :     float m_UV[2];
      61             : };
      62             : cassert(sizeof(SWavesVertex) == 64);
      63             : 
      64           0 : struct WaveObject
      65             : {
      66             :     CVertexBufferManager::Handle m_VBVertices;
      67             :     CBoundingBoxAligned m_AABB;
      68             :     size_t m_Width;
      69             :     float m_TimeDiff;
      70             : };
      71             : 
      72           6 : WaterManager::WaterManager()
      73             : {
      74             :     // water
      75           6 :     m_RenderWater = false; // disabled until textures are successfully loaded
      76           6 :     m_WaterHeight = 5.0f;
      77             : 
      78           6 :     m_RefTextureSize = 0;
      79             : 
      80           6 :     m_WaterTexTimer = 0.0;
      81             : 
      82           6 :     m_WindAngle = 0.0f;
      83           6 :     m_Waviness = 8.0f;
      84           6 :     m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f);
      85           6 :     m_WaterTint = CColor(0.28f, 0.3f, 0.59f, 1.0f);
      86           6 :     m_Murkiness = 0.45f;
      87           6 :     m_RepeatPeriod = 16.0f;
      88             : 
      89           6 :     m_WaterEffects = true;
      90           6 :     m_WaterFancyEffects = false;
      91           6 :     m_WaterRealDepth = false;
      92           6 :     m_WaterRefraction = false;
      93           6 :     m_WaterReflection = false;
      94           6 :     m_WaterType = L"ocean";
      95             : 
      96           6 :     m_NeedsReloading = false;
      97           6 :     m_NeedInfoUpdate = true;
      98             : 
      99           6 :     m_MapSize = 0;
     100             : 
     101           6 :     m_updatei0 = 0;
     102           6 :     m_updatej0 = 0;
     103           6 :     m_updatei1 = 0;
     104           6 :     m_updatej1 = 0;
     105           6 : }
     106             : 
     107          12 : WaterManager::~WaterManager()
     108             : {
     109             :     // Cleanup if the caller messed up
     110           6 :     UnloadWaterTextures();
     111             : 
     112           6 :     m_ShoreWaves.clear();
     113           6 :     m_ShoreWavesVBIndices.Reset();
     114             : 
     115           6 :     m_DistanceHeightmap.reset();
     116           6 :     m_WindStrength.reset();
     117             : 
     118           6 :     m_FancyEffectsFramebuffer.reset();
     119           6 :     m_FancyEffectsOccludersFramebuffer.reset();
     120           6 :     m_RefractionFramebuffer.reset();
     121           6 :     m_ReflectionFramebuffer.reset();
     122             : 
     123           6 :     m_FancyTexture.reset();
     124           6 :     m_FancyTextureDepth.reset();
     125           6 :     m_ReflFboDepthTexture.reset();
     126           6 :     m_RefrFboDepthTexture.reset();
     127           6 : }
     128             : 
     129           6 : void WaterManager::Initialize()
     130             : {
     131           6 :     const uint32_t stride = sizeof(SWavesVertex);
     132             : 
     133           6 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 6> attributes{{
     134             :         {Renderer::Backend::VertexAttributeStream::POSITION,
     135             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
     136             :             offsetof(SWavesVertex, m_BasePosition), stride,
     137             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     138             :         {Renderer::Backend::VertexAttributeStream::NORMAL,
     139             :             Renderer::Backend::Format::R32G32_SFLOAT,
     140             :             offsetof(SWavesVertex, m_PerpVect), stride,
     141             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     142             :         {Renderer::Backend::VertexAttributeStream::UV0,
     143             :             Renderer::Backend::Format::R32G32_SFLOAT,
     144             :             offsetof(SWavesVertex, m_UV), stride,
     145             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     146             : 
     147             :         {Renderer::Backend::VertexAttributeStream::UV1,
     148             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
     149             :             offsetof(SWavesVertex, m_ApexPosition), stride,
     150             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     151             :         {Renderer::Backend::VertexAttributeStream::UV2,
     152             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
     153             :             offsetof(SWavesVertex, m_SplashPosition), stride,
     154             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     155             :         {Renderer::Backend::VertexAttributeStream::UV3,
     156             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
     157             :             offsetof(SWavesVertex, m_RetreatPosition), stride,
     158             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     159             :     }};
     160           6 :     m_ShoreVertexInputLayout = g_Renderer.GetVertexInputLayout(attributes);
     161           6 : }
     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             :     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.SetAddressMode(
     178             :             Renderer::Backend::Sampler::AddressMode::REPEAT);
     179             : 
     180           0 :         CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
     181           0 :         texture->Prefetch();
     182           0 :         m_WaterTexture[i] = texture;
     183             :     }
     184             : 
     185           0 :     m_RenderWater = true;
     186             : 
     187             :     // Load normalmaps (for fancy water)
     188           0 :     ReloadWaterNormalTextures();
     189             : 
     190             :     // Load CoastalWaves
     191             :     {
     192           0 :         CTextureProperties textureProps(L"art/textures/terrain/types/water/coastalWave.png");
     193           0 :         textureProps.SetAddressMode(
     194             :             Renderer::Backend::Sampler::AddressMode::REPEAT);
     195           0 :         CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
     196           0 :         texture->Prefetch();
     197           0 :         m_WaveTex = texture;
     198             :     }
     199             : 
     200             :     // Load Foam
     201             :     {
     202           0 :         CTextureProperties textureProps(L"art/textures/terrain/types/water/foam.png");
     203           0 :         textureProps.SetAddressMode(
     204             :             Renderer::Backend::Sampler::AddressMode::REPEAT);
     205           0 :         CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
     206           0 :         texture->Prefetch();
     207           0 :         m_FoamTex = texture;
     208             :     }
     209             : 
     210           0 :     RecreateOrLoadTexturesIfNeeded();
     211             : 
     212           0 :     return 0;
     213             : }
     214             : 
     215           0 : void WaterManager::RecreateOrLoadTexturesIfNeeded()
     216             : {
     217           0 :     Renderer::Backend::IDevice* backendDevice = g_VideoMode.GetBackendDevice();
     218             : 
     219             :     // Use screen-sized textures for minimum artifacts.
     220           0 :     const size_t newRefTextureSize = round_up_to_pow2(g_Renderer.GetHeight());
     221             : 
     222           0 :     if (m_RefTextureSize != newRefTextureSize)
     223             :     {
     224           0 :         m_ReflectionFramebuffer.reset();
     225           0 :         m_ReflectionTexture.reset();
     226           0 :         m_ReflFboDepthTexture.reset();
     227             : 
     228           0 :         m_RefractionFramebuffer.reset();
     229           0 :         m_RefractionTexture.reset();
     230           0 :         m_RefrFboDepthTexture.reset();
     231             : 
     232           0 :         m_RefTextureSize = newRefTextureSize;
     233             :     }
     234             : 
     235             :     const Renderer::Backend::Format depthFormat =
     236             :         backendDevice->GetPreferredDepthStencilFormat(
     237             :             Renderer::Backend::ITexture::Usage::SAMPLED |
     238             :                 Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
     239           0 :             true, false);
     240             : 
     241             :     // Create reflection textures.
     242             :     const bool needsReflectionTextures =
     243           0 :         g_RenderingOptions.GetWaterEffects() &&
     244           0 :         g_RenderingOptions.GetWaterReflection();
     245           0 :     if (needsReflectionTextures && !m_ReflectionTexture)
     246             :     {
     247           0 :         m_ReflectionTexture = backendDevice->CreateTexture2D("WaterReflectionTexture",
     248             :             Renderer::Backend::ITexture::Usage::SAMPLED |
     249             :                 Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT,
     250           0 :             Renderer::Backend::Format::R8G8B8A8_UNORM, m_RefTextureSize, m_RefTextureSize,
     251           0 :             Renderer::Backend::Sampler::MakeDefaultSampler(
     252             :                 Renderer::Backend::Sampler::Filter::LINEAR,
     253           0 :                 Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT));
     254             : 
     255           0 :         m_ReflFboDepthTexture = backendDevice->CreateTexture2D("WaterReflectionDepthTexture",
     256             :             Renderer::Backend::ITexture::Usage::SAMPLED |
     257             :                 Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
     258           0 :             depthFormat, m_RefTextureSize, m_RefTextureSize,
     259           0 :             Renderer::Backend::Sampler::MakeDefaultSampler(
     260             :                 Renderer::Backend::Sampler::Filter::NEAREST,
     261           0 :                 Renderer::Backend::Sampler::AddressMode::REPEAT));
     262             : 
     263           0 :         Renderer::Backend::SColorAttachment colorAttachment{};
     264           0 :         colorAttachment.texture = m_ReflectionTexture.get();
     265           0 :         colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
     266           0 :         colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     267           0 :         colorAttachment.clearColor = CColor{0.5f, 0.5f, 1.0f, 0.0f};
     268             : 
     269           0 :         Renderer::Backend::SDepthStencilAttachment depthStencilAttachment{};
     270           0 :         depthStencilAttachment.texture = m_ReflFboDepthTexture.get();
     271           0 :         depthStencilAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
     272           0 :         depthStencilAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     273             : 
     274           0 :         m_ReflectionFramebuffer = backendDevice->CreateFramebuffer("ReflectionFramebuffer",
     275           0 :             &colorAttachment, &depthStencilAttachment);
     276           0 :         if (!m_ReflectionFramebuffer)
     277             :         {
     278           0 :             g_RenderingOptions.SetWaterReflection(false);
     279           0 :             UpdateQuality();
     280             :         }
     281             :     }
     282             : 
     283             :     // Create refraction textures.
     284             :     const bool needsRefractionTextures =
     285           0 :         g_RenderingOptions.GetWaterEffects() &&
     286           0 :         g_RenderingOptions.GetWaterRefraction();
     287           0 :     if (needsRefractionTextures && !m_RefractionTexture)
     288             :     {
     289           0 :         m_RefractionTexture = backendDevice->CreateTexture2D("WaterRefractionTexture",
     290             :             Renderer::Backend::ITexture::Usage::SAMPLED |
     291             :                 Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT,
     292           0 :             Renderer::Backend::Format::R8G8B8A8_UNORM, m_RefTextureSize, m_RefTextureSize,
     293           0 :             Renderer::Backend::Sampler::MakeDefaultSampler(
     294             :                 Renderer::Backend::Sampler::Filter::LINEAR,
     295           0 :                 Renderer::Backend::Sampler::AddressMode::MIRRORED_REPEAT));
     296             : 
     297           0 :         m_RefrFboDepthTexture = backendDevice->CreateTexture2D("WaterRefractionDepthTexture",
     298             :             Renderer::Backend::ITexture::Usage::SAMPLED |
     299             :                 Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
     300           0 :             depthFormat, m_RefTextureSize, m_RefTextureSize,
     301           0 :             Renderer::Backend::Sampler::MakeDefaultSampler(
     302             :                 Renderer::Backend::Sampler::Filter::NEAREST,
     303           0 :                 Renderer::Backend::Sampler::AddressMode::REPEAT));
     304             : 
     305           0 :         Renderer::Backend::SColorAttachment colorAttachment{};
     306           0 :         colorAttachment.texture = m_RefractionTexture.get();
     307           0 :         colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
     308           0 :         colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     309           0 :         colorAttachment.clearColor = CColor{1.0f, 0.0f, 0.0f, 0.0f};
     310             : 
     311           0 :         Renderer::Backend::SDepthStencilAttachment depthStencilAttachment{};
     312           0 :         depthStencilAttachment.texture = m_RefrFboDepthTexture.get();
     313           0 :         depthStencilAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
     314           0 :         depthStencilAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     315             : 
     316           0 :         m_RefractionFramebuffer = backendDevice->CreateFramebuffer("RefractionFramebuffer",
     317           0 :             &colorAttachment, &depthStencilAttachment);
     318           0 :         if (!m_RefractionFramebuffer)
     319             :         {
     320           0 :             g_RenderingOptions.SetWaterRefraction(false);
     321           0 :             UpdateQuality();
     322             :         }
     323             :     }
     324             : 
     325           0 :     const uint32_t newWidth = static_cast<uint32_t>(g_Renderer.GetWidth());
     326           0 :     const uint32_t newHeight = static_cast<uint32_t>(g_Renderer.GetHeight());
     327           0 :     if (m_FancyTexture && (m_FancyTexture->GetWidth() != newWidth || m_FancyTexture->GetHeight() != newHeight))
     328             :     {
     329           0 :         m_FancyEffectsFramebuffer.reset();
     330           0 :         m_FancyEffectsOccludersFramebuffer.reset();
     331           0 :         m_FancyTexture.reset();
     332           0 :         m_FancyTextureDepth.reset();
     333             :     }
     334             : 
     335             :     // Create the Fancy Effects textures.
     336             :     const bool needsFancyTextures =
     337           0 :         g_RenderingOptions.GetWaterEffects() &&
     338           0 :         g_RenderingOptions.GetWaterFancyEffects();
     339           0 :     if (needsFancyTextures && !m_FancyTexture)
     340             :     {
     341           0 :         m_FancyTexture = backendDevice->CreateTexture2D("WaterFancyTexture",
     342             :             Renderer::Backend::ITexture::Usage::SAMPLED |
     343             :                 Renderer::Backend::ITexture::Usage::COLOR_ATTACHMENT,
     344           0 :             Renderer::Backend::Format::R8G8B8A8_UNORM, g_Renderer.GetWidth(), g_Renderer.GetHeight(),
     345           0 :             Renderer::Backend::Sampler::MakeDefaultSampler(
     346             :                 Renderer::Backend::Sampler::Filter::LINEAR,
     347           0 :                 Renderer::Backend::Sampler::AddressMode::REPEAT));
     348             : 
     349           0 :         m_FancyTextureDepth = backendDevice->CreateTexture2D("WaterFancyDepthTexture",
     350             :             Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
     351           0 :             depthFormat, g_Renderer.GetWidth(), g_Renderer.GetHeight(),
     352           0 :             Renderer::Backend::Sampler::MakeDefaultSampler(
     353             :                 Renderer::Backend::Sampler::Filter::LINEAR,
     354           0 :                 Renderer::Backend::Sampler::AddressMode::REPEAT));
     355             : 
     356           0 :         Renderer::Backend::SColorAttachment colorAttachment{};
     357           0 :         colorAttachment.texture = m_FancyTexture.get();
     358           0 :         colorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
     359           0 :         colorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     360           0 :         colorAttachment.clearColor = CColor{0.0f, 0.0f, 0.0f, 0.0f};
     361             : 
     362           0 :         Renderer::Backend::SDepthStencilAttachment depthStencilAttachment{};
     363           0 :         depthStencilAttachment.texture = m_FancyTextureDepth.get();
     364           0 :         depthStencilAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::CLEAR;
     365             :         // We need to store depth for later rendering occluders.
     366           0 :         depthStencilAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     367             : 
     368           0 :         m_FancyEffectsFramebuffer = backendDevice->CreateFramebuffer("FancyEffectsFramebuffer",
     369           0 :             &colorAttachment, &depthStencilAttachment);
     370             : 
     371           0 :         Renderer::Backend::SColorAttachment occludersColorAttachment{};
     372           0 :         occludersColorAttachment.texture = m_FancyTexture.get();
     373           0 :         occludersColorAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::LOAD;
     374           0 :         occludersColorAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::STORE;
     375           0 :         occludersColorAttachment.clearColor = CColor{0.0f, 0.0f, 0.0f, 0.0f};
     376             : 
     377           0 :         Renderer::Backend::SDepthStencilAttachment occludersDepthStencilAttachment{};
     378           0 :         occludersDepthStencilAttachment.texture = m_FancyTextureDepth.get();
     379           0 :         occludersDepthStencilAttachment.loadOp = Renderer::Backend::AttachmentLoadOp::LOAD;
     380           0 :         occludersDepthStencilAttachment.storeOp = Renderer::Backend::AttachmentStoreOp::DONT_CARE;
     381             : 
     382           0 :         m_FancyEffectsOccludersFramebuffer = backendDevice->CreateFramebuffer("FancyEffectsOccludersFramebuffer",
     383           0 :             &occludersColorAttachment, &occludersDepthStencilAttachment);
     384           0 :         if (!m_FancyEffectsFramebuffer || !m_FancyEffectsOccludersFramebuffer)
     385             :         {
     386           0 :             g_RenderingOptions.SetWaterRefraction(false);
     387           0 :             UpdateQuality();
     388             :         }
     389             :     }
     390           0 : }
     391             : 
     392           0 : void WaterManager::ReloadWaterNormalTextures()
     393             : {
     394             :     wchar_t pathname[PATH_MAX];
     395           0 :     for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i)
     396             :     {
     397           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);
     398           0 :         CTextureProperties textureProps(pathname);
     399           0 :         textureProps.SetAddressMode(
     400             :             Renderer::Backend::Sampler::AddressMode::REPEAT);
     401           0 :         textureProps.SetAnisotropicFilter(true);
     402             : 
     403           0 :         CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps);
     404           0 :         texture->Prefetch();
     405           0 :         m_NormalMap[i] = texture;
     406             :     }
     407           0 : }
     408             : 
     409             : ///////////////////////////////////////////////////////////////////
     410             : // Unload water textures
     411           6 : void WaterManager::UnloadWaterTextures()
     412             : {
     413         366 :     for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
     414         360 :         m_WaterTexture[i].reset();
     415             : 
     416         366 :     for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++)
     417         360 :         m_NormalMap[i].reset();
     418             : 
     419           6 :     m_RefractionFramebuffer.reset();
     420           6 :     m_ReflectionFramebuffer.reset();
     421           6 :     m_ReflectionTexture.reset();
     422           6 :     m_RefractionTexture.reset();
     423           6 : }
     424             : 
     425             : template<bool Transpose>
     426           0 : static inline void ComputeDirection(float* distanceMap, const u16* heightmap, float waterHeight, size_t SideSize, size_t maxLevel)
     427             : {
     428             : #define ABOVEWATER(x, z) (HEIGHT_SCALE * heightmap[z*SideSize + x] >= waterHeight)
     429             : #define UPDATELOOKAHEAD \
     430             :     for (; lookahead <= id2+maxLevel && lookahead < SideSize && \
     431             :            ((!Transpose && !ABOVEWATER(lookahead, id1)) || (Transpose && !ABOVEWATER(id1, lookahead))); ++lookahead)
     432             :     // Algorithm:
     433             :     // We want to know the distance to the closest shore point. Go through each line/column,
     434             :     // keep track of when we encountered the last shore point and how far ahead the next one is.
     435           0 :     for (size_t id1 = 0; id1 < SideSize; ++id1)
     436             :     {
     437           0 :         size_t id2 = 0;
     438           0 :         const size_t& x = Transpose ? id1 : id2;
     439           0 :         const size_t& z = Transpose ? id2 : id1;
     440             : 
     441           0 :         size_t level = ABOVEWATER(x, z) ? 0 : maxLevel;
     442           0 :         size_t lookahead = (size_t)(level > 0);
     443             : 
     444           0 :         UPDATELOOKAHEAD;
     445             : 
     446             :         // start moving
     447           0 :         for (; id2 < SideSize; ++id2)
     448             :         {
     449             :             // update current level
     450           0 :             if (ABOVEWATER(x, z))
     451           0 :                 level = 0;
     452             :             else
     453           0 :                 level = std::min(level+1, maxLevel);
     454             : 
     455             :             // move lookahead
     456           0 :             if (lookahead == id2)
     457           0 :                 ++lookahead;
     458           0 :             UPDATELOOKAHEAD;
     459             : 
     460             :             // This is the important bit: set the distance to either:
     461             :             // - the distance to the previous shore point (level)
     462             :             // - the distance to the next shore point (lookahead-id2)
     463           0 :             distanceMap[z*SideSize + x] = std::min(distanceMap[z*SideSize + x], (float)std::min(lookahead-id2, level));
     464             :         }
     465             :     }
     466             : #undef ABOVEWATER
     467             : #undef UPDATELOOKAHEAD
     468           0 : }
     469             : 
     470             : ///////////////////////////////////////////////////////////////////
     471             : // Calculate our binary heightmap from the terrain heightmap.
     472           0 : void WaterManager::RecomputeDistanceHeightmap()
     473             : {
     474           0 :     CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     475           0 :     if (!terrain || !terrain->GetHeightMap())
     476           0 :         return;
     477             : 
     478           0 :     size_t SideSize = m_MapSize;
     479             : 
     480             :     // we want to look ahead some distance, but not too much (less efficient and not interesting). This is our lookahead.
     481           0 :     const size_t maxLevel = 5;
     482             : 
     483           0 :     if (!m_DistanceHeightmap)
     484             :     {
     485           0 :         m_DistanceHeightmap = std::make_unique<float[]>(SideSize * SideSize);
     486           0 :         std::fill(m_DistanceHeightmap.get(), m_DistanceHeightmap.get() + SideSize * SideSize, static_cast<float>(maxLevel));
     487             :     }
     488             : 
     489             :     // Create a manhattan-distance heightmap.
     490             :     // This could be refined to only be done near the coast itself, but it's probably not necessary.
     491             : 
     492           0 :     u16* heightmap = terrain->GetHeightMap();
     493             : 
     494           0 :     ComputeDirection<false>(m_DistanceHeightmap.get(), heightmap, m_WaterHeight, SideSize, maxLevel);
     495           0 :     ComputeDirection<true>(m_DistanceHeightmap.get(), heightmap, m_WaterHeight, SideSize, maxLevel);
     496             : }
     497             : 
     498             : // This requires m_DistanceHeightmap to be defined properly.
     499           0 : void WaterManager::CreateWaveMeshes()
     500             : {
     501           0 :     if (m_MapSize == 0)
     502           0 :         return;
     503             : 
     504           0 :     CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     505           0 :     if (!terrain || !terrain->GetHeightMap())
     506           0 :         return;
     507             : 
     508           0 :     m_ShoreWaves.clear();
     509           0 :     m_ShoreWavesVBIndices.Reset();
     510             : 
     511           0 :     if (m_Waviness < 5.0f && m_WaterType != L"ocean")
     512           0 :         return;
     513             : 
     514           0 :     size_t SideSize = m_MapSize;
     515             : 
     516             :     // First step: get the points near the coast.
     517           0 :     std::set<int> CoastalPointsSet;
     518           0 :     for (size_t z = 1; z < SideSize-1; ++z)
     519           0 :         for (size_t x = 1; x < SideSize-1; ++x)
     520             :             // get the points not on the shore but near it, ocean-side
     521           0 :             if (m_DistanceHeightmap[z*m_MapSize + x] > 0.5f && m_DistanceHeightmap[z*m_MapSize + x] < 1.5f)
     522           0 :                 CoastalPointsSet.insert((z)*SideSize + x);
     523             : 
     524             :     // Second step: create chains out of those coastal points.
     525             :     static const int around[8][2] = { { -1,-1 }, { -1,0 }, { -1,1 }, { 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 } };
     526             : 
     527           0 :     std::vector<std::deque<CoastalPoint> > CoastalPointsChains;
     528           0 :     while (!CoastalPointsSet.empty())
     529             :     {
     530           0 :         int index = *(CoastalPointsSet.begin());
     531           0 :         int x = index % SideSize;
     532           0 :         int y = (index - x ) / SideSize;
     533             : 
     534           0 :         std::deque<CoastalPoint> Chain;
     535             : 
     536           0 :         Chain.push_front(CoastalPoint(index,CVector2D(x*4,y*4)));
     537             : 
     538             :         // Erase us.
     539           0 :         CoastalPointsSet.erase(CoastalPointsSet.begin());
     540             : 
     541             :         // We're our starter points. At most we can have 2 points close to us.
     542             :         // We'll pick the first one and look for its neighbors (he can only have one new)
     543             :         // Up until we either reach the end of the chain, or ourselves.
     544             :         // Then go down the other direction if there is any.
     545           0 :         int neighbours[2] = { -1, -1 };
     546           0 :         int nbNeighb = 0;
     547           0 :         for (int i = 0; i < 8; ++i)
     548             :         {
     549           0 :             if (CoastalPointsSet.count(x + around[i][0] + (y + around[i][1])*SideSize))
     550             :             {
     551           0 :                 if (nbNeighb < 2)
     552           0 :                     neighbours[nbNeighb] = x + around[i][0] + (y + around[i][1])*SideSize;
     553           0 :                 ++nbNeighb;
     554             :             }
     555             :         }
     556           0 :         if (nbNeighb > 2)
     557           0 :             continue;
     558             : 
     559           0 :         for (int i = 0; i < 2; ++i)
     560             :         {
     561           0 :             if (neighbours[i] == -1)
     562           0 :                 continue;
     563             :             // Move to our neighboring point
     564           0 :             int xx = neighbours[i] % SideSize;
     565           0 :             int yy = (neighbours[i] - xx ) / SideSize;
     566           0 :             int indexx = xx + yy*SideSize;
     567           0 :             int endedChain = false;
     568             : 
     569           0 :             if (i == 0)
     570           0 :                 Chain.push_back(CoastalPoint(indexx,CVector2D(xx*4,yy*4)));
     571             :             else
     572           0 :                 Chain.push_front(CoastalPoint(indexx,CVector2D(xx*4,yy*4)));
     573             : 
     574             :             // If there's a loop we'll be the "other" neighboring point already so check for that.
     575             :             // We'll readd at the end/front the other one to have full squares.
     576           0 :             if (CoastalPointsSet.count(indexx) == 0)
     577           0 :                 break;
     578             : 
     579           0 :             CoastalPointsSet.erase(indexx);
     580             : 
     581             :             // Start checking from there.
     582           0 :             while(!endedChain)
     583             :             {
     584           0 :                 bool found = false;
     585           0 :                 nbNeighb = 0;
     586           0 :                 for (int p = 0; p < 8; ++p)
     587             :                 {
     588           0 :                     if (CoastalPointsSet.count(xx+around[p][0] + (yy + around[p][1])*SideSize))
     589             :                     {
     590           0 :                         if (nbNeighb >= 2)
     591             :                         {
     592           0 :                             CoastalPointsSet.erase(xx + yy*SideSize);
     593           0 :                             continue;
     594             :                         }
     595           0 :                         ++nbNeighb;
     596             :                         // We've found a new point around us.
     597             :                         // Move there
     598           0 :                         xx = xx + around[p][0];
     599           0 :                         yy = yy + around[p][1];
     600           0 :                         indexx = xx + yy*SideSize;
     601           0 :                         if (i == 0)
     602           0 :                             Chain.push_back(CoastalPoint(indexx,CVector2D(xx*4,yy*4)));
     603             :                         else
     604           0 :                             Chain.push_front(CoastalPoint(indexx,CVector2D(xx*4,yy*4)));
     605           0 :                         CoastalPointsSet.erase(xx + yy*SideSize);
     606           0 :                         found = true;
     607           0 :                         break;
     608             :                     }
     609             :                 }
     610           0 :                 if (!found)
     611           0 :                     endedChain = true;
     612             :             }
     613             :         }
     614           0 :         if (Chain.size() > 10)
     615           0 :             CoastalPointsChains.push_back(Chain);
     616             :     }
     617             : 
     618             :     // (optional) third step: Smooth chains out.
     619             :     // This is also really dumb.
     620           0 :     for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
     621             :     {
     622             :         // Bump 1 for smoother.
     623           0 :         for (int p = 0; p < 3; ++p)
     624             :         {
     625           0 :             for (size_t j = 1; j < CoastalPointsChains[i].size()-1; ++j)
     626             :             {
     627           0 :                 CVector2D realPos = CoastalPointsChains[i][j-1].position + CoastalPointsChains[i][j+1].position;
     628             : 
     629           0 :                 CoastalPointsChains[i][j].position = (CoastalPointsChains[i][j].position + realPos/2.0f)/2.0f;
     630             :             }
     631             :         }
     632             :     }
     633             : 
     634             :     // Fourth step: create waves themselves, using those chains. We basically create subchains.
     635           0 :     u16 waveSizes = 14; // maximal size in width.
     636             : 
     637             :     // Construct indices buffer (we can afford one for all of them)
     638           0 :     std::vector<u16> water_indices;
     639           0 :     for (u16 a = 0; a < waveSizes - 1; ++a)
     640             :     {
     641           0 :         for (u16 rect = 0; rect < 7; ++rect)
     642             :         {
     643           0 :             water_indices.push_back(a * 9 + rect);
     644           0 :             water_indices.push_back(a * 9 + 9 + rect);
     645           0 :             water_indices.push_back(a * 9 + 1 + rect);
     646           0 :             water_indices.push_back(a * 9 + 9 + rect);
     647           0 :             water_indices.push_back(a * 9 + 10 + rect);
     648           0 :             water_indices.push_back(a * 9 + 1 + rect);
     649             :         }
     650             :     }
     651             :     // Generic indexes, max-length
     652           0 :     m_ShoreWavesVBIndices = g_VBMan.AllocateChunk(
     653             :         sizeof(u16), water_indices.size(),
     654             :         Renderer::Backend::IBuffer::Type::INDEX, false,
     655           0 :         nullptr, CVertexBufferManager::Group::WATER);
     656           0 :     m_ShoreWavesVBIndices->m_Owner->UpdateChunkVertices(m_ShoreWavesVBIndices.Get(), &water_indices[0]);
     657             : 
     658           0 :     float diff = (rand() % 50) / 5.0f;
     659             : 
     660           0 :     std::vector<SWavesVertex> vertices, reversed;
     661           0 :     for (size_t i = 0; i < CoastalPointsChains.size(); ++i)
     662             :     {
     663           0 :         for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j)
     664             :         {
     665           0 :             if (CoastalPointsChains[i].size()- 1 - j < waveSizes)
     666           0 :                 break;
     667             : 
     668           0 :             u16 width = waveSizes;
     669             : 
     670             :             // First pass to get some parameters out.
     671           0 :             float outmost = 0.0f;   // how far to move on the shore.
     672           0 :             float avgDepth = 0.0f;
     673           0 :             int sign = 1;
     674           0 :             CVector2D firstPerp(0,0), perp(0,0), lastPerp(0,0);
     675           0 :             for (u16 a = 0; a < waveSizes;++a)
     676             :             {
     677           0 :                 lastPerp = perp;
     678           0 :                 perp = CVector2D(0,0);
     679           0 :                 int nb = 0;
     680           0 :                 CVector2D pos = CoastalPointsChains[i][j+a].position;
     681           0 :                 CVector2D posPlus;
     682           0 :                 CVector2D posMinus;
     683           0 :                 if (a > 0)
     684             :                 {
     685           0 :                     ++nb;
     686           0 :                     posMinus = CoastalPointsChains[i][j+a-1].position;
     687           0 :                     perp += pos-posMinus;
     688             :                 }
     689           0 :                 if (a < waveSizes-1)
     690             :                 {
     691           0 :                     ++nb;
     692           0 :                     posPlus = CoastalPointsChains[i][j+a+1].position;
     693           0 :                     perp += posPlus-pos;
     694             :                 }
     695           0 :                 perp /= nb;
     696           0 :                 perp = CVector2D(-perp.Y,perp.X).Normalized();
     697             : 
     698           0 :                 if (a == 0)
     699           0 :                     firstPerp = perp;
     700             : 
     701           0 :                 if ( a > 1 && perp.Dot(lastPerp) < 0.90f && perp.Dot(firstPerp) < 0.70f)
     702             :                 {
     703           0 :                     width = a+1;
     704           0 :                     break;
     705             :                 }
     706             : 
     707           0 :                 if (terrain->GetExactGroundLevel(pos.X+perp.X*1.5f, pos.Y+perp.Y*1.5f) > m_WaterHeight)
     708           0 :                     sign = -1;
     709             : 
     710           0 :                 avgDepth += terrain->GetExactGroundLevel(pos.X+sign*perp.X*20.0f, pos.Y+sign*perp.Y*20.0f) - m_WaterHeight;
     711             : 
     712           0 :                 float localOutmost = -2.0f;
     713           0 :                 while (localOutmost < 0.0f)
     714             :                 {
     715           0 :                     float depth = terrain->GetExactGroundLevel(pos.X+sign*perp.X*localOutmost, pos.Y+sign*perp.Y*localOutmost) - m_WaterHeight;
     716           0 :                     if (depth < 0.0f || depth > 0.6f)
     717           0 :                         localOutmost += 0.2f;
     718             :                     else
     719             :                         break;
     720             :                 }
     721             : 
     722           0 :                 outmost += localOutmost;
     723             :             }
     724           0 :             if (width < 5)
     725             :             {
     726           0 :                 j += 6;
     727           0 :                 continue;
     728             :             }
     729             : 
     730           0 :             outmost /= width;
     731             : 
     732           0 :             if (outmost > -0.5f)
     733             :             {
     734           0 :                 j += 3;
     735           0 :                 continue;
     736             :             }
     737           0 :             outmost = -2.5f + outmost * m_Waviness/10.0f;
     738             : 
     739           0 :             avgDepth /= width;
     740             : 
     741           0 :             if (avgDepth > -1.3f)
     742             :             {
     743           0 :                 j += 3;
     744           0 :                 continue;
     745             :             }
     746             :             // we passed the checks, we can create a wave of size "width".
     747             : 
     748           0 :             std::unique_ptr<WaveObject> shoreWave = std::make_unique<WaveObject>();
     749           0 :             vertices.clear();
     750           0 :             vertices.reserve(9 * width);
     751             : 
     752           0 :             shoreWave->m_Width = width;
     753           0 :             shoreWave->m_TimeDiff = diff;
     754           0 :             diff += (rand() % 100) / 25.0f + 4.0f;
     755             : 
     756           0 :             for (u16 a = 0; a < width;++a)
     757             :             {
     758           0 :                 perp = CVector2D(0,0);
     759           0 :                 int nb = 0;
     760           0 :                 CVector2D pos = CoastalPointsChains[i][j+a].position;
     761           0 :                 CVector2D posPlus;
     762           0 :                 CVector2D posMinus;
     763           0 :                 if (a > 0)
     764             :                 {
     765           0 :                     ++nb;
     766           0 :                     posMinus = CoastalPointsChains[i][j+a-1].position;
     767           0 :                     perp += pos-posMinus;
     768             :                 }
     769           0 :                 if (a < waveSizes-1)
     770             :                 {
     771           0 :                     ++nb;
     772           0 :                     posPlus = CoastalPointsChains[i][j+a+1].position;
     773           0 :                     perp += posPlus-pos;
     774             :                 }
     775           0 :                 perp /= nb;
     776           0 :                 perp = CVector2D(-perp.Y,perp.X).Normalized();
     777             : 
     778           0 :                 SWavesVertex point[9];
     779             : 
     780           0 :                 float baseHeight = 0.04f;
     781             : 
     782           0 :                 float halfWidth = (width-1.0f)/2.0f;
     783           0 :                 float sideNess = sqrtf(Clamp( (halfWidth - fabsf(a - halfWidth)) / 3.0f, 0.0f, 1.0f));
     784             : 
     785           0 :                 point[0].m_UV[0] = a; point[0].m_UV[1] = 8;
     786           0 :                 point[1].m_UV[0] = a; point[1].m_UV[1] = 7;
     787           0 :                 point[2].m_UV[0] = a; point[2].m_UV[1] = 6;
     788           0 :                 point[3].m_UV[0] = a; point[3].m_UV[1] = 5;
     789           0 :                 point[4].m_UV[0] = a; point[4].m_UV[1] = 4;
     790           0 :                 point[5].m_UV[0] = a; point[5].m_UV[1] = 3;
     791           0 :                 point[6].m_UV[0] = a; point[6].m_UV[1] = 2;
     792           0 :                 point[7].m_UV[0] = a; point[7].m_UV[1] = 1;
     793           0 :                 point[8].m_UV[0] = a; point[8].m_UV[1] = 0;
     794             : 
     795           0 :                 point[0].m_PerpVect = perp;
     796           0 :                 point[1].m_PerpVect = perp;
     797           0 :                 point[2].m_PerpVect = perp;
     798           0 :                 point[3].m_PerpVect = perp;
     799           0 :                 point[4].m_PerpVect = perp;
     800           0 :                 point[5].m_PerpVect = perp;
     801           0 :                 point[6].m_PerpVect = perp;
     802           0 :                 point[7].m_PerpVect = perp;
     803           0 :                 point[8].m_PerpVect = perp;
     804             : 
     805             :                 static const float perpT1[9] = { 6.0f, 6.05f, 6.1f, 6.2f, 6.3f, 6.4f, 6.5f, 6.6f, 9.7f };
     806             :                 static const float perpT2[9] = { 2.0f, 2.1f,  2.2f, 2.3f, 2.4f, 3.0f, 3.3f, 3.6f, 9.5f };
     807             :                 static const float perpT3[9] = { 1.1f, 0.7f, -0.2f, 0.0f, 0.6f, 1.3f, 2.2f, 3.6f, 9.0f };
     808             :                 static const float perpT4[9] = { 2.0f, 2.1f,  1.2f, 1.5f, 1.7f, 1.9f, 2.7f, 3.8f, 9.0f };
     809             : 
     810             :                 static const float heightT1[9] = { 0.0f, 0.2f, 0.5f, 0.8f, 0.9f, 0.85f, 0.6f, 0.2f, 0.0 };
     811             :                 static const float heightT2[9] = { -0.8f, -0.4f, 0.0f, 0.1f, 0.1f, 0.03f, 0.0f, 0.0f, 0.0 };
     812             :                 static const float heightT3[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0 };
     813             : 
     814           0 :                 for (size_t t = 0; t < 9; ++t)
     815             :                 {
     816           0 :                     float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost),
     817           0 :                                                                             pos.Y+sign*perp.Y*(perpT1[t]+outmost));
     818           0 :                     point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight),
     819           0 :                                                         pos.Y+sign*perp.Y*(perpT1[t]+outmost));
     820             :                 }
     821           0 :                 for (size_t t = 0; t < 9; ++t)
     822             :                 {
     823           0 :                     float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost),
     824           0 :                                                                             pos.Y+sign*perp.Y*(perpT2[t]+outmost));
     825           0 :                     point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight),
     826           0 :                                                         pos.Y+sign*perp.Y*(perpT2[t]+outmost));
     827             :                 }
     828           0 :                 for (size_t t = 0; t < 9; ++t)
     829             :                 {
     830           0 :                     float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess),
     831           0 :                                                                             pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess));
     832           0 :                     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));
     833             :                 }
     834           0 :                 for (size_t t = 0; t < 9; ++t)
     835             :                 {
     836           0 :                     float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost),
     837           0 :                                                                             pos.Y+sign*perp.Y*(perpT4[t]+outmost));
     838           0 :                     point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterHeight,terrHeight),
     839           0 :                                                            pos.Y+sign*perp.Y*(perpT4[t]+outmost));
     840             :                 }
     841             : 
     842           0 :                 vertices.push_back(point[8]);
     843           0 :                 vertices.push_back(point[7]);
     844           0 :                 vertices.push_back(point[6]);
     845           0 :                 vertices.push_back(point[5]);
     846           0 :                 vertices.push_back(point[4]);
     847           0 :                 vertices.push_back(point[3]);
     848           0 :                 vertices.push_back(point[2]);
     849           0 :                 vertices.push_back(point[1]);
     850           0 :                 vertices.push_back(point[0]);
     851             : 
     852           0 :                 shoreWave->m_AABB += point[8].m_SplashPosition;
     853           0 :                 shoreWave->m_AABB += point[8].m_BasePosition;
     854           0 :                 shoreWave->m_AABB += point[0].m_SplashPosition;
     855           0 :                 shoreWave->m_AABB += point[0].m_BasePosition;
     856           0 :                 shoreWave->m_AABB += point[4].m_ApexPosition;
     857             :             }
     858             : 
     859           0 :             if (sign == 1)
     860             :             {
     861             :                 // Let's do some fancy reversing.
     862           0 :                 reversed.clear();
     863           0 :                 reversed.reserve(vertices.size());
     864           0 :                 for (int a = width - 1; a >= 0; --a)
     865             :                 {
     866           0 :                     for (size_t t = 0; t < 9; ++t)
     867           0 :                         reversed.push_back(vertices[a * 9 + t]);
     868             :                 }
     869           0 :                 std::swap(vertices, reversed);
     870             :             }
     871           0 :             j += width/2-1;
     872             : 
     873           0 :             shoreWave->m_VBVertices = g_VBMan.AllocateChunk(
     874             :                 sizeof(SWavesVertex), vertices.size(),
     875             :                 Renderer::Backend::IBuffer::Type::VERTEX, false,
     876           0 :                 nullptr, CVertexBufferManager::Group::WATER);
     877           0 :             shoreWave->m_VBVertices->m_Owner->UpdateChunkVertices(shoreWave->m_VBVertices.Get(), &vertices[0]);
     878             : 
     879           0 :             m_ShoreWaves.emplace_back(std::move(shoreWave));
     880             :         }
     881             :     }
     882             : }
     883             : 
     884           0 : void WaterManager::RenderWaves(
     885             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     886             :     const CFrustum& frustrum)
     887             : {
     888           0 :     if (!m_WaterFancyEffects)
     889           0 :         return;
     890             : 
     891           0 :     m_WaveTex->UploadBackendTextureIfNeeded(deviceCommandContext);
     892           0 :     m_FoamTex->UploadBackendTextureIfNeeded(deviceCommandContext);
     893             : 
     894           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render Waves");
     895             : 
     896             :     Renderer::Backend::IFramebuffer* framebuffer =
     897           0 :         m_FancyEffectsFramebuffer.get();
     898           0 :     deviceCommandContext->BeginFramebufferPass(framebuffer);
     899           0 :     Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
     900           0 :     viewportRect.width = framebuffer->GetWidth();
     901           0 :     viewportRect.height = framebuffer->GetHeight();
     902           0 :     deviceCommandContext->SetViewports(1, &viewportRect);
     903             : 
     904           0 :     CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_water_waves);
     905           0 :     deviceCommandContext->SetGraphicsPipelineState(
     906           0 :         tech->GetGraphicsPipelineState());
     907           0 :     deviceCommandContext->BeginPass();
     908           0 :     Renderer::Backend::IShaderProgram* shader = tech->GetShader();
     909             : 
     910           0 :     deviceCommandContext->SetTexture(
     911           0 :         shader->GetBindingSlot(str_waveTex), m_WaveTex->GetBackendTexture());
     912           0 :     deviceCommandContext->SetTexture(
     913           0 :         shader->GetBindingSlot(str_foamTex), m_FoamTex->GetBackendTexture());
     914             : 
     915           0 :     deviceCommandContext->SetUniform(
     916           0 :         shader->GetBindingSlot(str_time), static_cast<float>(m_WaterTexTimer));
     917             :     const CMatrix3D transform =
     918           0 :         g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
     919           0 :     deviceCommandContext->SetUniform(
     920           0 :         shader->GetBindingSlot(str_transform), transform.AsFloatArray());
     921             : 
     922           0 :     for (size_t a = 0; a < m_ShoreWaves.size(); ++a)
     923             :     {
     924           0 :         if (!frustrum.IsBoxVisible(m_ShoreWaves[a]->m_AABB))
     925           0 :             continue;
     926             : 
     927           0 :         CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBVertices.Get();
     928           0 :         ENSURE(!VBchunk->m_Owner->GetBuffer()->IsDynamic());
     929           0 :         ENSURE(!m_ShoreWavesVBIndices->m_Owner->GetBuffer()->IsDynamic());
     930             : 
     931           0 :         const uint32_t stride = sizeof(SWavesVertex);
     932           0 :         const uint32_t firstVertexOffset = VBchunk->m_Index * stride;
     933             : 
     934           0 :         deviceCommandContext->SetVertexInputLayout(m_ShoreVertexInputLayout);
     935             : 
     936           0 :         deviceCommandContext->SetUniform(
     937           0 :             shader->GetBindingSlot(str_translation), m_ShoreWaves[a]->m_TimeDiff);
     938           0 :         deviceCommandContext->SetUniform(
     939           0 :             shader->GetBindingSlot(str_width), static_cast<float>(m_ShoreWaves[a]->m_Width));
     940             : 
     941           0 :         deviceCommandContext->SetVertexBuffer(
     942           0 :             0, VBchunk->m_Owner->GetBuffer(), firstVertexOffset);
     943           0 :         deviceCommandContext->SetIndexBuffer(m_ShoreWavesVBIndices->m_Owner->GetBuffer());
     944             : 
     945           0 :         const uint32_t indexCount = (m_ShoreWaves[a]->m_Width - 1) * (7 * 6);
     946           0 :         deviceCommandContext->DrawIndexed(m_ShoreWavesVBIndices->m_Index, indexCount, 0);
     947             : 
     948           0 :         g_Renderer.GetStats().m_DrawCalls++;
     949           0 :         g_Renderer.GetStats().m_WaterTris += indexCount / 3;
     950             :     }
     951           0 :     deviceCommandContext->EndPass();
     952           0 :     deviceCommandContext->EndFramebufferPass();
     953             : }
     954             : 
     955           0 : void WaterManager::RecomputeWaterData()
     956             : {
     957           0 :     if (!m_MapSize)
     958           0 :         return;
     959             : 
     960           0 :     RecomputeDistanceHeightmap();
     961           0 :     RecomputeWindStrength();
     962           0 :     CreateWaveMeshes();
     963             : }
     964             : 
     965             : ///////////////////////////////////////////////////////////////////
     966             : // Calculate the strength of the wind at a given point on the map.
     967           0 : void WaterManager::RecomputeWindStrength()
     968             : {
     969           0 :     if (m_MapSize <= 0)
     970           0 :         return;
     971             : 
     972           0 :     if (!m_WindStrength)
     973           0 :         m_WindStrength = std::make_unique<float[]>(m_MapSize * m_MapSize);
     974             : 
     975           0 :     CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
     976           0 :     if (!terrain || !terrain->GetHeightMap())
     977           0 :         return;
     978             : 
     979           0 :     CVector2D windDir = CVector2D(cos(m_WindAngle), sin(m_WindAngle));
     980             : 
     981           0 :     int stepSize = 10;
     982           0 :     ssize_t windX = -round(stepSize * windDir.X);
     983           0 :     ssize_t windY = -round(stepSize * windDir.Y);
     984             : 
     985             :     struct SWindPoint {
     986           0 :         SWindPoint(size_t x, size_t y, float strength) : X(x), Y(y), windStrength(strength) {}
     987             :         ssize_t X;
     988             :         ssize_t Y;
     989             :         float windStrength;
     990             :     };
     991             : 
     992           0 :     std::vector<SWindPoint> startingPoints;
     993           0 :     std::vector<std::pair<int, int>> movement; // Every increment, move each starting point by all of these.
     994             : 
     995             :     // Compute starting points (one or two edges of the map) and how much to move each computation increment.
     996           0 :     if (fabs(windDir.X) < 0.01f)
     997             :     {
     998           0 :         movement.emplace_back(0, windY > 0.f ? 1 : -1);
     999           0 :         startingPoints.reserve(m_MapSize);
    1000           0 :         size_t start = windY > 0 ? 0 : m_MapSize - 1;
    1001           0 :         for (size_t x = 0; x < m_MapSize; ++x)
    1002           0 :             startingPoints.emplace_back(x, start, 0.f);
    1003             :     }
    1004           0 :     else if (fabs(windDir.Y) < 0.01f)
    1005             :     {
    1006           0 :         movement.emplace_back(windX > 0.f ? 1 : - 1, 0);
    1007           0 :         startingPoints.reserve(m_MapSize);
    1008           0 :         size_t start = windX > 0 ? 0 : m_MapSize - 1;
    1009           0 :         for (size_t z = 0; z < m_MapSize; ++z)
    1010           0 :             startingPoints.emplace_back(start, z, 0.f);
    1011             :     }
    1012             :     else
    1013             :     {
    1014           0 :         startingPoints.reserve(m_MapSize * 2);
    1015             :         // Points along X.
    1016           0 :         size_t start = windY > 0 ? 0 : m_MapSize - 1;
    1017           0 :         for (size_t x = 0; x < m_MapSize; ++x)
    1018           0 :             startingPoints.emplace_back(x, start, 0.f);
    1019             :         // Points along Z, avoid repeating the corner point.
    1020           0 :         start = windX > 0 ? 0 : m_MapSize - 1;
    1021           0 :         if (windY > 0)
    1022           0 :             for (size_t z = 1; z < m_MapSize; ++z)
    1023           0 :                 startingPoints.emplace_back(start, z, 0.f);
    1024             :         else
    1025           0 :             for (size_t z = 0; z < m_MapSize-1; ++z)
    1026           0 :                 startingPoints.emplace_back(start, z, 0.f);
    1027             : 
    1028             :         // Compute movement array.
    1029           0 :         movement.reserve(std::max(std::abs(windX),std::abs(windY)));
    1030           0 :         while (windX != 0 || windY != 0)
    1031             :         {
    1032             :             std::pair<ssize_t, ssize_t> move = {
    1033           0 :                 windX == 0 ? 0 : windX > 0 ? +1 : -1,
    1034           0 :                 windY == 0 ? 0 : windY > 0 ? +1 : -1
    1035           0 :             };
    1036           0 :             windX -= move.first;
    1037           0 :             windY -= move.second;
    1038           0 :             movement.push_back(move);
    1039             :         }
    1040             :     }
    1041             : 
    1042             :     // We have all starting points ready, move them all until the map is covered.
    1043           0 :     for (SWindPoint& point : startingPoints)
    1044             :     {
    1045             :         // Starting velocity is 1.0 unless in shallow water.
    1046           0 :         m_WindStrength[point.Y * m_MapSize + point.X] = 1.f;
    1047           0 :         float depth = m_WaterHeight - terrain->GetVertexGroundLevel(point.X, point.Y);
    1048           0 :         if (depth > 0.f && depth < 2.f)
    1049           0 :             m_WindStrength[point.Y * m_MapSize + point.X] = depth / 2.f;
    1050           0 :         point.windStrength = m_WindStrength[point.Y * m_MapSize + point.X];
    1051             : 
    1052           0 :         bool onMap = true;
    1053           0 :         while (onMap)
    1054           0 :             for (size_t step = 0; step < movement.size(); ++step)
    1055             :             {
    1056             :                 // Move wind speed towards the mean.
    1057           0 :                 point.windStrength = 0.15f + point.windStrength * 0.85f;
    1058             : 
    1059             :                 // Adjust speed based on height difference, a positive height difference slowly increases speed (simulate venturi effect)
    1060             :                 // and a lower height reduces speed (wind protection from hills/...)
    1061           0 :                 float heightDiff = std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X + movement[step].first, point.Y + movement[step].second)) -
    1062           0 :                     std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X, point.Y));
    1063           0 :                 if (heightDiff > 0.f)
    1064           0 :                     point.windStrength = std::min(2.f, point.windStrength + std::min(4.f, heightDiff) / 40.f);
    1065             :                 else
    1066           0 :                     point.windStrength = std::max(0.f, point.windStrength + std::max(-4.f, heightDiff) / 5.f);
    1067             : 
    1068           0 :                 point.X += movement[step].first;
    1069           0 :                 point.Y += movement[step].second;
    1070             : 
    1071           0 :                 if (point.X < 0 || point.X >= static_cast<ssize_t>(m_MapSize) || point.Y < 0 || point.Y >= static_cast<ssize_t>(m_MapSize))
    1072             :                 {
    1073           0 :                     onMap = false;
    1074           0 :                     break;
    1075             :                 }
    1076           0 :                 m_WindStrength[point.Y * m_MapSize + point.X] = point.windStrength;
    1077             :             }
    1078             :     }
    1079             :     // TODO: should perhaps blur a little, or change the above code to incorporate neighboring tiles a bit.
    1080             : }
    1081             : 
    1082             : ////////////////////////////////////////////////////////////////////////
    1083             : // TODO: This will always recalculate for now
    1084           0 : void WaterManager::SetMapSize(size_t size)
    1085             : {
    1086             :     // TODO: Im' blindly trusting the user here.
    1087           0 :     m_MapSize = size;
    1088           0 :     m_NeedInfoUpdate = true;
    1089           0 :     m_updatei0 = 0;
    1090           0 :     m_updatei1 = size;
    1091           0 :     m_updatej0 = 0;
    1092           0 :     m_updatej1 = size;
    1093             : 
    1094           0 :     m_DistanceHeightmap.reset();
    1095           0 :     m_WindStrength.reset();
    1096           0 : }
    1097             : 
    1098             : ////////////////////////////////////////////////////////////////////////
    1099             : // This will set the bools properly
    1100           0 : void WaterManager::UpdateQuality()
    1101             : {
    1102           0 :     if (g_RenderingOptions.GetWaterEffects() != m_WaterEffects)
    1103             :     {
    1104           0 :         m_WaterEffects = g_RenderingOptions.GetWaterEffects();
    1105           0 :         m_NeedsReloading = true;
    1106             :     }
    1107           0 :     if (g_RenderingOptions.GetWaterFancyEffects() != m_WaterFancyEffects)
    1108             :     {
    1109           0 :         m_WaterFancyEffects = g_RenderingOptions.GetWaterFancyEffects();
    1110           0 :         m_NeedsReloading = true;
    1111             :     }
    1112           0 :     if (g_RenderingOptions.GetWaterRealDepth() != m_WaterRealDepth)
    1113             :     {
    1114           0 :         m_WaterRealDepth = g_RenderingOptions.GetWaterRealDepth();
    1115           0 :         m_NeedsReloading = true;
    1116             :     }
    1117           0 :     if (g_RenderingOptions.GetWaterRefraction() != m_WaterRefraction)
    1118             :     {
    1119           0 :         m_WaterRefraction = g_RenderingOptions.GetWaterRefraction();
    1120           0 :         m_NeedsReloading = true;
    1121             :     }
    1122           0 :     if (g_RenderingOptions.GetWaterReflection() != m_WaterReflection)
    1123             :     {
    1124           0 :         m_WaterReflection = g_RenderingOptions.GetWaterReflection();
    1125           0 :         m_NeedsReloading = true;
    1126             :     }
    1127           0 : }
    1128             : 
    1129           0 : bool WaterManager::WillRenderFancyWater() const
    1130             : {
    1131             :     return
    1132           0 :         m_RenderWater && g_VideoMode.GetBackendDevice()->GetBackend() != Renderer::Backend::Backend::GL_ARB &&
    1133           0 :         g_RenderingOptions.GetWaterEffects();
    1134             : }
    1135             : 
    1136           0 : size_t WaterManager::GetCurrentTextureIndex(const double& period) const
    1137             : {
    1138           0 :     ENSURE(period > 0.0);
    1139           0 :     return static_cast<size_t>(m_WaterTexTimer * ARRAY_SIZE(m_WaterTexture) / period) % ARRAY_SIZE(m_WaterTexture);
    1140             : }
    1141             : 
    1142           0 : size_t WaterManager::GetNextTextureIndex(const double& period) const
    1143             : {
    1144           0 :     ENSURE(period > 0.0);
    1145           0 :     return (GetCurrentTextureIndex(period) + 1) % ARRAY_SIZE(m_WaterTexture);
    1146           3 : }

Generated by: LCOV version 1.13