/* * 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 "gl_local.h" #include "gl_refl.h" /* MPO */ 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; /* MH - detail textures begin */ /* * this should most likely properly belong in a state management struct * somewhere */ static msurface_t *r_detailsurfaces; /* MH - detail textures end */ 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; } /* * ================ DrawGLPoly ================ */ void 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(); } /* ============ */ /* PGM */ /* * ================ DrawGLFlowingPoly -- version of DrawGLPoly that handles * scrolling texture ================ */ void DrawGLFlowingPoly(msurface_t * fa) { int i; float *v; glpoly_t *p; float scroll; p = fa->polys; scroll = -64 * ((r_refdef.time / 40.0) - (int)(r_refdef.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(); } /* PGM */ /* ============ */ /* * * R_DrawTriangleOutlines */ void R_DrawTriangleOutlines(msurface_t * surf) { /*** Guy */ int i, j; glpoly_t *p; if (!gl_showtris->value) return; /** Guy: *\/\/\/ gl_showtris fix begin \/\/\/ **/ qglDisable(GL_DEPTH_TEST); qglColor4f(1.0, 1.0, 1.0, 1.0); 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(.5, 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 (i = 2; i < p->numverts; i++) { qglBegin(GL_LINE_STRIP); qglColor4f(.5, 1, 1, 1); qglVertex3fv(p->verts[0]); qglVertex3fv(p->verts[i - 1]); qglVertex3fv(p->verts[i]); 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 / \/\/\ **/ /* thanks guy who ever you are .. ;) */ } /* * * 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; 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(); } } } /* * * 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 (gl_monolightmap->string[0]) { case 'I': case 'i': qglBlendFunc(GL_ZERO, GL_SRC_COLOR); break; case 'L': case 'l': qglBlendFunc(GL_ZERO, GL_SRC_COLOR); break; case 'A': 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)) { 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) 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; 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 ); */ qglColor4f(1.0, 1.0, 1.0, 1.0); /* jit */ EmitWaterPolys(fa); GL_TexEnv(GL_REPLACE); return; } else { GL_Bind(image->texnum); GL_TexEnv(GL_REPLACE); } /* MH - detail textures begin */ /* accumulate the detail chain */ /* * wait until we get here so that we don't get any detail texturing * on DRAWTURB polys */ if ((maxTextureUnits < 3) && (gl_detailtextures->value)) { fa->detailchain = r_detailsurfaces; r_detailsurfaces = fa; } /* MH - detail textures end */ /* ====== */ /* PGM */ if (fa->texinfo->flags & SURF_FLOWING) DrawGLFlowingPoly(fa); else DrawGLPoly(fa->polys); /* PGM */ /* ====== */ /* * * check for lightmap modification */ for (maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) { if (r_refdef.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 R_DrawAlphaSurfaces(void) { msurface_t *s; float intens; // /* go back to the world matrix */ // qglLoadMatrixf(r_world_matrix); qglEnable(GL_BLEND); GL_TexEnv(GL_MODULATE); GL_BlendFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* 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) { GL_Bind(s->texinfo->image->texnum); c_brush_polys++; if (s->texinfo->flags & SURF_TRANS33 && s->texinfo->flags & SURF_TRANS66) qglColor4f(intens, intens, intens, DIV254BY255); else 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) { EmitWaterPolys(s); } else if (s->texinfo->flags & SURF_FLOWING) { DrawGLFlowingPoly(s); } else DrawGLPoly(s->polys); } GL_BlendFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_TexEnv(GL_REPLACE); qglColor4f(1, 1, 1, 1); qglDisable(GL_BLEND); r_alpha_surfaces = NULL; } static void GL_RenderLightmappedPoly(msurface_t * surf); void R_DrawAlphaSurfaces_Jitspoe(void) { msurface_t *s; /* go back to the world matrix */ qglLoadMatrixf(r_world_matrix); GLSTATE_ENABLE_BLEND qglDepthMask(0); /* jitalpha - disable depth writing */ GL_TexEnv(GL_MODULATE); /* 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 (!s->flags & SURF_DRAWTURB) { GL_TexEnv(GL_REPLACE); GLSTATE_ENABLE_BLEND GL_TexEnv(GL_MODULATE); } else { if (s->texinfo->flags & SURF_TRANS33) { if (s->texinfo->flags & SURF_TRANS66) /* jittrans -- * trans33+trans66 only * uses texture * transparency. */ qglColor4f(1.0, 1.0, 1.0, 1.0); else qglColor4f(1.0, 1.0, 1.0, 0.33); } else if (s->texinfo->flags & SURF_TRANS66) { qglColor4f(1.0, 1.0, 1.0, 0.66); } else { qglColor4f(1.0, 1.0, 1.0, 1.0); } if (s->flags & SURF_DRAWTURB) { EmitWaterPolys(s); } else if (s->texinfo->flags & SURF_FLOWING) { /* PGM 9/16/98 */ DrawGLFlowingPoly(s); /* PGM */ } else if (qglMTexCoord2fSGIS) { GL_EnableMultitexture(true); GL_SelectTexture(GL_TEXTURE0); GL_TexEnv(GL_REPLACE); GL_SelectTexture(GL_TEXTURE1); if (gl_lightmap->value) GL_TexEnv(GL_REPLACE); else GL_TexEnv(GL_COMBINE_EXT); GL_RenderLightmappedPoly(s); GL_EnableMultitexture(false); } else { DrawGLPoly(s->polys); } } } GL_TexEnv(GL_REPLACE); qglColor4f(1.0, 1.0, 1.0, 1.0); GLSTATE_DISABLE_BLEND qglDepthMask(1); 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; 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); } } GL_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; } /* GL_EnableMultitexture( true ); */ } GL_TexEnv(GL_REPLACE); } 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; } void EmitCausticPolys(msurface_t * fa) { glpoly_t *p; float *v; int i; float scroll; scroll = -64 * ((r_refdef.time / 40.0) - (int)(r_refdef.time / 40.0)); if (scroll == 0.0) scroll = -64.0; #if 1 if (/* !gl_fogenable->value || */!gl_fogunderwater->value) { qglDisable(GL_FOG); } #endif GL_SelectTexture(GL_TEXTURE1); qglDisable(GL_TEXTURE_2D); GL_SelectTexture(GL_TEXTURE0); GL_Bind(r_caustictexture->texnum); qglBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); qglColor4f(1, 1, 1, 0.275); qglEnable(GL_BLEND); for (p = fa->polys; p; p = p->chain) { qglBegin(GL_POLYGON); for (i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE) { qglTexCoord2f(v[3] + (scroll / 30), v[4] + (scroll / 30)); qglVertex3fv(v); } qglEnd(); } qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglColor4f(1, 1, 1, 1); qglDisable(GL_BLEND); GL_SelectTexture(GL_TEXTURE1); qglEnable(GL_TEXTURE_2D); GL_SelectTexture(GL_TEXTURE0); #if 1 if (gl_fogenable->value) { qglEnable(GL_FOG); } #endif } static void GL_RenderLightmappedPoly(msurface_t * surf) { int nv = surf->polys->numverts; int i; int map; float *v; image_t *image = R_TextureAnimation(surf->texinfo); qboolean is_dynamic = false; unsigned lmtex = surf->lightmaptexturenum; glpoly_t *p; /* MH - detail textures begin */ if ((gl_detailtextures->value) && (maxTextureUnits > 2) && (!surf->texinfo->flags & SURF_LIGHT)) { GL_Enable3dTextureUnit(true); GL_MBind(GL_TEXTURE2, r_detailtexture->texnum); /* bind detail texture */ GL_TexEnv(GL_COMBINE_ARB); /* set blending mode */ qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2); /* bump up lighting a * little */ } else if ((gl_detailtextures->value) && (maxTextureUnits < 3) && (!surf->texinfo->flags & SURF_LIGHT)) { /* * accumulate the detail texture chain - this is used if we * have 2 or less texture units */ surf->detailchain = r_detailsurfaces; r_detailsurfaces = surf; } /* MH - detail textures end */ for (map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++) { if (r_refdef.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; 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_TEXTURE0, image->texnum); GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex); /* ========== */ /* PGM */ if (surf->texinfo->flags & SURF_FLOWING) { float scroll; scroll = -64 * ((r_refdef.time / 40.0) - (int)(r_refdef.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 { for (p = surf->polys; p; p = p->chain) { v = p->verts[0]; qglBegin(GL_POLYGON); for (i = 0; i < nv; i++, v += VERTEXSIZE) { qglMTexCoord2fSGIS(GL_TEXTURE0, v[3], v[4]); qglMTexCoord2fSGIS(GL_TEXTURE1, v[5], v[6]); if ((gl_detailtextures->value > 0) && (maxTextureUnits > 2)) { qglMTexCoord2fSGIS(GL_TEXTURE2, v[7] * gl_detailtextures->value, v[8] * gl_detailtextures->value); } qglVertex3fv(v); } qglEnd(); /* RenderPolyFunc(nv, v, 0); */ } } /* PGM */ /* ========== */ } else { c_brush_polys++; GL_MBind(GL_TEXTURE0, image->texnum); GL_MBind(GL_TEXTURE1, gl_state.lightmap_textures + lmtex); /* ========== */ /* PGM */ if (surf->texinfo->flags & SURF_FLOWING) { float scroll; scroll = -64 * ((r_refdef.time / 40.0) - (int)(r_refdef.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 { /* PGM */ /* ========== */ for (p = surf->polys; p; p = p->chain) { v = p->verts[0]; qglBegin(GL_POLYGON); for (i = 0; i < nv; i++, v += VERTEXSIZE) { qglMTexCoord2fSGIS(GL_TEXTURE0, v[3], v[4]); qglMTexCoord2fSGIS(GL_TEXTURE1, v[5], v[6]); if ((gl_detailtextures->value > 0) && (maxTextureUnits > 2)) { qglMTexCoord2fSGIS(GL_TEXTURE2, v[7] * gl_detailtextures->value, v[8] * gl_detailtextures->value); } qglVertex3fv(v); } qglEnd(); /* RenderPolyFunc(nv, v, 0); */ } /* ========== */ /* PGM */ } /* PGM */ /* ========== */ } if ((gl_detailtextures->value) && (maxTextureUnits > 2)) { GL_Enable3dTextureUnit(false); } if (surf->wallLight != NULL) { if (numberOfWallLights > 500) return; } if ((surf->flags & SURF_UNDERWATER) && !image->has_alpha) { if (gl_water_caustics->value == 2) { EmitCausticPolys(surf); EmitCausticPolys(surf); } else if (gl_water_caustics->value == 1) { EmitCausticPolys(surf); } else return; } } /* MH - detail textures begin */ /* here we draw the detail textures! */ /* * we could do this as a multitextured render along with the other stuff, but * my own tests */ /* * have indicated that for something like this there's no real benefit to be * had from doing */ /* * so. the difference is in the order of 1 or 2 FPS - the real hit from * detail texturing */ /* comes from scrunching large images into small spaces */ static void R_DrawDetailSurfaces(void) { msurface_t *surf = r_detailsurfaces; int i; float *v; glpoly_t *p; /* nothing to draw! */ if (!surf) return; /* * we do this rather than checking for 0 (checking for 0 is always * bad with floats) */ /* standard 6 decimal place precision */ if (gl_detailtextures->value < 0.00001) return; qglEnable(GL_POLYGON_OFFSET_FILL); /* to stop z buffer fighting */ qglPolygonOffset(-1, -2); /* * we *could* use the standard glBindTexture here as we don't need to * do any bind checking */ /* but again we'll maintain consistency */ GL_Bind(r_detailtexture->texnum); /* set the correct blending mode */ qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); qglBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); qglEnable(GL_BLEND); /* * here we get clever and do our texture scaling in the texture * matrix. this saves */ /* us from more runtime calculations in software */ qglMatrixMode(GL_TEXTURE); qglLoadIdentity(); /* * we only need to scale the X and Y co-ords, correcponding to * texture s and t. */ /* * everyone will have their own favourite scaling amount, so i did a * cvar */ qglScalef(gl_detailtextures->value, gl_detailtextures->value, 1.0); /* * we can safely leave the texture matrix as the active mode while * we're doing the draw */ for (; surf; surf = surf->detailchain) { for (p = surf->polys; p; p = p->chain) { /* * using GL_TRIANGLE_FAN is theoretically more * efficient than GL_POLYGON as */ /* * the GL implementation can know something about * what we are going to draw */ /* * in advance, and set things up accordingly. the * visual result is identical. */ qglBegin(GL_TRIANGLE_FAN); for (v = p->verts[0], i = 0; i < p->numverts; i++, v += VERTEXSIZE) { /* * take the 128 * 128 version of the s and t * co-ords */ qglTexCoord2f(v[7], v[8]); qglVertex3fv(v); } qglEnd(); } } /* restore original texture matrix */ qglLoadIdentity(); qglMatrixMode(GL_MODELVIEW); /* restore original blend */ qglTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglDisable(GL_BLEND); qglDisable(GL_POLYGON_OFFSET_FILL); } /* MH - detail textures end */ /* * ================= R_DrawInlineBModel ================= */ 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_refdef.dlights; for (k = 0; k < r_refdef.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); qglDepthMask(false); GL_BlendFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglColor4f(1, 1, 1, 0.25); GL_TexEnv(GL_MODULATE); } /* MH - detail textures begin */ /* null the detail chain again */ r_detailsurfaces = NULL; /* MH - detail textures end */ // /* 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)) { GL_RenderLightmappedPoly(psurf); } else { GL_EnableMultitexture(false); R_RenderBrushPoly(psurf); GL_EnableMultitexture(true); } } } if (!(currententity->flags & RF_TRANSLUCENT)) { if (!qglMTexCoord2fSGIS) R_BlendLightmaps(); /* MH - detail textures begin */ /* * shut down multitexturing (we can't do this in * R_DrawDetailSurfaces cos then it */ /* messes things up for the world) */ GL_EnableMultitexture(false); /* don't put them on translucent surfs */ if (maxTextureUnits < 3) R_DrawDetailSurfaces(); /* slow old school method */ /* bring multitexturing back up */ GL_EnableMultitexture(true); /* MH - detail textures end */ } else { qglDisable(GL_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)); /* start MPO */ /* * if this is a reflection we're drawing we need to flip the vertical * position across the water */ if (g_drawing_refl) { float distance; distance = DotProduct(modelorg, waterNormals[g_active_refl]) - g_waterDistance2[g_active_refl]; VectorMA(r_refdef.vieworg, distance * -2, waterNormals[g_active_refl], modelorg); } VectorSubtract(r_refdef.vieworg, e->origin, modelorg); /* stop MPO */ 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); e->angles[0] = -e->angles[0]; /* stupid quake bug */ e->angles[2] = -e->angles[2]; /* stupid quake bug */ GL_EnableMultitexture(true); GL_SelectTexture(GL_TEXTURE0); /* * GL_TexEnv( GL_REPLACE ); GL_SelectTexture( GL_TEXTURE1); * GL_TexEnv( GL_MODULATE ); */ /* Vic */ if (!gl_config.mtexcombine) { 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); } } /*- */ R_DrawInlineBModel(); 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_refdef.areabits) { if (!(r_refdef.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)) { 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; } } if (gl_showtris->value && qglMTexCoord2fSGIS) /*** DMP added extra check to avoid func call overhead in this inner loop */ R_DrawTriangleOutlines(surf); } /* recurse down the back side */ R_RecursiveWorldNode(node->children[!side]); #if 0 /* * for ( ; 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 = alpha_surfaces; */ /* alpha_surfaces = surf; */ } 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; } } #endif } /* * ============= R_DrawWorld ============= */ void R_DrawWorld(void) { entity_t ent; if (!r_drawworld->value) return; if (r_refdef.rdflags & RDF_NOWORLDMODEL) return; currentmodel = r_worldmodel; VectorCopy(r_refdef.vieworg, modelorg); /* start MPO */ /* * if this is a reflection we're drawing, we need to flip vertically * across the water */ if (g_drawing_refl) { float distance; distance = DotProduct(modelorg, waterNormals[g_active_refl]) - g_waterDistance2[g_active_refl]; VectorMA(r_refdef.vieworg, distance * -2, waterNormals[g_active_refl], modelorg); } /* stop MPO */ /* auto cycle the world frame for texture animation */ memset(&ent, 0, sizeof(ent)); ent.frame = (int)(r_refdef.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(); /* MH - detail textures begin */ r_detailsurfaces = NULL; /* MH - detail textures end */ if (qglMTexCoord2fSGIS) { GL_EnableMultitexture(true); GL_SelectTexture(GL_TEXTURE0); /* * GL_TexEnv( GL_REPLACE ); GL_SelectTexture( GL_TEXTURE1); * * if ( gl_lightmap->value ) GL_TexEnv( GL_REPLACE ); else * GL_TexEnv( GL_MODULATE ); */ if (!gl_config.mtexcombine) { 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); } } 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(); /* MH - detail textures begin */ if (maxTextureUnits < 3) R_DrawDetailSurfaces(); /* MH - detail textures end */ if (gl_fogunderwater->value && (r_refdef.rdflags & RDF_UNDERWATER)) { if (gl_fogenable->value) { qglDisable(GL_FOG); } R_DrawSkyBox(); } else R_DrawSkyBox(); 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; 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); } } #if 0 for (i = 0; i < r_worldmodel->vis->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) ri.Sys_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 < BLOCK_WIDTH - w; i++) { best2 = 0; for (j = 0; j < w; j++) { if (gl_lms.allocated[i + j] >= 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; i < w; i++) gl_lms.allocated[*x + i] = best + h; return true; } /* * ================ GL_BuildPolygonFromSurface ================ */ void GL_BuildPolygonFromSurface(msurface_t * fa) { int i, lindex, lnumverts; medge_t *pedges, *r_pedge; int vertpage; float *vec; float s, t; glpoly_t *poly; vec3_t total; /* reconstruct the polygon */ pedges = currentmodel->edges; 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; i < lnumverts; i++) { lindex = currentmodel->surfedges[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->image->width; t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t /= fa->texinfo->image->height; 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; /* MH - detail textures begin */ /* * here we build a set of texture s and t co-ords suitable * for a 128 * 128 version */ /* * of the texture. this is necessary because using the * original texture s and t */ /* * will cause the detail texture (and anything else we may * ever consider adding) */ /* * to rescale itself to the same aspect ratio as the original * texture, which looks */ /* * bad. NOTE - to enable this we *must* increase the define * of VERTEXSIZE to 9. */ s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s /= 128; t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t /= 128; /* and load them in */ poly->verts[i][7] = s; poly->verts[i][8] = t; /* MH - detail textures end */ } poly->numverts = lnumverts; } /* * ======================== GL_CreateSurfaceLightmap ======================== */ void GL_CreateSurfaceLightmap(msurface_t * surf) { int smax, tmax; byte *base; if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB)) 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)) { ri.Sys_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); } /* * ================== GL_BeginBuildingLightmaps * * ================== */ /* PixelInternalFormat */ /* GL_ALPHA4 */ /* GL_ALPHA8 */ /* GL_ALPHA12 */ /* GL_ALPHA16 */ /* GL_LUMINANCE4 */ /* GL_LUMINANCE8 */ /* GL_LUMINANCE12 */ /* GL_LUMINANCE16 */ /* GL_LUMINANCE4_ALPHA4 */ /* GL_LUMINANCE6_ALPHA2 */ /* GL_LUMINANCE8_ALPHA8 */ /* GL_LUMINANCE12_ALPHA4 */ /* GL_LUMINANCE12_ALPHA12 */ /* GL_LUMINANCE16_ALPHA16 */ /* GL_INTENSITY */ /* GL_INTENSITY4 */ /* GL_INTENSITY8 */ /* GL_INTENSITY12 */ /* GL_INTENSITY16 */ /* GL_R3_G3_B2 */ /* GL_RGB4 */ /* GL_RGB5 */ /* GL_RGB8 */ /* GL_RGB10 */ /* GL_RGB12 */ /* GL_RGB16 */ /* GL_RGBA2 */ /* GL_RGBA4 */ /* GL_RGB5_A1 */ /* GL_RGBA8 */ /* GL_RGB10_A2 */ /* GL_RGBA12 */ /* GL_RGBA16 */ 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; i < MAX_LIGHTSTYLES; i++) { lightstyles[i].rgb[0] = 1; lightstyles[i].rgb[1] = 1; lightstyles[i].rgb[2] = 1; lightstyles[i].white = 3; } r_refdef.lightstyles = lightstyles; if (!gl_state.lightmap_textures) { gl_state.lightmap_textures = TEXNUM_LIGHTMAPS; /* * gl_state.lightmap_textures = * gl_state.texture_extension_number; */ /* * gl_state.texture_extension_number = * gl_state.lightmap_textures + MAX_LIGHTMAPS; */ } gl_lms.current_lightmap_texture = 1; /* * * if mono lightmaps are enabled and we want to use alpha * * blending (a,1-a) then we're likely running on a 3DLabs * * Permedia2. In a perfect world we'd use a GL_ALPHA lightmap * in * order to conserve space and maximize bandwidth, however * this * isn't a perfect world. * * * So we have to use alpha lightmaps, but stored in GL_RGBA format, * * which means we only get 1/16th the color resolution we should when * * using alpha lightmaps. If we find another board that supports * * only alpha lightmaps but that can at least support the GL_ALPHA * * format then we should change this code to use real alpha maps. */ if (toupper(gl_monolightmap->string[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); }