Skip to content
Open
Show file tree
Hide file tree
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
53 changes: 49 additions & 4 deletions Core/GameEngine/Include/GameClient/View.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class View : public Snapshot
virtual UnsignedInt getID() { return m_id; }

virtual void setZoomLimited( Bool limit ) { m_zoomLimited = limit; } ///< limit the zoom height
virtual Bool isZoomLimited() { return m_zoomLimited; } ///< get status of zoom limit
virtual Bool isZoomLimited() const { return m_zoomLimited; } ///< get status of zoom limit

/// pick drawable given the screen pixel coords. If force attack, picks bridges as well.
virtual Drawable *pickDrawable( const ICoord2D *screen, Bool forceAttack, PickType pickType ) = 0;
Expand All @@ -128,12 +128,11 @@ class View : public Snapshot
virtual void setOrigin( Int x, Int y) { m_originX=x; m_originY=y;} ///< Sets location of top-left view corner on display
virtual void getOrigin( Int *x, Int *y) { *x=m_originX; *y=m_originY;} ///< Return location of top-left view corner on display

virtual void lockViewUntilFrame(UnsignedInt frame); ///< Locks the current view until the given frame is reached.
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 scrollBy( Coord2D *delta ); ///< Shift the view by the given delta
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 ); }
virtual void moveCameraAlongWaypointPath(Waypoint *way, Int frames, Int shutter, Bool orient, Real easeIn=0.0f, Real easeOut=0.0f) { }
Expand Down Expand Up @@ -194,6 +193,25 @@ class View : public Snapshot
virtual void setZoomToDefault() { m_zoom = 1.0f; } ///< Set zoom to default value
virtual void setOkToAdjustHeight( Bool val ) { m_okToAdjustHeight = val; } ///< Set this to adjust camera height

// TheSuperHackers @info Functions to call for user camera controls, not by the scripted camera.
Bool userSetPosition(const Coord3D *pos) { return doUserAction(&View::setPosition, pos); }
Bool userSetAngle(Real radians) { return doUserAction(&View::setAngle, radians); }
Bool userSetAngleToDefault() { return doUserAction(&View::setAngleToDefault); }
Bool userSetPitch(Real radians) { return doUserAction(&View::setPitch, radians); }
Bool userSetPitchToDefault() { return doUserAction(&View::setPitchToDefault); }
Bool userZoom(Real height) { return doUserAction(&View::zoom, height); }
Bool userSetZoom(Real z) { return doUserAction(&View::setZoom, z); }
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 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); }
Bool userSetCameraLockDrawable(Drawable *drawable) { return doUserAction(&View::setCameraLockDrawable, drawable); }

void lockUserControlUntilFrame(UnsignedInt frame) { m_userControlLockedUntilFrame = frame; } ///< Locks the user control over camera until the given frame is reached.
Bool isUserControlLocked() const;

// for debugging
virtual Real getTerrainHeightAtPivot() { return m_terrainHeightAtPivot; }
virtual Real getCurrentHeightAboveGround() { return m_currentHeightAboveGround; }
Expand Down Expand Up @@ -246,14 +264,41 @@ class View : public Snapshot
virtual View *prependViewToList( View *list ); ///< Prepend this view to the given list, return the new list
virtual View *getNextView() { return m_next; } ///< Return next view in the set

virtual void setUserControlled(Bool value) { m_isUserControlled = value; }

private:

template<typename Function>
Bool doUserAction(Function function)
{
if (isUserControlLocked())
return false;
stopDoingScriptedCamera();
setUserControlled(true);
(this->*function)();
return true;
}

template<typename Function, typename Arg1>
Bool doUserAction(Function function, Arg1 arg1)
{
if (isUserControlLocked())
return false;
stopDoingScriptedCamera();
setUserControlled(true);
(this->*function)(arg1);
return true;
}

protected:

View *m_next; ///< List links used by the Display class

UnsignedInt m_id; ///< The ID of this view
static UnsignedInt m_idNext; ///< Used for allocating view ID's for all views

UnsignedInt m_viewLockedUntilFrame;
UnsignedInt m_userControlLockedUntilFrame; ///< Locks the user control over camera until the given frame is reached
Bool m_isUserControlled; ///< True if the user moved the camera last, false if the scripted camera moved the camera last

