From 1affc36f0628ffb380428b26303ff534aa735204 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 18 Apr 2023 17:28:20 +0300 Subject: [PATCH] engine: client: more accurate ScreenShake implementation, thanks @vasiavasiavasia95 for sharing it --- engine/client/cl_game.c | 77 +++++++++++++++++++--------------------- engine/client/cl_parse.c | 15 +++++--- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 43b2e952..41b32711 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -2119,58 +2119,52 @@ pfnCalcShake */ void GAME_EXPORT pfnCalcShake( void ) { - int i; - float fraction, freq; - float localAmp; - - if( clgame.shake.time == 0 ) - return; + screen_shake_t *const shake = &clgame.shake; + float frametime, fraction, freq; + int i; - if(( cl.time > clgame.shake.time ) || clgame.shake.amplitude <= 0 || clgame.shake.frequency <= 0 ) + if( cl.time > shake->time || shake->amplitude <= 0 || shake->frequency <= 0 || shake->duration <= 0 ) { - memset( &clgame.shake, 0, sizeof( clgame.shake )); + // reset shake + if( shake->time != 0 ) + { + shake->time = 0; + shake->applied_angle = 0; + VectorClear( shake->applied_offset ); + } + return; } - if( cl.time > clgame.shake.next_shake ) + frametime = cl_clientframetime(); + + if( cl.time > shake->next_shake ) { - // higher frequency means we recalc the extents more often and perturb the display again - clgame.shake.next_shake = cl.time + ( 1.0f / clgame.shake.frequency ); + // get next shake time based on frequency over duration + shake->next_shake = (float)cl.time + shake->frequency / shake->duration; - // compute random shake extents (the shake will settle down from this) + // randomize each shake for( i = 0; i < 3; i++ ) - clgame.shake.offset[i] = COM_RandomFloat( -clgame.shake.amplitude, clgame.shake.amplitude ); - clgame.shake.angle = COM_RandomFloat( -clgame.shake.amplitude * 0.25f, clgame.shake.amplitude * 0.25f ); + shake->offset[i] = COM_RandomFloat( -shake->amplitude, shake->amplitude ); + shake->angle = COM_RandomFloat( -shake->amplitude * 0.25f, shake->amplitude * 0.25f ); } - // ramp down amplitude over duration (fraction goes from 1 to 0 linearly with slope 1/duration) - fraction = ( clgame.shake.time - cl.time ) / clgame.shake.duration; + // get initial fraction and frequency values over the duration + fraction = ((float)cl.time - shake->time ) / shake->duration; + freq = fraction != 0.0f ? ( shake->frequency / fraction ) * shake->frequency : 0.0f; - // ramp up frequency over duration - if( fraction ) - { - freq = ( clgame.shake.frequency / fraction ); - } - else - { - freq = 0; - } - - // square fraction to approach zero more quickly - fraction *= fraction; + // quickly approach zero but apply time over sine wave + fraction *= fraction * sin( cl.time * freq ); - // Sine wave that slowly settles to zero - fraction = fraction * sin( cl.time * freq ); + // apply shake offset + for( i = 0; i < 3; i++ ) + shake->applied_offset[i] = shake->offset[i] * fraction; - // add to view origin - VectorScale( clgame.shake.offset, fraction, clgame.shake.applied_offset ); + // apply roll angle + shake->applied_angle = shake->angle * fraction; - // add to roll - clgame.shake.applied_angle = clgame.shake.angle * fraction; - - // drop amplitude a bit, less for higher frequency shakes - localAmp = clgame.shake.amplitude * ( host.frametime / ( clgame.shake.duration * clgame.shake.frequency )); - clgame.shake.amplitude -= localAmp; + // decrease amplitude, but slower on longer shakes or higher frequency + shake->amplitude -= shake->amplitude * ( frametime / ( shake->frequency * shake->duration )); } /* @@ -2181,8 +2175,11 @@ pfnApplyShake */ void GAME_EXPORT pfnApplyShake( float *origin, float *angles, float factor ) { - if( origin ) VectorMA( origin, factor, clgame.shake.applied_offset, origin ); - if( angles ) angles[ROLL] += clgame.shake.applied_angle * factor; + if( origin ) + VectorMA( origin, factor, clgame.shake.applied_offset, origin ); + + if( angles ) + angles[ROLL] += clgame.shake.applied_angle * factor; } /* diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index fd689ff0..8b6dbd3f 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1854,10 +1854,17 @@ Set screen shake */ void CL_ParseScreenShake( sizebuf_t *msg ) { - clgame.shake.amplitude = (float)(word)MSG_ReadShort( msg ) * (1.0f / (float)(1<<12)); - clgame.shake.duration = (float)(word)MSG_ReadShort( msg ) * (1.0f / (float)(1<<12)); - clgame.shake.frequency = (float)(word)MSG_ReadShort( msg ) * (1.0f / (float)(1<<8)); - clgame.shake.time = cl.time + Q_max( clgame.shake.duration, 0.01f ); + float amplitude = (float)(word)MSG_ReadShort( msg ) * ( 1.0f / (float)( 1 << 12 )); + float duration = (float)(word)MSG_ReadShort( msg ) * ( 1.0f / (float)( 1 << 12 )); + float frequency = (float)(word)MSG_ReadShort( msg ) * ( 1.0f / (float)( 1 << 8 )); + + // don't overwrite larger existing shake + if( amplitude > clgame.shake.amplitude ) + clgame.shake.amplitude = amplitude; + + clgame.shake.duration = duration; + clgame.shake.time = cl.time + clgame.shake.duration; + clgame.shake.frequency = frequency; clgame.shake.next_shake = 0.0f; // apply immediately }