/* * 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. * * ======================================================================= * * Lightmap handling * * ======================================================================= */ #include "header/local.h" extern gllightmapstate_t gl_lms; void R_SetCacheState(msurface_t *surf); void R_BuildLightMap(msurface_t *surf, byte *dest, int stride); void LM_InitBlock(void) { memset(gl_lms.allocated, 0, sizeof(gl_lms.allocated)); } void LM_UploadBlock(qboolean dynamic) { int texture; int height = 0; if (dynamic) { texture = 0; } else { texture = gl_lms.current_lightmap_texture; } R_Bind(gl_state.lightmap_textures + texture); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameteri(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 */ 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; } void LM_BuildPolygonFromSurface(msurface_t *fa) { int i, lindex, lnumverts; medge_t *pedges, *r_pedge; float *vec; float s, t; glpoly_t *poly; vec3_t total; /* reconstruct the polygon */ pedges = currentmodel->edges; lnumverts = fa->numedges; 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; } poly->numverts = lnumverts; } void LM_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); } void LM_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 */ R_EnableMultitexture(true); R_SelectTexture(QGL_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_newrefdef.lightstyles = lightstyles; if (!gl_state.lightmap_textures) { gl_state.lightmap_textures = TEXNUM_LIGHTMAPS; } gl_lms.current_lightmap_texture = 1; gl_lms.internal_format = gl_tex_solid_format; /* initialize the dynamic lightmap texture */ R_Bind(gl_state.lightmap_textures + 0); qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); qglTexParameteri(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); } void LM_EndBuildingLightmaps(void) { LM_UploadBlock(false); R_EnableMultitexture(false); }