Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3715,6 +3715,88 @@ void W3DView::updateTerrain()
drawHeight = WorldHeightMap::LOW_ANGLE_DRAW_HEIGHT;
}

// TheSuperHackers @bugfix sailro 13/06/2026 Grow the terrain draw window to cover the visible ground
// when zoomed far out, for all maps. Terrain is only drawn in a window around the view center; when the
// visible ground extends past it, the uncovered area lets the map water plane show through (maps with no
// real water still keep a map-sized one, so they look flooded). We size the window to the footprint of
// the four view corners projected onto the ground: a no-op at normal zoom, growing only when zoomed out.
if (WorldHeightMap *map = TheTerrainRenderObject->getMap())
{
const Matrix3D &cameraTransform = m_3DCamera->Get_Transform();
const Vector3 cameraLocation = m_3DCamera->Get_Position();
Vector2 viewPlaneMin, viewPlaneMax;
m_3DCamera->Get_View_Plane(viewPlaneMin, viewPlaneMax);
Vector2 viewportMin, viewportMax;
m_3DCamera->Get_Viewport(viewportMin, viewportMax);

const Real viewPlaneScaleX = viewPlaneMax.X - viewPlaneMin.X;
const Real viewPlaneScaleY = viewPlaneMax.Y - viewPlaneMin.Y;
const Real viewPlaneDist = -1.0f; // The view plane is always 1.0 from the camera, looking down -Z.
const Real groundZ = m_pos.z;

// Bound the projected corners to the map extent so a near horizontal corner ray - which meets the
// ground far away, behind the camera, or not at all - cannot produce a degenerate draw size.
const Int mapExtent = (map->getXExtent() > map->getYExtent()) ? map->getXExtent() : map->getYExtent();
const Real worldBound = (Real)mapExtent * MAP_XY_FACTOR;

Real footprintMinX = cameraLocation.X, footprintMaxX = cameraLocation.X;
Real footprintMinY = cameraLocation.Y, footprintMaxY = cameraLocation.Y;

for (Int i = 0; i < 2; ++i)
{
for (Int j = 0; j < 2; ++j)
{
const Real xMod = (-i + 0.5f + viewportMin.X) * viewPlaneDist * viewPlaneScaleX;
const Real yMod = ( j - 0.5f - viewportMin.Y) * viewPlaneDist * viewPlaneScaleY;

Vector3 rayDirection(
viewPlaneDist * cameraTransform[0][2] + xMod * cameraTransform[0][0] + yMod * cameraTransform[0][1],
viewPlaneDist * cameraTransform[1][2] + xMod * cameraTransform[1][0] + yMod * cameraTransform[1][1],
viewPlaneDist * cameraTransform[2][2] + xMod * cameraTransform[2][0] + yMod * cameraTransform[2][1]);
rayDirection.Normalize();
const Vector3 rayPoint = cameraLocation + rayDirection;

Real groundX = Vector3::Find_X_At_Z(groundZ, cameraLocation, rayPoint);
Real groundY = Vector3::Find_Y_At_Z(groundZ, cameraLocation, rayPoint);

// Clamp into [camera +/- worldBound]; the !(>=) form also rejects NaN from parallel rays.
if (!(groundX >= cameraLocation.X - worldBound))
groundX = cameraLocation.X - worldBound;
else if (groundX > cameraLocation.X + worldBound)
groundX = cameraLocation.X + worldBound;
if (!(groundY >= cameraLocation.Y - worldBound))
groundY = cameraLocation.Y - worldBound;
else if (groundY > cameraLocation.Y + worldBound)
groundY = cameraLocation.Y + worldBound;

if (groundX < footprintMinX)
footprintMinX = groundX;
if (groundX > footprintMaxX)
footprintMaxX = groundX;
if (groundY < footprintMinY)
footprintMinY = groundY;
if (groundY > footprintMaxY)
footprintMaxY = groundY;
}
}

// Use a single square size (the larger footprint dimension) so the window does not reallocate as the
// camera rotates, plus one tile block of margin.
const Real footprintSpanX = footprintMaxX - footprintMinX;
const Real footprintSpanY = footprintMaxY - footprintMinY;
const Real footprintSpan = (footprintSpanX > footprintSpanY) ? footprintSpanX : footprintSpanY;
Int footprintTiles = (Int)(footprintSpan / MAP_XY_FACTOR) + 1 + VERTEX_BUFFER_TILE_LENGTH;
if (footprintTiles > mapExtent)
footprintTiles = mapExtent;

// Snap up to 1 + N * VERTEX_BUFFER_TILE_LENGTH to match the engine's tile-based draw sizes.
const Int zoomDrawSize = ((footprintTiles - 1 + VERTEX_BUFFER_TILE_LENGTH) / VERTEX_BUFFER_TILE_LENGTH) * VERTEX_BUFFER_TILE_LENGTH + 1;
if (zoomDrawSize > drawWidth)
drawWidth = zoomDrawSize;
if (zoomDrawSize > drawHeight)
drawHeight = zoomDrawSize;
}

TheTerrainRenderObject->setTerrainDrawSize(drawWidth, drawHeight);
TheTerrainRenderObject->updateCenter(m_3DCamera, &cameraPivot, it);

Expand Down