Skip to content

Commit 3c73dd0

Browse files
authored
feat(view): Save position and view direction of player camera in MSG_SET_REPLAY_CAMERA (#2631)
1 parent dc43b31 commit 3c73dd0

18 files changed

Lines changed: 217 additions & 57 deletions

File tree

Core/GameEngine/Include/GameClient/View.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,9 @@ class View : public Snapshot
192192
const Coord3D &getPosition() const { return m_pos; } ///< Returns position camera is looking at
193193
Coord2D getPosition2D() const { Coord2D c = { m_pos.x, m_pos.y }; return c; } ///< Returns position camera is looking at
194194

195-
virtual const Coord3D& get3DCameraPosition() const = 0; ///< Returns the actual camera position
195+
virtual Coord3D get3DCameraPosition() const { Coord3D c={0,0,0}; return c; } ///< Returns the actual camera position
196+
virtual Coord3D get3DCameraDirection() const { Coord3D c={0,0,0}; return c; } ///< Returns the actual camera view direction
197+
virtual void set3DCameraLookAt(const Coord3D &pos, const Coord3D &dir, Real roll) {} ///< Set the actual camera position and view direction
196198

197199
virtual Real getZoom() { return m_zoom; }
198200
virtual void setZoom(Real z) { m_zoom = z; }
@@ -395,11 +397,6 @@ class ViewDummy : public View
395397
return 0;
396398
}
397399
virtual void forceRedraw() override {}
398-
virtual const Coord3D& get3DCameraPosition() const override
399-
{
400-
static Coord3D zero = {0,0,0};
401-
return zero;
402-
}
403400
virtual WorldToScreenReturn worldToScreenTriReturn(const Coord3D *w, ICoord2D *s ) override
404401
{
405402
return WTS_INVALID;

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,9 @@ class W3DView : public View, public SubsystemInterface
220220

221221
CameraClass *get3DCamera() const { return m_3DCamera; }
222222

223-
virtual const Coord3D& get3DCameraPosition() const override;
223+
virtual Coord3D get3DCameraPosition() const override; ///< Returns the actual camera position
224+
virtual Coord3D get3DCameraDirection() const override; ///< Returns the actual camera view direction
225+
virtual void set3DCameraLookAt(const Coord3D &pos, const Coord3D &dir, Real roll) override; ///< Set the actual camera position and view direction
224226

225227
virtual void setCameraLock(ObjectID id) override;
226228
virtual void setSnapMode( CameraLockType lockType, Real lockDist ) override;
@@ -295,7 +297,9 @@ class W3DView : public View, public SubsystemInterface
295297
Real getDesiredZoom(Real x, Real y) const;
296298
Real getMaxHeight(Real x, Real y) const;
297299
Real getMaxZoom(Real x, Real y) const;
298-
void setCameraTransform(); ///< set the transform matrix of m_3DCamera, based on m_pos & m_angle
300+
void updateCameraTransform(); ///< update the transform matrix of m_3DCamera, based on m_pos & m_angle
301+
void updateCameraClipPlanes();
302+
void setCameraTransform(const Matrix3D &transform);
299303
void buildCameraPosition(Vector3 &sourcePos, Vector3 &targetPos);
300304
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
301305
Bool zoomCameraToDesiredHeight();

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

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -705,13 +705,27 @@ Real W3DView::getMaxZoom(Real x, Real y) const
705705
//-------------------------------------------------------------------------------------------------
706706
/** set the transform matrix of m_3DCamera, based on m_pos & m_angle */
707707
//-------------------------------------------------------------------------------------------------
708-
void W3DView::setCameraTransform()
708+
void W3DView::updateCameraTransform()
709709
{
710710
if (TheGlobalData->m_headless)
711711
return;
712712

713-
m_cameraHasMovedSinceRequest = true;
713+
updateCameraClipPlanes();
714+
715+
Vector3 sourcePos;
716+
Vector3 targetPos;
717+
buildCameraPosition(sourcePos, targetPos);
718+
719+
Matrix3D cameraTransform;
720+
buildCameraTransform(&cameraTransform, sourcePos, targetPos);
721+
722+
setCameraTransform(cameraTransform);
723+
}
714724

725+
//-------------------------------------------------------------------------------------------------
726+
//-------------------------------------------------------------------------------------------------
727+
void W3DView::updateCameraClipPlanes()
728+
{
715729
Real farZ = 1200.0f;
716730

717731
if (m_useRealZoomCam) //WST 10.19.2002
@@ -730,18 +744,19 @@ void W3DView::setCameraTransform()
730744
}
731745

732746
m_3DCamera->Set_Clip_Planes(NearZ, farZ);
747+
}
748+
749+
//-------------------------------------------------------------------------------------------------
750+
//-------------------------------------------------------------------------------------------------
751+
void W3DView::setCameraTransform(const Matrix3D &transform)
752+
{
753+
m_cameraHasMovedSinceRequest = true;
733754

734755
#if defined(RTS_DEBUG)
735756
m_3DCamera->Set_View_Plane( m_FOV, -1 );
736757
#endif
737758

738-
// rebuild it (even if we just did it due to camera constraints)
739-
Vector3 sourcePos;
740-
Vector3 targetPos;
741-
buildCameraPosition(sourcePos, targetPos);
742-
Matrix3D cameraTransform;
743-
buildCameraTransform(&cameraTransform, sourcePos, targetPos);
744-
m_3DCamera->Set_Transform( cameraTransform );
759+
m_3DCamera->Set_Transform(transform);
745760

746761
if (TheTerrainRenderObject)
747762
{
@@ -790,14 +805,35 @@ void W3DView::init()
790805
}
791806

792807
//-------------------------------------------------------------------------------------------------
793-
const Coord3D& W3DView::get3DCameraPosition() const
808+
Coord3D W3DView::get3DCameraPosition() const
794809
{
795810
Vector3 camera = m_3DCamera->Get_Position();
796-
static Coord3D pos;
797-
pos.set( camera.X, camera.Y, camera.Z );
811+
Coord3D pos = { camera.X, camera.Y, camera.Z };
798812
return pos;
799813
}
800814

815+
//-------------------------------------------------------------------------------------------------
816+
Coord3D W3DView::get3DCameraDirection() const
817+
{
818+
Vector3 forward = m_3DCamera->Get_Forward_Dir();
819+
Coord3D dir = { forward.X, forward.Y, forward.Z };
820+
return dir;
821+
}
822+
823+
//-------------------------------------------------------------------------------------------------
824+
void W3DView::set3DCameraLookAt(const Coord3D &pos, const Coord3D &dir, Real roll)
825+
{
826+
Vector3 camPos(pos.x, pos.y, pos.z);
827+
Vector3 camDir(dir.x, dir.y, dir.z);
828+
Matrix3D transform;
829+
transform.Look_At_Dir(camPos, camDir, roll);
830+
831+
updateCameraClipPlanes();
832+
setCameraTransform(transform);
833+
834+
m_recalcCamera = false;
835+
}
836+
801837
//-------------------------------------------------------------------------------------------------
802838
//-------------------------------------------------------------------------------------------------
803839
void W3DView::reset()
@@ -1560,7 +1596,7 @@ void W3DView::update()
15601596
// (gth) C&C3 if m_isCameraSlaved then force the camera to update each frame
15611597
if (m_recalcCamera || m_isCameraSlaved)
15621598
{
1563-
setCameraTransform();
1599+
updateCameraTransform();
15641600
m_recalcCamera = false;
15651601
}
15661602

Core/Libraries/Include/Lib/BaseType.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ struct RealRange
219219
hi = 0.0f;
220220
}
221221

