Skip to content
Merged
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
4 changes: 4 additions & 0 deletions Core/GameEngine/Include/Common/GameDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
#define PRESERVE_RETAIL_BEHAVIOR (1) // Retain behavior present in retail Generals 1.08 and Zero Hour 1.04
#endif

#ifndef PRESERVE_RETAIL_SCRIPTED_CAMERA
#define PRESERVE_RETAIL_SCRIPTED_CAMERA (1) // Retain scripted camera behavior present in retail Generals 1.08 and Zero Hour 1.04
#endif

#ifndef RETAIL_COMPATIBLE_CRC
#define RETAIL_COMPATIBLE_CRC (1) // Game is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04
#endif
Expand Down
13 changes: 11 additions & 2 deletions Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DView.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,18 +275,27 @@ class W3DView : public View, public SubsystemInterface
Bool m_cameraHasMovedSinceRequest; ///< If true, throw out all saved locations
VecPosRequests m_locationRequests; ///< These are cached. New requests are added here

Coord3D m_cameraOffset; ///< offset for camera from view center
Coord3D m_previousLookAtPosition; ///< offset for camera from view center
Coord3D m_previousLookAtPosition;
Coord2D m_scrollAmount; ///< scroll speed
Real m_scrollAmountCutoffSqr; ///< scroll speed at which we do not adjust height

Real m_groundLevel; ///< height of ground.
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
// TheSuperHackers @tweak Uses the initial ground level for preserving the original look of the scripted camera,
// because alterations to the ground level do affect the positioning in subtle ways.
Real m_initialGroundLevel;
#endif

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

Real getCameraOffsetZ() const;
Real getDesiredHeight(Real x, Real y) const;
Real getDesiredZoom(Real x, Real y) const;
Real getMaxHeight(Real x, Real y) const;
Real getMaxZoom(Real x, Real y) const;
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
Expand Down
135 changes: 87 additions & 48 deletions Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ W3DView::W3DView()
m_3DCamera = nullptr;
m_2DCamera = nullptr;
m_groundLevel = 10.0f;
m_cameraOffset.z = 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)));
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
m_initialGroundLevel = m_groundLevel;
#endif

m_viewFilterMode = FM_VIEW_DEFAULT;
m_viewFilter = FT_VIEW_DEFAULT;
Expand Down Expand Up @@ -261,9 +261,11 @@ void W3DView::buildCameraPosition( Vector3& sourcePos, Vector3& targetPos )
pos.x += m_shakeOffset.x;
pos.y += m_shakeOffset.y;

sourcePos.X = m_cameraOffset.x;
sourcePos.Y = m_cameraOffset.y;
sourcePos.Z = m_cameraOffset.z;
// TheSuperHackers @info The default pitch affects the look-at distance to the target.
// This is strange math which would need special attention when changed.
sourcePos.Z = getCameraOffsetZ();
sourcePos.Y = -(sourcePos.Z / tan(TheGlobalData->m_cameraPitch * (PI / 180.0)));
sourcePos.X = -(sourcePos.Y * tan(TheGlobalData->m_cameraYaw * (PI / 180.0)));

