diff --git a/source/core/gameinput.cpp b/source/core/gameinput.cpp index d6ec60433..b41b8a0e5 100644 --- a/source/core/gameinput.cpp +++ b/source/core/gameinput.cpp @@ -178,33 +178,33 @@ void PlayerAngles::applyPitch(float const horz, ESyncBits* actions, double const // Process mouse input. if (horz) { - pActor->spr.Angles.Pitch += DAngle::fromDeg(horz); + activeAngles().Pitch += DAngle::fromDeg(horz); *actions &= ~SB_CENTERVIEW; } // Process keyboard input. if (auto aiming = !!(*actions & SB_AIM_DOWN) - !!(*actions & SB_AIM_UP)) { - pActor->spr.Angles.Pitch += getTicrateScale(PITCH_AIMSPEED) * scaleAdjust * aiming; + activeAngles().Pitch += getTicrateScale(PITCH_AIMSPEED) * scaleAdjust * aiming; *actions &= ~SB_CENTERVIEW; } if (auto looking = !!(*actions & SB_LOOK_DOWN) - !!(*actions & SB_LOOK_UP)) { - pActor->spr.Angles.Pitch += getTicrateScale(PITCH_LOOKSPEED) * scaleAdjust * looking; + activeAngles().Pitch += getTicrateScale(PITCH_LOOKSPEED) * scaleAdjust * looking; *actions |= SB_CENTERVIEW; } // Do return to centre. if ((*actions & SB_CENTERVIEW) && !(*actions & (SB_LOOK_UP|SB_LOOK_DOWN))) { - const auto pitch = abs(pActor->spr.Angles.Pitch); + const auto pitch = abs(activeAngles().Pitch); const auto scale = pitch > PITCH_CNTRSINEOFFSET ? (pitch - PITCH_CNTRSINEOFFSET).Cos() : 1.; - scaletozero(pActor->spr.Angles.Pitch, PITCH_CENTERSPEED * scale, scaleAdjust); - if (!pActor->spr.Angles.Pitch.Sgn()) *actions &= ~SB_CENTERVIEW; + scaletozero(activeAngles().Pitch, PITCH_CENTERSPEED * scale, scaleAdjust); + if (!activeAngles().Pitch.Sgn()) *actions &= ~SB_CENTERVIEW; } // clamp before we finish, even if it's clamped in the drawer. - pActor->spr.Angles.Pitch = ClampViewPitch(pActor->spr.Angles.Pitch); + activeAngles().Pitch = ClampViewPitch(activeAngles().Pitch); } @@ -217,7 +217,7 @@ void PlayerAngles::applyPitch(float const horz, ESyncBits* actions, double const void PlayerAngles::applyYaw(float const avel, ESyncBits* actions, double const scaleAdjust) { // add player's input - pActor->spr.Angles.Yaw += DAngle::fromDeg(avel); + activeAngles().Yaw += DAngle::fromDeg(avel); if (*actions & SB_TURNAROUND) { @@ -240,7 +240,7 @@ void PlayerAngles::applyYaw(float const avel, ESyncBits* actions, double const s add -= YawSpin; YawSpin = nullAngle; } - pActor->spr.Angles.Yaw += add; + activeAngles().Yaw += add; } } @@ -336,7 +336,7 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, PlayerAngles& w, P if (arc.isReading()) { - w.backupViewAngles(); + w.resetRenderAngles(); } } return arc; diff --git a/source/core/gameinput.h b/source/core/gameinput.h index ad8b3cade..ac85fbc9c 100644 --- a/source/core/gameinput.h +++ b/source/core/gameinput.h @@ -11,6 +11,9 @@ struct PlayerAngles // Player viewing angles, separate from the camera. DRotator PrevViewAngles, ViewAngles; + // Player camera angles, not for direct manipulation within the playsim. + DRotator RenderAngles; + // Holder of current yaw spin state for the 180 degree turn. DAngle YawSpin; @@ -25,13 +28,20 @@ struct PlayerAngles void doViewYaw(const ESyncBits actions); // General methods. - void backupViewAngles() { PrevViewAngles = ViewAngles; } - void setActor(DCoreActor* const actor) { pActor = actor; } - - // Angle getters. + void initialize(DCoreActor* const actor, const DAngle viewyaw = nullAngle) + { + if ((pActor = actor)) RenderAngles = PrevLerpAngles = pActor->spr.Angles; + PrevViewAngles.Yaw = ViewAngles.Yaw = viewyaw; + } DAngle getPitchWithView() { - return pActor->spr.Angles.Pitch + ViewAngles.Pitch; + return ClampViewPitch(pActor->spr.Angles.Pitch + ViewAngles.Pitch); + } + + // Render angle functions. + DRotator& activeAngles() + { + return !SyncInput() ? RenderAngles : pActor->spr.Angles; } DRotator lerpViewAngles(const double interpfrac) { @@ -39,7 +49,24 @@ struct PlayerAngles } DRotator getRenderAngles(const double interpfrac) { - return (!SyncInput() ? pActor->spr.Angles : pActor->interpolatedangles(interpfrac)) + lerpViewAngles(interpfrac); + // Get angles and return with clamped off pitch. + auto angles = RenderAngles + lerpViewAngles(interpfrac); + angles.Pitch = ClampViewPitch(angles.Pitch); + return angles; + } + void updateRenderAngles(const double interpfrac) + { + // Apply the current interpolated angle state to the render angles. + const auto lerpAngles = pActor->interpolatedangles(interpfrac); + RenderAngles += lerpAngles - PrevLerpAngles; + PrevLerpAngles = lerpAngles; + } + void resetRenderAngles() + { + // Apply any last remaining ticrate angle updates and reset variables. + RenderAngles += pActor->spr.Angles - PrevLerpAngles; + PrevLerpAngles = pActor->spr.Angles = RenderAngles; + PrevViewAngles = ViewAngles; } // Draw code helpers. @@ -84,6 +111,7 @@ private: }; // Private data which should never be accessed publically. + DRotator PrevLerpAngles; DCoreActor* pActor; // Internal angle updater to reduce boilerplate from the public setters. diff --git a/source/core/inputstate.h b/source/core/inputstate.h index dad92afe0..6aa203d6e 100644 --- a/source/core/inputstate.h +++ b/source/core/inputstate.h @@ -109,7 +109,7 @@ extern double inputScale; inline bool SyncInput() { - return true; //gamesetinput || cl_syncinput || cl_capfps; + return gamesetinput || cl_syncinput || cl_capfps; } inline float backendinputscale() diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp index 68377cc84..53342ef72 100644 --- a/source/games/blood/src/blood.cpp +++ b/source/games/blood/src/blood.cpp @@ -423,6 +423,9 @@ void GameInterface::Ticker() thinktime.Clock(); for (int i = connecthead; i >= 0; i = connectpoint2[i]) { + // this must be done before the view is backed up. + gPlayer[i].Angles.resetRenderAngles(); + viewBackupView(i); playerProcess(&gPlayer[i]); } diff --git a/source/games/blood/src/player.cpp b/source/games/blood/src/player.cpp index d007d2d6a..08c0ad948 100644 --- a/source/games/blood/src/player.cpp +++ b/source/games/blood/src/player.cpp @@ -801,7 +801,8 @@ void playerStart(int nPlayer, int bNewLevel) auto actor = actSpawnSprite(pStartZone->sector, pStartZone->pos, 6, 1); assert(actor->hasX()); pPlayer->actor = actor; - pPlayer->Angles.setActor(actor); + pPlayer->Angles = {}; + pPlayer->Angles.initialize(pPlayer->actor); DUDEINFO* pDudeInfo = &dudeInfo[kDudePlayer1 + nPlayer - kDudeBase]; pPlayer->pDudeInfo = pDudeInfo; playerSetRace(pPlayer, kModeHuman); @@ -1522,8 +1523,6 @@ void ProcessInput(PLAYER* pPlayer) Item_JumpBoots = 3 }; - pPlayer->Angles.backupViewAngles(); - DBloodActor* actor = pPlayer->actor; POSTURE* pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture]; InputPacket* pInput = &pPlayer->input; diff --git a/source/games/blood/src/view.cpp b/source/games/blood/src/view.cpp index f3b2a3c24..3fb5958c4 100644 --- a/source/games/blood/src/view.cpp +++ b/source/games/blood/src/view.cpp @@ -611,6 +611,9 @@ void viewDrawScreen(bool sceneonly) } else interpfrac = 1.; + // update render angles. + pPlayer->Angles.updateRenderAngles(interpfrac); + if (cl_interpolate) { DoInterpolations(interpfrac); diff --git a/source/games/duke/src/gameloop.cpp b/source/games/duke/src/gameloop.cpp index 687ff9183..fbb4a98d5 100644 --- a/source/games/duke/src/gameloop.cpp +++ b/source/games/duke/src/gameloop.cpp @@ -69,6 +69,9 @@ void GameInterface::Ticker() ud.cameraactor = nullptr; everyothertime++; + // this must be done before the view is backed up. + ps[myconnectindex].Angles.resetRenderAngles(); + DukeSpriteIterator it; while (auto ac = it.Next()) { diff --git a/source/games/duke/src/player_d.cpp b/source/games/duke/src/player_d.cpp index 1ed4c6d70..27bcc0fde 100644 --- a/source/games/duke/src/player_d.cpp +++ b/source/games/duke/src/player_d.cpp @@ -2700,8 +2700,6 @@ void processinput_d(int snum) p = &ps[snum]; auto pact = p->GetActor(); - p->Angles.backupViewAngles(); - ESyncBits& actions = p->sync.actions; auto sb_fvel = PlayerInputForwardVel(snum); diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp index 2c6da7fe2..f9eeadc63 100644 --- a/source/games/duke/src/player_r.cpp +++ b/source/games/duke/src/player_r.cpp @@ -3214,8 +3214,6 @@ void processinput_r(int snum) auto p = &ps[snum]; auto pact = p->GetActor(); - p->Angles.backupViewAngles(); - ESyncBits& actions = p->sync.actions; auto sb_fvel = PlayerInputForwardVel(snum); diff --git a/source/games/duke/src/premap.cpp b/source/games/duke/src/premap.cpp index 6c7614285..31e6f71d9 100644 --- a/source/games/duke/src/premap.cpp +++ b/source/games/duke/src/premap.cpp @@ -125,7 +125,6 @@ void resetplayerstats(int snum) p->footprintpal = 0; p->footprintshade = 0; p->jumping_toggle = 0; - p->Angles.PrevViewAngles.Pitch = p->Angles.ViewAngles.Pitch = nullAngle; p->bobcounter = 0; p->on_ground = 0; p->player_par = 0; @@ -149,10 +148,6 @@ void resetplayerstats(int snum) p->heat_on = 0; p->jetpack_on = 0; p->holoduke_on = nullptr; - - p->Angles.PrevViewAngles.Yaw = p->Angles.ViewAngles.Yaw = (currentLevel->levelNumber & 1)? DAngle90 : -DAngle90; - p->Angles.PrevViewAngles.Roll = p->Angles.ViewAngles.Roll = nullAngle; - p->newOwner =nullptr; p->jumping_counter = 0; p->hard_landing = 0; @@ -160,7 +155,6 @@ void resetplayerstats(int snum) p->fric.X = 0; p->fric.Y = 0; p->somethingonplayer = nullptr; - p->Angles.YawSpin = nullAngle; p->on_crane = nullptr; @@ -622,7 +616,8 @@ void resetpspritevars(int g, const DVector3& startpos, const DAngle startang) act->spr.pal = ps[j].palookup = ud.user_pals[j]; ps[j].actor = act; - ps[j].Angles.setActor(act); + ps[j].Angles = {}; + ps[j].Angles.initialize(ps[j].actor, (currentLevel->levelNumber & 1)? DAngle90 : -DAngle90); ps[j].frag_ps = j; act->SetOwner(act); diff --git a/source/games/duke/src/render.cpp b/source/games/duke/src/render.cpp index 7a4162a53..990fa37dd 100644 --- a/source/games/duke/src/render.cpp +++ b/source/games/duke/src/render.cpp @@ -221,6 +221,9 @@ void displayrooms(int snum, double interpfrac, bool sceneonly) player_struct* p = &ps[snum]; + // update render angles. + p->Angles.updateRenderAngles(interpfrac); + if (automapMode == am_full || !p->insector()) return; diff --git a/source/games/exhumed/src/exhumed.cpp b/source/games/exhumed/src/exhumed.cpp index 2ab6b867a..4fed68089 100644 --- a/source/games/exhumed/src/exhumed.cpp +++ b/source/games/exhumed/src/exhumed.cpp @@ -369,6 +369,10 @@ void GameInterface::Ticker() } else if (EndLevel == 0) { + // this must be done before the view is backed up. + PlayerList[nLocalPlayer].Angles.resetRenderAngles(); + UpdatePlayerSpriteAngle(&PlayerList[nLocalPlayer]); + inita = inita.Normalized360(); auto& lPlayerVel = sPlayerInput[nLocalPlayer].vel; diff --git a/source/games/exhumed/src/init.cpp b/source/games/exhumed/src/init.cpp index fc368b9d1..cc6277a4b 100644 --- a/source/games/exhumed/src/init.cpp +++ b/source/games/exhumed/src/init.cpp @@ -156,7 +156,7 @@ uint8_t LoadLevel(MapRecord* map) for (i = 0; i < kMaxPlayers; i++) { PlayerList[i].pActor = nullptr; - PlayerList[i].Angles.setActor(nullptr); + PlayerList[i].Angles = {}; } g_visibility = 1024; diff --git a/source/games/exhumed/src/player.cpp b/source/games/exhumed/src/player.cpp index 797cbbe1e..1f1b9d06f 100644 --- a/source/games/exhumed/src/player.cpp +++ b/source/games/exhumed/src/player.cpp @@ -181,7 +181,7 @@ void InitPlayer() { for (int i = 0; i < kMaxPlayers; i++) { PlayerList[i].pActor = nullptr; - PlayerList[i].Angles.setActor(nullptr); + PlayerList[i].Angles = {}; PlayerList[i].pPlayerPushSect = nullptr; PlayerList[i].pPlayerViewSect = nullptr; } @@ -210,7 +210,7 @@ void InitPlayerInventory(int nPlayer) PlayerList[nPlayer].nLives = kDefaultLives; PlayerList[nPlayer].pActor = nullptr; - PlayerList[nPlayer].Angles.setActor(nullptr); + PlayerList[nPlayer].Angles = {}; PlayerList[nPlayer].nRun = -1; PlayerList[nPlayer].nPistolClip = 6; @@ -262,7 +262,7 @@ void RestartPlayer(int nPlayer) ChangeActorStat(pActor, 0); plr->pActor = nullptr; - plr->Angles.setActor(nullptr); + plr->Angles = {}; DExhumedActor* pFloorSprite = plr->pPlayerFloorSprite; if (pFloorSprite != nullptr) { @@ -356,7 +356,8 @@ void RestartPlayer(int nPlayer) plr->nSeqSize = 0; plr->pActor = pActor; - plr->Angles.setActor(pActor); + plr->Angles = {}; + plr->Angles.initialize(plr->pActor); plr->bIsMummified = false; if (plr->invincibility >= 0) { @@ -904,8 +905,6 @@ void AIPlayer::Tick(RunListEvent* ev) int nAction = PlayerList[nPlayer].nAction; int nActionB = PlayerList[nPlayer].nAction; - PlayerList[nPlayer].Angles.backupViewAngles(); - pPlayerActor->vel.XY() = sPlayerInput[nPlayer].vel; if (sPlayerInput[nPlayer].nItem > -1) diff --git a/source/games/exhumed/src/view.cpp b/source/games/exhumed/src/view.cpp index fd6a85766..7f2e572f5 100644 --- a/source/games/exhumed/src/view.cpp +++ b/source/games/exhumed/src/view.cpp @@ -201,6 +201,10 @@ void DrawView(double interpfrac, bool sceneonly) auto pDop = pPlayer->pDoppleSprite; auto nDoppleOldCstat = pDop->spr.cstat; + // update render angles. + pPlayer->Angles.updateRenderAngles(interpfrac); + UpdatePlayerSpriteAngle(pPlayer); + if (nSnakeCam >= 0 && !sceneonly) { DExhumedActor* pActor = SnakeList[nSnakeCam].pSprites[0]; diff --git a/source/games/sw/src/draw.cpp b/source/games/sw/src/draw.cpp index 2f9a9ad90..e8e832b62 100644 --- a/source/games/sw/src/draw.cpp +++ b/source/games/sw/src/draw.cpp @@ -1237,6 +1237,9 @@ void drawscreen(PLAYER* pp, double interpfrac, bool sceneonly) if (cl_sointerpolation) so_dointerpolations(interpfrac); } + // update render angles. + pp->Angles.updateRenderAngles(interpfrac); + // Get initial player position, interpolating if required. DVector3 tpos = camerapp->actor->getRenderPos(interpfrac); DRotator tangles = camerapp->Angles.getRenderAngles(interpfrac); diff --git a/source/games/sw/src/game.cpp b/source/games/sw/src/game.cpp index b901ec979..9e1ac646f 100644 --- a/source/games/sw/src/game.cpp +++ b/source/games/sw/src/game.cpp @@ -572,7 +572,7 @@ void TerminateLevel(void) pp->DoPlayerAction = nullptr; pp->actor = nullptr; - pp->Angles.setActor(nullptr); + pp->Angles = {}; pp->PlayerUnderActor = nullptr; diff --git a/source/games/sw/src/ninja.cpp b/source/games/sw/src/ninja.cpp index 98c9a6e5b..11b439665 100644 --- a/source/games/sw/src/ninja.cpp +++ b/source/games/sw/src/ninja.cpp @@ -2407,7 +2407,8 @@ void InitPlayerSprite(PLAYER* pp, const DVector3& spawnpos, const DAngle startan pp->actor = actor; pp->pnum = pnum; - pp->Angles.setActor(actor); + pp->Angles = {}; + pp->Angles.initialize(pp->actor); actor->spr.cstat |= (CSTAT_SPRITE_BLOCK | CSTAT_SPRITE_BLOCK_HITSCAN); actor->spr.extra |= (SPRX_PLAYER_OR_ENEMY); diff --git a/source/games/sw/src/player.cpp b/source/games/sw/src/player.cpp index 43b151c19..90c953aba 100644 --- a/source/games/sw/src/player.cpp +++ b/source/games/sw/src/player.cpp @@ -1553,10 +1553,10 @@ void DoPlayerTurnVehicle(PLAYER* pp, float avel, double zz, double floordist) if (avel != 0) { - auto sum = pp->actor->spr.Angles.Yaw + DAngle::fromDeg(avel); + auto sum = pp->Angles.activeAngles().Yaw + DAngle::fromDeg(avel); if (MultiClipTurn(pp, sum, zz, floordist)) { - pp->actor->spr.Angles.Yaw = sum; + pp->Angles.activeAngles().Yaw = sum; } } } @@ -1619,7 +1619,7 @@ void DoPlayerTurnTurret(PLAYER* pp, float avel) if (fabs(avel) >= FLT_EPSILON) { - new_ang = pp->actor->spr.Angles.Yaw + DAngle::fromDeg(avel); + new_ang = pp->Angles.activeAngles().Yaw + DAngle::fromDeg(avel); if (sop->limit_ang_center >= nullAngle) { @@ -1634,10 +1634,10 @@ void DoPlayerTurnTurret(PLAYER* pp, float avel) } } - pp->actor->spr.Angles.Yaw = new_ang; + pp->Angles.activeAngles().Yaw = new_ang; } - OperateSectorObject(pp->sop, pp->actor->spr.Angles.Yaw, pp->sop->pmid); + OperateSectorObject(pp->sop, pp->Angles.activeAngles().Yaw, pp->sop->pmid); } //--------------------------------------------------------------------------- @@ -6697,6 +6697,9 @@ void MoveSkipSavePos(void) { pp = Player + pnum; + // this must be done before the view is backed up. + pp->Angles.resetRenderAngles(); + pp->actor->backuploc(); pp->obob_z = pp->bob_z; pp->opbob_amt = pp->pbob_amt; @@ -6991,7 +6994,6 @@ void domovethings(void) // Reset flags used while tying input to framerate pp->Flags2 &= ~(PF2_INPUT_CAN_AIM|PF2_INPUT_CAN_TURN_GENERAL|PF2_INPUT_CAN_TURN_VEHICLE|PF2_INPUT_CAN_TURN_TURRET); - pp->Angles.backupViewAngles(); // disable synchronised input if set by game. resetForcedSyncInput();