Coord3D m_pos; ///< Pivot of the camera, in world coordinates // TheSuperHackers @todo Make this Coord2D or use the Z component
Int m_width, m_height; ///< Dimensions of the view
Expand Down
21 changes: 12 additions & 9 deletions Core/GameEngine/Source/GameClient/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@

#include "Common/GameEngine.h"
#include "Common/Xfer.h"
#include "GameClient/View.h"
#include "GameClient/Drawable.h"
#include "GameClient/GameClient.h"
#include "GameClient/View.h"

UnsignedInt View::m_idNext = 1;

Expand All @@ -41,7 +42,8 @@ View *TheTacticalView = nullptr;

View::View()
{
m_viewLockedUntilFrame = 0u;
m_userControlLockedUntilFrame = 0u;
m_isUserControlled = true;
m_currentHeightAboveGround = 0.0f;
m_defaultAngle = 0.0f;
m_defaultPitch = 0.0f;
Expand Down Expand Up @@ -109,7 +111,8 @@ void View::reset()
// Only fixing the reported bug. Who knows what side effects resetting the rest could have.
m_zoomLimited = TRUE;

m_viewLockedUntilFrame = 0u;
m_userControlLockedUntilFrame = 0u;
m_isUserControlled = true;
}

/**
Expand All @@ -126,11 +129,6 @@ void View::zoom( Real height )
setHeightAboveGround(getHeightAboveGround() + height);
}

void View::lockViewUntilFrame(UnsignedInt frame)
{
m_viewLockedUntilFrame = frame;
}

/**
* Center the view on the given coordinate.
*/
Expand All @@ -147,7 +145,7 @@ void View::lookAt( const Coord3D *o )
/**
* Shift the view by the given delta.
*/
void View::scrollBy( Coord2D *delta )
void View::scrollBy( const Coord2D *delta )
{
// update view's world position
m_pos.x += delta->x;
Expand Down Expand Up @@ -228,6 +226,11 @@ void View::setLocation( const ViewLocation *location )

}

Bool View::isUserControlLocked() const
{
return m_userControlLockedUntilFrame > TheGameClient->getFrame();
}

//-------------------------------------------------------------------------------------------------
/** project the 4 corners of this view into the world and return each point as a parameter,
the world points are at the requested Z */
Expand Down
5 changes: 3 additions & 2 deletions Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class W3DView : public View, public SubsystemInterface
virtual void setHeight( Int height );
virtual void setOrigin( Int x, Int y); ///< Sets location of top-left view corner on display

virtual void scrollBy( Coord2D *delta ); ///< Shift the view by the given delta
virtual void scrollBy( const Coord2D *delta ); ///< Shift the view by the given delta

virtual void forceRedraw();

Expand Down Expand Up @@ -277,7 +277,7 @@ class W3DView : public View, public SubsystemInterface
Coord3D m_cameraOffset; ///< offset for camera from view center
Coord3D m_previousLookAtPosition; ///< offset for camera from view center
Coord2D m_scrollAmount; ///< scroll speed
Real m_scrollAmountCutoff; ///< scroll speed at which we do not adjust height
Real m_scrollAmountCutoffSqr; ///< scroll speed at which we do not adjust height

Real m_groundLevel; ///< height of ground.

Expand All @@ -289,6 +289,7 @@ class W3DView : public View, public SubsystemInterface
void buildCameraTransform(Matrix3D *transform); ///< calculate (but do not set) the transform matrix of m_3DCamera, based on m_pos & m_angle
void calcCameraAreaConstraints(); ///< Recalculates the camera area constraints
Bool isWithinCameraHeightConstraints() const;
virtual void setUserControlled(Bool value);
Bool hasScriptedState(ScriptedState state) const;
void addScriptedState(ScriptedState state);
void removeScriptedState(ScriptedState state);
Expand Down
50 changes: 36 additions & 14 deletions Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -527,9 +527,6 @@ void W3DView::setCameraTransform()
if (TheGlobalData->m_headless)
return;

if (m_viewLockedUntilFrame > TheGameClient->getFrame())
return;

m_cameraHasMovedSinceRequest = true;
Matrix3D cameraTransform;

