ref: gl: add gl_polyoffset_bmodels to enable poly offset for drawing brush models, avoiding zfighting

This commit is contained in:
Alibek Omarov
2026-03-27 03:26:56 +05:00
parent 82475c3b58
commit 02b1d15e42
6 changed files with 179 additions and 101 deletions

View File

@@ -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 );
}
}
/*
==============================================================================

View File

@@ -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

View File

@@ -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 );

View File

@@ -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

View File

@@ -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 );

View File

@@ -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();