Wind speed computation rework.
This reworks wind-speed computation to not use a blurred-heightmap approach but rather a slightly more wind simulation approach. It fixes issues near the edges of some maps. Differential Revision: https://code.wildfiregames.com/D1721 This was SVN commit r22668.
This commit is contained in:
parent
99f770f911
commit
2a485c5e3b
@ -1026,86 +1026,117 @@ void WaterManager::RecomputeWaterData()
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
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;
|
||||
};
|
||||
|
||||
for (size_t j = 0; j < m_MapSize; ++j)
|
||||
for (size_t i = 0; i < m_MapSize; ++i)
|
||||
std::vector<SWindPoint> startingPoints;
|
||||
std::vector<std::pair<int, int>> 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<ssize_t, ssize_t> 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;
|
||||
|
||||
// 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(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);
|
||||
|
||||
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 >= 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.
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
Reference in New Issue
Block a user