Skip to content

Commit

Permalink
ref: gl: ripply water implementation (bugfixed version from my Quake-…
Browse files Browse the repository at this point in the history
…2 patches)
  • Loading branch information
a1batross committed Oct 30, 2023
1 parent ea55e78 commit a22b389
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 4 deletions.
8 changes: 8 additions & 0 deletions ref/gl/gl_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,11 @@ void R_ClearSkyBox( void );
void R_DrawSkyBox( void );
void R_DrawClouds( void );
void EmitWaterPolys( msurface_t *warp, qboolean reverse );
void R_InitRipples( void );
void R_ResetRipples( void );
void R_AnimateRipples( void );
void R_UpdateRippleTexParams( void );
void R_UploadRipples( texture_t *image );

//
// gl_vgui.c
Expand Down Expand Up @@ -750,6 +755,9 @@ extern convar_t r_vbo;
extern convar_t r_vbo_dlightmode;
extern convar_t r_studio_sort_textures;
extern convar_t r_studio_drawelements;
extern convar_t r_ripple;
extern convar_t r_ripple_updatetime;
extern convar_t r_ripple_spawntime;

//
// engine shared convars
Expand Down
7 changes: 7 additions & 0 deletions ref/gl/gl_opengl.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ CVAR_DEFINE_AUTO( r_traceglow, "0", FCVAR_GLCONFIG, "cull flares behind models"
CVAR_DEFINE_AUTO( gl_round_down, "2", FCVAR_GLCONFIG|FCVAR_READ_ONLY, "round texture sizes to nearest POT value" );
CVAR_DEFINE( r_vbo, "gl_vbo", "0", FCVAR_ARCHIVE, "draw world using VBO (known to be glitchy)" );
CVAR_DEFINE( r_vbo_dlightmode, "gl_vbo_dlightmode", "1", FCVAR_ARCHIVE, "vbo dlight rendering mode (0-1)" );
CVAR_DEFINE_AUTO( r_ripple, "0", FCVAR_GLCONFIG, "enable software-like water texture ripple simulation" );
CVAR_DEFINE_AUTO( r_ripple_updatetime, "0.05", FCVAR_GLCONFIG, "how fast ripple simulation is" );
CVAR_DEFINE_AUTO( r_ripple_spawntime, "0.1", FCVAR_GLCONFIG, "how fast new ripples spawn" );


DEFINE_ENGINE_SHARED_CVAR_LIST()

Expand Down Expand Up @@ -1186,6 +1190,9 @@ void GL_InitCommands( void )
gEngfuncs.Cvar_RegisterVariable( &r_traceglow );
gEngfuncs.Cvar_RegisterVariable( &r_studio_sort_textures );
gEngfuncs.Cvar_RegisterVariable( &r_studio_drawelements );
gEngfuncs.Cvar_RegisterVariable( &r_ripple );
gEngfuncs.Cvar_RegisterVariable( &r_ripple_updatetime );
gEngfuncs.Cvar_RegisterVariable( &r_ripple_spawntime );

gEngfuncs.Cvar_RegisterVariable( &gl_extensions );
gEngfuncs.Cvar_RegisterVariable( &gl_texture_nearest );
Expand Down
218 changes: 214 additions & 4 deletions ref/gl/gl_warp.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,29 @@ static 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_ASSERT( RIPPLES_TEXSIZE == 0x4000, "fix the algorithm to work with custom resolution" );

static struct
{
double time;
double oldtime;

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

uint32_t texture[RIPPLES_TEXSIZE];
int gl_texturenum;
int rippletexturenum;
int texturescale; // not all textures are 128x128, scale the texcoords down
} g_ripple;

static qboolean CheckSkybox( const char *name, char out[6][MAX_STRING] )
{
const char *skybox_ext[3] = { "dds", "tga", "bmp" };
Expand Down Expand Up @@ -767,7 +790,9 @@ 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( 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 @@ -799,10 +824,18 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse )
os = v[3];
ot = v[4];

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

t = ot + r_turbsin[(int)((os * 0.125f + gpGlobals->time) * TURBSCALE) & 255];
s *= ( 1.0f / SUBDIVIDE_SIZE );
t *= ( 1.0f / SUBDIVIDE_SIZE );

pglTexCoord2f( s, t );
Expand All @@ -822,3 +855,180 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse )

GL_SetupFogColorForSurfaces();
}

/*
============================================================
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 = gpGlobals->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 = gpGlobals->time - g_ripple.time;

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

if( !g_ripple.update )
return;

g_ripple.time = gpGlobals->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 = gEngfuncs.COM_RandomLong( 0, 0x7fff );
y = gEngfuncs.COM_RandomLong( 0, 0x7fff );
val = gEngfuncs.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( XASH_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;
uint32_t *pixels;
int v, wmask, hmask;

// discard unuseful textures
if( !r_ripple.value || image->width > RIPPLES_CACHEWIDTH || image->width != image->height )
{
GL_Bind( XASH_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( XASH_TEXTURE0, image->gl_texturenum );
return;
}

GL_Bind( XASH_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;
g_ripple.texturescale = RIPPLES_CACHEWIDTH / image->width;

pixels = (uint32_t *)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 );
}

0 comments on commit a22b389

Please sign in to comment.