diff --git a/ref/gl/gl_backend.c b/ref/gl/gl_backend.c index e1c38755..2e86149b 100644 --- a/ref/gl/gl_backend.c +++ b/ref/gl/gl_backend.c @@ -469,6 +469,57 @@ void GL_SetRenderMode( int mode ) } } +/* +============== +GL_PushPolygonOffset + +enable polygon offset and push it onto the stack +============== +*/ +void GL_PushPolygonOffset( float factor, float units ) +{ + if( glState.num_polyoffsets >= ARRAYSIZE( glState.polyoffset_state )) + { + gEngfuncs.Con_Reportf( "%s: overflow\n", __func__ ); + return; + } + + if( glState.num_polyoffsets == 0 ) + pglEnable( GL_POLYGON_OFFSET_FILL ); + + pglPolygonOffset( factor, units ); + + glState.polyoffset_state[glState.num_polyoffsets].factor = factor; + glState.polyoffset_state[glState.num_polyoffsets].units = units; + glState.num_polyoffsets++; +} + +/* +============== +GL_PopPolygonOffset + +pop polygon offset from the stack and restore previous state +============== +*/ +void GL_PopPolygonOffset( void ) +{ + if( glState.num_polyoffsets <= 0 ) + { + gEngfuncs.Con_Reportf( "%s: underflow\n", __func__ ); + return; + } + + glState.num_polyoffsets--; + + if( glState.num_polyoffsets == 0 ) + pglDisable( GL_POLYGON_OFFSET_FILL ); + else + { + pglPolygonOffset( glState.polyoffset_state[glState.num_polyoffsets].factor, + glState.polyoffset_state[glState.num_polyoffsets].units ); + } +} + /* ============================================================================== diff --git a/ref/gl/gl_cull.c b/ref/gl/gl_cull.c index b4fad790..27f394a4 100644 --- a/ref/gl/gl_cull.c +++ b/ref/gl/gl_cull.c @@ -40,7 +40,7 @@ qboolean R_CullBox( const vec3_t mins, const vec3_t maxs ) R_CullModel ============= */ -qboolean R_CullModel( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax ) +qboolean R_CullModel( const cl_entity_t *e, const vec3_t absmin, const vec3_t absmax ) { if( e == tr.viewent ) { @@ -63,18 +63,18 @@ R_CullSurface cull invisible surfaces ================= */ -int R_CullSurface( msurface_t *surf, gl_frustum_t *frustum, uint clipflags ) +int R_CullSurface( const msurface_t *surf, const gl_frustum_t *frustum, uint clipflags ) { cl_entity_t *e = RI.currententity; - if( !surf || !surf->texinfo || !surf->texinfo->texture ) + if( !e || !surf || !surf->texinfo || !surf->texinfo->texture ) return CULL_OTHER; if( r_nocull.value ) return CULL_VISIBLE; // world surfaces can be culled by vis frame too - if( RI.currententity == CL_GetEntityByIndex( 0 ) && surf->visframe != tr.framecount ) + if( e == CL_GetEntityByIndex( 0 ) && surf->visframe != tr.framecount ) return CULL_VISFRAME; // only static ents can be culled by frustum diff --git a/ref/gl/gl_decals.c b/ref/gl/gl_decals.c index 79a84557..207ff57f 100644 --- a/ref/gl/gl_decals.c +++ b/ref/gl/gl_decals.c @@ -919,10 +919,7 @@ void DrawSurfaceDecals( msurface_t *fa, qboolean single, qboolean reverse ) GL_Cull( GL_NONE ); if( gl_polyoffset.value ) - { - pglEnable( GL_POLYGON_OFFSET_FILL ); - pglPolygonOffset( -1.0f, -gl_polyoffset.value ); - } + GL_PushPolygonOffset( -1.0f, -gl_polyoffset.value ); } if( FBitSet( fa->flags, SURF_TRANSPARENT ) && glState.stencilEnabled ) @@ -1012,7 +1009,7 @@ void DrawSurfaceDecals( msurface_t *fa, qboolean single, qboolean reverse ) } if( gl_polyoffset.value ) - pglDisable( GL_POLYGON_OFFSET_FILL ); + GL_PopPolygonOffset(); if( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd ) GL_Cull( GL_FRONT ); @@ -1030,8 +1027,8 @@ void DrawSurfaceDecals( msurface_t *fa, qboolean single, qboolean reverse ) void DrawDecalsBatch( void ) { - cl_entity_t *e; - int i; + cl_entity_t *e; + int i; if( !tr.num_draw_decals ) return; @@ -1050,10 +1047,7 @@ void DrawDecalsBatch( void ) GL_Cull( GL_NONE ); if( gl_polyoffset.value ) - { - pglEnable( GL_POLYGON_OFFSET_FILL ); - pglPolygonOffset( -1.0f, -gl_polyoffset.value ); - } + GL_PushPolygonOffset( -1.0f, -gl_polyoffset.value ); for( i = 0; i < tr.num_draw_decals; i++ ) { @@ -1068,7 +1062,7 @@ void DrawDecalsBatch( void ) } if( gl_polyoffset.value ) - pglDisable( GL_POLYGON_OFFSET_FILL ); + GL_PopPolygonOffset(); if( e->curstate.rendermode == kRenderTransTexture || e->curstate.rendermode == kRenderTransAdd ) GL_Cull( GL_FRONT ); diff --git a/ref/gl/gl_local.h b/ref/gl/gl_local.h index 2df9a7a4..103083f3 100644 --- a/ref/gl/gl_local.h +++ b/ref/gl/gl_local.h @@ -315,6 +315,8 @@ void GL_SetRenderMode( int mode ); void GL_EnableTextureUnit( int tmu, qboolean enable ); void GL_TextureTarget( uint target ); void GL_Cull( GLenum cull ); +void GL_PushPolygonOffset( float factor, float units ); +void GL_PopPolygonOffset( void ); void SCR_TimeRefresh_f( void ); // @@ -326,9 +328,9 @@ qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly ); // // gl_cull.c // -qboolean R_CullModel( cl_entity_t *e, const vec3_t absmin, const vec3_t absmax ); +qboolean R_CullModel( const cl_entity_t *e, const vec3_t absmin, const vec3_t absmax ); qboolean R_CullBox( const vec3_t mins, const vec3_t maxs ); -int R_CullSurface( msurface_t *surf, gl_frustum_t *frustum, uint clipflags ); +int R_CullSurface( const msurface_t *surf, const gl_frustum_t *frustum, uint clipflags ); // // gl_decals.c @@ -660,6 +662,12 @@ typedef struct int prev_height; } glconfig_t; +typedef struct polyoffset_state_s +{ + float factor; + float units; +} polyoffset_state_t; + typedef struct { int activeTMU; @@ -675,6 +683,9 @@ typedef struct qboolean stencilEnabled; qboolean in2DMode; + + polyoffset_state_t polyoffset_state[2]; + int num_polyoffsets; } glstate_t; typedef struct @@ -774,6 +785,7 @@ extern convar_t gl_keeptjunctions; extern convar_t gl_round_down; extern convar_t gl_wireframe; extern convar_t gl_polyoffset; +extern convar_t gl_polyoffset_bmodels; extern convar_t gl_finish; extern convar_t gl_nosort; extern convar_t gl_test; // cvar to testify new effects diff --git a/ref/gl/gl_opengl.c b/ref/gl/gl_opengl.c index 458ac74d..162aa467 100644 --- a/ref/gl/gl_opengl.c +++ b/ref/gl/gl_opengl.c @@ -12,7 +12,8 @@ CVAR_DEFINE_AUTO( gl_texture_nearest, "0", FCVAR_GLCONFIG, "disable texture filt CVAR_DEFINE_AUTO( gl_lightmap_nearest, "0", FCVAR_GLCONFIG, "disable lightmap filter" ); CVAR_DEFINE_AUTO( gl_keeptjunctions, "1", FCVAR_GLCONFIG, "removing tjuncs causes blinking pixels" ); CVAR_DEFINE_AUTO( gl_check_errors, "1", FCVAR_GLCONFIG, "ignore video engine errors" ); -CVAR_DEFINE_AUTO( gl_polyoffset, "2.0", FCVAR_GLCONFIG, "polygon offset for decals" ); +CVAR_DEFINE_AUTO( gl_polyoffset, "2", FCVAR_GLCONFIG, "polygon offset for decals" ); +CVAR_DEFINE_AUTO( gl_polyoffset_bmodels, "2", FCVAR_GLCONFIG, "polygon offset for brush models" ); CVAR_DEFINE_AUTO( gl_wireframe, "0", FCVAR_GLCONFIG|FCVAR_SPONLY, "show wireframe overlay" ); CVAR_DEFINE_AUTO( gl_finish, "0", FCVAR_GLCONFIG, "use glFinish instead of glFlush" ); CVAR_DEFINE_AUTO( gl_nosort, "0", FCVAR_GLCONFIG, "disable sorting of translucent surfaces" ); @@ -1180,8 +1181,8 @@ static void GL_InitCommands( void ) gEngfuncs.Cvar_RegisterVariable( &gl_overbright ); gEngfuncs.Cvar_RegisterVariable( &gl_fog ); - // these cvar not used by engine but some mods requires this gEngfuncs.Cvar_RegisterVariable( &gl_polyoffset ); + gEngfuncs.Cvar_RegisterVariable( &gl_polyoffset_bmodels ); // make sure gl_vsync is checked after vid_restart SetBits( gl_vsync->flags, FCVAR_CHANGED ); diff --git a/ref/gl/gl_rsurf.c b/ref/gl/gl_rsurf.c index d9b85ad6..d21cf787 100644 --- a/ref/gl/gl_rsurf.c +++ b/ref/gl/gl_rsurf.c @@ -1131,10 +1131,10 @@ static void R_BlendLightmaps( void ) R_RenderFullbrights ================ */ -static void R_RenderFullbrights( void ) +static void R_RenderFullbrights( qboolean allow_vbo ) { - mextrasurf_t *es, *p; - int i; + mextrasurf_t *es, *p; + int i; if( !R_SeparatePassActive( &draw_fullbrights )) return; @@ -1148,11 +1148,8 @@ static void R_RenderFullbrights( void ) // if fullbright textures are drawn in separate pass from VBO they // cause z-fighting, this is noticeable on `slide_waifufu.bsp` map - if( R_HasEnabledVBO() && gl_polyoffset.value ) - { - pglEnable( GL_POLYGON_OFFSET_FILL ); - pglPolygonOffset( -1.0f, -gl_polyoffset.value ); - } + if( allow_vbo && gl_polyoffset.value ) + GL_PushPolygonOffset( -1.0f, -gl_polyoffset.value ); for( i = draw_fullbrights.first; i <= draw_fullbrights.last; i++ ) { @@ -1169,8 +1166,8 @@ static void R_RenderFullbrights( void ) es->lumachain = NULL; } - if( R_HasEnabledVBO() && gl_polyoffset.value ) - pglDisable( GL_POLYGON_OFFSET_FILL ); + if( allow_vbo && gl_polyoffset.value ) + GL_PopPolygonOffset(); pglDisable( GL_BLEND ); pglDepthMask( GL_TRUE ); @@ -1201,7 +1198,7 @@ static void R_RenderDetails( int passes ) pglEnable( GL_BLEND ); pglBlendFunc( GL_DST_COLOR, GL_SRC_COLOR ); pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); - if(passes == 3) + if( passes == 3 ) pglDepthFunc( GL_EQUAL ); else { @@ -1668,6 +1665,51 @@ static void R_SetRenderMode( cl_entity_t *e ) } } +static int R_SortBrushModelSurfaces( cl_entity_t *e, model_t *clmodel, vec3_t mins ) +{ + qboolean quake_compatible = ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ); + int num_sorted = 0; + + for( int i = 0; i < clmodel->nummodelsurfaces; i++ ) + { + msurface_t *psurf = &clmodel->surfaces[clmodel->firstmodelsurface + i]; + + if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !quake_compatible ) + { + if( psurf->plane->type != PLANE_Z && !FBitSet( e->curstate.effects, EF_WATERSIDES )) + continue; + + if( mins[2] + 1.0f >= psurf->plane->dist ) + continue; + } + + int cull_type = R_CullSurface( psurf, &RI.frustum, RI.frustum.clipFlags ); + + if( cull_type >= CULL_FRUSTUM ) + continue; + + if( cull_type == CULL_BACKSIDE ) + { + if( !FBitSet( psurf->flags, SURF_DRAWTURB ) && !( psurf->pdecals && e->curstate.rendermode == kRenderTransTexture )) + continue; + } + + if( num_sorted < gpGlobals->max_surfaces ) + { + gpGlobals->draw_surfaces[num_sorted].surf = psurf; + gpGlobals->draw_surfaces[num_sorted].cull = cull_type; + num_sorted++; + } + } + + // sort faces if needs + if( !FBitSet( clmodel->flags, MODEL_LIQUID ) && e->curstate.rendermode == kRenderTransTexture && !gl_nosort.value ) + qsort( gpGlobals->draw_surfaces, num_sorted, sizeof( sortedface_t ), R_SurfaceCompare ); + + return num_sorted; + +} + /* ================= R_DrawBrushModel @@ -1675,20 +1717,14 @@ R_DrawBrushModel */ void R_DrawBrushModel( cl_entity_t *e ) { - int i, k, num_sorted; - vec3_t origin_l, oldorigin; - int old_rendermode; - vec3_t mins, maxs; - int cull_type; - msurface_t *psurf; - model_t *clmodel; - qboolean rotated; - dlight_t *l; + int old_rendermode; + vec3_t mins, maxs; + qboolean rotated; qboolean allow_vbo = R_HasEnabledVBO(); if( !RI.drawWorld ) return; - clmodel = e->model; + model_t *clmodel = e->model; // external models not loaded to VBO if( clmodel->surfaces != WORLDMODEL->surfaces ) @@ -1696,7 +1732,7 @@ void R_DrawBrushModel( cl_entity_t *e ) if( !VectorIsNull( e->angles )) { - for( i = 0; i < 3; i++ ) + for( int i = 0; i < 3; i++ ) { mins[i] = e->origin[i] - clmodel->radius; maxs[i] = e->origin[i] + clmodel->radius; @@ -1717,21 +1753,29 @@ void R_DrawBrushModel( cl_entity_t *e ) old_rendermode = e->curstate.rendermode; gl_lms.dynamic_surfaces = NULL; - if( rotated ) R_RotateForEntity( e ); - else R_TranslateForEntity( e ); + if( rotated ) + { + R_RotateForEntity( e ); + + Matrix4x4_VectorITransform( RI.objectMatrix, RI.cullorigin, tr.modelorg ); + } + else + { + R_TranslateForEntity( e ); + + VectorSubtract( RI.cullorigin, e->origin, tr.modelorg ); + } if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ) && FBitSet( clmodel->flags, MODEL_TRANSPARENT )) e->curstate.rendermode = kRenderTransAlpha; e->visframe = tr.realframecount; // visible - if( rotated ) Matrix4x4_VectorITransform( RI.objectMatrix, RI.cullorigin, tr.modelorg ); - else VectorSubtract( RI.cullorigin, e->origin, tr.modelorg ); - // calculate dynamic lighting for bmodel - for( k = 0; k < MAX_DLIGHTS; k++ ) + for( int k = 0; k < MAX_DLIGHTS; k++ ) { - l = &tr.dlights[k]; + dlight_t *l = &tr.dlights[k]; + vec3_t origin_l, oldorigin; if( l->die < gp_cl->time || !l->radius ) continue; @@ -1757,56 +1801,35 @@ void R_DrawBrushModel( cl_entity_t *e ) if( !allow_vbo ) GL_SetupFogColorForSurfaces (); - psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; - num_sorted = 0; + int num_sorted = R_SortBrushModelSurfaces( e, clmodel, mins ); - for( i = 0; i < clmodel->nummodelsurfaces; i++, psurf++ ) - { - if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) - { - if( psurf->plane->type != PLANE_Z && !FBitSet( e->curstate.effects, EF_WATERSIDES )) - continue; - if( mins[2] + 1.0f >= psurf->plane->dist ) - continue; - } - - cull_type = R_CullSurface( psurf, &RI.frustum, RI.frustum.clipFlags ); - - if( cull_type >= CULL_FRUSTUM ) - continue; - - if( cull_type == CULL_BACKSIDE ) - { - if( !FBitSet( psurf->flags, SURF_DRAWTURB ) && !( psurf->pdecals && e->curstate.rendermode == kRenderTransTexture )) - continue; - } - - if( num_sorted < gpGlobals->max_surfaces ) - { - gpGlobals->draw_surfaces[num_sorted].surf = psurf; - gpGlobals->draw_surfaces[num_sorted].cull = cull_type; - num_sorted++; - } - } - - // sort faces if needs - if( !FBitSet( clmodel->flags, MODEL_LIQUID ) && e->curstate.rendermode == kRenderTransTexture && !gl_nosort.value ) - qsort( gpGlobals->draw_surfaces, num_sorted, sizeof( sortedface_t ), R_SurfaceCompare ); + // draw bmodels with a polyoffset to avoid flickering when they are too close to world + // R_DrawVBO and DrawDecalsBatch will restore polyoffset + if( gl_polyoffset_bmodels.value ) + GL_PushPolygonOffset( -0.5f, -gl_polyoffset_bmodels.value ); // draw sorted translucent surfaces - for( i = 0; i < num_sorted; i++ ) - if( !allow_vbo || !R_AddSurfToVBO( gpGlobals->draw_surfaces[i].surf, true ) ) + for( int i = 0; i < num_sorted; i++ ) + { + if( !allow_vbo || !R_AddSurfToVBO( gpGlobals->draw_surfaces[i].surf, true )) R_RenderBrushPoly( gpGlobals->draw_surfaces[i].surf, gpGlobals->draw_surfaces[i].cull ); + } + R_DrawVBO( R_HasLightmap(), true ); if( e->curstate.rendermode == kRenderTransColor ) pglEnable( GL_TEXTURE_2D ); DrawDecalsBatch(); + GL_ResetFogColor(); R_BlendLightmaps(); - R_RenderFullbrights(); - R_RenderDetails( allow_vbo? 2: 3 ); + R_RenderFullbrights( allow_vbo ); + R_RenderDetails( allow_vbo ? 2 : 3 ); + + if( gl_polyoffset_bmodels.value ) + GL_PopPolygonOffset(); + R_DrawTriangleOutlines(); // restore fog here @@ -1821,17 +1844,9 @@ void R_DrawBrushModel( cl_entity_t *e ) if( r_showhull->value > 0.0f ) { - GLfloat factor, units; - - pglGetFloatv( GL_POLYGON_OFFSET_FACTOR, &factor ); - pglGetFloatv( GL_POLYGON_OFFSET_UNITS, &units ); - - pglPolygonOffset( 1.0f, 2.0f ); - pglEnable( GL_POLYGON_OFFSET_FILL ); + GL_PushPolygonOffset( 1.0f, 2.0f ); gEngfuncs.R_DrawModelHull( clmodel ); // draw before restore - pglDisable( GL_POLYGON_OFFSET_FILL ); - - pglPolygonOffset( factor, units ); + GL_PopPolygonOffset(); } R_LoadIdentity(); // restore worldmatrix @@ -2856,7 +2871,6 @@ static void R_DrawVBODlights( vboarray_t *vbo, vbotexture_t *vbotex, texture_t * uint indexbase = vbos.surfdata[((char*)surf - (char*)WORLDMODEL->surfaces) / sizeof( *surf )].startindex; uint index; mextrasurf_t *info; // this stores current dlight offset - decal_t *pdecal; int sample_size; info = surf->info; @@ -3074,7 +3088,6 @@ static void R_DrawStaticDecals( vboarray_t *vbo, qboolean drawlightmap, int ilig pglDepthMask( GL_FALSE ); pglEnable( GL_BLEND ); - pglEnable( GL_POLYGON_OFFSET_FILL ); if( RI.currententity->curstate.rendermode == kRenderTransAlpha ) pglDisable( GL_ALPHA_TEST ); @@ -3140,7 +3153,6 @@ static void R_DrawStaticDecals( vboarray_t *vbo, qboolean drawlightmap, int ilig // prepare for next texture pglDepthMask( GL_TRUE ); pglDisable( GL_BLEND ); - pglDisable( GL_POLYGON_OFFSET_FILL ); R_SetupVBOArrayStatic( vbo, drawlightmap, true ); @@ -3280,11 +3292,19 @@ void R_DrawVBO( qboolean drawlightmap, qboolean drawtextures ) if( drawtextures && drawlightmap && vbos.decaldata->lm[k] ) { + if( gl_polyoffset.value ) + GL_PushPolygonOffset( -1.0f, -gl_polyoffset.value ); + R_DrawStaticDecals( vbo, drawlightmap, k ); + + if( gl_polyoffset.value ) + GL_PopPolygonOffset(); } + if( !drawtextures || !drawlightmap ) vbos.decaldata->lm[k] = NULL; } + // Assert( !vbo->next ); R_ClearVBOState( drawlightmap, drawtextures ); @@ -3659,7 +3679,7 @@ void R_DrawWorld( void ) DrawDecalsBatch(); GL_ResetFogColor(); R_BlendLightmaps(); - R_RenderFullbrights(); + R_RenderFullbrights( R_HasEnabledVBO( )); R_RenderDetails( R_HasEnabledVBO() ? 2 : 3 ); R_DrawTriangleOutlines();