222+
bool is(Real value) const
223+
{
224+
return lo == value && hi == value;
225+
}
226+
222227
// combine the given range with us such that we now encompass
223228
// both ranges
224229
void combine( RealRange &other )
@@ -238,6 +243,11 @@ struct Coord2D
238243
y = 0.0f;
239244
}
240245

246+
bool is(Real value) const
247+
{
248+
return x == value && y == value;
249+
}
250+
241251
Real length() const { return (Real)sqrt( x*x + y*y ); }
242252
Real lengthSqr() const { return x*x + y*y; }
243253

@@ -325,6 +335,11 @@ struct ICoord2D
325335
y = 0;
326336
}
327337

338+
bool is(Int value) const
339+
{
340+
return x == value && y == value;
341+
}
342+
328343
Int length() const { return (Int)sqrt( (double)(x*x + y*y) ); }
329344
};
330345

@@ -338,6 +353,11 @@ struct Region2D
338353
hi.zero();
339354
}
340355

356+
bool is(Real value) const
357+
{
358+
return lo.is(value) && hi.is(value);
359+
}
360+
341361
Real width() const { return hi.x - lo.x; }
342362
Real height() const { return hi.y - lo.y; }
343363
Bool isInRegion( Real x, Real y ) const { return (lo.x < x) && (x < hi.x) && (lo.y < y) && (y < hi.y); }
@@ -353,6 +373,11 @@ struct IRegion2D
353373
hi.zero();
354374
}
355375

