@@ -435,6 +435,57 @@ Bool W3DView::zoomCameraToDesiredHeight()
435435 return false ;
436436}
437437
438+ // -------------------------------------------------------------------------------------------------
439+ // TheSuperHackers @bugfix New logic responsible for moving the camera pivot to the terrain ground.
440+ // This is essential to correctly center the camera above the ground when playing.
441+ Bool W3DView::movePivotToGround ()
442+ {
443+ const Real fpsRatio = TheFramePacer->getBaseOverUpdateFpsRatio ();
444+ const Real adjustFactor = TheGlobalData->m_cameraAdjustSpeed * fpsRatio;
445+ const Real groundLevel = m_groundLevel;
446+ const Real groundLevelDiff = m_terrainHeightAtPivot - groundLevel;
447+ if (fabs (groundLevelDiff) > 0 .1f )
448+ {
449+ // Adjust the ground level. This will change the world height of the camera.
450+ m_groundLevel += groundLevelDiff * adjustFactor;
451+
452+ // Reposition the camera relative to its pitch.
453+ // This effectively zooms the camera in the view direction together with the ground level change.
454+ Vector3 sourcePos;
455+ Vector3 targetPos;
456+ buildCameraPosition (sourcePos, targetPos);
457+ const Vector3 delta = targetPos - sourcePos;
458+
459+ if (fabs (delta.Z ) > 0 .1f )
460+ {
461+ Vector2 groundLevelCenter;
462+ Vector2 terrainHeightCenter;
463+ groundLevelCenter.X = Vector3::Find_X_At_Z (groundLevel, sourcePos, targetPos);
464+ groundLevelCenter.Y = Vector3::Find_Y_At_Z (groundLevel, sourcePos, targetPos);
465+ terrainHeightCenter.X = Vector3::Find_X_At_Z (m_terrainHeightAtPivot, sourcePos, targetPos);
466+ terrainHeightCenter.Y = Vector3::Find_Y_At_Z (m_terrainHeightAtPivot, sourcePos, targetPos);
467+ Vector2 posDiff = terrainHeightCenter - groundLevelCenter;
468+
469+ // Adjust the strength of the repositioning for low camera pitch, because
470+ // it feels bad to move the camera around when it looks over the terrain.
471+ const Real pitch = WWMath::Asin (fabs (delta.Z ) / delta.Length ());
472+ constexpr const Real lowerPitch = DEG_TO_RADF (15 .f );
473+ constexpr const Real upperPitch = DEG_TO_RADF (30 .f );
474+ Real repositionStrength = WWMath::Inverse_Lerp (lowerPitch, upperPitch, pitch);
475+ repositionStrength = WWMath::Clamp (repositionStrength, 0 .0f , 1 .0f );
476+ posDiff *= repositionStrength;
477+
478+ Coord3D pos = *getPosition ();
479+ pos.x += posDiff.X * adjustFactor;
480+ pos.y += posDiff.Y * adjustFactor;
481+ setPosition (&pos);
482+ }
483+
484+ return true ;
485+ }
486+ return false ;
487+ }
488+
438489void W3DView::updateCameraAreaConstraints ()
439490{
440491#if defined(RTS_DEBUG)
@@ -1396,7 +1447,26 @@ void W3DView::update()
13961447
13971448 if (adjustZoomWhenScrolling || adjustZoomWhenNotScrolling)
13981449 {
1450+ // TheSuperHackers @info The camera zoom has two modes:
1451+ // 1. Zoom by scaling the distance of the camera origin to the look-at target.
1452+ // Used by user zooming and the scripted camera.
1453+ // 2. Zoom by moving the camera pivot to the ground while repositioning the
1454+ // camera origin towards the look-at target. Visually this looks identical
1455+ // to (1), but changes the pivot point which is important for the rotation
1456+ // origin and map border collisions.
1457+ Bool isZoomingOrMovingPivot = false ;
1458+
13991459 if (zoomCameraToDesiredHeight ())
1460+ {
1461+ isZoomingOrMovingPivot = true ;
1462+ }
1463+
1464+ if (movePivotToGround ())
1465+ {
1466+ isZoomingOrMovingPivot = true ;
1467+ }
1468+
1469+ if (isZoomingOrMovingPivot)
14001470 {
14011471 m_recalcCamera = true ;
14021472
@@ -2376,16 +2446,18 @@ void W3DView::lookAt( const Coord3D *o )
23762446// -------------------------------------------------------------------------------------------------
23772447void W3DView::initHeightForMap ()
23782448{
2379- m_groundLevel = TheTerrainLogic->getGroundHeight (m_pos.x , m_pos.y );
2380- const Real MAX_GROUND_LEVEL = 120.0 ; // jba - starting ground level can't exceed this height.
2381- if (m_groundLevel>MAX_GROUND_LEVEL) {
2382- m_groundLevel = MAX_GROUND_LEVEL;
2383- }
2449+ resetPivotToGround ();
23842450
23852451 m_cameraOffset.z = m_groundLevel+TheGlobalData->m_cameraHeight ;
23862452 m_cameraOffset.y = -(m_cameraOffset.z / tan (TheGlobalData->m_cameraPitch * (PI / 180.0 )));
23872453 m_cameraOffset.x = -(m_cameraOffset.y * tan (TheGlobalData->m_cameraYaw * (PI / 180.0 )));
2388- m_cameraAreaConstraintsValid = false ; // possible ground level change invalidates camera constraints
2454+ }
2455+ // -------------------------------------------------------------------------------------------------
2456+ // -------------------------------------------------------------------------------------------------
2457+ void W3DView::resetPivotToGround ( void )
2458+ {
2459+ m_groundLevel = getHeightAroundPos (m_pos.x , m_pos.y );
2460+ m_cameraAreaConstraintsValid = false ; // possible ground level change invalidates camera constraints
23892461 m_recalcCamera = true ;
23902462}
23912463
0 commit comments