Skip to content

Commit

Permalink
Implemented software water rippling effect thanks to A1batross.
Browse files Browse the repository at this point in the history
Original pull request for FWGS here: FWGS/xash3d-fwgs#1475
  • Loading branch information
Magic-Nipples committed Oct 30, 2023
1 parent 5e8ab7b commit 8551632
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 16 deletions.
5 changes: 5 additions & 0 deletions src/engine/client/gl_image.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@ void R_SetTextureParameters( void )
// change all the existing mipmapped texture objects
for( i = 0; i < gl_numTextures; i++ )
GL_UpdateTextureParams( i );

R_UpdateRippleTexParams();
}

/*
Expand Down Expand Up @@ -2288,9 +2290,12 @@ void R_InitImages( void )
gl_texturesHashTable[gl_textures->hashValue] = gl_textures;
gl_numTextures = 1;

R_InitRipples(); //placed this before texture parameters to prevent *default texture upload error in game.

// validate cvars
R_SetTextureParameters();
GL_CreateInternalTextures();


Cmd_AddCommand( "texturelist", R_TextureList_f, "display loaded textures list" );
}
Expand Down
12 changes: 12 additions & 0 deletions src/engine/client/gl_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -733,4 +733,16 @@ extern convar_t* r_overbright; //magic nipples - overbright
extern convar_t* r_lighting_lambert;
extern convar_t* gammaboost;


//software water
void R_InitRipples(void);
void R_ResetRipples(void);
void R_AnimateRipples(void);
void R_UpdateRippleTexParams(void);
void R_UploadRipples(texture_t* image);

extern convar_t* r_ripple;
extern convar_t* r_ripple_updatetime;
extern convar_t* r_ripple_spawntime;

#endif//GL_LOCAL_H
4 changes: 4 additions & 0 deletions src/engine/client/gl_rmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,10 @@ void R_RenderScene( void )

R_CheckGLFog();
R_DrawWorld();

if (RI.drawWorld)
R_AnimateRipples();

R_CheckFog();

CL_ExtraUpdate (); // don't let sound get messed up if going slow
Expand Down
2 changes: 2 additions & 0 deletions src/engine/client/gl_rmisc.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,6 @@ void R_NewMap( void )
R_SetupSky( clgame.movevars.skyName );

GL_BuildLightmaps ();

R_ResetRipples();
}
12 changes: 8 additions & 4 deletions src/engine/client/gl_rsurf.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ void GL_SetupFogColorForSurfaces( void )
return;

if( RI.currententity && RI.currententity->curstate.rendermode == kRenderTransTexture )
{
{
pglFogfv( GL_FOG_COLOR, RI.fogColor );
return;
}
Expand Down Expand Up @@ -1095,14 +1095,17 @@ void R_RenderBrushPoly( msurface_t *fa, int cull_type )
// DEBUG: reset the mirror texture after drawing
fa->info->mirrortexturenum = 0;
}
else GL_Bind(GL_TEXTURE0, t->gl_texturenum); //if mirrors removed. leave just this line.
//else GL_Bind(GL_TEXTURE0, t->gl_texturenum); //if mirrors removed. leave just this line.

if( FBitSet( fa->flags, SURF_DRAWTURB ))
else if( FBitSet( fa->flags, SURF_DRAWTURB )) //might not need else
{
R_UploadRipples(t);

// warp texture, no lightmaps
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE));
return;
}
else GL_Bind(GL_TEXTURE0, t->gl_texturenum); //if mirrors removed. leave just this line.

if( t->fb_texturenum )
{
Expand Down Expand Up @@ -1377,7 +1380,8 @@ void R_DrawWaterSurfaces( void )
continue;

// set modulate mode explicitly
GL_Bind( GL_TEXTURE0, t->gl_texturenum );
//GL_Bind( GL_TEXTURE0, t->gl_texturenum );
R_UploadRipples(t);

for( ; s; s = s->texturechain )
EmitWaterPolys( s, false );
Expand Down
8 changes: 8 additions & 0 deletions src/engine/client/gl_vidnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ convar_t* vid_mode;

convar_t* r_downsample; //magic nipples - down sampling

convar_t* r_ripple;
convar_t* r_ripple_updatetime;
convar_t* r_ripple_spawntime;

byte* r_temppool;

ref_globals_t tr;
Expand Down Expand Up @@ -1654,6 +1658,10 @@ void GL_InitCommands( void )
gl_round_down = Cvar_Get( "gl_round_down", "2", FCVAR_RENDERINFO, "round texture sizes to nearest POT value" );
gl_msaa = Cvar_Get( "gl_msaa", "1", FCVAR_ARCHIVE, "enable multi sample anti-aliasing" );

r_ripple = Cvar_Get("r_ripple", "1", FCVAR_ARCHIVE, "enable software - like water texture ripple simulation");
r_ripple_updatetime = Cvar_Get("r_ripple_updatetime", "0.05", FCVAR_ARCHIVE, "how fast ripple simulation is");
r_ripple_spawntime = Cvar_Get("r_ripple_spawntime", "0.1", FCVAR_ARCHIVE, "how fast new ripples spawn");

// these cvar not used by engine but some mods requires this
gl_polyoffset = Cvar_Get( "gl_polyoffset", "2.0", FCVAR_ARCHIVE, "polygon offset for decals" );

Expand Down
228 changes: 223 additions & 5 deletions src/engine/client/gl_warp.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,29 @@ float r_turbsin[] =
#include "warpsin.h"
};

#define RIPPLES_CACHEWIDTH_BITS 7
#define RIPPLES_CACHEWIDTH ( 1 << RIPPLES_CACHEWIDTH_BITS )
#define RIPPLES_CACHEWIDTH_MASK (( RIPPLES_CACHEWIDTH ) - 1 )
#define RIPPLES_TEXSIZE ( RIPPLES_CACHEWIDTH * RIPPLES_CACHEWIDTH )
#define RIPPLES_TEXSIZE_MASK ( RIPPLES_TEXSIZE - 1 )

static struct
{
double time;
double oldtime;

short* curbuf, * oldbuf;
short buf[2][RIPPLES_TEXSIZE];
qboolean update;

uint texture[RIPPLES_TEXSIZE]; //might be a gl_texture_t
int gl_texturenum;
int rippletexturenum;
int texturescale; // not all textures are 128x128, scale the texcoords down
} g_ripple;



#define SKYBOX_MISSED 0
#define SKYBOX_HLSTYLE 1
#define SKYBOX_Q1STYLE 2
Expand Down Expand Up @@ -769,7 +792,12 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse )
if( !warp->polys ) return;

// set the current waveheight
if( warp->polys->verts[0][2] >= RI.vieworg[2] )
//if( warp->polys->verts[0][2] >= RI.vieworg[2] )
// waveHeight = -RI.currententity->curstate.scale;
//else waveHeight = RI.currententity->curstate.scale;
if (r_ripple->value)
waveHeight = 0;
else if (warp->polys->verts[0][2] >= RI.vieworg[2])
waveHeight = -RI.currententity->curstate.scale;
else waveHeight = RI.currententity->curstate.scale;

Expand Down Expand Up @@ -817,11 +845,23 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse )
os = v[3];
ot = v[4];

s = os + r_turbsin[(int)((ot * 0.125f + cl.time) * TURBSCALE) & 255];
s *= ( 1.0f / SUBDIVIDE_SIZE );
//s = os + r_turbsin[(int)((ot * 0.125f + cl.time) * TURBSCALE) & 255];
//s *= ( 1.0f / SUBDIVIDE_SIZE );

t = ot + r_turbsin[(int)((os * 0.125f + cl.time) * TURBSCALE) & 255];
t *= ( 1.0f / SUBDIVIDE_SIZE );
//t = ot + r_turbsin[(int)((os * 0.125f + cl.time) * TURBSCALE) & 255];
//t *= ( 1.0f / SUBDIVIDE_SIZE );
if (!r_ripple->value)
{
s = os + r_turbsin[(int)((ot * 0.125f + cl.time) * TURBSCALE) & 255];
t = ot + r_turbsin[(int)((os * 0.125f + cl.time) * TURBSCALE) & 255];
}
else
{
s = os / g_ripple.texturescale;
t = ot / g_ripple.texturescale;
}
s *= (1.0f / SUBDIVIDE_SIZE);
t *= (1.0f / SUBDIVIDE_SIZE);

pglTexCoord2f( s, t );
pglVertex3f( v[0], v[1], nv );
Expand All @@ -840,4 +880,182 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse )

//GL_SetupFogColorForSurfaces();
GL_ResetFogColor(); //MAGIC NIPPLES - func_water fix!!
}


/*
============================================================
HALF-LIFE SOFTWARE WATER
============================================================
*/
void R_ResetRipples(void)
{
g_ripple.curbuf = g_ripple.buf[0];
g_ripple.oldbuf = g_ripple.buf[1];
g_ripple.time = g_ripple.oldtime = cl.time - 0.1;
memset(g_ripple.buf, 0, sizeof(g_ripple.buf));
}