376+
bool is(Int value) const
377+
{
378+
return lo.is(value) && hi.is(value);
379+
}
380+
356381
Int width() const { return hi.x - lo.x; }
357382
Int height() const { return hi.y - lo.y; }
358383
Bool isInRegion( Int x, Int y ) const { return (lo.x < x) && (x < hi.x) && (lo.y < y) && (y < hi.y); }
@@ -392,6 +417,11 @@ struct Coord3D
392417
z = 0.0f;
393418
}
394419

420+
bool is(Real value) const
421+
{
422+
return x == value && y == value && z == value;
423+
}
424+
395425
void add( const Coord3D *a )
396426
{
397427
x += a->x;
@@ -454,6 +484,11 @@ struct ICoord3D
454484
y = 0;
455485
z = 0;
456486
}
487+
488+
bool is(Int value) const
489+
{
490+
return x == value && y == value && z == value;
491+
}
457492
};
458493

459494
// For alternative see AABoxClass
@@ -467,6 +502,11 @@ struct Region3D
467502

468503
void zero() { lo.zero(); hi.zero(); }
469504

505+
bool is(Real value) const
506+
{
507+
return lo.is(value) && hi.is(value);
508+
}
509+
470510
void setFromPointsNoZ(const Coord3D* points, Int count)
471511
{
472512
lo.x = points[0].x;
@@ -531,6 +571,11 @@ struct IRegion3D
531571
hi.zero();
532572
}
533573

574+
bool is(Int value) const
575+
{
576+
return lo.is(value) && hi.is(value);
577+
}
578+
534579
Int width() const { return hi.x - lo.x; }
535580
Int height() const { return hi.y - lo.y; }
536581
Int depth() const { return hi.z - lo.z; }

Core/Libraries/Source/WWVegas/WWMath/matrix3d.cpp

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -355,26 +355,30 @@ Vector3 Matrix3D::Inverse_Rotate_Vector(const Vector3 &vect) const
355355
*=============================================================================================*/
356356
void Matrix3D::Look_At(const Vector3 &p,const Vector3 &t,float roll)
357357
{
358-
float dx,dy,dz; //vector from p to t
359-
float len1,len2;
360-
float sinp,cosp; //sine and cosine of the pitch ("up-down" tilt about x)
361-
float siny,cosy; //sine and cosine of the yaw ("left-right"tilt about z)
358+
Vector3 dir(t - p);
359+
dir.Normalize();
362360

363-
dx = (t[0] - p[0]);
364-
dy = (t[1] - p[1]);
365-
dz = (t[2] - p[2]);
361+
Look_At_Dir(p, dir, roll);
362+
}
366363

367-
len1 = (float)WWMath::Sqrt(dx*dx + dy*dy + dz*dz);
368-
len2 = (float)WWMath::Sqrt(dx*dx + dy*dy);
369364

