/* 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. */ // GL_RSURF.C: surface-related refresh code #include #include "r_local.h" static vec3_t modelorg; // relative to viewpoint msurface_t *r_alpha_surfaces; #define DYNAMIC_LIGHT_WIDTH 128 #define DYNAMIC_LIGHT_HEIGHT 128 #define LIGHTMAP_BYTES 4 #define BLOCK_WIDTH 128 #define BLOCK_HEIGHT 128 #define MAX_LIGHTMAPS 128 int c_visible_lightmaps; int c_visible_textures; #define GL_LIGHTMAP_FORMAT GL_RGBA typedef struct { int internal_format; int current_lightmap_texture; msurface_t *lightmap_surfaces[MAX_LIGHTMAPS]; int allocated[BLOCK_WIDTH]; // the lightmap texture data needs to be kept in // main memory so texsubimage can update properly byte lightmap_buffer[4*BLOCK_WIDTH*BLOCK_HEIGHT]; } gllightmapstate_t; static gllightmapstate_t gl_lms; static void LM_InitBlock( void ); static void LM_UploadBlock( qboolean dynamic ); static qboolean LM_AllocBlock (int w, int h, int *x, int *y); extern void R_SetCacheState( msurface_t *surf ); extern void R_BuildLightMap (msurface_t *surf, byte *dest, int stride); /* ============================================================= BRUSH MODELS ============================================================= */ /* =============== R_TextureAnimation 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; } //Knightmare added /* =============== R_AlphaTextureAnimation Variant of the above for alpha surfaces Uses msurface_t entity pointer, since currententity is not valid for the alpha surface pass =============== */ image_t *R_AlphaTextureAnimation (msurface_t *surf) { int c, frame; mtexinfo_t *tex = surf->texinfo; if (!tex->next) return tex->image; if (!surf->entity) frame = r_worldframe; // use worldspawn frame else frame = surf->entity->frame; c = frame % tex->numframes; while (c) { tex = tex->next; c--; } return tex->image; } /* =============== SetLightingMode =============== */ void SetLightingMode (int renderflags) { GL_SelectTexture( GL_TEXTURE0); if (!gl_config.mtexcombine || (renderflags & RF_TRANSLUCENT)) { GL_TexEnv( GL_REPLACE ); GL_SelectTexture( GL_TEXTURE1); if ( gl_lightmap->value ) GL_TexEnv( GL_REPLACE ); else GL_TexEnv( GL_MODULATE ); } else { GL_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 ); GL_SelectTexture( GL_TEXTURE1 ); GL_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 ( r_overbrightbits->value ) { qglTexEnvi ( GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, r_overbrightbits->value ); } } } #if 0 /* ================= WaterWarpPolyVerts Mangles the x and y coordinates in a copy of the poly so that any drawing routine can be water warped ================= */ glpoly_t *WaterWarpPolyVerts (glpoly_t *p) { int i; float *v, *nv; static byte buffer[1024]; glpoly_t *out; out = (glpoly_t *)buffer; out->numverts = p->numverts; v = p->verts[0]; nv = out->verts[0]; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE, nv+=VERTEXSIZE) { nv[0] = v[0] + 4*sin(v[1]*0.05+r_newrefdef.time)*sin(v[2]*0.05+r_newrefdef.time); nv[1] = v[1] + 4*sin(v[0]*0.05+r_newrefdef.time)*sin(v[2]*0.05+r_newrefdef.time); nv[2] = v[2]; nv[3] = v[3]; nv[4] = v[4]; nv[5] = v[5]; nv[6] = v[6]; } return out; } /* ================ DrawGLWaterPoly Warp the vertex coordinates ================ */ void DrawGLWaterPoly (glpoly_t *p) { int i; float *v; p = WaterWarpPolyVerts (p); qglBegin (GL_TRIANGLE_FAN); v = p->verts[0]; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { qglTexCoord2f (v[3], v[4]); qglVertex3fv (v); } qglEnd (); } void DrawGLWaterPolyLightmap (glpoly_t *p) { int i; float *v; p = WaterWarpPolyVerts (p); qglBegin (GL_TRIANGLE_FAN); v = p->verts[0]; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { qglTexCoord2f (v[5], v[6]); qglVertex3fv (v); } qglEnd (); } #endif /* ================ DrawGLPoly ================ */ // Knightmare added- Psychospaz's lightmaps on alpha surfaces void DrawGLPoly (msurface_t *fa, qboolean light, float alpha) { int i; float *v; glpoly_t *p = fa->polys; if (light) { SetVertexOverbrights(true); //Quake2Max add GL_ShadeModel (GL_SMOOTH); } else qglColor4f( gl_state.inverse_intensity, gl_state.inverse_intensity, gl_state.inverse_intensity, alpha); qglBegin (GL_POLYGON); v = p->verts[0]; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { if (light && p->vertexlight) { //Quake2Max change qglColor4ub( p->vertexlight[i*3+0], p->vertexlight[i*3+1], p->vertexlight[i*3+2], (byte)(alpha*255)); /*vec3_t color; R_LightPoint (v, color, false); R_MaxColorVec (color); qglColor4f( color[0], color[1], color[2], alpha);*/ } qglTexCoord2f (v[3], v[4]); qglVertex3fv (v); } qglEnd (); if (light) { SetVertexOverbrights(false); //Quake2Max add GL_ShadeModel (GL_FLAT); } } /* void DrawGLPoly (glpoly_t *p) { int i; float *v; qglBegin (GL_POLYGON); v = p->verts[0]; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { qglTexCoord2f (v[3], v[4]); qglVertex3fv (v); } qglEnd (); } */ //============ //PGM /* ================ DrawGLFlowingPoly -- version of DrawGLPoly that handles scrolling texture ================ */ // Knightmare added- Psychospaz's lightmaps on alpha surfaces void DrawGLFlowingPoly (msurface_t *fa, qboolean light, float alpha) { 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; if (light) { //Quake2Max change SetVertexOverbrights(true); GL_ShadeModel (GL_SMOOTH); } else qglColor4f( gl_state.inverse_intensity, gl_state.inverse_intensity, gl_state.inverse_intensity, alpha); qglBegin (GL_POLYGON); v = p->verts[0]; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { if (light && p->vertexlight) { //Quake2Max change qglColor4ub( p->vertexlight[i*3+0], p->vertexlight[i*3+1], p->vertexlight[i*3+2], (byte)(alpha*255)); /*vec3_t color; R_LightPoint (v, color, false); R_MaxColorVec (color); qglColor4f( color[0], color[1], color[2], alpha);*/ } qglTexCoord2f ((v[3] + scroll), v[4]); qglVertex3fv (v); } qglEnd (); if (light) { //Quake2Max change SetVertexOverbrights(false); GL_ShadeModel (GL_FLAT); } } /* void 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 ; inumverts ; i++, v+= VERTEXSIZE) { qglTexCoord2f ((v[3] + scroll), v[4]); qglVertex3fv (v); } qglEnd (); } */ //PGM //============ /* ** R_DrawTriangleOutlines */ //Knightmare- added GuyP's fix to let this work in multitexturing mode //void R_DrawTriangleOutlines (void) void R_DrawTriangleOutlines(msurface_t *surf) { 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 ; ilightmapchain ) { p = surf->polys; for ( ; p ; p=p->chain) { for (j=2 ; jnumverts ; 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);*/ // Guy: *\/\/\/ gl_showtris fix begin \/\/\/* qglDisable(GL_DEPTH_TEST); if (!surf) // Guy: Called from non-multitexture mode; need to loop through surfaces defined by non-mtex functions { qglDisable(GL_TEXTURE_2D); for (i = 0; i < MAX_LIGHTMAPS; i++) { for (surf = gl_lms.lightmap_surfaces[i]; surf != 0; surf = surf->lightmapchain) { for (p = surf->polys; p; p = p->chain) { for (j = 2; j < p->numverts; j++) { qglBegin(GL_LINE_STRIP); qglColor4f(1, 1, 1, 1); qglVertex3fv(p->verts[0]); qglVertex3fv(p->verts[j-1]); qglVertex3fv(p->verts[j]); qglVertex3fv(p->verts[0]); qglEnd(); } } } } qglEnable(GL_TEXTURE_2D); } else // Guy: Called from multitexture mode; surface to be rendered in wireframe already passed in { float tex_state0, tex_state1; GL_SelectTexture(GL_TEXTURE0); qglGetTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &tex_state0); GL_SelectTexture(GL_TEXTURE1); qglGetTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &tex_state1); GL_EnableMultitexture(false); qglDisable(GL_TEXTURE_2D); for (p = surf->polys; p; p = p->chain) { for (j = 2; j < p->numverts; j++) { qglBegin(GL_LINE_STRIP); qglColor4f(1, 1, 1, 1); qglVertex3fv(p->verts[0]); qglVertex3fv(p->verts[j-1]); qglVertex3fv(p->verts[j]); qglVertex3fv(p->verts[0]); qglEnd(); } } qglEnable(GL_TEXTURE_2D); GL_EnableMultitexture(true); GL_SelectTexture(GL_TEXTURE0); GL_TexEnv(tex_state0); GL_SelectTexture(GL_TEXTURE1); GL_TexEnv(tex_state1); } qglEnable(GL_DEPTH_TEST); // Guy: */\/\/\ gl_showtris fix end /\/\/\* } /* ** DrawGLPolyChain */ void DrawGLPolyChain( glpoly_t *p, float soffset, float toffset ) { if ( soffset == 0 && toffset == 0 ) { for ( ; p != 0; p = p->chain ) { float *v; int j; qglBegin (GL_POLYGON); v = p->verts[0]; for (j=0 ; jnumverts ; 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 ; jnumverts ; j++, v+= VERTEXSIZE) { qglTexCoord2f (v[5] - soffset, v[6] - toffset ); qglVertex3fv (v); } qglEnd (); } } } /* ** R_BlendLightMaps ** ** 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 (r_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 { if ( gl_monolightmap->string[0] != '0' ) { switch ( toupper( gl_monolightmap->string[0] ) ) { case 'I': qglBlendFunc (GL_ZERO, GL_SRC_COLOR ); break; case 'L': qglBlendFunc (GL_ZERO, GL_SRC_COLOR ); break; case 'A': default: qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); break; } } 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++; GL_Bind( gl_state.lightmap_textures + i); for ( surf = gl_lms.lightmap_surfaces[i]; surf != 0; surf = surf->lightmapchain ) { if ( surf->polys ) DrawGLPolyChain( surf->polys, 0, 0 ); } } } /* ** render dynamic lightmaps */ if ( gl_dynamic->value ) { LM_InitBlock(); GL_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 ) 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 ) ) { VID_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 ) 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 ); } /* ================ R_RenderBrushPoly ================ */ void R_RenderBrushPoly (msurface_t *fa) { int maps; image_t *image; qboolean is_dynamic = false; qboolean litPoly = gl_trans_lightmaps->value && !(fa->texinfo->flags & SURF_NOLIGHTENV); c_brush_polys++; image = R_TextureAnimation (fa->texinfo); if (fa->flags & SURF_DRAWTURB) { GL_Bind( image->texnum ); // warp texture, no lightmaps GL_TexEnv( GL_MODULATE ); qglColor4f( gl_state.inverse_intensity, gl_state.inverse_intensity, gl_state.inverse_intensity, 1.0F ); EmitWaterPolys (fa, litPoly, 1); GL_TexEnv( GL_REPLACE ); return; } GL_Bind( image->texnum ); GL_TexEnv( GL_REPLACE ); //====== //PGM GL_Bind( image->texnum ); GL_TexEnv( GL_REPLACE ); if (fa->texinfo->flags & SURF_FLOWING) DrawGLFlowingPoly (fa, litPoly, 1); else DrawGLPoly (fa, litPoly, 1); //PGM //====== // // 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 ); GL_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; } } /* ================ R_DrawAlphaSurfaces 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 GL_BuildVertexLight (msurface_t *surf); void drawAlphaSurface (msurface_t *s, float intens, float alpha, qboolean light, qboolean envmap) { // Knighmare added- animated texture support image_t *image = R_AlphaTextureAnimation(s); /* if (envmap) GL_Bind( r_envmappic->texnum ); else */ GL_Bind( image->texnum ); //GL_Bind(s->texinfo->image->texnum); // end Knightmare qglColor4f (intens,intens,intens, alpha); if (s->flags & SURF_DRAWTURB) EmitWaterPolys (s, light, alpha); else if(s->texinfo->flags & SURF_FLOWING) DrawGLFlowingPoly (s, light, alpha); else DrawGLPoly (s, light, alpha); } float SurfAlphaCalc (int flags) { if ((flags & SURF_TRANS33) && (flags & SURF_TRANS66)) return DIV254BY255; else if (flags & SURF_TRANS33) return 0.33333; else if (flags & SURF_TRANS66) return 0.66666; else return DIV254BY255; } void R_DrawAlphaSurfaces (qboolean elements) { msurface_t *s; qboolean transLit = gl_trans_lightmaps->value; // the textures are prescaled up for a better lighting range, // so scale it back down for (s = r_alpha_surfaces; s; s = s->texturechain) { //GL_Bind(s->texinfo->image->texnum); c_brush_polys++; /*if (elements) { surf_ElementList(s, true); surf_ElementList(s, false); }*/ // // go back to the world matrix // qglLoadMatrixf (r_world_matrix); GL_BuildVertexLight(s); GLSTATE_ENABLE_BLEND GL_TexEnv( GL_MODULATE ); GL_BlendFunction (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // disable depth testing for all bmodel surfs except solid alphas if ( s->entity && !((s->flags & SURF_TRANS33) && (s->flags & SURF_TRANS66)) ) qglDepthMask (false); else qglDepthMask (true); // moving trans brushes - spaz if (s->entity) { s->entity->angles[0] = -s->entity->angles[0]; // stupid quake bug s->entity->angles[2] = -s->entity->angles[2]; // stupid quake bug R_RotateForEntity (s->entity, true); s->entity->angles[0] = -s->entity->angles[0]; // stupid quake bug s->entity->angles[2] = -s->entity->angles[2]; // stupid quake bug } //transLit = gl_trans_lightmaps->value && !(s->texinfo->flags & SURF_NOLIGHTENV); drawAlphaSurface(s, gl_state.inverse_intensity, SurfAlphaCalc(s->texinfo->flags), transLit, false); // Knightmare- Psychospaz's envmapping // psychospaz- envmap baby... glass... if (gl_glass_envmaps->value && (s->flags & SURF_ENVMAP)) { float alpha; // lightmapped trans surfaces have more solid envmapping /* if (gl_trans_lightmaps->value && !(s->texinfo->flags & SURF_NOLIGHTENV)) alpha = 0.15; else alpha = 0.10; */ qglTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); qglTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); qglEnable(GL_TEXTURE_GEN_S); qglEnable(GL_TEXTURE_GEN_T); alpha = 0.1; drawAlphaSurface(s, gl_state.inverse_intensity, alpha, transLit, true); qglDisable(GL_TEXTURE_GEN_S); qglDisable(GL_TEXTURE_GEN_T); } } // re-align view origin after shifting trans faces qglLoadMatrixf (r_world_matrix); GL_BlendFunction (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_TexEnv( GL_REPLACE ); qglColor4f (1,1,1,1); GLSTATE_DISABLE_BLEND qglDepthMask (true); r_alpha_surfaces = NULL; } /* ================ DrawTextureChains ================ */ void DrawTextureChains (void) { int i; msurface_t *s; image_t *image; c_visible_textures = 0; // GL_TexEnv( GL_REPLACE ); if ( !qglSelectTextureSGIS && !qglActiveTextureARB ) { for ( i = 0, image=gltextures; iregistration_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 ; iregistration_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); } } GL_EnableMultitexture( false ); for ( i = 0, image=gltextures ; iregistration_sequence) continue; s = image->texturechain; if (!s) continue; for ( ; s ; s=s->texturechain) { if ( s->flags & SURF_DRAWTURB ) R_RenderBrushPoly (s); } image->texturechain = NULL; } // GL_EnableMultitexture( true ); } GL_TexEnv( GL_REPLACE ); } /* ================ RenderPolyFunc ================ */ void RenderPolyFunc (int nv, float *v, float scroll) { float *poly; int i; qglBegin (GL_POLYGON); for (i=0, poly=v ; i< nv; i++, poly+= VERTEXSIZE) { qglMTexCoord2fSGIS( GL_TEXTURE0, (poly[3]+scroll), poly[4]); qglMTexCoord2fSGIS( GL_TEXTURE1, poly[5], poly[6]); qglVertex3fv (poly); } qglEnd (); v = poly; } /* ================ GL_RenderLightmappedPoly ================ */ static void GL_RenderLightmappedPoly (msurface_t *surf) { int nv = surf->polys->numverts; int map; float *v; float scroll; 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; } // dynamic this frame or dynamic previously 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; smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; R_BuildLightMap( surf, (void *)temp, smax*4 ); if ( ( surf->styles[map] >= 32 || surf->styles[map] == 0 ) && ( surf->dlightframe != r_framecount ) ) { R_SetCacheState( surf ); GL_MBind( GL_TEXTURE1, gl_state.lightmap_textures + surf->lightmaptexturenum ); lmtex = surf->lightmaptexturenum; } else { GL_MBind( GL_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 ); } // added c_brush_polys++; GL_MBind( GL_TEXTURE0, image->texnum ); GL_MBind( GL_TEXTURE1, gl_state.lightmap_textures + lmtex ); if (surf->texinfo->flags & SURF_FLOWING) { scroll = -64 * ( (r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0) ); if (scroll == 0.0) scroll = -64.0; } else scroll = 0.0; for ( p = surf->polys; p; p = p->chain ) { v = p->verts[0]; RenderPolyFunc(nv, v, scroll); } /* 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 ); GL_MBind( GL_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 ); GL_MBind( GL_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++; GL_MBind( GL_TEXTURE1, gl_state.lightmap_textures + lmtex ); if (surf->texinfo->flags & SURF_FLOWING) { float scroll; GL_MBind( GL_TEXTURE0, image->texnum ); 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]; RenderPolyFunc(nv, v, scroll); } } else { GL_MBind( GL_TEXTURE0, image->texnum ); for ( p = surf->polys; p; p = p->chain ) { v = p->verts[0]; RenderPolyFunc(nv, v, 0); } } } else { c_brush_polys++; GL_MBind( GL_TEXTURE1, gl_state.lightmap_textures + lmtex ); if (surf->texinfo->flags & SURF_FLOWING) { float scroll; GL_MBind( GL_TEXTURE0, image->texnum ); 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]; RenderPolyFunc(nv, v, scroll); } } else { GL_MBind( GL_TEXTURE0, image->texnum ); for ( p = surf->polys; p; p = p->chain ) { v = p->verts[0]; RenderPolyFunc(nv, v, 0); } } }*/ } // Quake2 Max stuff.. void R_SurfLightPoint (msurface_t *surf, vec3_t p, vec3_t color, qboolean baselight); void GL_BuildVertexLightBase (msurface_t *surf, glpoly_t *poly) { vec3_t color, point; int i; float *v; for (i=0, v=poly->verts[0]; inumverts; i++, v+=VERTEXSIZE) { VectorCopy(v, point); // lerp outward away from plane to avoid dark spots? // lerp between each vertex and origin - use check for too dark? // this messes up curved glass surfaces //VectorSubtract (poly->center, v, point); //VectorMA(v, 0.01, point, point); R_SurfLightPoint (surf, point, color, true); R_MaxColorVec (color); poly->vertexlightbase[i*3+0] = (byte)(color[0]*255.0); poly->vertexlightbase[i*3+1] = (byte)(color[1]*255.0); poly->vertexlightbase[i*3+2] = (byte)(color[2]*255.0); } } void GL_ResetVertextLight (msurface_t *surf) { glpoly_t *poly; if (!surf->polys) return; for (poly=surf->polys ; poly ; poly=poly->next) poly->vertexlightset = false; } void GL_BuildVertexLight (msurface_t *surf) { vec3_t color, point; int i; float *v; glpoly_t *poly; if (!surf->polys) return; // Don't do on warp surfaces, since they are lit later if (surf->flags & SURF_DRAWTURB) return; for (poly=surf->polys; poly; poly=poly->next) { if (!poly->vertexlightbase) continue; if (!poly->vertexlightset) { GL_BuildVertexLightBase(surf, poly); poly->vertexlightset = true; } for (i=0, v=poly->verts[0]; inumverts; i++, v+=VERTEXSIZE) { VectorCopy(v, point); // lerp outward away from plane to avoid dark spots? // lerp between each vertex and origin - use check for too dark? // this messes up curved glass surfaces //VectorSubtract (poly->center, v, point); //VectorMA(v, 0.01, point, point); R_SurfLightPoint (surf, point, color, false); VectorSet(color, (float)poly->vertexlightbase[i*3+0]/255.0 + color[0], (float)poly->vertexlightbase[i*3+1]/255.0 + color[1], (float)poly->vertexlightbase[i*3+2]/255.0 + color[2]); R_MaxColorVec (color); poly->vertexlight[i*3+0] = (byte)(color[0]*255.0); poly->vertexlight[i*3+1] = (byte)(color[1]*255.0); poly->vertexlight[i*3+2] = (byte)(color[2]*255.0); } } } // end Quake2 Max stuff... /* ================= SurfInFront Returns true if surf1 is in front of surf2 FIXME- need to find a better way to sort trans surfaces like an algorithm that uses psurf->extents and psurf->plane->normal relative to vieworigin and takes into account e's offset and angles ================= */ qboolean SurfInFront (msurface_t *surf1, msurface_t *surf2) { float dist1, dist2; vec3_t org1, org2; if (!r_trans_surf_sorting->value) // check if sorting disabled return true; if (!surf1->plane || !surf2->plane) return false; if (surf1->entity) VectorSubtract(r_newrefdef.vieworg, surf1->entity->origin, org1); else VectorCopy (r_newrefdef.vieworg, org1); if (surf2->entity) VectorSubtract(r_newrefdef.vieworg, surf2->entity->origin, org2); else VectorCopy (r_newrefdef.vieworg, org2); dist1 = DotProduct(org1, surf1->plane->normal) - surf1->plane->dist; dist2 = DotProduct(org2, surf2->plane->normal) - surf2->plane->dist; if (dist1 < dist2) return true; else return false; //return (surf2->plane->dist > surf1->plane->dist); } /* ================= R_DrawInlineBModel ================= */ void R_DrawInlineBModel (entity_t *e) { int i, k; cplane_t *pplane; float dot; msurface_t *psurf; dlight_t *lt; qboolean modelalpha = false; psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; for (i=0; inummodelsurfaces; i++, psurf++) { // find which side of the face we are on pplane = psurf->plane; if ( pplane->type < 3 ) dot = modelorg[pplane->type] - pplane->dist; else dot = DotProduct (modelorg, pplane->normal) - pplane->dist; // cull the polygon if (dot > BACKFACE_EPSILON) psurf->visframe = r_framecount; } // calculate dynamic lighting for bmodel if ( !gl_flashblend->value ) { lt = r_newrefdef.dlights; if (currententity->angles[0] || currententity->angles[1] || currententity->angles[2]) { vec3_t temp; vec3_t forward, right, up; AngleVectors (currententity->angles, forward, right, up); for (k=0; korigin, currententity->origin, temp); lt->origin[0] = DotProduct (temp, forward); lt->origin[1] = -DotProduct (temp, right); lt->origin[2] = DotProduct (temp, up); R_MarkLights (lt, 1<nodes + currentmodel->firstnode); VectorAdd (temp, currententity->origin, lt->origin); } } else { for (k=0; korigin, currententity->origin, lt->origin); R_MarkLights (lt, 1<nodes + currentmodel->firstnode); VectorAdd (lt->origin, currententity->origin, lt->origin); } } } psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; if ( currententity->flags & RF_TRANSLUCENT ) { modelalpha = true; GLSTATE_ENABLE_BLEND qglDepthMask (false); GL_BlendFunction (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglColor4f (1, 1, 1, currententity->alpha); // Knightmare- draw correct alpha value GL_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 if (!modelalpha) // Don't allow mixing of alpha surfaces and model alpha (fixes hang) { #if 0 msurface_t *s, *last = NULL; psurf->entity = e; // entity pointer to support movement for (s = r_alpha_surfaces; s; last = s, s = s->texturechain) { if (SurfInFront (s, psurf)) // s is in front of psurf break; // we know to insert here } if (last) { // if in front of at least one surface psurf->texturechain = s; last->texturechain = psurf; } else { // stuff in beginning of chain psurf->texturechain = r_alpha_surfaces; r_alpha_surfaces = psurf; } #else psurf->texturechain = r_alpha_surfaces; r_alpha_surfaces = psurf; psurf->entity = e; // Knightmare added #endif } } else if ( qglMTexCoord2fSGIS && !( psurf->flags & SURF_DRAWTURB ) ) { GL_RenderLightmappedPoly( psurf ); } else { GL_BuildVertexLight(psurf); GL_EnableMultitexture( false ); R_RenderBrushPoly( psurf ); GL_EnableMultitexture( true ); } } } if ( !(currententity->flags & RF_TRANSLUCENT) ) { if ( !qglMTexCoord2fSGIS ) R_BlendLightmaps (); } else { GLSTATE_DISABLE_BLEND qglDepthMask (true); GL_BlendFunction (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglColor4f (1,1,1,1); GL_TexEnv( GL_REPLACE ); } } /* ================= R_DrawBrushModel ================= */ 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]; // stupid quake bug e->angles[2] = -e->angles[2]; // stupid quake bug R_RotateForEntity (e, true); e->angles[0] = -e->angles[0]; // stupid quake bug e->angles[2] = -e->angles[2]; // stupid quake bug GL_EnableMultitexture( true ); // Vic - begin SetLightingMode(e->flags); // Vic - end R_DrawInlineBModel (e); GL_EnableMultitexture( false ); qglPopMatrix (); } /* ============================================================= WORLD MODEL ============================================================= */ /* ================ R_RecursiveWorldNode ================ */ 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; surf->entity = NULL; // Knightmare added } else { if ( qglMTexCoord2fSGIS && !( surf->flags & SURF_DRAWTURB ) ) { GL_RenderLightmappedPoly( surf ); } else { // the polygon is visible, so add it to the texture // sorted chain // FIXME: this is a hack for animation image = R_TextureAnimation (surf->texinfo); surf->texturechain = image->texturechain; image->texturechain = surf; } //Knightmare- GuyP's gl_showtris fix if (qglMTexCoord2fSGIS) R_DrawTriangleOutlines(surf); //end Knightmare } } // recurse down the back side R_RecursiveWorldNode (node->children[!side]); } /* ============= R_DrawWorld ============= */ void R_DrawWorld (void) { entity_t ent; if (!r_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)); // Knightmare added r_worldframe for trans animations ent.frame = r_worldframe = (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 ) { GL_EnableMultitexture ( true ); // Vic - begin SetLightingMode(0); // Vic - end R_RecursiveWorldNode (r_worldmodel->nodes); GL_EnableMultitexture( false ); } else { R_RecursiveWorldNode (r_worldmodel->nodes); } /* ** theoretically nothing should happen in the next two functions ** if multitexture is enabled */ DrawTextureChains (); R_BlendLightmaps (); R_DrawSkyBox (); //Knightmare- GuyP's gl_showtris fix if (!qglMTexCoord2fSGIS) R_DrawTriangleOutlines (NULL); } /* =============== R_MarkLeaves 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 && !r_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 (r_novis->value || r_viewcluster == -1 || !r_worldmodel->vis) { // mark everything for (i=0 ; inumleafs ; i++) r_worldmodel->leafs[i].visframe = r_visframecount; for (i=0 ; inumnodes ; 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 ; ileafs ; inumleafs ; 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); } } #if 0 for (i=0 ; ivis->numclusters ; i++) { if (vis[i>>3] & (1<<(i&7))) { node = (mnode_t *)&r_worldmodel->leafs[i]; // FIXME: cluster do { if (node->visframe == r_visframecount) break; node->visframe = r_visframecount; node = node->parent; } while (node); } } #endif } /* ============================================================================= LIGHTMAP ALLOCATION ============================================================================= */ static void LM_InitBlock( void ) { memset( gl_lms.allocated, 0, sizeof( gl_lms.allocated ) ); } static void LM_UploadBlock( qboolean dynamic ) { int texture; int height = 0; if ( dynamic ) { texture = 0; } else { texture = gl_lms.current_lightmap_texture; } GL_Bind( gl_state.lightmap_textures + texture ); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if ( dynamic ) { int i; for ( i = 0; i < BLOCK_WIDTH; i++ ) { if ( gl_lms.allocated[i] > height ) height = gl_lms.allocated[i]; } qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, height, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer ); } else { qglTexImage2D( GL_TEXTURE_2D, 0, gl_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer ); if ( ++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS ) VID_Error( ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n" ); } } // returns a texture number and the position inside it static qboolean LM_AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; best = BLOCK_HEIGHT; for (i=0 ; i= best) break; if (gl_lms.allocated[i+j] > best2) best2 = gl_lms.allocated[i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) return false; for (i=0 ; iedges; lnumverts = fa->numedges; vertpage = 0; VectorClear (total); // // draw texture // poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); poly->next = fa->polys; poly->flags = fa->flags; fa->polys = poly; poly->numverts = lnumverts; for (i=0 ; isurfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; vec = currentmodel->vertexes[r_pedge->v[0]].position; } else { r_pedge = &pedges[-lindex]; vec = currentmodel->vertexes[r_pedge->v[1]].position; } s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s /= fa->texinfo->texWidth; //fa->texinfo->image->width; changed to Q2E hack t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t /= fa->texinfo->texHeight; //fa->texinfo->image->height; changed to Q2E hack VectorAdd (total, vec, total); VectorCopy (vec, poly->verts[i]); poly->verts[i][3] = s; poly->verts[i][4] = t; // // lightmap texture coordinates // s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s -= fa->texturemins[0]; s += fa->light_s*16; s += 8; s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t -= fa->texturemins[1]; t += fa->light_t*16; t += 8; t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height; poly->verts[i][5] = s; poly->verts[i][6] = t; } poly->numverts = lnumverts; } /* ======================== GL_CreateSurfaceLightmap ======================== */ void GL_CreateSurfaceLightmap (msurface_t *surf) { int smax, tmax; byte *base; if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) return; if (surf->texinfo->flags & (SURF_SKY|SURF_TRANS33|SURF_TRANS66|SURF_WARP)) //if (surf->texinfo->flags & (SURF_SKY|SURF_WARP)) return; smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; if ( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t ) ) { LM_UploadBlock( false ); LM_InitBlock(); if ( !LM_AllocBlock( smax, tmax, &surf->light_s, &surf->light_t ) ) { VID_Error( ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed\n", smax, tmax ); } } surf->lightmaptexturenum = gl_lms.current_lightmap_texture; base = gl_lms.lightmap_buffer; base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * LIGHTMAP_BYTES; R_SetCacheState( surf ); R_BuildLightMap (surf, base, BLOCK_WIDTH*LIGHTMAP_BYTES); } // Quake2Max stuff... /* ======================== GL_CreateVertexLightmap ======================== */ void GL_CreateVertexLightmap (msurface_t *surf) { glpoly_t *poly; int size; size = sizeof(byte)*3*(surf->polys->numverts); for (poly=surf->polys; poly; poly=poly->next) { poly->vertexlight = Hunk_Alloc(size); poly->vertexlightbase = Hunk_Alloc(size); memset(poly->vertexlight, 0, size); memset(poly->vertexlightbase, 0, size); poly->vertexlightset = false; } } #ifdef FLARES void GL_FindPolyCenters(msurface_t * surf) { int i, j; float *v; vec3_t poly_avg; vec3_t surf_avg; glpoly_t *poly; VectorClear(surf_avg); for (poly = surf->polys, i = 0; poly; poly = poly->next, i++) { VectorClear(poly_avg); for (j = 0, v = poly->verts[0]; j < poly->numverts; j++, v += VERTEXSIZE) VectorAdd(poly_avg, v, poly_avg); VectorScale(poly_avg, 1.0 / (float)poly->numverts, poly->center); VectorAdd(poly->center, surf_avg, surf_avg); } if (i > 0) VectorScale(surf_avg, 1.0 / (float)i, surf->center); else VectorClear(surf->center); } #else void GL_FindPolyCenters (msurface_t *surf) { int i; float *v; vec3_t average; glpoly_t *poly; for (poly=surf->polys ; poly ; poly=poly->next) { VectorClear(average); for (i=0,v=poly->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) VectorAdd(average, v, average); VectorScale(average, 1.0/(float)poly->numverts, poly->center); } } #endif // end Q2max stuff /* ================== GL_BeginBuildingLightmaps ================== */ void GL_BeginBuildingLightmaps (model_t *m) { static lightstyle_t lightstyles[MAX_LIGHTSTYLES]; int i; unsigned dummy[128*128]; memset( gl_lms.allocated, 0, sizeof(gl_lms.allocated) ); r_framecount = 1; // no dlightcache GL_EnableMultitexture( true ); GL_SelectTexture( GL_TEXTURE1); /* ** setup the base lightstyles so the lightmaps won't have to be regenerated ** the first time they're seen */ for (i=0 ; istring[0] ) == 'A' ) { gl_lms.internal_format = gl_tex_alpha_format; } /* ** try to do hacked colored lighting with a blended texture */ else if ( toupper( gl_monolightmap->string[0] ) == 'C' ) { gl_lms.internal_format = gl_tex_alpha_format; } else if ( toupper( gl_monolightmap->string[0] ) == 'I' ) { gl_lms.internal_format = GL_INTENSITY8; } else if ( toupper( gl_monolightmap->string[0] ) == 'L' ) { gl_lms.internal_format = GL_LUMINANCE8; } else { gl_lms.internal_format = gl_tex_solid_format; } /* ** initialize the dynamic lightmap texture */ GL_Bind( gl_state.lightmap_textures + 0 ); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); qglTexImage2D( GL_TEXTURE_2D, 0, gl_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, dummy ); } /* ======================= GL_EndBuildingLightmaps ======================= */ void GL_EndBuildingLightmaps (void) { LM_UploadBlock( false ); GL_EnableMultitexture( false ); }