void R_InitRipples(void)
{
rgbdata_t pic = { 0 };

pic.width = pic.height = RIPPLES_CACHEWIDTH;
pic.depth = 1;
pic.flags = IMAGE_HAS_COLOR;
pic.buffer = (byte*)g_ripple.texture;
pic.type = PF_RGBA_32;
pic.size = sizeof(g_ripple.texture);
pic.numMips = 1;
memset(pic.buffer, 0, pic.size);

g_ripple.rippletexturenum = GL_LoadTextureInternal("*rippletex", &pic, TF_NOMIPMAP);
}

static void R_SwapBufs(void)
{
short* tempbufp = g_ripple.curbuf;
g_ripple.curbuf = g_ripple.oldbuf;
g_ripple.oldbuf = tempbufp;
}

static void R_SpawnNewRipple(int x, int y, short val)
{
#define PIXEL( x, y ) ((( x ) & RIPPLES_CACHEWIDTH_MASK ) + ((( y ) & RIPPLES_CACHEWIDTH_MASK) << 7 ))
g_ripple.oldbuf[PIXEL(x, y)] += val;

val >>= 2;
g_ripple.oldbuf[PIXEL(x + 1, y)] += val;
g_ripple.oldbuf[PIXEL(x - 1, y)] += val;
g_ripple.oldbuf[PIXEL(x, y + 1)] += val;
g_ripple.oldbuf[PIXEL(x, y - 1)] += val;
#undef PIXEL
}

