Index: source/ps/ranges/iota_view.h =================================================================== --- /dev/null +++ source/ps/ranges/iota_view.h @@ -0,0 +1,105 @@ +/* Copyright (C) 2022 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +// limited implementation of C++20 std::views::iota +// extend if needed: decrement, more comparisons... + +#if __has_include() +#include +#ifdef __cpp_lib_ranges +#if __cpp_lib_ranges >= 201911 +#define CPP20_DEPRECATED deprecated("replace with std::views::iota") +#endif +#endif +#endif + +#ifndef CPP20_DEPRECATED +#define CPP20_DEPRECATED +#endif + + namespace PS{ + +template +class [[CPP20_DEPRECATED]] iota +{ +public: + class Iterator + { + public: + explicit Iterator(T begin) + : m_iter(begin) + {} + + T& operator * () + { + return m_iter; + } + + Iterator& operator ++ () // ++it + { + ++m_iter; + return *this; + } + + Iterator operator ++ (int) // it++ + { + Iterator old = *this; + ++(*this); + return old; + } + + bool operator != (const Iterator rhs) const + { + return m_iter != rhs.m_iter; + } + + private: + T m_iter; + }; + + constexpr iota(T begin, T end) + : m_begin(begin), + m_end(end) + {} + + constexpr Iterator begin() const + { + return Iterator(m_begin); + } + + constexpr Iterator end() const + { + return Iterator(m_end); + } + +private: + const T m_begin; + const T m_end; +}; + +template +iota(T0, T1) -> iota; + +// This is not in the standard library and should be used sparsely +// since a proper algorithm often fits better. +template +constexpr iota iotaViewFor(const Range& r) +{ + return iota(0, r.size()); +} + +} Index: source/renderer/WaterManager.h =================================================================== --- source/renderer/WaterManager.h +++ source/renderer/WaterManager.h @@ -31,6 +31,7 @@ #include "renderer/backend/ITexture.h" #include "renderer/VertexBufferManager.h" +#include #include #include @@ -46,8 +47,8 @@ class WaterManager { public: - CTexturePtr m_WaterTexture[60]; - CTexturePtr m_NormalMap[60]; + std::array m_WaterTexture; + std::array m_NormalMap; // How strong the waves are at point X. % of waviness. std::unique_ptr m_WindStrength; Index: source/renderer/WaterManager.cpp =================================================================== --- source/renderer/WaterManager.cpp +++ source/renderer/WaterManager.cpp @@ -28,6 +28,7 @@ #include "ps/CLogger.h" #include "ps/CStrInternStatic.h" #include "ps/Game.h" +#include "ps/ranges/iota_view.h" #include "ps/VideoMode.h" #include "ps/World.h" #include "renderer/backend/IDevice.h" @@ -57,10 +58,10 @@ CVector3D m_RetreatPosition; CVector2D m_PerpVect; - u8 m_UV[3]; + std::array m_UV; // pad to a power of two - u8 m_Padding[5]; + std::array m_Padding; }; cassert(sizeof(SWavesVertex) == 64); @@ -136,20 +137,21 @@ // TODO: this doesn't need to be progressive-loading any more // (since texture loading is async now) - wchar_t pathname[PATH_MAX]; - // Load diffuse grayscale images (for non-fancy water) - for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); ++i) + const PS::iota counter = PS::iotaViewFor(m_WaterTexture); + std::transform(counter.begin(), counter.end(), m_WaterTexture.begin(), [](std::size_t i) { - swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/default/diffuse%02d.dds", (int)i+1); - CTextureProperties textureProps(pathname); + std::array pathname; + swprintf_s(pathname.data(), pathname.size(), + L"art/textures/animated/water/default/diffuse%02d.dds", static_cast(i) + 1); + CTextureProperties textureProps(pathname.data()); textureProps.SetAddressMode( Renderer::Backend::Sampler::AddressMode::REPEAT); CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); texture->Prefetch(); - m_WaterTexture[i] = texture; - } + return texture; + }); m_RenderWater = true; @@ -294,30 +296,31 @@ void WaterManager::ReloadWaterNormalTextures() { - wchar_t pathname[PATH_MAX]; - for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); ++i) + const PS::iota counter = PS::iotaViewFor(m_NormalMap); + std::transform(counter.begin(), counter.end(), m_NormalMap.begin(), [this](std::size_t i) { - swprintf_s(pathname, ARRAY_SIZE(pathname), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), static_cast(i) + 1); - CTextureProperties textureProps(pathname); + std::array pathname; + swprintf_s(pathname.data(), pathname.size(), L"art/textures/animated/water/%ls/normal00%02d.png", m_WaterType.c_str(), static_cast(i) + 1); + CTextureProperties textureProps(pathname.data()); textureProps.SetAddressMode( Renderer::Backend::Sampler::AddressMode::REPEAT); textureProps.SetAnisotropicFilter(true); CTexturePtr texture = g_Renderer.GetTextureManager().CreateTexture(textureProps); texture->Prefetch(); - m_NormalMap[i] = texture; - } + return texture; + }); } /////////////////////////////////////////////////////////////////// // Unload water textures void WaterManager::UnloadWaterTextures() { - for (size_t i = 0; i < ARRAY_SIZE(m_WaterTexture); i++) - m_WaterTexture[i].reset(); + for (CTexturePtr& elem : m_WaterTexture) + elem.reset(); - for (size_t i = 0; i < ARRAY_SIZE(m_NormalMap); i++) - m_NormalMap[i].reset(); + for (CTexturePtr& elem : m_NormalMap) + elem.reset(); m_RefractionFramebuffer.reset(); m_ReflectionFramebuffer.reset(); @@ -335,7 +338,7 @@ // Algorithm: // We want to know the distance to the closest shore point. Go through each line/column, // keep track of when we encountered the last shore point and how far ahead the next one is. - for (size_t id1 = 0; id1 < SideSize; ++id1) + for (const std::size_t id1 : PS::iota(0, SideSize)) { size_t id2 = 0; const size_t& x = Transpose ? id1 : id2; @@ -418,14 +421,15 @@ // First step: get the points near the coast. std::set CoastalPointsSet; - for (size_t z = 1; z < SideSize-1; ++z) - for (size_t x = 1; x < SideSize-1; ++x) + for (const size_t z : PS::iota(1, SideSize - 1)) + for (const size_t x : PS::iota(1, SideSize - 1)) // get the points not on the shore but near it, ocean-side if (m_DistanceHeightmap[z*m_MapSize + x] > 0.5f && m_DistanceHeightmap[z*m_MapSize + x] < 1.5f) CoastalPointsSet.insert((z)*SideSize + x); // Second step: create chains out of those coastal points. - static const int around[8][2] = { { -1,-1 }, { -1,0 }, { -1,1 }, { 0,1 }, { 1,1 }, { 1,0 }, { 1,-1 }, { 0,-1 } }; + constexpr std::array, 8> around = + {{{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}}}; std::vector > CoastalPointsChains; while (!CoastalPointsSet.empty()) @@ -445,21 +449,21 @@ // We'll pick the first one and look for its neighbors (he can only have one new) // Up until we either reach the end of the chain, or ourselves. // Then go down the other direction if there is any. - int neighbours[2] = { -1, -1 }; + std::array neighbours = { -1, -1 }; int nbNeighb = 0; - for (int i = 0; i < 8; ++i) + for (const auto& elem : around) { - if (CoastalPointsSet.count(x + around[i][0] + (y + around[i][1])*SideSize)) + if (CoastalPointsSet.count(x + elem[0] + (y + elem[1])*SideSize)) { if (nbNeighb < 2) - neighbours[nbNeighb] = x + around[i][0] + (y + around[i][1])*SideSize; + neighbours[nbNeighb] = x + elem[0] + (y + elem[1])*SideSize; ++nbNeighb; } } if (nbNeighb > 2) continue; - for (int i = 0; i < 2; ++i) + for (const std::size_t i : PS::iotaViewFor(neighbours)) { if (neighbours[i] == -1) continue; @@ -484,33 +488,25 @@ // Start checking from there. while(!endedChain) { - bool found = false; - nbNeighb = 0; - for (int p = 0; p < 8; ++p) + const auto it = std::find_if(around.begin(), around.end(), + [&CoastalPointsSet, xx, yy, SideSize](const auto& elem) { - if (CoastalPointsSet.count(xx+around[p][0] + (yy + around[p][1])*SideSize)) - { - if (nbNeighb >= 2) - { - CoastalPointsSet.erase(xx + yy*SideSize); - continue; - } - ++nbNeighb; - // We've found a new point around us. - // Move there - xx = xx + around[p][0]; - yy = yy + around[p][1]; - indexx = xx + yy*SideSize; - if (i == 0) - Chain.push_back(CoastalPoint(indexx,CVector2D(xx*4,yy*4))); - else - Chain.push_front(CoastalPoint(indexx,CVector2D(xx*4,yy*4))); - CoastalPointsSet.erase(xx + yy*SideSize); - found = true; - break; - } + return CoastalPointsSet.count(xx+elem[0] + (yy + elem[1])*SideSize); + }); + if(it != around.end()) + { + // We've found a new point around us. + // Move there + xx = xx + (*it)[0]; + yy = yy + (*it)[1]; + indexx = xx + yy*SideSize; + if (i == 0) + Chain.push_back(CoastalPoint(indexx,CVector2D(xx*4,yy*4))); + else + Chain.push_front(CoastalPoint(indexx,CVector2D(xx*4,yy*4))); + CoastalPointsSet.erase(xx + yy*SideSize); } - if (!found) + else endedChain = true; } } @@ -520,18 +516,19 @@ // (optional) third step: Smooth chains out. // This is also really dumb. - for (size_t i = 0; i < CoastalPointsChains.size(); ++i) + for (std::deque& coastalChain : CoastalPointsChains) { // Bump 1 for smoother. - for (int p = 0; p < 3; ++p) + const PS::iota counter(0, 3); + std::for_each(counter.begin(), counter.end(), [&](int) { - for (size_t j = 1; j < CoastalPointsChains[i].size()-1; ++j) + for (const size_t j : PS::iota(1, coastalChain.size() - 1)) { - CVector2D realPos = CoastalPointsChains[i][j-1].position + CoastalPointsChains[i][j+1].position; + CVector2D realPos = coastalChain[j-1].position + coastalChain[j+1].position; - CoastalPointsChains[i][j].position = (CoastalPointsChains[i][j].position + realPos/2.0f)/2.0f; + coastalChain[j].position = (coastalChain[j].position + realPos/2.0f)/2.0f; } - } + }); } // Fourth step: create waves themselves, using those chains. We basically create subchains. @@ -539,9 +536,9 @@ // Construct indices buffer (we can afford one for all of them) std::vector water_indices; - for (GLushort a = 0; a < waveSizes - 1; ++a) + for (const GLushort a : PS::iota(0, waveSizes - 1)) { - for (GLushort rect = 0; rect < 7; ++rect) + for (const GLushort rect : PS::iota(0, 7)) { water_indices.push_back(a * 9 + rect); water_indices.push_back(a * 9 + 9 + rect); @@ -561,11 +558,11 @@ float diff = (rand() % 50) / 5.0f; std::vector vertices, reversed; - for (size_t i = 0; i < CoastalPointsChains.size(); ++i) + for (const std::deque& coastalChain : CoastalPointsChains) { - for (size_t j = 0; j < CoastalPointsChains[i].size()-waveSizes; ++j) + for (size_t j = 0; j < coastalChain.size()-waveSizes; ++j) { - if (CoastalPointsChains[i].size()- 1 - j < waveSizes) + if (coastalChain.size()- 1 - j < waveSizes) break; GLushort width = waveSizes; @@ -580,19 +577,19 @@ lastPerp = perp; perp = CVector2D(0,0); int nb = 0; - CVector2D pos = CoastalPointsChains[i][j+a].position; + CVector2D pos = coastalChain[j+a].position; CVector2D posPlus; CVector2D posMinus; if (a > 0) { ++nb; - posMinus = CoastalPointsChains[i][j+a-1].position; + posMinus = coastalChain[j+a-1].position; perp += pos-posMinus; } if (a < waveSizes-1) { ++nb; - posPlus = CoastalPointsChains[i][j+a+1].position; + posPlus = coastalChain[j+a+1].position; perp += posPlus-pos; } perp /= nb; @@ -656,107 +653,121 @@ shoreWave->m_TimeDiff = diff; diff += (rand() % 100) / 25.0f + 4.0f; - for (GLushort a = 0; a < width;++a) + for (const GLushort a : PS::iota(0, width)) { perp = CVector2D(0,0); int nb = 0; - CVector2D pos = CoastalPointsChains[i][j+a].position; + CVector2D pos = coastalChain[j+a].position; CVector2D posPlus; CVector2D posMinus; if (a > 0) { ++nb; - posMinus = CoastalPointsChains[i][j+a-1].position; + posMinus = coastalChain[j+a-1].position; perp += pos-posMinus; } if (a < waveSizes-1) { ++nb; - posPlus = CoastalPointsChains[i][j+a+1].position; + posPlus = coastalChain[j+a+1].position; perp += posPlus-pos; } perp /= nb; perp = CVector2D(-perp.Y,perp.X).Normalized(); - SWavesVertex point[9]; float baseHeight = 0.04f; float halfWidth = (width-1.0f)/2.0f; float sideNess = sqrtf(Clamp( (halfWidth - fabsf(a - halfWidth)) / 3.0f, 0.0f, 1.0f)); - point[0].m_UV[0] = a; point[0].m_UV[1] = 8; - point[1].m_UV[0] = a; point[1].m_UV[1] = 7; - point[2].m_UV[0] = a; point[2].m_UV[1] = 6; - point[3].m_UV[0] = a; point[3].m_UV[1] = 5; - point[4].m_UV[0] = a; point[4].m_UV[1] = 4; - point[5].m_UV[0] = a; point[5].m_UV[1] = 3; - point[6].m_UV[0] = a; point[6].m_UV[1] = 2; - point[7].m_UV[0] = a; point[7].m_UV[1] = 1; - point[8].m_UV[0] = a; point[8].m_UV[1] = 0; - - point[0].m_PerpVect = perp; - point[1].m_PerpVect = perp; - point[2].m_PerpVect = perp; - point[3].m_PerpVect = perp; - point[4].m_PerpVect = perp; - point[5].m_PerpVect = perp; - point[6].m_PerpVect = perp; - point[7].m_PerpVect = perp; - point[8].m_PerpVect = perp; - - static const float perpT1[9] = { 6.0f, 6.05f, 6.1f, 6.2f, 6.3f, 6.4f, 6.5f, 6.6f, 9.7f }; - static const float perpT2[9] = { 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 3.0f, 3.3f, 3.6f, 9.5f }; - static const float perpT3[9] = { 1.1f, 0.7f, -0.2f, 0.0f, 0.6f, 1.3f, 2.2f, 3.6f, 9.0f }; - static const float perpT4[9] = { 2.0f, 2.1f, 1.2f, 1.5f, 1.7f, 1.9f, 2.7f, 3.8f, 9.0f }; - - static const float heightT1[9] = { 0.0f, 0.2f, 0.5f, 0.8f, 0.9f, 0.85f, 0.6f, 0.2f, 0.0 }; - static const float heightT2[9] = { -0.8f, -0.4f, 0.0f, 0.1f, 0.1f, 0.03f, 0.0f, 0.0f, 0.0 }; - static const float heightT3[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0 }; - - for (size_t t = 0; t < 9; ++t) - { - float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT1[t]+outmost), - pos.Y+sign*perp.Y*(perpT1[t]+outmost)); - point[t].m_BasePosition = CVector3D(pos.X+sign*perp.X*(perpT1[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), - pos.Y+sign*perp.Y*(perpT1[t]+outmost)); - } - for (size_t t = 0; t < 9; ++t) - { - float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT2[t]+outmost), - pos.Y+sign*perp.Y*(perpT2[t]+outmost)); - point[t].m_ApexPosition = CVector3D(pos.X+sign*perp.X*(perpT2[t]+outmost), baseHeight + heightT1[t]*sideNess + std::max(m_WaterHeight,terrHeight), - pos.Y+sign*perp.Y*(perpT2[t]+outmost)); - } - for (size_t t = 0; t < 9; ++t) + + struct SProtoWavesVertex { - float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT3[t]+outmost*sideNess), - pos.Y+sign*perp.Y*(perpT3[t]+outmost*sideNess)); - 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)); - } - for (size_t t = 0; t < 9; ++t) + float perpT1; + float perpT2; + float perpT3; + float perpT4; + float heightT1; + float heightT2; + float heightT3; + }; + constexpr std::array protoVertex + {{ + {9.7f, 9.5f, 9.0f, 9.0f, 0.0f, 0.0f, 0.0f}, + {6.6f, 3.6f, 3.6f, 3.8f, 0.2f, 0.0f, 0.0f}, + {6.5f, 3.3f, 2.2f, 2.7f, 0.6f, 0.0f, 0.0f}, + {6.4f, 3.0f, 1.3f, 1.9f, 0.85f, 0.03f, 0.0f}, + {6.3f, 2.4f, 0.6f, 1.7f, 0.9f, 0.1f, 0.0f}, + {6.2f, 2.3f, 0.0f, 1.5f, 0.8f, 0.1f, 0.0f}, + {6.1f, 2.2f, -0.2f, 1.2f, 0.5f, 0.0f, 0.0f}, + {6.05f, 2.1f, 0.7f, 2.1f, 0.2f, -0.4f, 0.0f}, + {6.0f, 2.0f, 1.1f, 2.0f, 0.0f, -0.8f, 0.0f} + }}; + + std::transform(protoVertex.begin(), protoVertex.end(), + PS::iotaViewFor(protoVertex).begin(), std::back_inserter(vertices), + [&](const SProtoWavesVertex& proto, const std::size_t index) { - float terrHeight = 0.05f + terrain->GetExactGroundLevel(pos.X+sign*perp.X*(perpT4[t]+outmost), - pos.Y+sign*perp.Y*(perpT4[t]+outmost)); - point[t].m_RetreatPosition = CVector3D(pos.X+sign*perp.X*(perpT4[t]+outmost), baseHeight + heightT3[t]*sideNess + std::max(m_WaterHeight,terrHeight), - pos.Y+sign*perp.Y*(perpT4[t]+outmost)); - } - - vertices.push_back(point[8]); - vertices.push_back(point[7]); - vertices.push_back(point[6]); - vertices.push_back(point[5]); - vertices.push_back(point[4]); - vertices.push_back(point[3]); - vertices.push_back(point[2]); - vertices.push_back(point[1]); - vertices.push_back(point[0]); - - shoreWave->m_AABB += point[8].m_SplashPosition; - shoreWave->m_AABB += point[8].m_BasePosition; - shoreWave->m_AABB += point[0].m_SplashPosition; - shoreWave->m_AABB += point[0].m_BasePosition; - shoreWave->m_AABB += point[4].m_ApexPosition; + const float terrHeight1 = 0.05f + terrain->GetExactGroundLevel( + pos.X+sign*perp.X*(proto.perpT1+outmost), + pos.Y+sign*perp.Y*(proto.perpT1+outmost)); + const CVector3D basePosition( + pos.X+sign*perp.X*(proto.perpT1+outmost), + baseHeight + proto.heightT1*sideNess + + std::max(m_WaterHeight,terrHeight1), + pos.Y+sign*perp.Y*(proto.perpT1+outmost)); + + + const float terrHeight2 = 0.05f + terrain->GetExactGroundLevel( + pos.X+sign*perp.X*(proto.perpT2+outmost), + pos.Y+sign*perp.Y*(proto.perpT2+outmost)); + const CVector3D apexPosition( + pos.X+sign*perp.X*(proto.perpT2+outmost), + baseHeight + proto.heightT1*sideNess + + std::max(m_WaterHeight,terrHeight2), + pos.Y+sign*perp.Y*(proto.perpT2+outmost)); + + + const float terrHeight3 = 0.05f + terrain->GetExactGroundLevel( + pos.X+sign*perp.X*(proto.perpT3+outmost*sideNess), + pos.Y+sign*perp.Y*(proto.perpT3+outmost*sideNess)); + const CVector3D splashPosition( + pos.X+sign*perp.X*(proto.perpT3+outmost*sideNess), + baseHeight + proto.heightT2*sideNess + + std::max(m_WaterHeight,terrHeight3), + pos.Y+sign*perp.Y*(proto.perpT3+outmost*sideNess)); + + + const float terrHeight4 = 0.05f + terrain->GetExactGroundLevel( + pos.X+sign*perp.X*(proto.perpT4+outmost), + pos.Y+sign*perp.Y*(proto.perpT4+outmost)); + const CVector3D retreatPosition( + pos.X+sign*perp.X*(proto.perpT4+outmost), + baseHeight + proto.heightT3*sideNess + + std::max(m_WaterHeight,terrHeight4), + pos.Y+sign*perp.Y*(proto.perpT4+outmost)); + + return SWavesVertex + { + basePosition, + apexPosition, + splashPosition, + retreatPosition, + perp, + { + static_cast(a), + static_cast(index), + 0 + } + }; + }); + + shoreWave->m_AABB += (vertices.end()-1)->m_SplashPosition; + shoreWave->m_AABB += (vertices.end()-1)->m_BasePosition; + shoreWave->m_AABB += (vertices.end()-9)->m_SplashPosition; + shoreWave->m_AABB += (vertices.end()-9)->m_BasePosition; + shoreWave->m_AABB += (vertices.end()-6)->m_ApexPosition; } if (sign == 1) @@ -766,7 +777,7 @@ reversed.reserve(vertices.size()); for (int a = width - 1; a >= 0; --a) { - for (size_t t = 0; t < 9; ++t) + for (const size_t t : PS::iota(0, 9)) reversed.push_back(vertices[a * 9 + t]); } std::swap(vertices, reversed); @@ -818,12 +829,12 @@ deviceCommandContext->SetUniform( shader->GetBindingSlot(str_transform), transform.AsFloatArray()); - for (size_t a = 0; a < m_ShoreWaves.size(); ++a) + for (const std::unique_ptr& wave : m_ShoreWaves) { - if (!frustrum.IsBoxVisible(m_ShoreWaves[a]->m_AABB)) + if (!frustrum.IsBoxVisible(wave->m_AABB)) continue; - CVertexBuffer::VBChunk* VBchunk = m_ShoreWaves[a]->m_VBVertices.Get(); + CVertexBuffer::VBChunk* VBchunk = wave->m_VBVertices.Get(); VBchunk->m_Owner->UploadIfNeeded(deviceCommandContext); m_ShoreWavesVBIndices->m_Owner->UploadIfNeeded(deviceCommandContext); @@ -857,14 +868,14 @@ firstVertexOffset + offsetof(SWavesVertex, m_RetreatPosition), stride, 0); deviceCommandContext->SetUniform( - shader->GetBindingSlot(str_translation), m_ShoreWaves[a]->m_TimeDiff); + shader->GetBindingSlot(str_translation), wave->m_TimeDiff); deviceCommandContext->SetUniform( - shader->GetBindingSlot(str_width), static_cast(m_ShoreWaves[a]->m_Width)); + shader->GetBindingSlot(str_width), static_cast(wave->m_Width)); deviceCommandContext->SetVertexBuffer(0, VBchunk->m_Owner->GetBuffer()); deviceCommandContext->SetIndexBuffer(m_ShoreWavesVBIndices->m_Owner->GetBuffer()); - const uint32_t indexCount = (m_ShoreWaves[a]->m_Width - 1) * (7 * 6); + const uint32_t indexCount = (wave->m_Width - 1) * (7 * 6); deviceCommandContext->DrawIndexed(m_ShoreWavesVBIndices->m_Index, indexCount, 0); g_Renderer.GetStats().m_DrawCalls++; @@ -921,32 +932,45 @@ movement.emplace_back(0, windY > 0.f ? 1 : -1); startingPoints.reserve(m_MapSize); size_t start = windY > 0 ? 0 : m_MapSize - 1; - for (size_t x = 0; x < m_MapSize; ++x) - startingPoints.emplace_back(x, start, 0.f); + const PS::iota counter(0, m_MapSize); + std::transform(counter.begin(), counter.end(), std::back_inserter(startingPoints), + [start](const std::size_t x) + { + return SWindPoint(x, start, 0.f); + }); } else if (fabs(windDir.Y) < 0.01f) { movement.emplace_back(windX > 0.f ? 1 : - 1, 0); startingPoints.reserve(m_MapSize); size_t start = windX > 0 ? 0 : m_MapSize - 1; - for (size_t z = 0; z < m_MapSize; ++z) - startingPoints.emplace_back(start, z, 0.f); + const PS::iota counter(0, m_MapSize); + std::transform(counter.begin(), counter.end(), std::back_inserter(startingPoints), + [start](const std::size_t z) + { + return SWindPoint(start, z, 0.f); + }); } else { startingPoints.reserve(m_MapSize * 2); // Points along X. size_t start = windY > 0 ? 0 : m_MapSize - 1; - for (size_t x = 0; x < m_MapSize; ++x) - startingPoints.emplace_back(x, start, 0.f); + const PS::iota counter0(0, m_MapSize); + std::transform(counter0.begin(), counter0.end(), std::back_inserter(startingPoints), + [start](const std::size_t x) + { + return SWindPoint(x, start, 0.f); + }); + // Points along Z, avoid repeating the corner point. start = windX > 0 ? 0 : m_MapSize - 1; - if (windY > 0) - for (size_t z = 1; z < m_MapSize; ++z) - startingPoints.emplace_back(start, z, 0.f); - else - for (size_t z = 0; z < m_MapSize-1; ++z) - startingPoints.emplace_back(start, z, 0.f); + const PS::iota counter1 = (windY > 0) ? PS::iota(1, m_MapSize) : PS::iota(0, m_MapSize - 1); + std::transform(counter1.begin(), counter1.end(), std::back_inserter(startingPoints), + [start](const std::size_t z) + { + return SWindPoint(start, z, 0.f); + }); // Compute movement array. movement.reserve(std::max(std::abs(windX),std::abs(windY))); @@ -974,22 +998,23 @@ bool onMap = true; while (onMap) - for (size_t step = 0; step < movement.size(); ++step) + for (const std::pair& step : movement) { // Move wind speed towards the mean. point.windStrength = 0.15f + point.windStrength * 0.85f; // Adjust speed based on height difference, a positive height difference slowly increases speed (simulate venturi effect) // and a lower height reduces speed (wind protection from hills/...) - float heightDiff = std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X + movement[step].first, point.Y + movement[step].second)) - + float heightDiff = std::max(m_WaterHeight, terrain->GetVertexGroundLevel( + point.X + step.first, point.Y + step.second)) - std::max(m_WaterHeight, terrain->GetVertexGroundLevel(point.X, point.Y)); if (heightDiff > 0.f) point.windStrength = std::min(2.f, point.windStrength + std::min(4.f, heightDiff) / 40.f); else point.windStrength = std::max(0.f, point.windStrength + std::max(-4.f, heightDiff) / 5.f); - point.X += movement[step].first; - point.Y += movement[step].second; + point.X += step.first; + point.Y += step.second; if (point.X < 0 || point.X >= static_cast(m_MapSize) || point.Y < 0 || point.Y >= static_cast(m_MapSize)) { @@ -1059,11 +1084,11 @@ size_t WaterManager::GetCurrentTextureIndex(const double& period) const { ENSURE(period > 0.0); - return static_cast(m_WaterTexTimer * ARRAY_SIZE(m_WaterTexture) / period) % ARRAY_SIZE(m_WaterTexture); + return static_cast(m_WaterTexTimer * m_WaterTexture.size() / period) % m_WaterTexture.size(); } size_t WaterManager::GetNextTextureIndex(const double& period) const { ENSURE(period > 0.0); - return (GetCurrentTextureIndex(period) + 1) % ARRAY_SIZE(m_WaterTexture); + return (GetCurrentTextureIndex(period) + 1) % m_WaterTexture.size(); }