Expand Down Expand Up @@ -627,7 +624,7 @@ void W3DView::init()

m_cameraAreaConstraintsValid = false;

m_scrollAmountCutoff = TheGlobalData->m_scrollAmountCutoff;
m_scrollAmountCutoffSqr = sqr(TheGlobalData->m_scrollAmountCutoff);

m_recalcCamera = true;
}
Expand All @@ -650,10 +647,15 @@ void W3DView::reset()
// Just in case...
setTimeMultiplier(1); // Set time rate back to 1.

stopDoingScriptedCamera();
setUserControlled(true);

// Just move the camera to zero. It'll get repositioned at the beginning of the next game anyways.
Coord3D arbitraryPos = { 0, 0, 0 };
// Just move the camera to 0, 0, 0. It'll get repositioned at the beginning of the next game
// anyways.
resetCamera(&arbitraryPos, 1, 0.0f, 0.0f);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed from resetCamera to set function, because resetCamera is meant to be used by scripted actions and would set the user controlled flag to false, which we do not want here.

setPosition(&arbitraryPos);
setAngleToDefault();
setPitchToDefault();
setZoomToDefault();

setViewFilter(FT_VIEW_DEFAULT);

Expand Down Expand Up @@ -1281,6 +1283,12 @@ void W3DView::update()
didScriptedMovement = true; // don't mess up the scripted movement
}
}

if (!m_isUserControlled)
{
didScriptedMovement = true;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that the scripted camera state will prevail even if no script camera action is currently running, for as long as the user did no camera inputs. I cannot recall why this was necessary, but there are definitely gaps in Mission cutscenes where no scripted camera movements happen and it makes sense to keep the scripted state active for as long as the user is not controlling the camera.

}

//
// Process camera shake
//
Expand Down Expand Up @@ -1320,8 +1328,9 @@ void W3DView::update()
m_heightAboveGround = m_currentHeightAboveGround;
}

const Bool isScrolling = TheInGameUI && TheInGameUI->isScrolling();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I have decoupled the scrolling condition from TheInGameUI. This is necessary, because we cannot trust that the mouse input equals the actual camera scrolling, if the camera is allowed to block user input, like we do for the Replay Player Camera for every full logic frame.

const Bool isScrollingTooFast = m_scrollAmount.length() >= m_scrollAmountCutoff;
const Real scrollLenSqr = m_scrollAmount.lengthSqr();
const Bool isScrolling = scrollLenSqr > FLT_EPSILON;
const Bool isScrollingTooFast = scrollLenSqr >= m_scrollAmountCutoffSqr;
const Bool isWithinHeightConstraints = isWithinCameraHeightConstraints();

// if scrolling, only adjust if we're too close or too far
Expand Down Expand Up @@ -1766,9 +1775,8 @@ void W3DView::setSnapMode( CameraLockType lockType, Real lockDist )
// TheSuperHackers @bugfix Now rotates the view plane on the Z axis only to properly discard the
// camera pitch. The aspect ratio also no longer modifies the vertical scroll speed.
//-------------------------------------------------------------------------------------------------
void W3DView::scrollBy( Coord2D *delta )
void W3DView::scrollBy( const Coord2D *delta )
{
// if we haven't moved, ignore
if( delta && (delta->x != 0 || delta->y != 0) )
{
constexpr const Real SCROLL_RESOLUTION = 250.0f;
Expand Down Expand Up @@ -1808,7 +1816,11 @@ void W3DView::scrollBy( Coord2D *delta )
removeScriptedState(Scripted_Rotate);
m_recalcCamera = true;
}

else
{
m_scrollAmount.x = 0;
m_scrollAmount.y = 0;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This now allows to set zero scroll speed, to signal that it is no longer scrolling. Previously it would just leave the last value and we relied on TheInGameUI->isScrolling() to tell whether it is scrolling.

}
}