static void R_RunRipplesAnimation(const short* oldbuf, short* pbuf)
{
unsigned int i = 0;

for (i = 0; i < RIPPLES_TEXSIZE; i++, pbuf++)
{
int p = RIPPLES_CACHEWIDTH + i;
int val;

val = ((int)oldbuf[(p - (RIPPLES_CACHEWIDTH * 2)) & RIPPLES_TEXSIZE_MASK]
+ (int)oldbuf[(p - (RIPPLES_CACHEWIDTH + 1)) & RIPPLES_TEXSIZE_MASK]
+ (int)oldbuf[(p - (RIPPLES_CACHEWIDTH - 1)) & RIPPLES_TEXSIZE_MASK]
+ (int)oldbuf[p & RIPPLES_TEXSIZE_MASK]) >> 1;

val -= *pbuf;

*pbuf = (short)val - (short)(val >> 6);
}
}

static int MostSignificantBit(unsigned int v)
{
#if __GNUC__
return 31 - __builtin_clz(v);
#else
int i;
for (i = 0, v >>= 1; v; v >>= 1, i++);
return i;
#endif
}

void R_AnimateRipples(void)
{
double frametime = cl.time - g_ripple.time;

g_ripple.update = r_ripple->value && frametime >= r_ripple_updatetime->value;

if (!g_ripple.update)
return;

g_ripple.time = cl.time;

R_SwapBufs();

if (g_ripple.time - g_ripple.oldtime > r_ripple_spawntime->value)
{
int x, y, val;

g_ripple.oldtime = g_ripple.time;

x = rand() & 0x7fff;//COM_RandomLong(0, 0x7fff);
y = rand() & 0x7fff; //COM_RandomLong(0, 0x7fff);
val = rand() & 0x3ff; //COM_RandomLong(0, 0x3ff);

R_SpawnNewRipple(x, y, val);
}

R_RunRipplesAnimation(g_ripple.oldbuf, g_ripple.curbuf);
}

void R_UpdateRippleTexParams(void)
{
gl_texture_t* tex = R_GetTexture(g_ripple.rippletexturenum);

GL_Bind(GL_TEXTURE0, g_ripple.rippletexturenum);

if (gl_texture_nearest->value)
{
pglTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
pglTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
else
{
pglTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
pglTexParameteri(tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
}

void R_UploadRipples(texture_t* image)
{
gl_texture_t* glt;
uint* pixels;
int v, wmask, hmask;

// discard unuseful textures
if (!r_ripple->value || image->width > RIPPLES_CACHEWIDTH || image->width != image->height)
{
GL_Bind(GL_TEXTURE0, image->gl_texturenum);
return;
}

glt = R_GetTexture(image->gl_texturenum);
if (!glt || !glt->original || !glt->original->buffer || !FBitSet(glt->flags, TF_EXPAND_SOURCE))
{
GL_Bind(GL_TEXTURE0, image->gl_texturenum);
return;
}

GL_Bind(GL_TEXTURE0, g_ripple.rippletexturenum);

// no updates this frame
if (!g_ripple.update && image->gl_texturenum == g_ripple.gl_texturenum)
return;

g_ripple.gl_texturenum = image->gl_texturenum;

// TODO: original sw.dll always draws at 64x64
g_ripple.texturescale = Q_max(2, RIPPLES_CACHEWIDTH / image->width); //g_ripple.texturescale = RIPPLES_CACHEWIDTH / image->width;

pixels = (uint*)glt->original->buffer;
v = MostSignificantBit(image->width);
wmask = image->width - 1;
hmask = image->height - 1;

for (int i = 0; i < RIPPLES_TEXSIZE; i++)
{
int val = g_ripple.curbuf[i];
int x = (val >> 4) + i;
int y = (i >> 7) - (val >> 4);
int pixel = (x & wmask) + ((y & hmask) << (v & 0x1f)); // ???

g_ripple.texture[i] = pixels[pixel];
}

pglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, RIPPLES_CACHEWIDTH, RIPPLES_CACHEWIDTH, 0,
GL_RGBA, GL_UNSIGNED_BYTE, g_ripple.texture);
}
Loading

0 comments on commit 8551632

Please sign in to comment.