Compare commits

...

12 Commits

Author SHA1 Message Date
Alibek Omarov
3fb92e22fd Documentation: extensions: add documentation piece for lightmapped water 2024-08-26 21:28:06 +03:00
Alibek Omarov
7364765d62 ref: gl: implement reading engine's world flag and extra info for lightmapped water
* Rename gl_litwater to gl_litwater_force because it doesn't affect maps that declare
  litwater support
2024-08-26 21:27:13 +03:00
Alibek Omarov
b64329d1ee engine: add _litwater worldspawn key to allow mod developers to declare lightmapped water support 2024-08-26 17:10:06 +03:00
Alibek Omarov
ff1d3c4257 ref: gl: add cvars to scale and set minimum light for lightmapped water 2024-08-25 23:15:39 +03:00
Alibek Omarov
d418fb5f28 ref: gl: don't draw water backfaces when drawing lightmapped water 2024-08-25 22:30:12 +03:00
Alibek Omarov
62d7959843 ref: gl: move EmitWaterPolys to gl_rsurf and make it static. Slight refactoring, don't set SURF_DRAWTURB_QUADS if context isn't OpenGL 2024-08-25 19:19:03 +03:00
Alibek Omarov
980f356fe9 ref: gl: discard surfaces that don't have lightmaps in one place, remove unneeded checks 2024-08-25 18:56:46 +03:00
Alibek Omarov
83194ca0f5 ref: gl: support lightmapped water on transparent brushes 2024-08-22 23:13:34 +03:00
Alibek Omarov
d23968b12e ref: gl: add lightmapped water rendering, including wavy water 2024-08-22 23:13:08 +03:00
Alibek Omarov
5034b860c8 ref: gl: split lightmap chain handling from R_RenderBrushPoly 2024-08-22 23:08:52 +03:00
Alibek Omarov
5a708f90c6 ref: gl: build lightmap texcoords for subdivided SURF_DRAWTURB surface 2024-08-22 23:00:38 +03:00
Alibek Omarov
4118ac2561 ref: gl: add cvar gl_litwater to enable lightmapped water 2024-08-22 21:42:04 +03:00
9 changed files with 364 additions and 168 deletions

View File

@@ -0,0 +1,19 @@
## Lightmapped water
Xash3D FWGS supports lightmapped water, as an extension. It adds three new cvars and new worldspawn key values.
### For level designers:
If you're a level designer and intend to make your level to have lightmapped water, you can put these keyvalues to worldspawn entity description (always first entity in entities list):
| Key | Value | Description |
| ---------------------- | ------- | ----------- |
| `_litwater` | integer | Set to any non-zero value to enable lightmapped water. Overrides `gl_litwater_force` cvar value. |
| `_litwater_minlight` | integer | Minimal lightmap value water surface will receive. Helps to avoid too dark areas when water isn't properly lit. If not set, defaults to zero. |
| `_litwater_scale` | float | Scales up lightmap value for water surfaces. If not set, defaults to 1.0. |
### For players:
Some of the maps already have computed lightmap for water surfaces and sometimes water has been properly lit but the support hasn't been declared by the level designer.
As a player, you can enable it in `Video options` menu or through console with `gl_litwater_force` cvar. There are also `gl_litwater_minlight` and `gl_litwater_scale` cvars that function similar to keys above. The default values has been set to `192` and `1.25` respectively to slightly avoid issues with maps that wasn't intended to have lightmapped water.

View File

