diff --git a/Documentation/extensions/litwater.md b/Documentation/extensions/litwater.md new file mode 100644 index 0000000000..cb0c5b531f --- /dev/null +++ b/Documentation/extensions/litwater.md @@ -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. diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index fca6da57c0..04fa238f9b 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -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 } diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index c75246f5e0..18653e0ba5 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -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 diff --git a/engine/ref_api.h b/engine/ref_api.h index 4e7d76565f..41482f7761 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -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) diff --git a/ref/gl/gl_local.h b/ref/gl/gl_local.h index 3538145d77..3586e9840f 100644 --- a/ref/gl/gl_local.h +++ b/ref/gl/gl_local.h @@ -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; diff --git a/ref/gl/gl_opengl.c b/ref/gl/gl_opengl.c index 156b52e33e..d3b62057b6 100644 --- a/ref/gl/gl_opengl.c +++ b/ref/gl/gl_opengl.c @@ -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 ); diff --git a/ref/gl/gl_rmain.c b/ref/gl/gl_rmain.c index f647c41d99..3bf6628af8 100644 --- a/ref/gl/gl_rmain.c +++ b/ref/gl/gl_rmain.c @@ -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 ); } diff --git a/ref/gl/gl_rsurf.c b/ref/gl/gl_rsurf.c index f21ce00b5c..f8b18141bd 100644 --- a/ref/gl/gl_rsurf.c +++ b/ref/gl/gl_rsurf.c @@ -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 + } +} + +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 speed reasons - if( !FBitSet( warpface->flags, SURF_DRAWTURB )) + 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 diff --git a/ref/gl/gl_warp.c b/ref/gl/gl_warp.c index 079eae1381..3fa0948ed1 100644 --- a/ref/gl/gl_warp.c +++ b/ref/gl/gl_warp.c @@ -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; }