Index: ps/trunk/source/renderer/WaterManager.cpp =================================================================== --- ps/trunk/source/renderer/WaterManager.cpp +++ ps/trunk/source/renderer/WaterManager.cpp @@ -1026,86 +1026,117 @@ /////////////////////////////////////////////////////////////////// // 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_WindStrength == NULL) + if (m_MapSize <= 0) + return; + + if (m_WindStrength == nullptr) m_WindStrength = new float[m_MapSize*m_MapSize]; CTerrain* terrain = g_Game->GetWorld()->GetTerrain(); if (!terrain || !terrain->GetHeightMap()) return; - 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.f / windDir.X); + ssize_t windY = round(1.f / windDir.Y); - float* Temp = new float[m_MapSize*m_MapSize]; - std::fill(Temp, Temp + m_MapSize*m_MapSize, 1.0f); - - for (size_t j = 0; j < m_MapSize; ++j) - for (size_t i = 0; i < m_MapSize; ++i) + struct SWindPoint { + SWindPoint(size_t x, size_t y, float strength) : X(x), Y(y), windStrength(strength) {} + size_t X; + size_t Y; + float windStrength; + }; + + 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.emplace_back(0, windY); + 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); + } + else if (fabs(windDir.Y) < 0.01f) + { + movement.emplace_back(windX, 0); + 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); + } + 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); + // 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); + + // 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 = { + windX == 0 ? 0 : windX > 0 ? +1 : -1, + 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.f; + float depth = m_WaterHeight - terrain->GetVertexGroundLevel(point.X, point.Y); + if (depth > 0.f && depth < 2.f) + m_WindStrength[point.Y * m_MapSize + point.X] = depth / 2.f; + 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.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)) - + 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; - // 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; - - 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); - - // 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 >= m_MapSize || point.Y < 0 || point.Y >= 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. } ////////////////////////////////////////////////////////////////////////