//-------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -1902,7 +1914,8 @@ void W3DView::setHeightAboveGround(Real z)
// the camera height.
void W3DView::setZoom(Real z)
{
View::setZoom(z);
m_heightAboveGround = m_maxHeightAboveGround * z;
Copy link
Author

@xezon xezon Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary, because the m_heightAboveGround setting drives the auto-zoom for terrain elevation. Function setZoomToDefault already sets m_heightAboveGround correctly.

m_zoom = z;

stopDoingScriptedCamera();
m_CameraArrivedAtWaypointOnPathFlag = false;
Expand Down Expand Up @@ -2248,7 +2261,6 @@ void W3DView::lookAt( const Coord3D *o )
{
Coord3D pos = *o;


// no, don't call the super-lookAt, since it will munge our coords
// as for a 2d view. just call setPosition.
//View::lookAt(&pos);
Expand Down Expand Up @@ -3034,6 +3046,15 @@ void W3DView::pitchCameraOneFrame()
}
}

//-------------------------------------------------------------------------------------------------
void W3DView::setUserControlled(Bool value)
{
if (m_isUserControlled != value)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition does not make much sense right now but will do later because more code is added in the conditional body.

{
m_isUserControlled = value;
}
}

// ------------------------------------------------------------------------------------------------
Bool W3DView::isDoingScriptedCamera()
{
Expand All @@ -3056,6 +3077,7 @@ Bool W3DView::hasScriptedState(ScriptedState state) const
void W3DView::addScriptedState(ScriptedState state)
{
m_scriptedState |= state;
setUserControlled(false);
}

// ------------------------------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions Core/Libraries/Include/Lib/BaseType.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ struct Coord2D
Real x, y;

Real length() const { return (Real)sqrt( x*x + y*y ); }
Real lengthSqr() const { return x*x + y*y; }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor length to

return (Real)sqrt(lengthSqr())

maybe?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is fine to have them independent of each other. Their code will never change.


void normalize()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ WindowMsgHandledType LeftHUDInput( GameWindow *window, UnsignedInt msg,
|| (! TheGlobalData->m_useAlternateMouse && msg == GWM_RIGHT_DOWN)
|| (TheGlobalData->m_useAlternateMouse && msg == GWM_LEFT_DOWN) )
{
TheTacticalView->lookAt( &world );
TheTacticalView->userLookAt( &world );
break;
}

Expand Down
3 changes: 1 addition & 2 deletions GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,7 @@ void GameClient::update()
Drawable *draw = TheInGameUI->getFirstSelectedDrawable();
if ( draw )
{
const Coord3D *pos = draw->getPosition();
TheTacticalView->lookAt( pos );
TheTacticalView->userLookAt( draw->getPosition() );
}
else
TheInGameUI->setCameraTrackingDrawable( FALSE );
Expand Down
14 changes: 7 additions & 7 deletions GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2097,20 +2097,20 @@ void InGameUI::update()

if( m_cameraRotatingLeft && !m_cameraRotatingRight )
{
TheTacticalView->setAngle( TheTacticalView->getAngle() - rotateAngle );
TheTacticalView->userSetAngle( TheTacticalView->getAngle() - rotateAngle );
}
else if( m_cameraRotatingRight && !m_cameraRotatingLeft )
{
TheTacticalView->setAngle( TheTacticalView->getAngle() + rotateAngle );
TheTacticalView->userSetAngle( TheTacticalView->getAngle() + rotateAngle );
}

if( m_cameraZoomingIn && !m_cameraZoomingOut )
{
TheTacticalView->zoom( -zoomHeight );
TheTacticalView->userZoom( -zoomHeight );
}
else if( m_cameraZoomingOut && !m_cameraZoomingIn )
{
TheTacticalView->zoom( +zoomHeight );
TheTacticalView->userZoom( +zoomHeight );
}
}

Expand Down Expand Up @@ -3115,8 +3115,8 @@ void InGameUI::setScrolling( Bool isScrolling )
setMouseCursor( Mouse::SCROLL );

// break any camera locks
TheTacticalView->setCameraLock( INVALID_ID );
TheTacticalView->setCameraLockDrawable( nullptr );
TheTacticalView->userSetCameraLock( INVALID_ID );
TheTacticalView->userSetCameraLockDrawable( nullptr );
}
else
{
Expand Down Expand Up @@ -5904,7 +5904,7 @@ void InGameUI::selectNextIdleWorker()
}*/

// center on the unit
TheTacticalView->lookAt(selectThisObject->getPosition());
TheTacticalView->userLookAt(selectThisObject->getPosition());
}
}

Expand Down
Loading
Loading