diff --git a/Core/GameEngine/Include/GameClient/View.h b/Core/GameEngine/Include/GameClient/View.h index efe8fa5bc4e..8d937507f00 100644 --- a/Core/GameEngine/Include/GameClient/View.h +++ b/Core/GameEngine/Include/GameClient/View.h @@ -131,7 +131,8 @@ class View : public Snapshot virtual void forceRedraw() = 0; virtual void lookAt( const Coord3D *o ); ///< Center the view on the given coordinate - virtual void initHeightForMap() {}; ///< Init the camera height for the map at the current position. + virtual void initHeightForMap() {}; ///< Init the camera height for the map at the current position. + virtual void resetPivotToGround() {}; ///< Set the camera pivot to the terrain height at the current position. virtual void scrollBy( const Coord2D *delta ); ///< Shift the view by the given delta virtual void moveCameraTo(const Coord3D *o, Int frames, Int shutter, Bool orient, Real easeIn=0.0f, Real easeOut=0.0f) { lookAt( o ); } @@ -204,6 +205,7 @@ class View : public Snapshot Bool userSetZoomToDefault() { return doUserAction(&View::setZoomToDefault); } Bool userSetFieldOfView(Real angle) { return doUserAction(&View::setFieldOfView, angle); } Bool userLookAt(const Coord3D *o) { return doUserAction(&View::lookAt, o); } + Bool userResetPivotToGround() { return doUserAction(&View::resetPivotToGround); } Bool userScrollBy(const Coord2D *delta) { return doUserAction(&View::scrollBy, delta); } Bool userSetLocation(const ViewLocation *location) { return doUserAction(&View::setLocation, location); } Bool userSetCameraLock(ObjectID id) { return doUserAction(&View::setCameraLock, id); } diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h index 7f76fa43836..6dfd204bd71 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h @@ -175,7 +175,8 @@ class W3DView : public View, public SubsystemInterface virtual void setPitchToDefault() override; ///< Set the view pitch back to default virtual void lookAt( const Coord3D *o ) override; ///< Center the view on the given coordinate - virtual void initHeightForMap() override; ///< Init the camera height for the map at the current position. + virtual void initHeightForMap() override; ///< Init the camera height for the map at the current position. + virtual void resetPivotToGround() override; ///< Set the camera pivot to the terrain height at the current position. virtual void moveCameraTo(const Coord3D *o, Int milliseconds, Int shutter, Bool orient, Real easeIn, Real easeOut) override; virtual void moveCameraAlongWaypointPath(Waypoint *pWay, Int frames, Int shutter, Bool orient, Real easeIn, Real easeOut) override; virtual Bool isCameraMovementFinished() override; @@ -238,7 +239,7 @@ class W3DView : public View, public SubsystemInterface virtual void set3DWireFrameMode(Bool enable) override; ///x; m_guardBandBias.y = gb->y; } @@ -283,13 +284,19 @@ class W3DView : public View, public SubsystemInterface Region2D m_cameraAreaConstraints; ///< Camera should be constrained to be within this area Bool m_cameraAreaConstraintsValid; ///< If false, recalculates the camera area constraints in the next render update + Bool m_recalcCameraConstraintsAfterScrolling; ///< Recalculates the camera area constraints after the user has moved the camera Bool m_recalcCamera; ///< Recalculates the camera transform in the next render update void setCameraTransform(); ///< set the transform matrix of m_3DCamera, based on m_pos & m_angle void buildCameraPosition(Vector3 &sourcePos, Vector3 &targetPos); 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 + Bool zoomCameraToDesiredHeight(); + Bool movePivotToGround(); + void updateCameraAreaConstraints(); void calcCameraAreaConstraints(); ///< Recalculates the camera area constraints - Real calcCameraAreaOffset(Real maxEdgeZ); + Real calcCameraAreaOffset(Real maxEdgeZ, Bool isLookingDown); + void clipCameraIntoAreaConstraints(); + Bool isWithinCameraAreaConstraints() const; Bool isWithinCameraHeightConstraints() const; virtual void setUserControlled(Bool value); Bool hasScriptedState(ScriptedState state) const; diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp index f5f24c5f4e5..1f913d2af82 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp @@ -167,6 +167,7 @@ W3DView::W3DView() m_shakerAngles.Y =0.0f; m_shakerAngles.Z =0.0f; + m_cameraAreaConstraints.zero(); m_recalcCamera = false; } @@ -260,14 +261,6 @@ void W3DView::buildCameraPosition( Vector3& sourcePos, Vector3& targetPos ) pos.x += m_shakeOffset.x; pos.y += m_shakeOffset.y; - if (m_cameraAreaConstraintsValid) - { - pos.x = maxf(m_cameraAreaConstraints.lo.x, pos.x); - pos.x = minf(m_cameraAreaConstraints.hi.x, pos.x); - pos.y = maxf(m_cameraAreaConstraints.lo.y, pos.y); - pos.y = minf(m_cameraAreaConstraints.hi.y, pos.y); - } - sourcePos.X = m_cameraOffset.x; sourcePos.Y = m_cameraOffset.y; sourcePos.Z = m_cameraOffset.z; @@ -425,6 +418,93 @@ void W3DView::buildCameraTransform( Matrix3D *transform, const Vector3 &sourcePo } } +//------------------------------------------------------------------------------------------------- +// TheSuperHackers @info Original logic responsible for zooming the camera to the desired height. +Bool W3DView::zoomCameraToDesiredHeight() +{ + const Real desiredHeight = (m_terrainHeightAtPivot + m_heightAboveGround); + const Real desiredZoom = desiredHeight / m_cameraOffset.z; + const Real adjustZoom = desiredZoom - m_zoom; + if (fabs(adjustZoom) >= 0.001f) + { + const Real fpsRatio = TheFramePacer->getBaseOverUpdateFpsRatio(); + const Real adjustFactor = TheGlobalData->m_cameraAdjustSpeed * fpsRatio; + m_zoom += adjustZoom * adjustFactor; + return true; + } + return false; +} + +//------------------------------------------------------------------------------------------------- +// TheSuperHackers @bugfix New logic responsible for moving the camera pivot to the terrain ground. +// This is essential to correctly center the camera above the ground when playing. +Bool W3DView::movePivotToGround() +{ + const Real fpsRatio = TheFramePacer->getBaseOverUpdateFpsRatio(); + const Real adjustFactor = TheGlobalData->m_cameraAdjustSpeed * fpsRatio; + const Real groundLevel = m_groundLevel; + const Real groundLevelDiff = m_terrainHeightAtPivot - groundLevel; + if (fabs(groundLevelDiff) > 0.1f) + { + // Adjust the ground level. This will change the world height of the camera. + m_groundLevel += groundLevelDiff * adjustFactor; + + // Reposition the camera relative to its pitch. + // This effectively zooms the camera in the view direction together with the ground level change. + Vector3 sourcePos; + Vector3 targetPos; + buildCameraPosition(sourcePos, targetPos); + const Vector3 delta = targetPos - sourcePos; + + if (fabs(delta.Z) > 0.1f) + { + Vector2 groundLevelCenter; + Vector2 terrainHeightCenter; + groundLevelCenter.X = Vector3::Find_X_At_Z(groundLevel, sourcePos, targetPos); + groundLevelCenter.Y = Vector3::Find_Y_At_Z(groundLevel, sourcePos, targetPos); + terrainHeightCenter.X = Vector3::Find_X_At_Z(m_terrainHeightAtPivot, sourcePos, targetPos); + terrainHeightCenter.Y = Vector3::Find_Y_At_Z(m_terrainHeightAtPivot, sourcePos, targetPos); + Vector2 posDiff = terrainHeightCenter - groundLevelCenter; + + // Adjust the strength of the repositioning for low camera pitch, because + // it feels bad to move the camera around when it looks over the terrain. + const Real pitch = WWMath::Asin(fabs(delta.Z) / delta.Length()); + constexpr const Real lowerPitch = DEG_TO_RADF(15.f); + constexpr const Real upperPitch = DEG_TO_RADF(30.f); + Real repositionStrength = WWMath::Inverse_Lerp(lowerPitch, upperPitch, pitch); + repositionStrength = WWMath::Clamp(repositionStrength, 0.0f, 1.0f); + posDiff *= repositionStrength; + + Coord3D pos = *getPosition(); + pos.x += posDiff.X * adjustFactor; + pos.y += posDiff.Y * adjustFactor; + setPosition(&pos); + } + + return true; + } + return false; +} + +void W3DView::updateCameraAreaConstraints() +{ +#if defined(RTS_DEBUG) + if (!TheGlobalData->m_useCameraConstraints) + return; +#endif + + if (!m_cameraAreaConstraintsValid) + { + calcCameraAreaConstraints(); + } + + if (m_cameraAreaConstraintsValid && !isWithinCameraAreaConstraints()) + { + clipCameraIntoAreaConstraints(); + m_recalcCamera = true; + } +} + //------------------------------------------------------------------------------------------------- /* Note the following restrictions on camera constraints! @@ -448,7 +528,23 @@ void W3DView::calcCameraAreaConstraints() Region3D mapRegion; TheTerrainLogic->getExtent( &mapRegion ); - Real offset = calcCameraAreaOffset(m_groundLevel); + // Update the 3D camera before using its transform to calculate the constraints with. + Vector3 sourcePos; + Vector3 targetPos; + buildCameraPosition(sourcePos, targetPos); + Matrix3D cameraTransform; + buildCameraTransform(&cameraTransform, sourcePos, targetPos); + + Matrix3D prevCameraTransform = m_3DCamera->Get_Transform(); + m_3DCamera->Set_Transform(cameraTransform); + + const Vector3 cameraForward = -cameraTransform.Get_Z_Vector(); + const Bool isLookingDown = cameraForward.Z <= 0.0f; + Real offset = calcCameraAreaOffset(m_groundLevel, isLookingDown); + + // Revert the 3D camera transform. + m_3DCamera->Set_Transform(prevCameraTransform); + m_cameraAreaConstraints.lo.x = mapRegion.lo.x + offset; m_cameraAreaConstraints.hi.x = mapRegion.hi.x - offset; m_cameraAreaConstraints.lo.y = mapRegion.lo.y + offset; @@ -459,40 +555,60 @@ void W3DView::calcCameraAreaConstraints() } //------------------------------------------------------------------------------------------------- -Real W3DView::calcCameraAreaOffset(Real maxEdgeZ) +Real W3DView::calcCameraAreaOffset(Real maxEdgeZ, Bool isLookingDown) { - Coord3D center, bottom; + Coord2D center; ICoord2D screen; + Vector3 rayStart; + Vector3 rayEnd; //Pick at the center - screen.x=0.5f*getWidth()+m_originX; - screen.y=0.5f*getHeight()+m_originY; - - Vector3 rayStart,rayEnd; - - getPickRay(&screen,&rayStart,&rayEnd); + screen.x = 0.5f * getWidth() + m_originX; + screen.y = 0.5f * getHeight() + m_originY; + getPickRay(&screen, &rayStart, &rayEnd); center.x = Vector3::Find_X_At_Z(maxEdgeZ, rayStart, rayEnd); center.y = Vector3::Find_Y_At_Z(maxEdgeZ, rayStart, rayEnd); - center.z = maxEdgeZ; - screen.y = m_originY+ 0.95f*getHeight(); - getPickRay(&screen,&rayStart,&rayEnd); - bottom.x = Vector3::Find_X_At_Z(maxEdgeZ, rayStart, rayEnd); - bottom.y = Vector3::Find_Y_At_Z(maxEdgeZ, rayStart, rayEnd); - bottom.z = maxEdgeZ; - center.x -= bottom.x; - center.y -= bottom.y; + const Real height = isLookingDown ? getHeight() : 0.0f; + screen.y = height + m_originY; + getPickRay(&screen, &rayStart, &rayEnd); + + Real bottomX = Vector3::Find_X_At_Z(maxEdgeZ, rayStart, rayEnd); + Real bottomY = Vector3::Find_Y_At_Z(maxEdgeZ, rayStart, rayEnd); + + center.x -= bottomX; + center.y -= bottomY; Real offset = center.length(); + // TheSuperHackers @tweak Reduces the offset to allow scrolling closer to the edges. + offset *= 0.85f; + if (TheGlobalData->m_debugAI) { - offset = -1000; // push out the constraints so we can look at staging areas. + offset -= 1000; // push out the constraints so we can look at staging areas. } return offset; } +//------------------------------------------------------------------------------------------------- +void W3DView::clipCameraIntoAreaConstraints() +{ + constexpr const Real eps = 1e-6f; + Coord3D pos = *getPosition(); + pos.x = clamp(m_cameraAreaConstraints.lo.x + eps, pos.x, m_cameraAreaConstraints.hi.x - eps); + pos.y = clamp(m_cameraAreaConstraints.lo.y + eps, pos.y, m_cameraAreaConstraints.hi.y - eps); + setPosition(&pos); +} + +//------------------------------------------------------------------------------------------------- +Bool W3DView::isWithinCameraAreaConstraints() const +{ + const Coord3D* pos = getPosition(); + return m_cameraAreaConstraints.isInRegion(pos->x, pos->y); +} + //------------------------------------------------------------------------------------------------- Bool W3DView::isWithinCameraHeightConstraints() const { @@ -551,33 +667,6 @@ void W3DView::setCameraTransform() } } -#if defined(RTS_DEBUG) - if (TheGlobalData->m_useCameraConstraints) -#endif - { - if (!m_cameraAreaConstraintsValid) - { - Vector3 sourcePos; - Vector3 targetPos; - buildCameraPosition(sourcePos, targetPos); - Matrix3D cameraTransform; - buildCameraTransform(&cameraTransform, sourcePos, targetPos); - m_3DCamera->Set_Transform( cameraTransform ); - calcCameraAreaConstraints(); - } - DEBUG_ASSERTLOG(m_cameraAreaConstraintsValid,("*** cam constraints are not valid!!!")); - - if (m_cameraAreaConstraintsValid) - { - Coord3D pos = *getPosition(); - pos.x = maxf(m_cameraAreaConstraints.lo.x, pos.x); - pos.x = minf(m_cameraAreaConstraints.hi.x, pos.x); - pos.y = maxf(m_cameraAreaConstraints.lo.y, pos.y); - pos.y = minf(m_cameraAreaConstraints.hi.y, pos.y); - setPosition(&pos); - } - } - m_3DCamera->Set_Clip_Planes(NearZ, farZ); #if defined(RTS_DEBUG) @@ -634,10 +723,10 @@ void W3DView::init() m_2DCamera->Set_View_Plane( min, max ); m_2DCamera->Set_Clip_Planes( 0.995f, 2.0f ); - m_cameraAreaConstraintsValid = false; - m_scrollAmountCutoffSqr = sqr(TheGlobalData->m_scrollAmountCutoff); + m_cameraAreaConstraintsValid = false; + m_recalcCameraConstraintsAfterScrolling = false; m_recalcCamera = true; } @@ -673,6 +762,8 @@ void W3DView::reset() Coord2D gb = { 0,0 }; setGuardBandBias( &gb ); + + m_recalcCameraConstraintsAfterScrolling = false; } //------------------------------------------------------------------------------------------------- @@ -1325,14 +1416,17 @@ void W3DView::update() // TheSuperHackers @tweak Can now also zoom when the game is paused. // TheSuperHackers @tweak The camera zoom speed is now decoupled from the render update. // TheSuperHackers @bugfix The camera terrain height adjustment now also works in replay playback. + // TheSuperHackers @bugfix xezon 26/10/2025 The camera area constraints are now recalculated when + // the camera zoom changes, for example because of terrain elevation changes in the camera's view. + // Additionally, the camera can be smoothly pushed away from the constraints, but not while the user + // is scrolling, to make the scrolling along the map border a pleasant experience. This behavior + // ensures that the view can reach and see all areas of the map, and especially the bottom map border. m_terrainHeightAtPivot = getHeightAroundPos(m_pos.x, m_pos.y); m_currentHeightAboveGround = m_cameraOffset.z * m_zoom - m_terrainHeightAtPivot; if (m_okToAdjustHeight) { - Real desiredHeight = (m_terrainHeightAtPivot + m_heightAboveGround); - Real desiredZoom = desiredHeight / m_cameraOffset.z; if (didScriptedMovement) { @@ -1353,20 +1447,54 @@ void W3DView::update() if (adjustZoomWhenScrolling || adjustZoomWhenNotScrolling) { - const Real fpsRatio = TheFramePacer->getBaseOverUpdateFpsRatio(); - const Real zoomAdj = (desiredZoom - m_zoom) * TheGlobalData->m_cameraAdjustSpeed * fpsRatio; - if (fabs(zoomAdj) >= 0.0001f) + // TheSuperHackers @info The camera zoom has two modes: + // 1. Zoom by scaling the distance of the camera origin to the look-at target. + // Used by user zooming and the scripted camera. + // 2. Zoom by moving the camera pivot to the ground while repositioning the + // camera origin towards the look-at target. Visually this looks identical + // to (1), but changes the pivot point which is important for the rotation + // origin and map border collisions. + Bool isZoomingOrMovingPivot = false; + + if (zoomCameraToDesiredHeight()) + { + isZoomingOrMovingPivot = true; + } + + if (movePivotToGround()) + { + isZoomingOrMovingPivot = true; + } + + if (isZoomingOrMovingPivot) { - m_zoom += zoomAdj; m_recalcCamera = true; + + if (isScrolling) + { + // Does not update the constraints while scrolling to maintain consistent edge collisions. + m_recalcCameraConstraintsAfterScrolling = true; + } + else + { + m_cameraAreaConstraintsValid = false; + } } } + + if (m_recalcCameraConstraintsAfterScrolling && !isScrolling) + { + m_recalcCameraConstraintsAfterScrolling = false; + m_cameraAreaConstraintsValid = false; + } } if (TheScriptEngine->isTimeFast()) { return; // don't draw - makes it faster :) jba. } + updateCameraAreaConstraints(); + // (gth) C&C3 if m_isCameraSlaved then force the camera to update each frame if (m_recalcCamera || m_isCameraSlaved) { @@ -2318,16 +2446,18 @@ void W3DView::lookAt( const Coord3D *o ) //------------------------------------------------------------------------------------------------- void W3DView::initHeightForMap() { - m_groundLevel = TheTerrainLogic->getGroundHeight(m_pos.x, m_pos.y); - const Real MAX_GROUND_LEVEL = 120.0; // jba - starting ground level can't exceed this height. - if (m_groundLevel>MAX_GROUND_LEVEL) { - m_groundLevel = MAX_GROUND_LEVEL; - } + resetPivotToGround(); m_cameraOffset.z = m_groundLevel+TheGlobalData->m_cameraHeight; m_cameraOffset.y = -(m_cameraOffset.z / tan(TheGlobalData->m_cameraPitch * (PI / 180.0))); m_cameraOffset.x = -(m_cameraOffset.y * tan(TheGlobalData->m_cameraYaw * (PI / 180.0))); - m_cameraAreaConstraintsValid = false; // possible ground level change invalidates camera constraints +} +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void W3DView::resetPivotToGround( void ) +{ + m_groundLevel = getHeightAroundPos(m_pos.x, m_pos.y); + m_cameraAreaConstraintsValid = false; // possible ground level change invalidates camera constraints m_recalcCamera = true; } diff --git a/Core/Libraries/Include/Lib/BaseType.h b/Core/Libraries/Include/Lib/BaseType.h index f3d24185882..99361609f94 100644 --- a/Core/Libraries/Include/Lib/BaseType.h +++ b/Core/Libraries/Include/Lib/BaseType.h @@ -197,6 +197,12 @@ struct RealRange { Real lo, hi; // low and high values of the range + void zero() + { + lo = 0.0f; + hi = 0.0f; + } + // combine the given range with us such that we now encompass // both ranges void combine( RealRange &other ) @@ -210,6 +216,12 @@ struct Coord2D { Real x, y; + void zero() + { + x = 0.0f; + y = 0.0f; + } + Real length() const { return (Real)sqrt( x*x + y*y ); } Real lengthSqr() const { return x*x + y*y; } @@ -291,6 +303,12 @@ struct ICoord2D { Int x, y; + void zero() + { + x = 0; + y = 0; + } + Int length() const { return (Int)sqrt( (double)(x*x + y*y) ); } }; @@ -298,16 +316,30 @@ struct Region2D { Coord2D lo, hi; // bounds of 2D rectangular region + void zero() + { + lo.zero(); + hi.zero(); + } + Real width() const { return hi.x - lo.x; } Real height() const { return hi.y - lo.y; } + Bool isInRegion( Real x, Real y ) const { return (lo.x < x) && (x < hi.x) && (lo.y < y) && (y < hi.y); } }; struct IRegion2D { ICoord2D lo, hi; // bounds of 2D rectangular region + void zero() + { + lo.zero(); + hi.zero(); + } + Int width() const { return hi.x - lo.x; } Int height() const { return hi.y - lo.y; } + Bool isInRegion( Int x, Int y ) const { return (lo.x < x) && (x < hi.x) && (lo.y < y) && (y < hi.y); } }; @@ -399,15 +431,16 @@ struct ICoord3D Int x, y, z; Int length() const { return (Int)sqrt( (double)(x*x + y*y + z*z) ); } + void zero() { - x = 0; y = 0; z = 0; } }; +// For alternative see AABoxClass struct Region3D { Coord3D lo, hi; // axis-aligned bounding box @@ -460,14 +493,15 @@ struct Region3D Bool isInRegionNoZ( const Coord3D *query ) const { - return (lo.x < query->x) && (query->x < hi.x) - && (lo.y < query->y) && (query->y < hi.y); + return (lo.x < query->x) && (query->x < hi.x) && + (lo.y < query->y) && (query->y < hi.y); } - Bool isInRegionWithZ( const Coord3D *query ) const + + Bool isInRegion( const Coord3D *query ) const { - return (lo.x < query->x) && (query->x < hi.x) - && (lo.y < query->y) && (query->y < hi.y) - && (lo.z < query->z) && (query->z < hi.z); + return (lo.x < query->x) && (query->x < hi.x) && + (lo.y < query->y) && (query->y < hi.y) && + (lo.z < query->z) && (query->z < hi.z); } }; @@ -475,6 +509,12 @@ struct IRegion3D { ICoord3D lo, hi; // axis-aligned bounding box + void zero() + { + lo.zero(); + hi.zero(); + } + Int width() const { return hi.x - lo.x; } Int height() const { return hi.y - lo.y; } Int depth() const { return hi.z - lo.z; } diff --git a/Core/Libraries/Source/WWVegas/WWMath/wwmath.h b/Core/Libraries/Source/WWVegas/WWMath/wwmath.h index ec26590d71e..8adda21a796 100644 --- a/Core/Libraries/Source/WWVegas/WWMath/wwmath.h +++ b/Core/Libraries/Source/WWVegas/WWMath/wwmath.h @@ -155,8 +155,15 @@ static WWINLINE float Max(float a, float b); static WWINLINE int Float_As_Int(const float f) { return *((int*)&f); } -static WWINLINE float Lerp(float a, float b, float lerp ); -static WWINLINE double Lerp(double a, double b, float lerp ); +// Linearly interpolates between a and b using parameter t in [0, 1]. +// t = 0 returns a, t = 1 returns b, values in between return a proportionate blend. +static WWINLINE float Lerp(float a, float b, float t); +static WWINLINE double Lerp(double a, double b, float t); + +// Computes the interpolation parameter t such that v = Lerp(a, b, t). +// Returns where v lies between a and b as a ratio, typically in [0, 1]. +static WWINLINE float Inverse_Lerp(float a, float b, float v); +static WWINLINE double Inverse_Lerp(double a, double b, float v); static WWINLINE long Float_To_Long(double f); @@ -258,16 +265,25 @@ WWINLINE float WWMath::Max(float a, float b) return b; } -WWINLINE float WWMath::Lerp(float a, float b, float lerp ) +WWINLINE float WWMath::Lerp(float a, float b, float t) +{ + return (a + (b - a)*t); +} + +WWINLINE double WWMath::Lerp(double a, double b, float t) { - return (a + (b - a)*lerp); + return (a + (b - a)*t); } -WWINLINE double WWMath::Lerp(double a, double b, float lerp ) +WWINLINE float WWMath::Inverse_Lerp(float a, float b, float v) { - return (a + (b - a)*lerp); + return (v - a) / (b - a); } +WWINLINE double WWMath::Inverse_Lerp(double a, double b, float v) +{ + return (v - a) / (b - a); +} WWINLINE bool WWMath::Is_Valid_Float(float x) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 772b0f4f897..c8c6f556d5a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -4503,9 +4503,10 @@ Bool InGameUI::areSelectedObjectsControllable() const //------------------------------------------------------------------------------ void InGameUI::resetCamera() { - ViewLocation currentView; - TheTacticalView->getLocation( ¤tView ); - TheTacticalView->resetCamera( ¤tView.getPosition(), 1, 0.0f, 0.0f ); + TheTacticalView->userResetPivotToGround(); + TheTacticalView->userSetAngleToDefault(); + TheTacticalView->userSetPitchToDefault(); + TheTacticalView->userSetZoomToDefault(); } //------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp index 404dbe09f96..9a5be199f0c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp @@ -314,6 +314,7 @@ GameMessageDisposition LookAtTranslator::translateGameMessage(const GameMessage // if middle button is "clicked", reset to "home" orientation if (!didMove && elapsedMsec < CLICK_DURATION_MSEC) { + TheTacticalView->userResetPivotToGround(); TheTacticalView->userSetAngleToDefault(); TheTacticalView->userSetPitchToDefault(); TheTacticalView->userSetZoomToDefault();