@@ -1677,9 +1677,10 @@ static void Mod_SetupSubmodels( model_t *mod, dbspmodel_t *bmod )
// mark models that have origin brushes
if( !VectorIsNull( bm->origin ))
SetBits( mod->flags, MODEL_HAS_ORIGIN );
#ifdef HACKS_RELATED_HLMODS
// c2a1 doesn't have origin brush it's just placed at center of the level
if( !Q_stricmp( name, "maps/c2a1.bsp" ) && ( i == 11 ))
if( i == 11 && !Q_stricmp( name, "maps/c2a1.bsp" ))
SetBits( mod->flags, MODEL_HAS_ORIGIN );
#endif
}
@@ -1835,6 +1836,8 @@ static void Mod_LoadEntities( model_t *mod, dbspmodel_t *bmod )
world.generator[0] = '\0';
world.compiler[0] = '\0';
world.message[0] = '\0';
world.litwater_minlight = -1;
world.litwater_scale = -1.0f;
bmod->wadlist.count = 0;
// parse all the wads for loading textures in right ordering
@@ -1893,6 +1896,15 @@ static void Mod_LoadEntities( model_t *mod, dbspmodel_t *bmod )
Q_strncpy( world.compiler, token, sizeof( world.compiler ));
else if( !Q_stricmp( keyname, "generator" ) || !Q_stricmp( keyname, "_generator" ))
Q_strncpy( world.generator, token, sizeof( world.generator ));
else if( !Q_stricmp( keyname, "_litwater" ))
{
if( Q_atoi( token ) != 0 )
SetBits( world.flags, FWORLD_HAS_LITWATER );
}
else if( !Q_stricmp( keyname, "_litwater_minlight" ))
world.litwater_minlight = Q_atoi( token );
else if( !Q_stricmp( keyname, "_litwater_scale" ))
world.litwater_scale = Q_atof( token );
}
return; // all done
}

View File

@@ -119,6 +119,10 @@ typedef struct world_static_s
// Potentially Hearable Set
byte *compressed_phs;
size_t *phsofs;
// lightmapped water extra info
float litwater_scale;
int litwater_minlight;
} world_static_t;
#ifndef REF_DLL

View File

@@ -80,10 +80,11 @@ GNU General Public License for more details.
#define MODEL_CLIENT BIT( 30 ) // client sprite
// goes into world.flags
#define FWORLD_SKYSPHERE BIT( 0 )
#define FWORLD_CUSTOM_SKYBOX BIT( 1 )
#define FWORLD_WATERALPHA BIT( 2 )
#define FWORLD_HAS_DELUXEMAP BIT( 3 )
#define FWORLD_SKYSPHERE BIT( 0 )
#define FWORLD_CUSTOM_SKYBOX BIT( 1 )
#define FWORLD_WATERALPHA BIT( 2 )
#define FWORLD_HAS_DELUXEMAP BIT( 3 )
#define FWORLD_HAS_LITWATER BIT( 4 )
// special rendermode for screenfade modulate
// (probably will be expanded at some point)

View File

@@ -494,11 +494,10 @@ void R_ClearSkyBox( void );
void R_DrawSkyBox( void );
void R_DrawClouds( void );
void R_UnloadSkybox( void );
void EmitWaterPolys( msurface_t *warp, qboolean reverse );
void R_InitRipples( void );
void R_ResetRipples( void );
void R_AnimateRipples( void );
void R_UploadRipples( texture_t *image );
float R_UploadRipples( texture_t *image );
//#include "vid_common.h"
@@ -749,6 +748,9 @@ extern convar_t gl_test; // cvar to testify new effects
extern convar_t gl_msaa;
extern convar_t gl_stencilbits;
extern convar_t gl_overbright;
extern convar_t gl_litwater_force;
extern convar_t gl_litwater_minlight;
extern convar_t gl_litwater_scale;
extern convar_t r_lighting_extended;
extern convar_t r_lighting_ambient;

View File

