/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * Surface generation and drawing * * ======================================================================= */ #include #include "header/local.h" int c_visible_lightmaps; int c_visible_textures; static vec3_t modelorg; /* relative to viewpoint */ msurface_t *r_alpha_surfaces; gllightmapstate_t gl_lms; void LM_InitBlock ( void ); void LM_UploadBlock ( qboolean dynamic ); qboolean LM_AllocBlock ( int w, int h, int *x, int *y ); void R_SetCacheState ( msurface_t *surf ); void R_BuildLightMap ( msurface_t *surf, byte *dest, int stride ); /* * Returns the proper texture for a given time and base texture */ image_t * R_TextureAnimation ( mtexinfo_t *tex ) { int c; if ( !tex->next ) { return ( tex->image ); } c = currententity->frame % tex->numframes; while ( c ) { tex = tex->next; c--; } return ( tex->image ); } void R_DrawGLPoly ( glpoly_t *p ) { int i; float *v; qglBegin( GL_POLYGON ); v = p->verts [ 0 ]; for ( i = 0; i < p->numverts; i++, v += VERTEXSIZE ) { qglTexCoord2f( v [ 3 ], v [ 4 ] ); qglVertex3fv( v ); } qglEnd(); } void R_DrawGLFlowingPoly ( msurface_t *fa ) { int i; float *v; glpoly_t *p; float scroll; p = fa->polys; scroll = -64 * ( ( r_newrefdef.time / 40.0 ) - (int) ( r_newrefdef.time / 40.0 ) ); if ( scroll == 0.0 ) { scroll = -64.0; } qglBegin( GL_POLYGON ); v = p->verts [ 0 ]; for ( i = 0; i < p->numverts; i++, v += VERTEXSIZE ) { qglTexCoord2f( ( v [ 3 ] + scroll ), v [ 4 ] ); qglVertex3fv( v ); } qglEnd(); } void R_DrawTriangleOutlines ( void ) { int i, j; glpoly_t *p; if ( !gl_showtris->value ) { return; } qglDisable( GL_TEXTURE_2D ); qglDisable( GL_DEPTH_TEST ); qglColor4f( 1, 1, 1, 1 ); for ( i = 0; i < MAX_LIGHTMAPS; i++ ) { msurface_t *surf; for ( surf = gl_lms.lightmap_surfaces [ i ]; surf != 0; surf = surf->lightmapchain ) { p = surf->polys; for ( ; p; p = p->chain ) { for ( j = 2; j < p->numverts; j++ ) { qglBegin( GL_LINE_STRIP ); qglVertex3fv( p->verts [ 0 ] ); qglVertex3fv( p->verts [ j - 1 ] ); qglVertex3fv( p->verts [ j ] ); qglVertex3fv( p->verts [ 0 ] ); qglEnd(); } } } } qglEnable( GL_DEPTH_TEST ); qglEnable( GL_TEXTURE_2D ); } void R_DrawGLPolyChain ( glpoly_t *p, float soffset, float toffset ) { if ( ( soffset == 0 ) && ( toffset == 0 ) ) { for ( ; p != 0; p = p->chain ) { float *v; int j; v = p->verts [ 0 ]; if ( v == NULL ) { fprintf( stderr, "BUGFIX: R_DrawGLPolyChain: v==NULL\n" ); return; } qglBegin( GL_POLYGON ); for ( j = 0; j < p->numverts; j++, v += VERTEXSIZE ) { qglTexCoord2f( v [ 5 ], v [ 6 ] ); qglVertex3fv( v ); } qglEnd(); } } else { for ( ; p != 0; p = p->chain ) { float *v; int j; qglBegin( GL_POLYGON ); v = p->verts [ 0 ]; for ( j = 0; j < p->numverts; j++, v += VERTEXSIZE ) { qglTexCoord2f( v [ 5 ] - soffset, v [ 6 ] - toffset ); qglVertex3fv( v ); } qglEnd(); } } } /* * This routine takes all the given light mapped surfaces in the world * and blends them into the framebuffer. */ void R_BlendLightmaps ( void ) { int i; msurface_t *surf, *newdrawsurf = 0; /* don't bother if we're set to fullbright */ if ( gl_fullbright->value ) { return; } if ( !r_worldmodel->lightdata ) { return; } /* don't bother writing Z */ qglDepthMask( 0 ); /* set the appropriate blending mode unless * we're only looking at the lightmaps. */ if ( !gl_lightmap->value ) { qglEnable( GL_BLEND ); if ( gl_saturatelighting->value ) { qglBlendFunc( GL_ONE, GL_ONE ); } else { qglBlendFunc( GL_ZERO, GL_SRC_COLOR ); } } if ( currentmodel == r_worldmodel ) { c_visible_lightmaps = 0; } /* render static lightmaps first */ for ( i = 1; i < MAX_LIGHTMAPS; i++ ) { if ( gl_lms.lightmap_surfaces [ i ] ) { if ( currentmodel == r_worldmodel ) { c_visible_lightmaps++; } R_Bind( gl_state.lightmap_textures + i ); for ( surf = gl_lms.lightmap_surfaces [ i ]; surf != 0; surf = surf->lightmapchain ) { if ( surf->polys ) { R_DrawGLPolyChain( surf->polys, 0, 0 ); } } } } /* render dynamic lightmaps */ if ( gl_dynamic->value ) { LM_InitBlock(); R_Bind( gl_state.lightmap_textures + 0 ); if ( currentmodel == r_worldmodel ) { c_visible_lightmaps++; } newdrawsurf = gl_lms.lightmap_surfaces [ 0 ]; for ( surf = gl_lms.lightmap_surfaces [ 0 ]; surf != 0; surf = surf->lightmapchain ) { int smax, tmax; byte *base; smax = ( surf->extents [ 0 ] >> 4 ) + 1; tmax = ( surf->extents [ 1 ] >> 4 ) + 1; if ( LM_AllocBlock( smax, tmax, &surf->dlight_s, &surf->dlight_t ) ) { base = gl_lms.lightmap_buffer; base += ( surf->dlight_t * BLOCK_WIDTH + surf->dlight_s ) * LIGHTMAP_BYTES; R_BuildLightMap( surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES ); } else { msurface_t *drawsurf; /* upload what we have so far */ LM_UploadBlock( true ); /* draw all surfaces that use this lightmap */ for ( drawsurf = newdrawsurf; drawsurf != surf; drawsurf = drawsurf->lightmapchain ) { if ( drawsurf->polys ) { R_DrawGLPolyChain( drawsurf->polys, ( drawsurf->light_s - drawsurf->dlight_s ) * ( 1.0 / 128.0 ), ( drawsurf->light_t - drawsurf->dlight_t ) * ( 1.0 / 128.0 ) ); } } newdrawsurf = drawsurf; /* clear the block */ LM_InitBlock(); /* try uploading the block now */ if ( !LM_AllocBlock( smax, tmax, &surf->dlight_s, &surf->dlight_t ) ) { ri.Sys_Error( ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed (dynamic)\n", smax, tmax ); } base = gl_lms.lightmap_buffer; base += ( surf->dlight_t * BLOCK_WIDTH + surf->dlight_s ) * LIGHTMAP_BYTES; R_BuildLightMap( surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES ); } } /* draw remainder of dynamic lightmaps that haven't been uploaded yet */ if ( newdrawsurf ) { LM_UploadBlock( true ); } for ( surf = newdrawsurf; surf != 0; surf = surf->lightmapchain ) { if ( surf->polys ) { R_DrawGLPolyChain( surf->polys, ( surf->light_s - surf->dlight_s ) * ( 1.0 / 128.0 ), ( surf->light_t - surf->dlight_t ) * ( 1.0 / 128.0 ) ); } } } /* restore state */ qglDisable( GL_BLEND ); qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); qglDepthMask( 1 ); } void R_RenderBrushPoly ( msurface_t *fa ) { int maps; image_t *image; qboolean is_dynamic = false; c_brush_polys++; image = R_TextureAnimation( fa->texinfo ); if ( fa->flags & SURF_DRAWTURB ) { R_Bind( image->texnum ); /* warp texture, no lightmaps */ R_TexEnv( GL_MODULATE ); qglColor4f( gl_state.inverse_intensity, gl_state.inverse_intensity, gl_state.inverse_intensity, 1.0F ); R_EmitWaterPolys( fa ); R_TexEnv( GL_REPLACE ); return; } else { R_Bind( image->texnum ); R_TexEnv( GL_REPLACE ); } if ( fa->texinfo->flags & SURF_FLOWING ) { R_DrawGLFlowingPoly( fa ); } else { R_DrawGLPoly( fa->polys ); } /* check for lightmap modification */ for ( maps = 0; maps < MAXLIGHTMAPS && fa->styles [ maps ] != 255; maps++ ) { if ( r_newrefdef.lightstyles [ fa->styles [ maps ] ].white != fa->cached_light [ maps ] ) { goto dynamic; } } /* dynamic this frame or dynamic previously */ if ( ( fa->dlightframe == r_framecount ) ) { dynamic: if ( gl_dynamic->value ) { if ( !( fa->texinfo->flags & ( SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP ) ) ) { is_dynamic = true; } } } if ( is_dynamic ) { if ( ( ( fa->styles [ maps ] >= 32 ) || ( fa->styles [ maps ] == 0 ) ) && ( fa->dlightframe != r_framecount ) ) { unsigned temp [ 34 * 34 ]; int smax, tmax; smax = ( fa->extents [ 0 ] >> 4 ) + 1; tmax = ( fa->extents [ 1 ] >> 4 ) + 1; R_BuildLightMap( fa, (void *) temp, smax * 4 ); R_SetCacheState( fa ); R_Bind( gl_state.lightmap_textures + fa->lightmaptexturenum ); qglTexSubImage2D( GL_TEXTURE_2D, 0, fa->light_s, fa->light_t, smax, tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp ); fa->lightmapchain = gl_lms.lightmap_surfaces [ fa->lightmaptexturenum ]; gl_lms.lightmap_surfaces [ fa->lightmaptexturenum ] = fa; } else { fa->lightmapchain = gl_lms.lightmap_surfaces [ 0 ]; gl_lms.lightmap_surfaces [ 0 ] = fa; } } else { fa->lightmapchain = gl_lms.lightmap_surfaces [ fa->lightmaptexturenum ]; gl_lms.lightmap_surfaces [ fa->lightmaptexturenum ] = fa; } } /* * Draw water surfaces and windows. * The BSP tree is waled front to back, so unwinding the chain * of alpha_surfaces will draw back to front, giving proper ordering. */ void R_DrawAlphaSurfaces ( void ) { msurface_t *s; float intens; /* go back to the world matrix */ qglLoadMatrixf( r_world_matrix ); qglEnable( GL_BLEND ); R_TexEnv( GL_MODULATE ); /* the textures are prescaled up for a better lighting range, * so scale it back down */ intens = gl_state.inverse_intensity; for ( s = r_alpha_surfaces; s; s = s->texturechain ) { R_Bind( s->texinfo->image->texnum ); c_brush_polys++; if ( s->texinfo->flags & SURF_TRANS33 ) { qglColor4f( intens, intens, intens, 0.33 ); } else if ( s->texinfo->flags & SURF_TRANS66 ) { qglColor4f( intens, intens, intens, 0.66 ); } else { qglColor4f( intens, intens, intens, 1 ); } if ( s->flags & SURF_DRAWTURB ) { R_EmitWaterPolys( s ); } else if ( s->texinfo->flags & SURF_FLOWING ) { R_DrawGLFlowingPoly( s ); } else { R_DrawGLPoly( s->polys ); } } R_TexEnv( GL_REPLACE ); qglColor4f( 1, 1, 1, 1 ); qglDisable( GL_BLEND ); r_alpha_surfaces = NULL; } void R_DrawTextureChains ( void ) { int i; msurface_t *s; image_t *image; c_visible_textures = 0; if ( !qglSelectTextureSGIS && !qglActiveTextureARB ) { for ( i = 0, image = gltextures; i < numgltextures; i++, image++ ) { if ( !image->registration_sequence ) { continue; } s = image->texturechain; if ( !s ) { continue; } c_visible_textures++; for ( ; s; s = s->texturechain ) { R_RenderBrushPoly( s ); } image->texturechain = NULL; } } else { for ( i = 0, image = gltextures; i < numgltextures; i++, image++ ) { if ( !image->registration_sequence ) { continue; } if ( !image->texturechain ) { continue; } c_visible_textures++; for ( s = image->texturechain; s; s = s->texturechain ) { if ( !( s->flags & SURF_DRAWTURB ) ) { R_RenderBrushPoly( s ); } } } R_EnableMultitexture( false ); for ( i = 0, image = gltextures; i < numgltextures; i++, image++ ) { if ( !image->registration_sequence ) { continue; } s = image->texturechain; if ( !s ) { continue; } for ( ; s; s = s->texturechain ) { if ( s->flags & SURF_DRAWTURB ) { R_RenderBrushPoly( s ); } } image->texturechain = NULL; } } R_TexEnv( GL_REPLACE ); } static void R_RenderLightmappedPoly ( msurface_t *surf ) { int i, nv = surf->polys->numverts; int map; float *v; image_t *image = R_TextureAnimation( surf->texinfo ); qboolean is_dynamic = false; unsigned lmtex = surf->lightmaptexturenum; glpoly_t *p; for ( map = 0; map < MAXLIGHTMAPS && surf->styles [ map ] != 255; map++ ) { if ( r_newrefdef.lightstyles [ surf->styles [ map ] ].white != surf->cached_light [ map ] ) { goto dynamic; } } if ( ( surf->dlightframe == r_framecount ) ) { dynamic: if ( gl_dynamic->value ) { if ( !( surf->texinfo->flags & ( SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP ) ) ) { is_dynamic = true; } } } if ( is_dynamic ) { unsigned temp [ 128 * 128 ]; int smax, tmax; if ( ( ( surf->styles [ map ] >= 32 ) || ( surf->styles [ map ] == 0 ) ) && ( surf->dlightframe != r_framecount ) ) { smax = ( surf->extents [ 0 ] >> 4 ) + 1; tmax = ( surf->extents [ 1 ] >> 4 ) + 1; R_BuildLightMap( surf, (void *) temp, smax * 4 ); R_SetCacheState( surf ); R_MBind( QGL_TEXTURE1, gl_state.lightmap_textures + surf->lightmaptexturenum ); lmtex = surf->lightmaptexturenum; qglTexSubImage2D( GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax, tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp ); } else { smax = ( surf->extents [ 0 ] >> 4 ) + 1; tmax = ( surf->extents [ 1 ] >> 4 ) + 1; R_BuildLightMap( surf, (void *) temp, smax * 4 ); R_MBind( QGL_TEXTURE1, gl_state.lightmap_textures + 0 ); lmtex = 0; qglTexSubImage2D( GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax, tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp ); } c_brush_polys++; R_MBind( QGL_TEXTURE0, image->texnum ); R_MBind( QGL_TEXTURE1, gl_state.lightmap_textures + lmtex ); if ( surf->texinfo->flags & SURF_FLOWING ) { float scroll; scroll = -64 * ( ( r_newrefdef.time / 40.0 ) - (int) ( r_newrefdef.time / 40.0 ) ); if ( scroll == 0.0 ) { scroll = -64.0; } for ( p = surf->polys; p; p = p->chain ) { v = p->verts [ 0 ]; qglBegin( GL_POLYGON ); for ( i = 0; i < nv; i++, v += VERTEXSIZE ) { qglMTexCoord2fSGIS( QGL_TEXTURE0, ( v [ 3 ] + scroll ), v [ 4 ] ); qglMTexCoord2fSGIS( QGL_TEXTURE1, v [ 5 ], v [ 6 ] ); qglVertex3fv( v ); } qglEnd(); } } else { for ( p = surf->polys; p; p = p->chain ) { v = p->verts [ 0 ]; qglBegin( GL_POLYGON ); for ( i = 0; i < nv; i++, v += VERTEXSIZE ) { qglMTexCoord2fSGIS( QGL_TEXTURE0, v [ 3 ], v [ 4 ] ); qglMTexCoord2fSGIS( QGL_TEXTURE1, v [ 5 ], v [ 6 ] ); qglVertex3fv( v ); } qglEnd(); } } } else { c_brush_polys++; R_MBind( QGL_TEXTURE0, image->texnum ); R_MBind( QGL_TEXTURE1, gl_state.lightmap_textures + lmtex ); if ( surf->texinfo->flags & SURF_FLOWING ) { float scroll; scroll = -64 * ( ( r_newrefdef.time / 40.0 ) - (int) ( r_newrefdef.time / 40.0 ) ); if ( scroll == 0.0 ) { scroll = -64.0; } for ( p = surf->polys; p; p = p->chain ) { v = p->verts [ 0 ]; qglBegin( GL_POLYGON ); for ( i = 0; i < nv; i++, v += VERTEXSIZE ) { qglMTexCoord2fSGIS( QGL_TEXTURE0, ( v [ 3 ] + scroll ), v [ 4 ] ); qglMTexCoord2fSGIS( QGL_TEXTURE1, v [ 5 ], v [ 6 ] ); qglVertex3fv( v ); } qglEnd(); } } else { for ( p = surf->polys; p; p = p->chain ) { v = p->verts [ 0 ]; qglBegin( GL_POLYGON ); for ( i = 0; i < nv; i++, v += VERTEXSIZE ) { qglMTexCoord2fSGIS( QGL_TEXTURE0, v [ 3 ], v [ 4 ] ); qglMTexCoord2fSGIS( QGL_TEXTURE1, v [ 5 ], v [ 6 ] ); qglVertex3fv( v ); } qglEnd(); } } } } void R_DrawInlineBModel ( void ) { int i, k; cplane_t *pplane; float dot; msurface_t *psurf; dlight_t *lt; /* calculate dynamic lighting for bmodel */ if ( !gl_flashblend->value ) { lt = r_newrefdef.dlights; for ( k = 0; k < r_newrefdef.num_dlights; k++, lt++ ) { R_MarkLights( lt, 1 << k, currentmodel->nodes + currentmodel->firstnode ); } } psurf = ¤tmodel->surfaces [ currentmodel->firstmodelsurface ]; if ( currententity->flags & RF_TRANSLUCENT ) { qglEnable( GL_BLEND ); qglColor4f( 1, 1, 1, 0.25 ); R_TexEnv( GL_MODULATE ); } /* draw texture */ for ( i = 0; i < currentmodel->nummodelsurfaces; i++, psurf++ ) { /* find which side of the node we are on */ pplane = psurf->plane; dot = DotProduct( modelorg, pplane->normal ) - pplane->dist; /* draw the polygon */ if ( ( ( psurf->flags & SURF_PLANEBACK ) && ( dot < -BACKFACE_EPSILON ) ) || ( !( psurf->flags & SURF_PLANEBACK ) && ( dot > BACKFACE_EPSILON ) ) ) { if ( psurf->texinfo->flags & ( SURF_TRANS33 | SURF_TRANS66 ) ) { /* add to the translucent chain */ psurf->texturechain = r_alpha_surfaces; r_alpha_surfaces = psurf; } else if ( qglMTexCoord2fSGIS && !( psurf->flags & SURF_DRAWTURB ) ) { R_RenderLightmappedPoly( psurf ); } else { R_EnableMultitexture( false ); R_RenderBrushPoly( psurf ); R_EnableMultitexture( true ); } } } if ( !( currententity->flags & RF_TRANSLUCENT ) ) { if ( !qglMTexCoord2fSGIS ) { R_BlendLightmaps(); } } else { qglDisable( GL_BLEND ); qglColor4f( 1, 1, 1, 1 ); R_TexEnv( GL_REPLACE ); } } void R_DrawBrushModel ( entity_t *e ) { vec3_t mins, maxs; int i; qboolean rotated; if ( currentmodel->nummodelsurfaces == 0 ) { return; } currententity = e; gl_state.currenttextures [ 0 ] = gl_state.currenttextures [ 1 ] = -1; if ( e->angles [ 0 ] || e->angles [ 1 ] || e->angles [ 2 ] ) { rotated = true; for ( i = 0; i < 3; i++ ) { mins [ i ] = e->origin [ i ] - currentmodel->radius; maxs [ i ] = e->origin [ i ] + currentmodel->radius; } } else { rotated = false; VectorAdd( e->origin, currentmodel->mins, mins ); VectorAdd( e->origin, currentmodel->maxs, maxs ); } if ( R_CullBox( mins, maxs ) ) { return; } qglColor3f( 1, 1, 1 ); memset( gl_lms.lightmap_surfaces, 0, sizeof ( gl_lms.lightmap_surfaces ) ); VectorSubtract( r_newrefdef.vieworg, e->origin, modelorg ); if ( rotated ) { vec3_t temp; vec3_t forward, right, up; VectorCopy( modelorg, temp ); AngleVectors( e->angles, forward, right, up ); modelorg [ 0 ] = DotProduct( temp, forward ); modelorg [ 1 ] = -DotProduct( temp, right ); modelorg [ 2 ] = DotProduct( temp, up ); } qglPushMatrix(); e->angles [ 0 ] = -e->angles [ 0 ]; e->angles [ 2 ] = -e->angles [ 2 ]; R_RotateForEntity( e ); e->angles [ 0 ] = -e->angles [ 0 ]; e->angles [ 2 ] = -e->angles [ 2 ]; R_EnableMultitexture( true ); R_SelectTexture( QGL_TEXTURE0 ); R_TexEnv( GL_REPLACE ); R_SelectTexture( QGL_TEXTURE1 ); R_DrawInlineBModel(); if ( !gl_config.mtexcombine ) { R_TexEnv( GL_REPLACE ); R_SelectTexture( GL_TEXTURE1 ); if ( gl_lightmap->value ) { R_TexEnv( GL_REPLACE ); } else { R_TexEnv( GL_MODULATE ); } } else { R_TexEnv( GL_COMBINE_EXT ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE ); R_SelectTexture( GL_TEXTURE1 ); R_TexEnv( GL_COMBINE_EXT ); if ( gl_lightmap->value ) { qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE ); } else { qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_MODULATE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT ); } if ( gl_overbrightbits->value ) { qglTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value ); } } qglPopMatrix(); } void R_RecursiveWorldNode ( mnode_t *node ) { int c, side, sidebit; cplane_t *plane; msurface_t *surf, **mark; mleaf_t *pleaf; float dot; image_t *image; if ( node->contents == CONTENTS_SOLID ) { return; /* solid */ } if ( node->visframe != r_visframecount ) { return; } if ( R_CullBox( node->minmaxs, node->minmaxs + 3 ) ) { return; } /* if a leaf node, draw stuff */ if ( node->contents != -1 ) { pleaf = (mleaf_t *) node; /* check for door connected areas */ if ( r_newrefdef.areabits ) { if ( !( r_newrefdef.areabits [ pleaf->area >> 3 ] & ( 1 << ( pleaf->area & 7 ) ) ) ) { return; /* not visible */ } } mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if ( c ) { do { ( *mark )->visframe = r_framecount; mark++; } while ( --c ); } return; } /* node is just a decision point, so go down the apropriate sides * find which side of the node we are on */ plane = node->plane; switch ( plane->type ) { case PLANE_X: dot = modelorg [ 0 ] - plane->dist; break; case PLANE_Y: dot = modelorg [ 1 ] - plane->dist; break; case PLANE_Z: dot = modelorg [ 2 ] - plane->dist; break; default: dot = DotProduct( modelorg, plane->normal ) - plane->dist; break; } if ( dot >= 0 ) { side = 0; sidebit = 0; } else { side = 1; sidebit = SURF_PLANEBACK; } /* recurse down the children, front side first */ R_RecursiveWorldNode( node->children [ side ] ); /* draw stuff */ for ( c = node->numsurfaces, surf = r_worldmodel->surfaces + node->firstsurface; c; c--, surf++ ) { if ( surf->visframe != r_framecount ) { continue; } if ( ( surf->flags & SURF_PLANEBACK ) != sidebit ) { continue; /* wrong side */ } if ( surf->texinfo->flags & SURF_SKY ) { /* just adds to visible sky bounds */ R_AddSkySurface( surf ); } else if ( surf->texinfo->flags & ( SURF_TRANS33 | SURF_TRANS66 ) ) { /* add to the translucent chain */ surf->texturechain = r_alpha_surfaces; r_alpha_surfaces = surf; } else { if ( qglMTexCoord2fSGIS && !( surf->flags & SURF_DRAWTURB ) ) { R_RenderLightmappedPoly( surf ); } else { /* the polygon is visible, so add it to the texture sorted chain */ image = R_TextureAnimation( surf->texinfo ); surf->texturechain = image->texturechain; image->texturechain = surf; } } } /* recurse down the back side */ R_RecursiveWorldNode( node->children [ !side ] ); } void R_DrawWorld ( void ) { entity_t ent; if ( !gl_drawworld->value ) { return; } if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) { return; } currentmodel = r_worldmodel; VectorCopy( r_newrefdef.vieworg, modelorg ); /* auto cycle the world frame for texture animation */ memset( &ent, 0, sizeof ( ent ) ); ent.frame = (int) ( r_newrefdef.time * 2 ); currententity = &ent; gl_state.currenttextures [ 0 ] = gl_state.currenttextures [ 1 ] = -1; qglColor3f( 1, 1, 1 ); memset( gl_lms.lightmap_surfaces, 0, sizeof ( gl_lms.lightmap_surfaces ) ); R_ClearSkyBox(); if ( qglMTexCoord2fSGIS ) { R_EnableMultitexture( true ); R_SelectTexture( QGL_TEXTURE0 ); R_TexEnv( GL_REPLACE ); R_SelectTexture( QGL_TEXTURE1 ); if ( !gl_config.mtexcombine ) { R_TexEnv( GL_REPLACE ); R_SelectTexture( GL_TEXTURE1 ); if ( gl_lightmap->value ) { R_TexEnv( GL_REPLACE ); } else { R_TexEnv( GL_MODULATE ); } } else { R_TexEnv( GL_COMBINE_EXT ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE ); R_SelectTexture( GL_TEXTURE1 ); R_TexEnv( GL_COMBINE_EXT ); if ( gl_lightmap->value ) { qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE ); } else { qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT ); qglTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_MODULATE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE ); qglTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT ); } if ( gl_overbrightbits->value ) { qglTexEnvi( GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value ); } } R_RecursiveWorldNode( r_worldmodel->nodes ); R_EnableMultitexture( false ); } else { R_RecursiveWorldNode( r_worldmodel->nodes ); } R_DrawTextureChains(); R_BlendLightmaps(); R_DrawSkyBox(); R_DrawTriangleOutlines(); } /* * Mark the leaves and nodes that are in the PVS for the current * cluster */ void R_MarkLeaves ( void ) { byte *vis; byte fatvis [ MAX_MAP_LEAFS / 8 ]; mnode_t *node; int i, c; mleaf_t *leaf; int cluster; if ( ( r_oldviewcluster == r_viewcluster ) && ( r_oldviewcluster2 == r_viewcluster2 ) && !gl_novis->value && ( r_viewcluster != -1 ) ) { return; } /* development aid to let you run around and see exactly where * the pvs ends */ if ( gl_lockpvs->value ) { return; } r_visframecount++; r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2; if ( gl_novis->value || ( r_viewcluster == -1 ) || !r_worldmodel->vis ) { /* mark everything */ for ( i = 0; i < r_worldmodel->numleafs; i++ ) { r_worldmodel->leafs [ i ].visframe = r_visframecount; } for ( i = 0; i < r_worldmodel->numnodes; i++ ) { r_worldmodel->nodes [ i ].visframe = r_visframecount; } return; } vis = Mod_ClusterPVS( r_viewcluster, r_worldmodel ); /* may have to combine two clusters because of solid water boundaries */ if ( r_viewcluster2 != r_viewcluster ) { memcpy( fatvis, vis, ( r_worldmodel->numleafs + 7 ) / 8 ); vis = Mod_ClusterPVS( r_viewcluster2, r_worldmodel ); c = ( r_worldmodel->numleafs + 31 ) / 32; for ( i = 0; i < c; i++ ) { ( (int *) fatvis ) [ i ] |= ( (int *) vis ) [ i ]; } vis = fatvis; } for ( i = 0, leaf = r_worldmodel->leafs; i < r_worldmodel->numleafs; i++, leaf++ ) { cluster = leaf->cluster; if ( cluster == -1 ) { continue; } if ( vis [ cluster >> 3 ] & ( 1 << ( cluster & 7 ) ) ) { node = (mnode_t *) leaf; do { if ( node->visframe == r_visframecount ) { break; } node->visframe = r_visframecount; node = node->parent; } while ( node ); } } }