370-
if (len1 != 0.0f) {
371-
sinp = dz/len1;
372-
cosp = len2/len1;
373-
} else {
374-
sinp = 0.0f;
375-
cosp = 1.0f;
376-
}
365+
void Matrix3D::Look_At_Dir(const Vector3 &pos, const Vector3 &dir, float roll)
366+
{
367+
float sinp, cosp; //sine and cosine of the pitch ("up-down" tilt about x)
368+
float siny, cosy; //sine and cosine of the yaw ("left-right"tilt about z)
369+
370+
float dx = dir.X;
371+
float dy = dir.Y;
372+
float dz = dir.Z;
373+
374+
// length of projection onto XY plane
375+
float len2 = (float)WWMath::Sqrt(dx*dx + dy*dy);
376+
377+
// pitch
378+
sinp = dz;
379+
cosp = len2;
377380

381+
// yaw
378382
if (len2 != 0.0f) {
379383
siny = dy/len2;
380384
cosy = dx/len2;
@@ -388,9 +392,9 @@ void Matrix3D::Look_At(const Vector3 &p,const Vector3 &t,float roll)
388392
Row[1].X = -1.0f; Row[1].Y = 0.0f; Row[1].Z = 0.0f;
389393
Row[2].X = 0.0f; Row[2].Y = 1.0f; Row[2].Z = 0.0f;
390394

391-
Row[0].W = p.X;
392-
Row[1].W = p.Y;
393-
Row[2].W = p.Z;
395+
Row[0].W = pos.X;
396+
Row[1].W = pos.Y;
397+
Row[2].W = pos.Z;
394398

395399
// Yaw rotation to make the matrix look at the projection of the target
396400
// into the x-y plane

Core/Libraries/Source/WWVegas/WWMath/matrix3d.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,10 @@ class Matrix3D
284284
// Used for pointing cameras at targets.
285285
void Look_At(const Vector3 &p,const Vector3 &t,float roll);
286286

287+
// Points the negative Z axis at dir.
288+
// Used for looking with cameras into directions.
289+
void Look_At_Dir(const Vector3 &pos, const Vector3 &dir, float roll);
290+
287291
// Previous look_at function follows the camera coordinate convention.
288292
// This one follows the object convention used in Commando and G. I
289293
// special cased this convention since it is used so much by us rather

Generals/Code/GameEngine/Source/Common/MessageStream.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,14 @@ GameMessage::~GameMessage()
8888
*/
8989
const GameMessageArgumentType *GameMessage::getArgument( Int argIndex ) const
9090
{
91-
static const GameMessageArgumentType junk = { 0 };
92-
9391
int i=0;
9492
for( GameMessageArgument *a = m_argList; a; a=a->m_next, i++ )
9593
if (i == argIndex)
9694
return &a->m_data;
9795

9896
DEBUG_CRASH(("argument not found"));
99-
return &junk;
97+
static const GameMessageArgumentType zero = { 0 };
98+
return &zero;
10099
}
101100

102101
/**

Generals/Code/GameEngine/Source/GameClient/MessageStream/LookAtXlat.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,9 @@ GameMessageDisposition LookAtTranslator::translateGameMessage(const GameMessage
549549
msg->appendRealArgument( currentView.getZoom() );
550550
msg->appendIntegerArgument( (Int)TheMouse->getMouseCursor() );
551551
msg->appendPixelArgument( m_currentPos );
552+
// TheSuperHackers @tweak Save 3D camera position and direction to recover optimal playback precision
553+
msg->appendLocationArgument( TheTacticalView->get3DCameraPosition() );
554+
msg->appendLocationArgument( TheTacticalView->get3DCameraDirection() );
552555
}
553556
break;
554557
}

Generals/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,6 +1929,8 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
19291929
const Real angle = msg->getArgument( 1 )->real;
19301930
const Real pitch = msg->getArgument( 2 )->real;
19311931
const Real zoom = msg->getArgument( 3 )->real;
1932+
const Mouse::MouseCursor mouseCursor = static_cast<Mouse::MouseCursor>(msg->getArgument( 4 )->integer);
1933+
const ICoord2D mousePos = msg->getArgument( 5 )->pixel;
19321934

19331935
// TheSuperHackers @info Definitely call in user mode to ensure the camera operates with auto-zoom
19341936
// over terrain elevations, because the Replay Camera does not store the absolute camera location,
@@ -1942,13 +1944,26 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
19421944
const Coord2D scroll = {0, 0};
19431945
TheTacticalView->userScrollBy(&scroll);
19441946

1945-
// TheSuperHackers @fix xezon 18/09/2025 Lock the new location to avoid user input from changing the camera in this frame.
1947+
if (msg->getArgumentCount() >= 8)
1948+
{
1949+
// TheSuperHackers @feature Override all the settings above with real camera position and view direction.
1950+
// This ensures that the camera looks EXACTLY like it was at the time of recording, no matter how the
1951+
// View is configured or tweaked. Note that the above settings are still required to set regardless, because
1952+
// when the replay camera is exited, then the pivot position and angles will be needed to build the camera
1953+
// where it was left off.
1954+
const Coord3D camPos = msg->getArgument( 6 )->location;
1955+
const Coord3D camDir = msg->getArgument( 7 )->location;
1956+
1957+
TheTacticalView->setUserControlled(false);
1958+
TheTacticalView->set3DCameraLookAt(camPos, camDir, 0.0f);
1959+
}
1960+
1961+
// TheSuperHackers @fix Lock the new location to avoid user input from changing the camera in this frame.
19461962
TheTacticalView->lockUserControlUntilFrame( getFrame() + 1 );
19471963

19481964
if (!TheLookAtTranslator->hasMouseMovedRecently())
19491965
{
1950-
TheMouse->setCursor( (Mouse::MouseCursor)(msg->getArgument( 4 )->integer) );
1951-
ICoord2D mousePos = msg->getArgument( 5 )->pixel;
1966+
TheMouse->setCursor( mouseCursor );
19521967
TheMouse->setPosition( mousePos.x, mousePos.y );
19531968
TheLookAtTranslator->setCurrentPos( mousePos );
19541969
}

0 commit comments

Comments
 (0)