@@ -20,6 +20,9 @@ CVAR_DEFINE_AUTO( gl_test, "0", 0, "engine developer cvar for quick testing new
CVAR_DEFINE_AUTO( gl_msaa, "1", FCVAR_GLCONFIG, "enable or disable multisample anti-aliasing" );
CVAR_DEFINE_AUTO( gl_stencilbits, "8", FCVAR_GLCONFIG|FCVAR_READ_ONLY, "pixelformat stencil bits (0 - auto)" );
CVAR_DEFINE_AUTO( gl_overbright, "1", FCVAR_GLCONFIG, "overbrights" );
CVAR_DEFINE_AUTO( gl_litwater_force, "0", FCVAR_GLCONFIG, "force enable lightmapped water, even if support not declared in the map" );
CVAR_DEFINE_AUTO( gl_litwater_minlight, "192", FCVAR_GLCONFIG, "minimal light water receives, helps avoid too dark lightmapped water" );
CVAR_DEFINE_AUTO( gl_litwater_scale, "1.5", FCVAR_GLCONFIG, "lightmapped water scale factor" );
CVAR_DEFINE_AUTO( r_lighting_extended, "1", FCVAR_GLCONFIG, "allow to get lighting from world and bmodels" );
CVAR_DEFINE_AUTO( r_lighting_ambient, "0.3", FCVAR_GLCONFIG, "map ambient lighting scale" );
CVAR_DEFINE_AUTO( r_detailtextures, "1", FCVAR_ARCHIVE, "enable detail textures support" );
@@ -1207,6 +1210,9 @@ static void GL_InitCommands( void )
gEngfuncs.Cvar_RegisterVariable( &gl_stencilbits );
gEngfuncs.Cvar_RegisterVariable( &gl_round_down );
gEngfuncs.Cvar_RegisterVariable( &gl_overbright );
gEngfuncs.Cvar_RegisterVariable( &gl_litwater_force );
gEngfuncs.Cvar_RegisterVariable( &gl_litwater_minlight );
gEngfuncs.Cvar_RegisterVariable( &gl_litwater_scale );
// these cvar not used by engine but some mods requires this
gEngfuncs.Cvar_RegisterVariable( &gl_polyoffset );

View File

@@ -1020,13 +1020,21 @@ static void R_CheckGamma( void )
ClearBits( gl_overbright.flags, FCVAR_CHANGED );
}
if( gl_overbright.value && ( FBitSet( r_vbo.flags, FCVAR_CHANGED ) || FBitSet( r_vbo_overbrightmode.flags, FCVAR_CHANGED ) ) )
if( gl_overbright.value && FBitSet( r_vbo.flags|r_vbo_overbrightmode.flags, FCVAR_CHANGED ))
{
rebuild = true;
ClearBits( r_vbo.flags, FCVAR_CHANGED );
ClearBits( r_vbo_overbrightmode.flags, FCVAR_CHANGED );
}
// we only recalculate lightmap on the fly if map hasn't declared support for lightmapped water
if( !FBitSet( tr.world->flags, FWORLD_HAS_LITWATER ) && FBitSet( gl_litwater_scale.flags|gl_litwater_minlight.flags, FCVAR_CHANGED ))
{
rebuild = true;
ClearBits( gl_litwater_scale.flags, FCVAR_CHANGED );
ClearBits( gl_litwater_minlight.flags, FCVAR_CHANGED );
}
if( rebuild )
R_GammaChanged( false );
}

View File

