]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
Add underwater sound filter
authorDaft Mugi <daftmugi@gmail.com>
Thu, 29 Feb 2024 10:37:37 +0000 (04:37 -0600)
committerbones_was_here <bones_was_here@xonotic.au>
Sun, 10 Mar 2024 15:43:24 +0000 (01:43 +1000)
Enabled by default. Use 'snd_waterfx 0' to disable it.

Adapted code from Ironwail so that it works in this engine.

Cherry-picked from daftmugi/wrath-darkplaces

client.h
snd_main.c
snd_main.h
snd_mix.c
view.c

index fc44f3483f8d1b2747b1a1b155775502e8d58b7b..9523196d85ca49f6369986df12b1abc0f25c6946 100644 (file)
--- a/client.h
+++ b/client.h
@@ -1135,6 +1135,9 @@ typedef struct client_state_s
 
        // used by EntityState5_ReadUpdate
        skeleton_t *engineskeletonobjects;
+
+       // used by underwater sound filter (snd_waterfx)
+       qbool view_underwater;
 }
 client_state_t;
 
index 821ed7b403dcf955e367bb09ef6e50aab16cf74f..029e65d953f581e85c59c7078755f80771f2b3f0 100644 (file)
@@ -120,6 +120,7 @@ static const speakerlayout_t snd_speakerlayouts[] =
        }
 };
 
+void S_SetUnderwaterIntensity(void);
 
 // =======================================================================
 // Internal sound data & structures
@@ -183,6 +184,7 @@ cvar_t snd_spatialization_prologic_frontangle = {CF_CLIENT | CF_ARCHIVE, "snd_sp
 cvar_t snd_spatialization_occlusion = {CF_CLIENT | CF_ARCHIVE, "snd_spatialization_occlusion", "1", "enable occlusion testing on spatialized sounds, which simply quiets sounds that are blocked by the world; 1 enables PVS method, 2 enables LineOfSight method, 3 enables both"};
 
 // Cvars declared in snd_main.h (shared with other snd_*.c files)
+cvar_t snd_waterfx = {CF_CLIENT | CF_ARCHIVE, "snd_waterfx", "1", "underwater sound filter"};
 cvar_t _snd_mixahead = {CF_CLIENT | CF_ARCHIVE, "_snd_mixahead", "0.15", "how much sound to mix ahead of time"};
 cvar_t snd_streaming = {CF_CLIENT | CF_ARCHIVE, "snd_streaming", "1", "enables keeping compressed ogg sound files compressed, decompressing them only as needed, otherwise they will be decompressed completely at load (may use a lot of memory); when set to 2, streaming is performed even if this would waste memory"};
 cvar_t snd_streaming_length = {CF_CLIENT | CF_ARCHIVE, "snd_streaming_length", "1", "decompress sounds completely if they are less than this play time when snd_streaming is 1"};
@@ -812,6 +814,7 @@ void S_Init(void)
        Cvar_RegisterVariable(&ambient_fade);
        Cvar_RegisterVariable(&snd_noextraupdate);
        Cvar_RegisterVariable(&snd_show);
+       Cvar_RegisterVariable(&snd_waterfx);
        Cvar_RegisterVariable(&_snd_mixahead);
        Cvar_RegisterVariable(&snd_swapstereo); // for people with backwards sound wiring
        Cvar_RegisterVariable(&snd_channellayout);
@@ -1850,7 +1853,6 @@ void S_StaticSound (sfx_t *sfx, vec3_t origin, float fvol, float attenuation)
        S_PlaySfxOnChannel (sfx, target_chan, CHANNELFLAG_FORCELOOP, origin, fvol, attenuation, true, 0, 0, 0, 1.0f);
 }
 
