From affa7b4a0c5b5f02604aaa1959a1c3d325c88526 Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Mon, 19 Jul 2021 08:36:19 +0200 Subject: [PATCH] Client: add camera.qc and viewmodel.qc, better restructure how we apply effects to the view. Add CVARs v_cambob, v_camroll, v_bobstyle... --- src/client/camera.qc | 79 +++++++++++++++++++++ src/client/defs.h | 7 -- src/client/include.src | 2 + src/client/view.qc | 61 ++-------------- src/client/viewmodel.qc | 152 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 239 insertions(+), 62 deletions(-) create mode 100644 src/client/camera.qc create mode 100644 src/client/viewmodel.qc diff --git a/src/client/camera.qc b/src/client/camera.qc new file mode 100644 index 00000000..2da223e5 --- /dev/null +++ b/src/client/camera.qc @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct +{ + float m_flSpeed; + float m_flFracSin; + float m_flTime; + float m_flMove; + float m_flDelta; + int m_iCycle; +} g_camBobVars[4], *pCamBob; + +/* tilts the camera for a head-bob like effect when moving */ +void +Camera_RunBob(__inout vector camera_angle) +{ + if (!autocvar(v_cambob, 0, "Enables bobbing effect for the first-person camera")) + return; + + int s = (float)getproperty(VF_ACTIVESEAT); + pCamBob = &g_camBobVars[s]; + + /* we don't really care about the vertical velocity */ + vector speed = pSeat->m_vecPredictedVelocity; + speed[2] = 0.0f; + pCamBob->m_flSpeed = vlen(speed); + + /* don't bother on low speeds */ + if ( pCamBob->m_flSpeed < 5.0f ) { + pCamBob->m_flMove = 0.0f; + pCamBob->m_flTime = 0.0f; /* progress has halted, start anew */ + return; + } else if (pSeat->m_ePlayer.flags & FL_ONGROUND) { + pCamBob->m_flMove = clframetime * (pCamBob->m_flSpeed * 0.01); + } + + pCamBob->m_flTime = (pCamBob->m_flTime += pCamBob->m_flMove); + pCamBob->m_flFracSin = fabs(sin(pCamBob->m_flTime * M_PI)); + pCamBob->m_iCycle = (int)pCamBob->m_flTime; + pCamBob->m_flDelta = (pCamBob->m_flFracSin * 0.0025f) * pCamBob->m_flSpeed; + + camera_angle[0] += pCamBob->m_flDelta; + + if (pCamBob->m_iCycle & 1) { + pCamBob->m_flDelta = -pCamBob->m_flDelta; + } + + camera_angle[2] += pCamBob->m_flDelta; +} + +/* applies a tilt to the camera for when we're strafing left to right */ +float +Camera_StrafeRoll(__inout vector camera_angle) +{ + if (!autocvar(v_camroll, 1, "Enables strafe-roll for the first-person camera")) + return; + + float roll; + makevectors(camera_angle); + + roll = dotproduct(pSeat->m_vecPredictedVelocity, v_right); + roll *= 0.015f; + + camera_angle[2] += roll; +} diff --git a/src/client/defs.h b/src/client/defs.h index c05ca163..65ee4cbc 100644 --- a/src/client/defs.h +++ b/src/client/defs.h @@ -35,14 +35,9 @@ var float FONT_20; var float FONT_CON; /* clientside cvars */ -var float autocvar_cl_bob = 0; -var float autocvar_v_bob = 0.01; -var float autocvar_v_bobcycle = 0.8; -var float autocvar_v_bobup = 0.5; var float autocvar_zoom_sensitivity = 1.0f; var int autocvar_cl_smoothstairs = TRUE; var int autocvar_cl_thirdperson = FALSE; -var int autocvar_v_bobclassic = TRUE; var int autocvar_v_lefthanded = FALSE; var string autocvar_cl_logofile = "lambda"; var vector autocvar_cl_logocolor = [255,0,0]; @@ -134,8 +129,6 @@ struct int m_iLastWeapon; int m_iOldWeapon; - float m_flBobTime; - float m_flBob; /* damage overlay */ float m_flDamageAlpha; diff --git a/src/client/include.src b/src/client/include.src index d5c4e04f..c003d61b 100644 --- a/src/client/include.src +++ b/src/client/include.src @@ -15,6 +15,8 @@ predict.qc npc.qc entities.qc modelevent.qc +camera.qc +viewmodel.qc view.qc damage.qc chat.qc diff --git a/src/client/view.qc b/src/client/view.qc index 6b8d03ed..386b4e6e 100644 --- a/src/client/view.qc +++ b/src/client/view.qc @@ -14,8 +14,6 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -var int autocvar_v_camroll = TRUE; - void View_Init(void) { @@ -58,7 +56,7 @@ View_AddEvent(void(void) pCallback, float flTime) pSeat->m_flEventMdl = pSeat->m_eViewModel.modelindex; } -void +void View_CalcViewport(int s, float fWinWidth, float fWinHeight) { //FIXME: this is awkward. renderscene internally rounds to pixels. @@ -96,45 +94,6 @@ View_CalcViewport(int s, float fWinWidth, float fWinHeight) } } -void -View_CalcBob(void) -{ - float cycle; - vector vel; - - if (self.flags & FL_ONGROUND == -1) { - return; - } - - pSeat->m_flBobTime += clframetime; - cycle = pSeat->m_flBobTime - (int)(pSeat->m_flBobTime / autocvar_v_bobcycle) * autocvar_v_bobcycle; - cycle /= autocvar_v_bobcycle; - - if (cycle < autocvar_v_bobup) { - cycle = MATH_PI * cycle / autocvar_v_bobup; - } else { - cycle = MATH_PI + MATH_PI * (cycle - autocvar_v_bobup)/(1.0 - autocvar_v_bobup); - } - - vel = pSeat->m_vecPredictedVelocity; - vel[2] = 0; - - float flBob = sqrt(vel[0] * vel[0] + vel[1] * vel[1]) * autocvar_v_bob; - flBob = flBob * 0.3 + flBob * 0.7 * sin(cycle); - pSeat->m_flBob = bound(-7, flBob, 4); -} - -float -View_CalcRoll(void) -{ - float roll; - makevectors(view_angles); - - roll = dotproduct(pSeat->m_vecPredictedVelocity, v_right); - roll *= 0.015f; - return autocvar_v_camroll ? roll : 0; -} - /* ==================== View_DrawViewModel @@ -159,13 +118,12 @@ View_DrawViewModel(void) return; } - View_CalcBob(); View_UpdateWeapon(m_eViewModel, m_eMuzzleflash); float fBaseTime2 = m_eViewModel.frame1time; float fBaseTime = m_eViewModel.frame1time; m_eViewModel.frame2time = pl.weapontime; - m_eViewModel.frame1time = pl.weapontime; + m_eViewModel.frame1time = pl.weapontime; Event_Callback(m_eViewModel.frame1time, fBaseTime2); processmodelevents(m_eViewModel.modelindex, m_eViewModel.frame, fBaseTime, m_eViewModel.frame1time, ClientGame_ModelEvent); @@ -174,20 +132,12 @@ View_DrawViewModel(void) m_eViewModel.angles = view_angles; m_eViewModel.colormap = pSeat->m_ePlayer.colormap; - // Give the gun a tilt effect like in old HL/CS versions - if (autocvar_v_bobclassic == 1) { - m_eViewModel.angles[2] = -pSeat->m_flBob; - } - /* now apply the scale hack */ m_eViewModel.scale = autocvar_r_viewmodelscale; - pSeat->m_flBob *= autocvar_r_viewmodelscale; m_eViewModel.origin = pSeat->m_vecPredictedOrigin + pl.view_ofs; - m_eViewModel.origin += [0,0,-1] + (v_forward * (pSeat->m_flBob * 0.4)) - + (v_forward * autocvar_v_gunofs[0]) - + (v_right * autocvar_v_gunofs[1]) - + (v_up * autocvar_v_gunofs[2]); + Viewmodel_CalcBob(); + Viewmodel_ApplyBob(m_eViewModel); // Left-handed weapons if (autocvar_v_lefthanded) { @@ -220,7 +170,8 @@ View_DrawViewModel(void) } if (pl.movetype == MOVETYPE_WALK) { - view_angles[2] = View_CalcRoll(); + Camera_StrafeRoll(view_angles); + Camera_RunBob(view_angles); setproperty(VF_ANGLES, view_angles + pl.punchangle); } } diff --git a/src/client/viewmodel.qc b/src/client/viewmodel.qc new file mode 100644 index 00000000..b0bbdd3b --- /dev/null +++ b/src/client/viewmodel.qc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +var float autocvar_v_bob = 0.01; +var float autocvar_v_bobcycle = 0.8; +var float autocvar_v_bobup = 0.5; +var int autocvar_v_bobstyle = 1; + +enum +{ + BOB_NONE, + BOB_CLASSIC, + BOB_VISIONS +}; + +struct +{ + float m_flBobTime; + float m_flBob; + float m_flBobCycle; + float m_flSpeed; +} g_viewBobVars[4], *pViewBob; + +/* classic bob, similar to Q1 and HL */ +void +Viewmodel_ClassicBobCalc(void) +{ + vector vel; + + pViewBob->m_flBobTime += clframetime; + pViewBob->m_flBobCycle = pViewBob->m_flBobTime - (int)(pViewBob->m_flBobTime / autocvar_v_bobcycle) * autocvar_v_bobcycle; + pViewBob->m_flBobCycle /= autocvar_v_bobcycle; + + if (pViewBob->m_flBobCycle < autocvar_v_bobup) { + pViewBob->m_flBobCycle = MATH_PI * pViewBob->m_flBobCycle / autocvar_v_bobup; + } else { + pViewBob->m_flBobCycle = MATH_PI + MATH_PI * (pViewBob->m_flBobCycle - autocvar_v_bobup)/(1.0 - autocvar_v_bobup); + } + + vel = pSeat->m_vecPredictedVelocity; + vel[2] = 0; + pViewBob->m_flSpeed = vlen(vel); + + float flBob = pViewBob->m_flSpeed * autocvar_v_bob; + flBob = flBob * 0.3 + flBob * 0.7 * sin(pViewBob->m_flBobCycle); + pViewBob->m_flBob = bound(-7, flBob, 4); +} + +void +Viewmodel_ClassicBobRun(entity gun) +{ + // Give the gun a tilt effect like in old HL/CS versions + if (autocvar(v_bobclassic, 1, "Viewmodel bob classic tilt switch") == 1) { + gun.angles[2] = -pViewBob->m_flBob; + } + + gun.origin += [0,0,-1] + (v_forward * (pViewBob->m_flBob * 0.4)) + + (v_forward * autocvar_v_gunofs[0]) + + (v_right * autocvar_v_gunofs[1]) + + (v_up * autocvar_v_gunofs[2]); +} + +/* Vera Visions signature bob */ +void +Viewmodel_VisionsBobCalc(void) +{ + /* same for now */ + Viewmodel_ClassicBobCalc(); +} + +void +Viewmodel_VisionsBobRun(entity gun) +{ + float sintime; + float strength; + gun.angles[2] = -pViewBob->m_flBob; + + vector angmod; + angmod[0] += pViewBob->m_flBob * 0.5f; + angmod[1] += pViewBob->m_flBob; + angmod[2] += pViewBob->m_flBob * 2.0f; + gun.angles += angmod * 2.0f; + + /* sway with speed */ + sintime = sin( time * 1.5f); + strength = pViewBob->m_flSpeed * 0.01f; + gun.angles[0] += strength * sintime; + gun.angles[1] += strength * sintime; + gun.angles[2] += strength * sintime; + + gun.origin += [0,0,-1] - (v_up * (-pViewBob->m_flBob * 0.2)) + + (v_forward * autocvar_v_gunofs[0]) + + (v_right * autocvar_v_gunofs[1]) + + (v_up * autocvar_v_gunofs[2]); +} + +/* bob vars are calculated separately from application, so that if there's + * more than one viewmodel we won't affect the speed of the bob by running + * the math too many times */ +void +Viewmodel_CalcBob(void) +{ + int s = (float)getproperty(VF_ACTIVESEAT); + pViewBob = &g_viewBobVars[s]; + + switch (autocvar_v_bobstyle) + { + case BOB_CLASSIC: + Viewmodel_ClassicBobCalc(); + break; + case BOB_VISIONS: + Viewmodel_VisionsBobCalc(); + break; + default: + break; + } + + /* make sure it's adjusted for scale */ + pViewBob->m_flBob *= autocvar_r_viewmodelscale; +} + +void +Viewmodel_ApplyBob(entity gun) +{ + int s = (float)getproperty(VF_ACTIVESEAT); + pViewBob = &g_viewBobVars[s]; + + switch (autocvar_v_bobstyle) + { + case BOB_CLASSIC: + Viewmodel_ClassicBobRun(gun); + break; + case BOB_VISIONS: + Viewmodel_VisionsBobRun(gun); + break; + default: + break; + } +}