// set position of camera itself
if (m_useRealZoomCam) //WST 10/10/2002 Real Zoom using FOV
Expand Down Expand Up @@ -358,7 +360,7 @@ void W3DView::buildCameraTransform( Matrix3D *transform, const Vector3 &sourcePo
{
//m_3DCamera->Set_View_Plane(DEG_TO_RADF(50.0f));
//DEBUG_LOG(("zoom %f, SourceZ %f, posZ %f, groundLevel %f CamOffZ %f",
// zoom, sourcePos.Z, pos.z, groundLevel,m_cameraOffset.z));
// zoom, sourcePos.Z, pos.z, groundLevel, getCameraOffsetZ()));

// build new camera transform
transform->Make_Identity();
Expand Down Expand Up @@ -422,8 +424,7 @@ 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 desiredZoom = getDesiredZoom(m_pos.x, m_pos.y);
const Real adjustZoom = desiredZoom - m_zoom;
if (fabs(adjustZoom) >= 0.001f)
{
Expand Down Expand Up @@ -640,6 +641,67 @@ void W3DView::getPickRay(const ICoord2D *screen, Vector3 *rayStart, Vector3 *ray
*rayEnd += *rayStart; //get point on far clip plane along ray from camera.
}

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Real W3DView::getCameraOffsetZ() const
{
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
// TheSuperHackers @info xezon 04/12/2025 It is necessary to use the initial ground level for the
// scripted camera height to preserve the original look of it. Otherwise the forward distance
// of the camera will slightly change the view pitch.
if (!m_isUserControlled)
{
return m_initialGroundLevel + TheGlobalData->m_cameraHeight;
}
#endif

return m_groundLevel + TheGlobalData->m_maxCameraHeight;
}

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Real W3DView::getDesiredHeight(Real x, Real y) const
{
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
// TheSuperHackers @info xezon 06/12/2025 The height above ground must be relative to the current
// terrain height because the ground level is not updated for it.
if (!m_isUserControlled)
{
return getHeightAroundPos(x, y) + m_heightAboveGround;
}
#endif

return m_groundLevel + m_heightAboveGround;
}

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Real W3DView::getMaxHeight(Real x, Real y) const
{
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
if (!m_isUserControlled)
{
return getHeightAroundPos(x, y) + m_maxHeightAboveGround;
}
#endif

return m_groundLevel + m_maxHeightAboveGround;
}

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Real W3DView::getDesiredZoom(Real x, Real y) const
{
return getDesiredHeight(x, y) / getCameraOffsetZ();
}

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Real W3DView::getMaxZoom(Real x, Real y) const
{
return getMaxHeight(x, y) / getCameraOffsetZ();
}

//-------------------------------------------------------------------------------------------------
/** set the transform matrix of m_3DCamera, based on m_pos & m_angle */
//-------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -1423,7 +1485,7 @@ void W3DView::update()
// 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;
m_currentHeightAboveGround = getCameraOffsetZ() * m_zoom - m_terrainHeightAtPivot;

if (m_okToAdjustHeight)
{
Expand Down Expand Up @@ -2055,7 +2117,7 @@ void W3DView::setHeightAboveGround(Real z)
void W3DView::setZoom(Real z)
{
m_heightAboveGround = m_maxHeightAboveGround * z;
m_zoom = z;
m_zoom = getDesiredZoom(m_pos.x, m_pos.y);

stopDoingScriptedCamera();
m_CameraArrivedAtWaypointOnPathFlag = false;
Expand All @@ -2068,18 +2130,8 @@ void W3DView::setZoom(Real z)
void W3DView::setZoomToDefault()
{
// default zoom has to be max, otherwise players will just zoom to max always

// terrain height + desired height offset == cameraOffset * actual zoom
// find best approximation of max terrain height we can see
Real terrainHeightMax = getHeightAroundPos(m_pos.x, m_pos.y);

Real desiredHeight = (terrainHeightMax + m_maxHeightAboveGround);
Real desiredZoom = desiredHeight / m_cameraOffset.z;

//DEBUG_LOG(("W3DView::setZoomToDefault() Current zoom: %g Desired zoom: %g", m_zoom, desiredZoom));

m_zoom = desiredZoom;
m_heightAboveGround = m_maxHeightAboveGround;
m_zoom = getMaxZoom(m_pos.x, m_pos.y);

stopDoingScriptedCamera();
m_CameraArrivedAtWaypointOnPathFlag = false;
Expand Down Expand Up @@ -2448,10 +2500,14 @@ void W3DView::initHeightForMap()
{
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)));
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
// jba - starting ground level can't exceed this height.
constexpr const Real MAX_GROUND_LEVEL = 120.0f;
const Real accurateGroundLevel = TheTerrainLogic->getGroundHeight(m_pos.x, m_pos.y);
m_initialGroundLevel = min(MAX_GROUND_LEVEL, accurateGroundLevel);
#endif
}

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DView::resetPivotToGround( void )
Expand Down Expand Up @@ -2638,22 +2694,14 @@ void W3DView::cameraModFinalZoom( Real finalZoom, Real easeIn, Real easeOut )
{
if (hasScriptedState(Scripted_Rotate))
{
Real terrainHeightMax = getHeightAroundPos(m_pos.x, m_pos.y);
Real maxHeight = (terrainHeightMax + m_maxHeightAboveGround);
Real maxZoom = maxHeight / m_cameraOffset.z;

Real time = (m_rcInfo.numFrames + m_rcInfo.numHoldFrames - m_rcInfo.curFrame)*TheW3DFrameLengthInMsec;
zoomCamera( finalZoom*maxZoom, time, time*easeIn, time*easeOut );
zoomCamera( finalZoom*getMaxZoom(m_pos.x, m_pos.y), time, time*easeIn, time*easeOut );
}
if (hasScriptedState(Scripted_MoveOnWaypointPath))
{
Coord3D pos = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints];
Real terrainHeightMax = getHeightAroundPos(pos.x, pos.y);
Real maxHeight = (terrainHeightMax + m_maxHeightAboveGround);
Real maxZoom = maxHeight / m_cameraOffset.z;

Real time = m_mcwpInfo.totalTimeMilliseconds - m_mcwpInfo.elapsedTimeMilliseconds;
zoomCamera( finalZoom*maxZoom, time, time*easeIn, time*easeOut );
zoomCamera( finalZoom*getMaxZoom(pos.x, pos.y), time, time*easeIn, time*easeOut );
}
}

Expand Down Expand Up @@ -2888,13 +2936,7 @@ void W3DView::resetCamera(const Coord3D *location, Int milliseconds, Real easeIn
// m_mcwpInfo.cameraAngle[2] = m_defaultAngle;
View::setAngle(m_mcwpInfo.cameraAngle[0]);

// terrain height + desired height offset == cameraOffset * actual zoom
// find best approximation of max terrain height we can see
Real terrainHeightMax = getHeightAroundPos(location->x, location->y);
Real desiredHeight = (terrainHeightMax + m_maxHeightAboveGround);
Real desiredZoom = desiredHeight / m_cameraOffset.z;

zoomCamera( desiredZoom, milliseconds, easeIn, easeOut ); // this isn't right... or is it?
zoomCamera( getMaxZoom(location->x, location->y), milliseconds, easeIn, easeOut );

pitchCamera( 1.0f, milliseconds, easeIn, easeOut );
}
Expand Down Expand Up @@ -3194,6 +3236,9 @@ void W3DView::setUserControlled(Bool value)
if (m_isUserControlled != value)
{
m_isUserControlled = value;
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
m_zoom = getDesiredZoom(m_pos.x, m_pos.y);
#endif
}
}

Expand Down Expand Up @@ -3248,9 +3293,6 @@ void W3DView::moveAlongWaypointPath(Real milliseconds)
View::setAngle(m_mcwpInfo.cameraAngle[m_mcwpInfo.numWaypoints]);

m_groundLevel = m_mcwpInfo.groundHeight[m_mcwpInfo.numWaypoints];
/////////////////////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)));

Coord3D pos = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints];
pos.z = 0;
Expand Down Expand Up @@ -3324,9 +3366,6 @@ void W3DView::moveAlongWaypointPath(Real milliseconds)