-
 /*
 ===================
 S_UpdateAmbientSounds
@@ -1870,7 +1872,10 @@ static void S_UpdateAmbientSounds (void)
        if (cl.worldmodel && cl.worldmodel->brush.AmbientSoundLevelsForPoint)
                cl.worldmodel->brush.AmbientSoundLevelsForPoint(cl.worldmodel, listener_origin, ambientlevels, sizeof(ambientlevels));
 
+
        // Calc ambient sound levels
+       S_SetUnderwaterIntensity();
+
        for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++)
        {
                chan = &channels[ambient_channel];
index ff5e43f188a37f0aeef8ba4fbfaff6c2583024d2..23541bcc15bcaaddee3cc069f629d7d62374ea6f 100644 (file)
@@ -125,6 +125,7 @@ extern snd_ringbuffer_t *snd_renderbuffer;
 extern qbool snd_threaded; // enables use of snd_usethreadedmixing, provided that no sound hacks are in effect (like timedemo)
 extern qbool snd_usethreadedmixing; // if true, the main thread does not mix sound, soundtime does not advance, and neither does snd_renderbuffer->endframe, instead the audio thread will call S_MixToBuffer as needed
 
+extern struct cvar_s snd_waterfx;
 extern struct cvar_s _snd_mixahead;
 extern struct cvar_s snd_swapstereo;
 extern struct cvar_s snd_streaming;
index 736d6ea1964f54ab7d11485daafde3d5d498f039..499c16f487a40bb9336d9fcaf22de9d949f60008 100644 (file)
--- a/snd_mix.c
+++ b/snd_mix.c
@@ -309,6 +309,65 @@ static void S_ConvertPaintBuffer(portable_sampleframe_t *painted_ptr, void *rb_p
        }
 }
 
+/*
+===============================================================================
+
+UNDERWATER EFFECT
+
+===============================================================================
+*/
+
+static struct {
+       float   intensity;
+       float   alpha;
+       float   accum[SND_LISTENERS];
+} underwater = {0.f, 1.f, {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}};
+
+void S_SetUnderwaterIntensity(void)
+{
+       float host_frametime = cl.realframetime;
+       float target = cl.view_underwater ? 1.f : 0.f;
+
+       if (snd_waterfx.value < 0.f)
+               target *= 0.f;
+       else if (snd_waterfx.value > 2.f)
+               target *= 2.f;
+       else
+               target *= snd_waterfx.value;
+
+       if (underwater.intensity < target)
+       {
+               underwater.intensity += host_frametime * 4.f;
+               underwater.intensity = min(underwater.intensity, target);
+       }
+       else if (underwater.intensity > target)
+       {
+               underwater.intensity -= host_frametime * 4.f;
+               underwater.intensity = max(underwater.intensity, target);
+       }
+
+       underwater.alpha = exp(-underwater.intensity * log(12.f));
+}
+
+static void S_UnderwaterFilter(int endtime)
+{
+       int i;
+       int sl;
+       if (!underwater.intensity) {
+               if (endtime > 0) {
+                       for (sl = 0; sl < SND_LISTENERS; sl++) {
+                               underwater.accum[sl] = paintbuffer[endtime-1].sample[sl];
+                       }
+               }
+               return;
+       }
+       for (i = 0; i < endtime; i++) {
+               for (sl = 0; sl < SND_LISTENERS; sl++) {
+                       underwater.accum[sl] += underwater.alpha * (paintbuffer[i].sample[sl] - underwater.accum[sl]);
+                       paintbuffer[i].sample[sl] = underwater.accum[sl];
+               }
+       }
+}
 
 /*
 ===============================================================================
@@ -580,6 +639,9 @@ void S_MixToBuffer(void *stream, unsigned int bufferframes)
 
                S_SoftClipPaintBuffer(paintbuffer, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
 
+               S_UnderwaterFilter(totalmixframes);
+
+
 #ifdef CONFIG_VIDEO_CAPTURE
                if (!snd_usethreadedmixing)
                        S_CaptureAVISound(paintbuffer, totalmixframes);
diff --git a/view.c b/view.c
index 53567b95ae68420b73a6dfbdc462ec22813b7df9..e8eaec7e26a7b5a81d3fa500124c1d693ee674a1 100644 (file)
--- a/view.c
+++ b/view.c
@@ -1059,6 +1059,7 @@ void V_CalcViewBlend(void)
                supercontents = CL_PointSuperContents(vieworigin);
                if (supercontents & SUPERCONTENTS_LIQUIDSMASK)
                {
+                       cl.view_underwater = true;
                        r_refdef.frustumscale_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
                        r_refdef.frustumscale_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
                        if (supercontents & SUPERCONTENTS_LAVA)
@@ -1083,6 +1084,7 @@ void V_CalcViewBlend(void)
                }
                else
                {
+                       cl.view_underwater = false;
                        cl.cshifts[CSHIFT_CONTENTS].destcolor[0] = 0;
                        cl.cshifts[CSHIFT_CONTENTS].destcolor[1] = 0;
                        cl.cshifts[CSHIFT_CONTENTS].destcolor[2] = 0;