@@ -17,6 +17,14 @@ GNU General Public License for more details.
#include "xash3d_mathlib.h"
#include "mod_local.h"
// speed up sin calculations
static const float r_turbsin[] =
{
#include "warpsin.h"
};
static const float TURBSCALE = 256.0f / ( M_PI2 );
typedef struct
{
int allocated[BLOCK_SIZE_MAX];
@@ -42,6 +50,43 @@ static gllightmapstate_t gl_lms;
static void LM_UploadBlock( qboolean dynamic );
static qboolean R_AddSurfToVBO( msurface_t *surf, qboolean buildlightmaps );
static void R_DrawVBO( qboolean drawlightmaps, qboolean drawtextures );
static void R_RenderLightmap( msurface_t *fa );
static qboolean Mod_HaveLightmappedWater( void )
{
// if this flag set, it's level designer's job to ensure water has been properly lit
if( FBitSet( tr.world->flags, FWORLD_HAS_LITWATER ))
return true;
// otherwise, check user preference, as some maps have lightmapped water
return gl_litwater_force.value ? true : false;
}
static int Mod_LightmappedWaterMinlight( void )
{
if( FBitSet( tr.world->flags, FWORLD_HAS_LITWATER ))
{
if( tr.world->litwater_minlight >= 0 )
return tr.world->litwater_minlight;
return 0;
}
return Q_max( (int)gl_litwater_minlight.value, 0 );
}
static float Mod_LightmappedWaterScale( void )
{
if( FBitSet( tr.world->flags, FWORLD_HAS_LITWATER ))
{
if( tr.world->litwater_scale >= 0.0f )
return tr.world->litwater_scale;
return 1.0f;
}
return gl_litwater_scale.value;
}
byte *Mod_GetCurrentVis( void )
{
@@ -206,12 +251,29 @@ static void SubdividePolygon_r( model_t *loadmodel, msurface_t *warpface, int nu
{
VectorCopy( verts, poly->verts[i] );
R_TextureCoord( verts, warpface, &poly->verts[i][3] );
// lightmap texcoords for subdivided surfaces will be calculated later
}
}
// for speed reasons
if( !FBitSet( warpface->flags, SURF_DRAWTURB ))
static void GL_BuildLightmapWater( model_t *mod, msurface_t *fa )
{
float sample_size;
glpoly2_t *poly;
if( !mod || !fa->texinfo || !fa->texinfo->texture )
return; // bad polygon?
sample_size = gEngfuncs.Mod_SampleSizeForFace( fa );
for( poly = fa->polys; poly; poly = poly->next )
{
int i;
for( i = 0; i < poly->numverts; i++ )
{
// lightmap texture coordinates
R_LightmapCoord( verts, warpface, sample_size, &poly->verts[i][5] );
vec3_t vec;
VectorCopy( poly->verts[i], vec );
R_LightmapCoord( vec, fa, sample_size, &poly->verts[i][5] );
}
}
}
@@ -285,7 +347,8 @@ void GL_SubdivideSurface( model_t *loadmodel, msurface_t *fa )
for( i = 0; i < fa->numedges; i++ )
R_GetEdgePosition( loadmodel, fa, i, verts[i] );
SetBits( fa->flags, SURF_DRAWTURB_QUADS ); // predict state
if( glConfig.context == CONTEXT_TYPE_GL )
SetBits( fa->flags, SURF_DRAWTURB_QUADS ); // predict state
// do subdivide
SubdividePolygon_r( loadmodel, fa, fa->numedges, verts[0] );
@@ -712,6 +775,8 @@ static void R_BuildLightMap( msurface_t *surf, byte *dest, int stride, qboolean
mextrasurf_t *info = surf->info;
color24 *lm;
int lightscale;
const int litwater_minlight = Mod_LightmappedWaterMinlight();
const float litwater_scale = Mod_LightmappedWaterScale();
sample_size = gEngfuncs.Mod_SampleSizeForFace( surf );
smax = ( info->lightextents[0] / sample_size ) + 1;
@@ -755,6 +820,15 @@ static void R_BuildLightMap( msurface_t *surf, byte *dest, int stride, qboolean
{
int t = bl[i] * lightscale >> 14;
// amp up water lightmap to avoid too dark water
// when the it wasn't properly lit by the level designer
if( FBitSet( surf->flags, SURF_DRAWTURB ))
{
float ft = t * litwater_scale;
t = Q_max( litwater_minlight, Q_rint( ft ));
}
if( t > 1023 )
t = 1023;
@@ -844,6 +918,137 @@ void DrawGLPoly( glpoly2_t *p, float xScale, float yScale )
GL_SetupFogColorForSurfaces();
}
/*
=============
EmitWaterPolys
Does a water warp on the pre-fragmented glpoly_t chain
=============
*/
static void EmitWaterPolys( msurface_t *warp, qboolean reverse, float ripplescale )
{
const qboolean useQuads = FBitSet( warp->flags, SURF_DRAWTURB_QUADS );
const int vertexstep = reverse ? -VERTEXSIZE : VERTEXSIZE;
glpoly2_t *p;
float waveHeight = RI.currententity->curstate.scale;
if( !warp->polys ) return;
// set the current waveheight
if( warp->polys->verts[0][2] >= RI.vieworg[2] )
waveHeight = -waveHeight;
// reset fog color for nonlightmapped water
// TODO: don't do when water is lightmapped
GL_ResetFogColor();
if( useQuads )
pglBegin( GL_QUADS );
for( p = warp->polys; p; p = p->next )
{
float *v = p->verts[0];
int i;
if( reverse )
v += ( p->numverts - 1 ) * VERTEXSIZE;
if( !useQuads )
pglBegin( GL_POLYGON );
for( i = 0; i < p->numverts; i++, v += vertexstep )
{
float s, t, os = v[3], ot = v[4];
if( !r_ripple.value )
{
s = os + r_turbsin[(int)((ot * 0.125f + gp_cl->time) * TURBSCALE) & 255];
t = ot + r_turbsin[(int)((os * 0.125f + gp_cl->time) * TURBSCALE) & 255];
}
else
{
s = os / ripplescale;
t = ot / ripplescale;
}
s *= ( 1.0f / SUBDIVIDE_SIZE );
t *= ( 1.0f / SUBDIVIDE_SIZE );
pglTexCoord2f( s, t );
if( waveHeight )
{
float nv = r_turbsin[(int)(gp_cl->time * 160.0f + v[1] + v[0]) & 255] + 8.0f;
nv = (r_turbsin[(int)(v[0] * 5.0f + gp_cl->time * 171.0f - v[1]) & 255] + 8.0f ) * 0.8f + nv;
nv = nv * waveHeight + v[2];
pglVertex3f( v[0], v[1], nv );
}
else pglVertex3fv( v );
}
if( !useQuads )
pglEnd();
}
if( useQuads )
pglEnd();
GL_SetupFogColorForSurfaces();
}
/*
================
EmitWaterLightPolys
Render water lightmaps
================
*/
static void EmitWaterLightPolys( msurface_t *warp, float soffset, float toffset, qboolean dynamic )
{
const qboolean useQuads = FBitSet( warp->flags, SURF_DRAWTURB_QUADS );
glpoly2_t *p;
float waveHeight = RI.currententity->curstate.scale;
// set the current waveheight
if( warp->polys->verts[0][2] >= RI.vieworg[2] )
waveHeight = -waveHeight;
if( useQuads )
pglBegin( GL_QUADS );
for( p = warp->polys; p; p = p->next )
{
float *v = p->verts[0];
int i;
if( !useQuads )
pglBegin( GL_POLYGON );
for( i = 0; i < p->numverts; i++, v += VERTEXSIZE )
{
if( !dynamic ) pglTexCoord2f( v[5], v[6] );
else pglTexCoord2f( v[5] - soffset, v[6] - toffset );
if( waveHeight )
{
float nv = r_turbsin[(int)(gp_cl->time * 160.0f + v[1] + v[0]) & 255] + 8.0f;
nv = (r_turbsin[(int)(v[0] * 5.0f + gp_cl->time * 171.0f - v[1]) & 255] + 8.0f ) * 0.8f + nv;
nv = nv * waveHeight + v[2];
pglVertex3f( v[0], v[1], nv );
}
else pglVertex3fv( v );
}
if( !useQuads )
pglEnd ();
}
if( useQuads )
pglEnd();
}
/*
================
DrawGLPolyChain
@@ -851,12 +1056,15 @@ DrawGLPolyChain
Render lightmaps
================
*/
static void DrawGLPolyChain( glpoly2_t *p, float soffset, float toffset )
static void DrawGLPolyChain( glpoly2_t *p, float soffset, float toffset, msurface_t *surf )
{
qboolean dynamic = true;
qboolean dynamic = soffset != 0.0f || toffset != 0.0f;
if( soffset == 0.0f && toffset == 0.0f )
dynamic = false;
if( FBitSet( surf->flags, SURF_DRAWTURB ))
{
EmitWaterLightPolys( surf, soffset, toffset, dynamic );
return;
}
for( ; p != NULL; p = p->chain )
{
@@ -890,6 +1098,8 @@ static qboolean R_HasLightmap( void )
switch( RI.currententity->curstate.rendermode )
{
case kRenderTransTexture:
return FBitSet( RI.currentmodel->flags, MODEL_LIQUID ) ? true : false;
case kRenderTransColor:
case kRenderTransAdd:
case kRenderGlow:
@@ -919,21 +1129,29 @@ static void R_BlendLightmaps( void )
pglEnable( GL_BLEND );
else pglDisable( GL_BLEND );
// lightmapped solid surfaces
pglDepthMask( GL_FALSE );
pglDepthFunc( GL_EQUAL );
pglDisable( GL_ALPHA_TEST );
if( gl_overbright.value )
if( RI.currententity->curstate.rendermode == kRenderTransTexture )
{
pglBlendFunc( GL_DST_COLOR, GL_SRC_COLOR );
if(!( r_vbo.value && !r_vbo_overbrightmode.value ))
pglColor4f( 128.0f / 192.0f, 128.0f / 192.0f, 128.0f / 192.0f, 1.0f );
pglBlendFunc( GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA );
}
else
{
pglBlendFunc( GL_ZERO, GL_SRC_COLOR );
// lightmapped solid surfaces
pglDepthMask( GL_FALSE );
pglDepthFunc( GL_EQUAL );
if( gl_overbright.value )
{
pglBlendFunc( GL_DST_COLOR, GL_SRC_COLOR );
if(!( r_vbo.value && !r_vbo_overbrightmode.value ))
pglColor4f( 128.0f / 192.0f, 128.0f / 192.0f, 128.0f / 192.0f, 1.0f );
}
else
{
pglBlendFunc( GL_ZERO, GL_SRC_COLOR );
}
}
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
// render static lightmaps first
@@ -945,7 +1163,7 @@ static void R_BlendLightmaps( void )
for( surf = gl_lms.lightmap_surfaces[i]; surf != NULL; surf = surf->info->lightmapchain )
{
if( surf->polys ) DrawGLPolyChain( surf->polys, 0.0f, 0.0f );
DrawGLPolyChain( surf->polys, 0.0f, 0.0f, surf );
}
}
}
@@ -986,12 +1204,10 @@ static void R_BlendLightmaps( void )
// draw all surfaces that use this lightmap
for( drawsurf = newsurf; drawsurf != surf; drawsurf = drawsurf->info->lightmapchain )
{
if( drawsurf->polys )
{
DrawGLPolyChain( drawsurf->polys,
DrawGLPolyChain( drawsurf->polys,
( drawsurf->light_s - drawsurf->info->dlight_s ) * ( 1.0f / (float)BLOCK_SIZE ),
( drawsurf->light_t - drawsurf->info->dlight_t ) * ( 1.0f / (float)BLOCK_SIZE ));
}
( drawsurf->light_t - drawsurf->info->dlight_t ) * ( 1.0f / (float)BLOCK_SIZE ),
drawsurf );
}
newsurf = drawsurf;
@@ -1015,12 +1231,10 @@ static void R_BlendLightmaps( void )
for( surf = newsurf; surf != NULL; surf = surf->info->lightmapchain )
{
if( surf->polys )
{
DrawGLPolyChain( surf->polys,
DrawGLPolyChain( surf->polys,
( surf->light_s - surf->info->dlight_s ) * ( 1.0f / (float)BLOCK_SIZE ),
( surf->light_t - surf->info->dlight_t ) * ( 1.0f / (float)BLOCK_SIZE ));
}
( surf->light_t - surf->info->dlight_t ) * ( 1.0f / (float)BLOCK_SIZE ),
surf );
}
}
@@ -1142,9 +1356,7 @@ R_RenderBrushPoly
*/
static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
{
qboolean is_dynamic = false;
int maps;
texture_t *t;
texture_t *t;
r_stats.c_world_polys++;
@@ -1155,10 +1367,15 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
if( FBitSet( fa->flags, SURF_DRAWTURB ))
{
R_UploadRipples( t );
float ripplescale = R_UploadRipples( t );
// warp texture
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE), ripplescale );
// add lightmaps if requested
if( Mod_HaveLightmappedWater( ))
R_RenderLightmap( fa );
// warp texture, no lightmaps
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE));
return;
}
else GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
@@ -1213,14 +1430,25 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
DrawSurfaceDecals( fa, true, (cull_type == CULL_BACKSIDE));
}
if( FBitSet( fa->flags, SURF_DRAWTILED ))
return; // no lightmaps anyway
R_RenderLightmap( fa );
}
static void R_RenderLightmap( msurface_t *fa )
{
qboolean is_dynamic;
int maps;
if( !fa->polys || FBitSet( fa->flags, SURF_DRAWTILED ))
return;
// check for lightmap modification
for( maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++ )
{
if( tr.lightstylevalue[fa->styles[maps]] != fa->cached_light[maps] )
goto dynamic;
{
is_dynamic = true;
break;
}
}
// dynamic this frame or dynamic previously
@@ -1422,6 +1650,8 @@ void R_DrawWaterSurfaces( void )
for( i = 0; i < WORLDMODEL->numtextures; i++ )
{
float ripplescale;
t = WORLDMODEL->textures[i];
if( !t ) continue;
@@ -1432,14 +1662,22 @@ void R_DrawWaterSurfaces( void )
continue;
// set modulate mode explicitly
R_UploadRipples( t );
ripplescale = R_UploadRipples( t );
for( ; s; s = s->texturechain )
EmitWaterPolys( s, false );
{
EmitWaterPolys( s, false, ripplescale );
if( Mod_HaveLightmappedWater( ))
R_RenderLightmap( s );
}
t->texturechain = NULL;
}
GL_ResetFogColor();
R_BlendLightmaps();
pglDisable( GL_BLEND );
pglDepthMask( GL_TRUE );
pglDisable( GL_ALPHA_TEST );
@@ -1634,8 +1872,16 @@ void R_DrawBrushModel( cl_entity_t *e )
if( cull_type == CULL_BACKSIDE )
{
if( !FBitSet( psurf->flags, SURF_DRAWTURB ) && !( psurf->pdecals && e->curstate.rendermode == kRenderTransTexture ))
continue;
if( FBitSet( psurf->flags, SURF_DRAWTURB ))
{
if( Mod_HaveLightmappedWater( ))
continue; // we don't want back faces when drawing lightmaps to avoid Z fighting
}
else
{
if( !( psurf->pdecals && e->curstate.rendermode == kRenderTransTexture ))
continue;
}
}
if( num_sorted < gpGlobals->max_surfaces )
@@ -1663,7 +1909,7 @@ void R_DrawBrushModel( cl_entity_t *e )
GL_ResetFogColor();
R_BlendLightmaps();
R_RenderFullbrights();
R_RenderDetails( allow_vbo? 2: 3 );
R_RenderDetails( allow_vbo ? 2 : 3 );
// restore fog here
if( e->curstate.rendermode == kRenderTransAdd )
@@ -2282,14 +2528,13 @@ static texture_t *R_SetupVBOTexture( texture_t *tex, int number )
}
else R_DisableDetail();
GL_Bind( mtst.tmu_gl, r_lightmap->value ?tr.whiteTexture:tex->gl_texturenum );
if(number)
GL_Bind( mtst.tmu_gl, r_lightmap->value ? tr.whiteTexture : tex->gl_texturenum );
if( number )
vboarray.itexture = number;
return tex;
}
static void R_SetupVBOArrayStatic( vboarray_t *vbo, qboolean drawlightmap, qboolean drawtextures )
{
if( vboarray.astate != VBO_ARRAY_STATIC )
@@ -2333,11 +2578,8 @@ static void R_SetupVBOArrayStatic( vboarray_t *vbo, qboolean drawlightmap, qbool
}
}
static void R_SetupVBOArrayDlight( vboarray_t *vbo, texture_t *texture )
{
if( vboarray.astate != VBO_ARRAY_DLIGHT )
{
if( vboarray.astate == VBO_ARRAY_DECAL_DLIGHT )
@@ -2401,7 +2643,6 @@ static void R_SetupVBOArrayDecalDlight( int decalcount )
vboarray.lstate = VBO_LIGHTMAP_DYNAMIC;
}
/*
===================
R_AdditionalPasses
@@ -2469,11 +2710,9 @@ static void R_AdditionalPasses( vboarray_t *vbo, int indexlen, void *indexarray,
}
}
#define MINIMIZE_UPLOAD
#define DISCARD_DLIGHTS
static void R_DrawDlightedDecals( vboarray_t *vbo, msurface_t *newsurf, msurface_t *surf, int decalcount, texture_t *texture )
{
msurface_t *decalsurf;
@@ -3871,9 +4110,8 @@ void GL_BuildLightmaps( void )
GL_CreateSurfaceLightmap( m->surfaces + j, m );
if( m->surfaces[j].flags & SURF_DRAWTURB )
continue;
GL_BuildPolygonFromSurface( m, m->surfaces + j );
GL_BuildLightmapWater( m, m->surfaces + j );
else GL_BuildPolygonFromSurface( m, m->surfaces + j );
}
// clearing visframe

View File

@@ -19,7 +19,6 @@ GNU General Public License for more details.
#define SKYCLOUDS_QUALITY 12
#define MAX_CLIP_VERTS 128 // skybox clip vertices
#define TURBSCALE ( 256.0f / ( M_PI2 ))
static const int r_skyTexOrder[SKYBOX_MAX_SIDES] = { 0, 2, 1, 3, 4, 5 };
@@ -55,12 +54,6 @@ static const int vec_to_st[SKYBOX_MAX_SIDES][3] =
{ -2, 1, -3 }
};
// speed up sin calculations
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 )
@@ -81,10 +74,8 @@ static struct
uint32_t texture[RIPPLES_TEXSIZE];
int gl_texturenum;
int rippletexturenum;
float texturescale; // not all textures are 128x128, scale the texcoords down
} g_ripple;
static void DrawSkyPolygon( int nump, vec3_t vecs )
{
int i, j, axis;
@@ -558,89 +549,6 @@ void R_DrawClouds( void )
pglFogf( GL_FOG_DENSITY, RI.fogDensity );
}
/*
=============
EmitWaterPolys
Does a water warp on the pre-fragmented glpoly_t chain
=============
*/
void EmitWaterPolys( msurface_t *warp, qboolean reverse )
{
float *v, nv, waveHeight;
float s, t, os, ot;
glpoly2_t *p;
int i;
const qboolean useQuads = FBitSet( warp->flags, SURF_DRAWTURB_QUADS ) && glConfig.context == CONTEXT_TYPE_GL;
if( !warp->polys ) return;
// set the current waveheight
if( warp->polys->verts[0][2] >= RI.vieworg[2] )
waveHeight = -RI.currententity->curstate.scale;
else waveHeight = RI.currententity->curstate.scale;
// reset fog color for nonlightmapped water
GL_ResetFogColor();
if( useQuads )
pglBegin( GL_QUADS );
for( p = warp->polys; p; p = p->next )
{
if( reverse )
v = p->verts[0] + ( p->numverts - 1 ) * VERTEXSIZE;
else v = p->verts[0];
if( !useQuads )
pglBegin( GL_POLYGON );
for( i = 0; i < p->numverts; i++ )
{
if( waveHeight )
{
nv = r_turbsin[(int)(gp_cl->time * 160.0f + v[1] + v[0]) & 255] + 8.0f;
nv = (r_turbsin[(int)(v[0] * 5.0f + gp_cl->time * 171.0f - v[1]) & 255] + 8.0f ) * 0.8f + nv;
nv = nv * waveHeight + v[2];
}
else nv = v[2];
os = v[3];
ot = v[4];
if( !r_ripple.value )
{
s = os + r_turbsin[(int)((ot * 0.125f + gp_cl->time) * TURBSCALE) & 255];
t = ot + r_turbsin[(int)((os * 0.125f + gp_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 );
if( reverse )
v -= VERTEXSIZE;
else v += VERTEXSIZE;
}
if( !useQuads )
pglEnd();
}
if( useQuads )
pglEnd();
GL_SetupFogColorForSurfaces();
}
/*
============================================================
@@ -750,43 +658,39 @@ void R_AnimateRipples( void )
R_RunRipplesAnimation( g_ripple.oldbuf, g_ripple.curbuf );
}
void R_UploadRipples( texture_t *image )
float R_UploadRipples( texture_t *image )
{
gl_texture_t *glt;
uint32_t *pixels;
int wbits, wmask, wshft;
int y;
float texturescale;
// discard unuseful textures
if( !r_ripple.value || image->width > RIPPLES_CACHEWIDTH || image->width != image->height )
{
GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
return;
return 0.0f;
}
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;
return 0.0f;
}
GL_Bind( XASH_TEXTURE0, g_ripple.rippletexturenum );
if( r_ripple.value == 1.0f )
texturescale = Q_max( 1.0f, image->width / 64.0f );
else texturescale = 1.0f;
// no updates this frame
if( !g_ripple.update && image->gl_texturenum == g_ripple.gl_texturenum )
return;
return texturescale;
g_ripple.gl_texturenum = image->gl_texturenum;
if( r_ripple.value == 1.0f )
{
g_ripple.texturescale = Q_max( 1.0f, image->width / 64.0f );
}
else
{
g_ripple.texturescale = 1.0f;
}
pixels = (uint32_t *)glt->original->buffer;
wbits = MostSignificantBit( image->width );
@@ -813,4 +717,6 @@ void R_UploadRipples( texture_t *image )
pglTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->width, 0,
GL_RGBA, GL_UNSIGNED_BYTE, g_ripple.texture );
return texturescale;
}