Index: source/renderer/WaterManager.cpp =================================================================== --- source/renderer/WaterManager.cpp +++ source/renderer/WaterManager.cpp @@ -1022,9 +1022,11 @@ /////////////////////////////////////////////////////////////////// // Calculate the strength of the wind at a given point on the map. -// This is too slow and should support limited recomputation. void WaterManager::RecomputeWindStrength() { + if (m_MapSize <= 0) + return; + if (m_WindStrength == NULL) m_WindStrength = new float[m_MapSize*m_MapSize]; @@ -1035,73 +1037,103 @@ float waterLevel = m_WaterHeight; CVector2D windDir = CVector2D(cos(m_WindAngle),sin(m_WindAngle)); - CVector2D perp = CVector2D(-windDir.Y, windDir.X); - // Our kernel will sample 5 points going towards the wind (generally). - int kernel[5][2] = { {(int)windDir.X*2,(int)windDir.Y*2}, {(int)windDir.X*5,(int)windDir.Y*5}, {(int)windDir.X*9,(int)windDir.Y*9}, {(int)windDir.X*16,(int)windDir.Y*16}, {(int)windDir.X*25,(int)windDir.Y*25} }; + ssize_t windX = round(1.0 / windDir.X); + ssize_t windY = round(1.0 / windDir.Y); - float* Temp = new float[m_MapSize*m_MapSize]; - std::fill(Temp, Temp + m_MapSize*m_MapSize, 1.0f); + struct SWindPoint { + SWindPoint(ssize_t x, ssize_t y, float strength) : X(x), Y(y), windStrength(strength) {} + ssize_t X; + ssize_t Y; + float windStrength; + }; - for (size_t j = 0; j < m_MapSize; ++j) - for (size_t i = 0; i < m_MapSize; ++i) + std::vector startingPoints; + std::vector> movement; // Every increment, move each starting point by all of these. + + // Compute starting points (one or two edges of the map) and how much to move each computation increment. + if (fabs(windDir.X) < 0.01f) + { + movement.push_back({0, windY}); + startingPoints.reserve(m_MapSize); + ssize_t start = windY > 0 ? 0 : m_MapSize-1; + for (ssize_t x = 0; x < (ssize_t)m_MapSize; ++x) + startingPoints.push_back({x, start, 0.0f}); + } + else if (fabs(windDir.Y) < 0.01f) + { + movement.push_back({windX, 0}); + ssize_t start = windX > 0 ? 0 : m_MapSize-1; + for (ssize_t z = 0; z < (ssize_t)m_MapSize; ++z) + startingPoints.push_back({start, z, 0.0f}); + } + else + { + startingPoints.reserve(m_MapSize*2); + // points along X + ssize_t start = windY > 0 ? 0 : m_MapSize-1; + for (ssize_t x = 0; x < (ssize_t)m_MapSize; ++x) + startingPoints.push_back({x, start, 0.0f}); + // points along Z, avoid repeating the corner point. + start = windX > 0 ? 0 : m_MapSize-1; + if (windY > 0) + for (ssize_t z = 1; z < (ssize_t)m_MapSize; ++z) + startingPoints.push_back({start, z, 0.0f}); + else + for (ssize_t z = 0; z < (ssize_t)m_MapSize-1; ++z) + startingPoints.push_back({start, z, 0.0f}); + + // compute movement array. + movement.reserve(std::max(std::abs(windX),std::abs(windY))); + while (windX != 0 || windY != 0) { - float curHeight = terrain->GetVertexGroundLevel(i,j); - if (curHeight >= waterLevel) + std::pair move = {0,0}; + move.first = windX == 0 ? 0 : windX > 0 ? +1 : -1; + move.second = windY == 0 ? 0 : windY > 0 ? +1 : -1; + windX -= move.first; + windY -= move.second; + movement.push_back(move); + } + } + + // We have all starting points ready, move them all until the map is covered. + for (SWindPoint& point : startingPoints) + { + // Starting velocity is 1.0 unless in shallow water. + m_WindStrength[point.Y * m_MapSize + point.X] = 1.0; + float depth = waterLevel - terrain->GetVertexGroundLevel(point.X, point.Y); + if (depth > 0.0f && depth < 2.0f) + m_WindStrength[point.Y * m_MapSize + point.X] = depth/2.0f; + point.windStrength = m_WindStrength[point.Y * m_MapSize + point.X]; + + bool onMap = true; + while (onMap) + for (size_t step = 0; step < movement.size(); ++step) { - Temp[j*m_MapSize + i] = 0.3f; // blurs too strong otherwise - continue; - } - if (terrain->GetVertexGroundLevel(i + ceil(windDir.X),j + ceil(windDir.Y)) < waterLevel) - continue; + // Move wind speed towards the mean + point.windStrength = 0.15 + point.windStrength * 0.85; - // Calculate how dampened our waves should be. - float oldHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[4][0],j+kernel[4][1])); - float currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[3][0],j+kernel[3][1])); - float avgheight = oldHeight + currentHeight; - float tendency = currentHeight - oldHeight; - oldHeight = currentHeight; - currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[2][0],j+kernel[2][1])); - avgheight += currentHeight; - tendency += currentHeight - oldHeight; - oldHeight = currentHeight; - currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[1][0],j+kernel[1][1])); - avgheight += currentHeight; - tendency += currentHeight - oldHeight; - oldHeight = currentHeight; - currentHeight = std::max(waterLevel,terrain->GetVertexGroundLevel(i+kernel[0][0],j+kernel[0][1])); - avgheight += currentHeight; - tendency += currentHeight - oldHeight; + // 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(waterLevel, terrain->GetVertexGroundLevel(point.X + movement[step].first, point.Y + movement[step].second)) - + std::max(waterLevel, terrain->GetVertexGroundLevel(point.X, point.Y)); + if (heightDiff > 0.0) + point.windStrength = std::min(2.0f, point.windStrength + std::min(4.0f, heightDiff) / 40.0f); + else + point.windStrength = std::max(0.0f, point.windStrength + std::max(-4.0f, heightDiff) / 5.0f); - float baseLevel = std::max(0.0f,1.0f - (avgheight/5.0f-waterLevel)/20.0f); - baseLevel *= baseLevel; - tendency /= 15.0f; - baseLevel -= tendency; // if the terrain was sloping downwards, increase baselevel. Otherwise reduce. - baseLevel = clamp(baseLevel,0.0f,1.0f); + point.X += movement[step].first; + point.Y += movement[step].second; - // Draw on map. This is pretty slow. - float length = 35.0f * (1.0f-baseLevel/1.8f); - for (float y = 0; y < length; y += 0.6f) + if (point.X < 0 || point.X >= (ssize_t)m_MapSize || point.Y < 0 || point.Y >= (ssize_t)m_MapSize) { - int xx = clamp(i - y * windDir.X,0.0f,(float)(m_MapSize-1)); - int yy = clamp(j - y * windDir.Y,0.0f,(float)(m_MapSize-1)); - Temp[yy*m_MapSize + xx] = Temp[yy*m_MapSize + xx] < (0.0f+baseLevel/1.5f) * (1.0f-y/length) + y/length * 1.0f ? - Temp[yy*m_MapSize + xx] : (0.0f+baseLevel/1.5f) * (1.0f-y/length) + y/length * 1.0f; + onMap = false; + break; } - } - - int blurKernel[4][2] = { {(int)ceil(windDir.X),(int)ceil(windDir.Y)}, {(int)windDir.X*3,(int)windDir.Y*3}, {(int)ceil(perp.X),(int)ceil(perp.Y)}, {(int)-ceil(perp.X),(int)-ceil(perp.Y)} }; - float blurValue; - for (size_t j = 2; j < m_MapSize-2; ++j) - for (size_t i = 2; i < m_MapSize-2; ++i) - { - blurValue = Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]]; - blurValue += Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]]; - blurValue += Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]]; - blurValue += Temp[(j+blurKernel[0][1])*m_MapSize + i+blurKernel[0][0]]; - m_WindStrength[j*m_MapSize + i] = blurValue * 0.25f; - } - delete[] Temp; + m_WindStrength[point.Y * m_MapSize + point.X] = point.windStrength; + } + } + // TODO: should perhaps blur a little, or change the above code to incorporate neighboring tiles a bit. } ////////////////////////////////////////////////////////////////////////