m_groundLevel = m_mcwpInfo.groundHeight[m_mcwpInfo.curSegment]*factor1 +
m_mcwpInfo.groundHeight[m_mcwpInfo.curSegment+1]*factor2;
//////////////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)));

Coord3D start, mid, end;
if (factor<0.5) {
Expand Down
2 changes: 2 additions & 0 deletions Generals/Code/GameEngine/Include/Common/GlobalData.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ class GlobalData : public SubsystemInterface
Real m_viewportHeightScale; // The height scale of the tactical view ranging 0..1. Used to hide the world behind the Control Bar.
Real m_cameraPitch;
Real m_cameraYaw;
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
Real m_cameraHeight;
#endif
Real m_maxCameraHeight;
Real m_minCameraHeight;
Real m_terrainHeightAtEdgeOfMap;
Expand Down
4 changes: 4 additions & 0 deletions Generals/Code/GameEngine/Source/Common/GlobalData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ GlobalData* GlobalData::m_theOriginal = nullptr;
{ "ViewportHeightScale", INI::parseReal, nullptr, offsetof( GlobalData, m_viewportHeightScale ) },
{ "CameraPitch", INI::parseReal, nullptr, offsetof( GlobalData, m_cameraPitch ) },
{ "CameraYaw", INI::parseReal, nullptr, offsetof( GlobalData, m_cameraYaw ) },
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
{ "CameraHeight", INI::parseReal, nullptr, offsetof( GlobalData, m_cameraHeight ) },
#endif
{ "MaxCameraHeight", INI::parseReal, nullptr, offsetof( GlobalData, m_maxCameraHeight ) },
{ "MinCameraHeight", INI::parseReal, nullptr, offsetof( GlobalData, m_minCameraHeight ) },
{ "TerrainHeightAtEdgeOfMap", INI::parseReal, nullptr, offsetof( GlobalData, m_terrainHeightAtEdgeOfMap ) },
Expand Down Expand Up @@ -838,7 +840,9 @@ GlobalData::GlobalData()

m_cameraPitch = 0.0f;
m_cameraYaw = 0.0f;
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
m_cameraHeight = 0.0f;
#endif
m_minCameraHeight = 100.0f;
m_maxCameraHeight = 300.0f;
m_terrainHeightAtEdgeOfMap = 0.0f;
Expand Down
2 changes: 2 additions & 0 deletions GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,9 @@ class GlobalData : public SubsystemInterface
Real m_viewportHeightScale; // The height scale of the tactical view ranging 0..1. Used to hide the world behind the Control Bar.
Real m_cameraPitch;
Real m_cameraYaw;
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
Real m_cameraHeight;
#endif
Real m_maxCameraHeight;
Real m_minCameraHeight;
Real m_terrainHeightAtEdgeOfMap;
Expand Down
4 changes: 4 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ GlobalData* GlobalData::m_theOriginal = nullptr;
{ "ViewportHeightScale", INI::parseReal, nullptr, offsetof( GlobalData, m_viewportHeightScale ) },
{ "CameraPitch", INI::parseReal, nullptr, offsetof( GlobalData, m_cameraPitch ) },
{ "CameraYaw", INI::parseReal, nullptr, offsetof( GlobalData, m_cameraYaw ) },
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
{ "CameraHeight", INI::parseReal, nullptr, offsetof( GlobalData, m_cameraHeight ) },
#endif
{ "MaxCameraHeight", INI::parseReal, nullptr, offsetof( GlobalData, m_maxCameraHeight ) },
{ "MinCameraHeight", INI::parseReal, nullptr, offsetof( GlobalData, m_minCameraHeight ) },
{ "TerrainHeightAtEdgeOfMap", INI::parseReal, nullptr, offsetof( GlobalData, m_terrainHeightAtEdgeOfMap ) },
Expand Down Expand Up @@ -842,7 +844,9 @@ GlobalData::GlobalData()

m_cameraPitch = 0.0f;
m_cameraYaw = 0.0f;
#if PRESERVE_RETAIL_SCRIPTED_CAMERA
m_cameraHeight = 0.0f;
#endif
m_minCameraHeight = 100.0f;
m_maxCameraHeight = 300.0f;
m_terrainHeightAtEdgeOfMap = 0.0f;
Expand Down
Loading