Skip to content

Commit d776595

Browse files
committed
tweak(view): Align the camera far clip plane with the actual terrain draw size (#2677)
1 parent 90bbaa7 commit d776595

2 files changed

Lines changed: 62 additions & 15 deletions

File tree

Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ class W3DView : public View, public SubsystemInterface
302302
Real getMaxHeight(Real x, Real y) const;
303303
Real getMaxZoom(Real x, Real y) const;
304304
void updateCameraTransform(); ///< update the transform matrix of m_3DCamera, based on m_pos & m_angle
305-
void updateCameraClipPlanes();
305+
void updateCameraClipPlanes(const Matrix3D &transform);
306306
void setCameraTransform(const Matrix3D &transform);
307307
void buildCameraPosition(Vector3 &sourcePos, Vector3 &targetPos);
308308
void buildCameraTransform(Matrix3D *transform, const Vector3 &sourcePos, const Vector3 &targetPos); ///< calculate (but do not set) the transform matrix of m_3DCamera, based on m_pos & m_angle

Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080

8181
#include "W3DDevice/Common/W3DConvert.h"
8282
#include "W3DDevice/GameClient/HeightMap.h"
83+
#include "W3DDevice/GameClient/WorldHeightMap.h"
8384
#include "W3DDevice/GameClient/W3DAssetManager.h"
8485
#include "W3DDevice/GameClient/W3DDisplay.h"
8586
#include "W3DDevice/GameClient/W3DScene.h"
@@ -617,7 +618,7 @@ Real W3DView::calcCameraAreaOffset(Real maxEdgeZ)
617618
//-------------------------------------------------------------------------------------------------
618619
void W3DView::clipCameraIntoAreaConstraints()
619620
{
620-
constexpr const Real eps = 1e-6f;
621+
constexpr const Real eps = 0.1f;
621622
Coord2D pos = getPosition2D();
622623
pos.x = clamp(m_cameraAreaConstraints.lo.x + eps, pos.x, m_cameraAreaConstraints.hi.x - eps);
623624
pos.y = clamp(m_cameraAreaConstraints.lo.y + eps, pos.y, m_cameraAreaConstraints.hi.y - eps);
@@ -731,8 +732,6 @@ void W3DView::updateCameraTransform()
731732
if (TheGlobalData->m_headless)
732733
return;
733734

734-
updateCameraClipPlanes();
735-
736735
Vector3 sourcePos;
737736
Vector3 targetPos;
738737
buildCameraPosition(sourcePos, targetPos);
@@ -762,30 +761,75 @@ void W3DView::updateCameraTransform()
762761
}
763762

764763
//-------------------------------------------------------------------------------------------------
764+
// TheSuperHackers @tweak The far clip plane is now generally aligned with the actual terrain
765+
// draw size. This is most useful for low camera angles.
765766
//-------------------------------------------------------------------------------------------------
766-
void W3DView::updateCameraClipPlanes()
767+
void W3DView::updateCameraClipPlanes(const Matrix3D &transform)
767768
{
768-
// TheSuperHackers @bugfix Extends the initial far Z from 1200 because that is not enough at
769-
// default max height 310 and default pitch 37.5.
770-
static_assert(WorldHeightMap::NORMAL_DRAW_WIDTH == WorldHeightMap::NORMAL_DRAW_HEIGHT, "Expects squared draw area");
771-
Real farZ = (WorldHeightMap::NORMAL_DRAW_WIDTH * 1.08f) * MAP_XY_FACTOR;
769+
Real farZ;
770+
771+
if (TheGlobalData->m_drawEntireTerrain)
772+
{
773+
farZ = 100000.0f;
774+
}
775+
else if (TheTerrainRenderObject && TheTerrainRenderObject->getMap())
776+
{
777+
WorldHeightMap *heightMap = TheTerrainRenderObject->getMap();
778+
779+
const Vector3 camPos = transform.Get_Translation();
780+
const Vector3 camDir = -transform.Get_Z_Vector();
781+
782+
const Int loX = heightMap->getDrawOrgX() - heightMap->getBorderSize();
783+
const Int loY = heightMap->getDrawOrgY() - heightMap->getBorderSize();
784+
const Int hiX = loX + heightMap->getDrawWidth();
785+
const Int hiY = loY + heightMap->getDrawHeight();
786+
787+
// Convert to world coordinates
788+
const Real minX = loX * MAP_XY_FACTOR;
789+
const Real minY = loY * MAP_XY_FACTOR;
790+
const Real maxX = hiX * MAP_XY_FACTOR;
791+
const Real maxY = hiY * MAP_XY_FACTOR;
792+
793+
const Real minZ = TheTerrainRenderObject->getMinHeight();
794+
795+
// Bounding sphere
796+
Vector3 center;
797+
center.X = (minX + maxX) * 0.5f;
798+
center.Y = (minY + maxY) * 0.5f;
799+
center.Z = minZ - 1.0f; // -1 to avoid Z clipping when looking straight down
772800

773-
const Real heightMultiplier = m_heightAboveGround / ViewDefaultMaxHeightAboveTerrain;
774-
farZ *= std::max(heightMultiplier, 1.0f);
801+
// Half extents
802+
const Real dx = (maxX - minX) * 0.5f;
803+
const Real dy = (maxY - minY) * 0.5f;
804+
805+
// Project center
806+
const Vector3 v = center - camPos;
807+
const Real projectedDistanceToCenter = fabs(Vector3::Dot_Product(v, camDir));
808+
809+
// Project radius
810+
const Real projectedRadiusToEdge = fabs(dx * camDir.X) + fabs(dy * camDir.Y);
811+
812+
// Final far plane
813+
farZ = projectedDistanceToCenter + projectedRadiusToEdge;
814+
}
815+
else
816+
{
817+
farZ = WorldHeightMap::NORMAL_DRAW_WIDTH * MAP_XY_FACTOR;
818+
}
775819

776820
if (m_useRealZoomCam) //WST 10.19.2002
777821
{
778822
if (m_FXPitch < 0.95f)
779823
{
780-
//Extend far Z when we pitch up for RealZoomCam
824+
// Extend far Z when we pitch up for RealZoomCam
781825
farZ /= m_FXPitch;
782826
}
783827
}
784828
else
785829
{
786-
if (TheGlobalData->m_drawEntireTerrain || m_FXPitch < 0.95f)
830+
if (m_FXPitch < 0.95f)
787831
{
788-
//Extend far clip plane so entire terrain can be visible
832+
// Extend far clip plane so entire terrain can be visible
789833
farZ *= 10.0f;
790834
}
791835
}
@@ -810,6 +854,10 @@ void W3DView::setCameraTransform(const Matrix3D &transform)
810854
updateTerrain();
811855
}
812856

857+
// TheSuperHackers @info Camera clip planes must be updated after the terrain,
858+
// because the new terrain draw size is needed to calculate the far clip plane.
859+
updateCameraClipPlanes(transform);
860+
813861
// TheSuperHackers @fix Notify the Radar about the changed view always.
814862
// This way the radar view box should always be in sync with the camera view.
815863
if (TheRadar)
@@ -875,7 +923,6 @@ void W3DView::set3DCameraLookAt(const Coord3D &pos, const Coord3D &dir, Real rol
875923
Matrix3D transform;
876924
transform.Look_At_Dir(camPos, camDir, roll);
877925

878-
updateCameraClipPlanes();
879926
setCameraTransform(transform);
880927

881928
m_recalcCamera = false;

0 commit comments

Comments
 (0)