diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DModelDraw.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DModelDraw.h index 9defb85d715..50d47a29945 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DModelDraw.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/Module/W3DModelDraw.h @@ -507,6 +507,11 @@ class W3DModelDraw : public DrawModule, public ObjectDrawInterface Bool m_hideHeadlights; Bool m_pauseAnimation; Int m_animationMode; + Real m_prevTurretAngle[MAX_TURRETS]; + Real m_currTurretAngle[MAX_TURRETS]; + Real m_prevTurretPitch[MAX_TURRETS]; + Real m_currTurretPitch[MAX_TURRETS]; + UnsignedInt m_lastTurretSampledFrame; void adjustAnimation(const ModelConditionInfo* prevState, Real prevAnimFraction); Real getCurrentAnimFraction() const; diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp index 6b9d2bd3095..9129c73bebc 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp @@ -41,10 +41,11 @@ #include "Common/GameState.h" #include "Common/GlobalData.h" #include "Common/PerfTimer.h" -#include "Common/RandomValue.h" #include "Common/ThingTemplate.h" #include "Common/GameLOD.h" -#include "Common/Xfer.h" +#include "Common/GameEngine.h" +#include "Common/FramePacer.h" +#include "Common/GameUtility.h" #include "GameClient/Drawable.h" #include "GameClient/FXList.h" #include "GameClient/Shadow.h" @@ -1740,6 +1741,14 @@ W3DModelDraw::W3DModelDraw(Thing *thing, const ModuleData* moduleData) : DrawMod { m_weaponRecoilInfoVec[i].clear(); } + m_lastTurretSampledFrame = 0; + for (i = 0; i < MAX_TURRETS; ++i) + { + m_prevTurretAngle[i] = 0.0f; + m_currTurretAngle[i] = 0.0f; + m_prevTurretPitch[i] = 0.0f; + m_currTurretPitch[i] = 0.0f; + } m_needRecalcBoneParticleSystems = false; m_fullyObscuredByShroud = false; @@ -2422,28 +2431,62 @@ void W3DModelDraw::handleClientTurretPositioning() if (!m_curState || !(m_curState->m_validStuff & ModelConditionInfo::TURRETS_VALID)) return; + const Object* obj = getDrawable() ? getDrawable()->getObject() : nullptr; + const AIUpdateInterface* ai = obj ? obj->getAIUpdateInterface() : nullptr; + UnsignedInt currentLogicFrame = TheGameLogic ? TheGameLogic->getFrame() : 0; + + if (currentLogicFrame != m_lastTurretSampledFrame) + { + for (int tslot = 0; tslot < MAX_TURRETS; ++tslot) + { + Real rawAngle = 0.0f; + Real rawPitch = 0.0f; + if (ai) + { + ai->getTurretRotAndPitch((WhichTurretType)tslot, &rawAngle, &rawPitch); + } + + if (m_lastTurretSampledFrame > 0 && currentLogicFrame == m_lastTurretSampledFrame + 1) + { + m_prevTurretAngle[tslot] = m_currTurretAngle[tslot]; + m_currTurretAngle[tslot] = rawAngle; + + m_prevTurretPitch[tslot] = m_currTurretPitch[tslot]; + m_currTurretPitch[tslot] = rawPitch; + } + else + { + m_prevTurretAngle[tslot] = rawAngle; + m_currTurretAngle[tslot] = rawAngle; + + m_prevTurretPitch[tslot] = rawPitch; + m_currTurretPitch[tslot] = rawPitch; + } + } + m_lastTurretSampledFrame = currentLogicFrame; + } + + Real alpha = (TheGameEngine && TheFramePacer) ? TheGameEngine->getLogicTimeAccumulator() * TheFramePacer->getActualLogicTimeScaleFps() : 0.0f; + Real clampAlpha = (alpha > 1.0f) ? 1.0f : ((alpha < 0.0f) ? 0.0f : alpha); + for (int tslot = 0; tslot < MAX_TURRETS; ++tslot) { const ModelConditionInfo::TurretInfo& tur = m_curState->m_turrets[tslot]; - Real turretAngle = 0; - Real turretPitch = 0; if (tur.m_turretAngleBone || tur.m_turretPitchBone) { - const Object *obj = getDrawable()->getObject(); - if (obj) - { - const AIUpdateInterface* ai = obj->getAIUpdateInterface(); - if (ai) - ai->getTurretRotAndPitch((WhichTurretType)tslot, &turretAngle, &turretPitch); - } + Real angleDelta = stdAngleDiff(m_currTurretAngle[tslot], m_prevTurretAngle[tslot]); + Real interpTurretAngle = m_prevTurretAngle[tslot] + angleDelta * clampAlpha; + + Real pitchDelta = m_currTurretPitch[tslot] - m_prevTurretPitch[tslot]; + Real interpTurretPitch = m_prevTurretPitch[tslot] + pitchDelta * clampAlpha; // do turret, if any if (tur.m_turretAngleBone != 0) { if (m_curState) - turretAngle += tur.m_turretArtAngle; + interpTurretAngle += tur.m_turretArtAngle; Matrix3D turretXfrm(1); - turretXfrm.Rotate_Z(turretAngle); + turretXfrm.Rotate_Z(interpTurretAngle); if (m_renderObject) { m_renderObject->Capture_Bone( tur.m_turretAngleBone ); @@ -2455,9 +2498,9 @@ void W3DModelDraw::handleClientTurretPositioning() if (tur.m_turretPitchBone != 0) { if (m_curState) - turretPitch += tur.m_turretArtPitch; + interpTurretPitch += tur.m_turretArtPitch; Matrix3D turretPitchXfrm(1); - turretPitchXfrm.Rotate_Y(-turretPitch); + turretPitchXfrm.Rotate_Y(-interpTurretPitch); if (m_renderObject) { m_renderObject->Capture_Bone( tur.m_turretPitchBone ); diff --git a/Generals/Code/GameEngine/Include/Common/GameEngine.h b/Generals/Code/GameEngine/Include/Common/GameEngine.h index 8dfb25151ac..c1209974711 100644 --- a/Generals/Code/GameEngine/Include/Common/GameEngine.h +++ b/Generals/Code/GameEngine/Include/Common/GameEngine.h @@ -102,6 +102,9 @@ class GameEngine : public SubsystemInterface Bool m_quitting; ///< true when we need to quit the game Bool m_isActive; ///< app has OS focus. + +public: + Real getLogicTimeAccumulator() const { return m_logicTimeAccumulator; } }; inline void GameEngine::setQuitting( Bool quitting ) { m_quitting = quitting; } diff --git a/GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h b/GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h index bcf91c3643f..ebbc8dba0c1 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/GameEngine.h @@ -101,6 +101,9 @@ class GameEngine : public SubsystemInterface Bool m_quitting; ///< true when we need to quit the game Bool m_isActive; ///< app has OS focus. + +public: + Real getLogicTimeAccumulator() const { return m_logicTimeAccumulator; } }; inline void GameEngine::setQuitting( Bool quitting ) { m_quitting = quitting; } diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h b/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h index e0e4707f93c..2bc20664781 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/Drawable.h @@ -48,6 +48,7 @@ class DrawModule; class ClientUpdateModule; class View; class Locomotor; +class ProjectileUpdateInterface; class Anim2D; class Shadow; class ModuleInfo; @@ -652,6 +653,7 @@ class Drawable : public Thing, private: const Locomotor* getLocomotor() const; + void applySubFrameExtrapolation(); // note, these are lazily allocated! TintEnvelope* m_selectionFlashEnvelope; ///< used for selection flash, works WITH m_colorTintEnvelope @@ -711,6 +713,7 @@ class Drawable : public Thing, Matrix3D m_instance; ///< The instance matrix that holds the initial/default position & orientation Real m_instanceScale; ///< the uniform scale factor applied to the instance matrix before it is sent to W3D. + Matrix3D m_visualExtrapolationMtx; ///< Decoupled visual glide matrix DrawableInfo m_drawableInfo; ///< structure pointed to by W3D render objects so they know which drawable they belong to. @@ -730,11 +733,18 @@ class Drawable : public Thing, Bool m_hiddenByStealth; ///< drawable is hidden due to stealth Bool m_instanceIsIdentity; ///< If true, instance matrix can be skipped Bool m_drawableFullyObscuredByShroud; ///