/* * 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. * * ======================================================================= * * Lightmaps and dynamic lighting * * ======================================================================= */ #include "header/local.h" #define DLIGHT_CUTOFF 64 int r_dlightframecount; vec3_t pointcolor; cplane_t *lightplane; /* used as shadow plane */ vec3_t lightspot; static float s_blocklights[34 * 34 * 3]; void R_RenderDlight(dlight_t *light) { int i, j; float a; vec3_t v; float rad; rad = light->intensity * 0.35; VectorSubtract(light->origin, r_origin, v); qglBegin(GL_TRIANGLE_FAN); qglColor3f(light->color[0] * 0.2, light->color[1] * 0.2, light->color[2] * 0.2); for (i = 0; i < 3; i++) { v[i] = light->origin[i] - vpn[i] * rad; } qglVertex3fv(v); qglColor3f(0, 0, 0); for (i = 16; i >= 0; i--) { a = i / 16.0 * M_PI * 2; for (j = 0; j < 3; j++) { v[j] = light->origin[j] + vright[j] * cos(a) * rad + vup[j] * sin(a) * rad; } qglVertex3fv(v); } qglEnd(); } void R_RenderDlights(void) { int i; dlight_t *l; if (!gl_flashblend->value) { return; } /* because the count hasn't advanced yet for this frame */ r_dlightframecount = r_framecount + 1; qglDepthMask(0); qglDisable(GL_TEXTURE_2D); qglShadeModel(GL_SMOOTH); qglEnable(GL_BLEND); qglBlendFunc(GL_ONE, GL_ONE); l = r_newrefdef.dlights; for (i = 0; i < r_newrefdef.num_dlights; i++, l++) { R_RenderDlight(l); } qglColor3f(1, 1, 1); qglDisable(GL_BLEND); qglEnable(GL_TEXTURE_2D); qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglDepthMask(1); } void R_MarkLights(dlight_t *light, int bit, mnode_t *node) { cplane_t *splitplane; float dist; msurface_t *surf; int i; int sidebit; if (node->contents != -1) { return; } splitplane = node->plane; dist = DotProduct(light->origin, splitplane->normal) - splitplane->dist; if (dist > light->intensity - DLIGHT_CUTOFF) { R_MarkLights(light, bit, node->children[0]); return; } if (dist < -light->intensity + DLIGHT_CUTOFF) { R_MarkLights(light, bit, node->children[1]); return; } /* mark the polygons */ surf = r_worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { dist = DotProduct(light->origin, surf->plane->normal) - surf->plane->dist; if (dist >= 0) { sidebit = 0; } else { sidebit = SURF_PLANEBACK; } if ((surf->flags & SURF_PLANEBACK) != sidebit) { continue; } if (surf->dlightframe != r_dlightframecount) { surf->dlightbits = 0; surf->dlightframe = r_dlightframecount; } surf->dlightbits |= bit; } R_MarkLights(light, bit, node->children[0]); R_MarkLights(light, bit, node->children[1]); } void R_PushDlights(void) { int i; dlight_t *l; if (gl_flashblend->value) { return; } /* because the count hasn't advanced yet for this frame */ r_dlightframecount = r_framecount + 1; l = r_newrefdef.dlights; for (i = 0; i < r_newrefdef.num_dlights; i++, l++) { R_MarkLights(l, 1 << i, r_worldmodel->nodes); } } int R_RecursiveLightPoint(mnode_t *node, vec3_t start, vec3_t end) { float front, back, frac; int side; cplane_t *plane; vec3_t mid; msurface_t *surf; int s, t, ds, dt; int i; mtexinfo_t *tex; byte *lightmap; int maps; int r; if (node->contents != -1) { return -1; /* didn't hit anything */ } /* calculate mid point */ plane = node->plane; front = DotProduct(start, plane->normal) - plane->dist; back = DotProduct(end, plane->normal) - plane->dist; side = front < 0; if ((back < 0) == side) { return R_RecursiveLightPoint(node->children[side], start, end); } frac = front / (front - back); mid[0] = start[0] + (end[0] - start[0]) * frac; mid[1] = start[1] + (end[1] - start[1]) * frac; mid[2] = start[2] + (end[2] - start[2]) * frac; /* go down front side */ r = R_RecursiveLightPoint(node->children[side], start, mid); if (r >= 0) { return r; /* hit something */ } if ((back < 0) == side) { return -1; /* didn't hit anuthing */ } /* check for impact on this node */ VectorCopy(mid, lightspot); lightplane = plane; surf = r_worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) { continue; /* no lightmaps */ } tex = surf->texinfo; s = DotProduct(mid, tex->vecs[0]) + tex->vecs[0][3]; t = DotProduct(mid, tex->vecs[1]) + tex->vecs[1][3]; if ((s < surf->texturemins[0]) || (t < surf->texturemins[1])) { continue; } ds = s - surf->texturemins[0]; dt = t - surf->texturemins[1]; if ((ds > surf->extents[0]) || (dt > surf->extents[1])) { continue; } if (!surf->samples) { return 0; } ds >>= 4; dt >>= 4; lightmap = surf->samples; VectorCopy(vec3_origin, pointcolor); if (lightmap) { vec3_t scale; lightmap += 3 * (dt * ((surf->extents[0] >> 4) + 1) + ds); for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { for (i = 0; i < 3; i++) { scale[i] = gl_modulate->value * r_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; } pointcolor[0] += lightmap[0] * scale[0] * (1.0 / 255); pointcolor[1] += lightmap[1] * scale[1] * (1.0 / 255); pointcolor[2] += lightmap[2] * scale[2] * (1.0 / 255); lightmap += 3 * ((surf->extents[0] >> 4) + 1) * ((surf->extents[1] >> 4) + 1); } } return 1; } /* go down back side */ return R_RecursiveLightPoint(node->children[!side], mid, end); } void R_LightPoint(vec3_t p, vec3_t color) { vec3_t end; float r; int lnum; dlight_t *dl; vec3_t dist; float add; if (!r_worldmodel->lightdata) { color[0] = color[1] = color[2] = 1.0; return; } end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 2048; r = R_RecursiveLightPoint(r_worldmodel->nodes, p, end); if (r == -1) { VectorCopy(vec3_origin, color); } else { VectorCopy(pointcolor, color); } /* add dynamic lights */ dl = r_newrefdef.dlights; for (lnum = 0; lnum < r_newrefdef.num_dlights; lnum++, dl++) { VectorSubtract(currententity->origin, dl->origin, dist); add = dl->intensity - VectorLength(dist); add *= (1.0 / 256); if (add > 0) { VectorMA(color, add, dl->color, color); } } VectorScale(color, gl_modulate->value, color); } void R_AddDynamicLights(msurface_t *surf) { int lnum; int sd, td; float fdist, frad, fminlight; vec3_t impact, local; int s, t; int i; int smax, tmax; mtexinfo_t *tex; dlight_t *dl; float *pfBL; float fsacc, ftacc; smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; tex = surf->texinfo; for (lnum = 0; lnum < r_newrefdef.num_dlights; lnum++) { if (!(surf->dlightbits & (1 << lnum))) { continue; /* not lit by this light */ } dl = &r_newrefdef.dlights[lnum]; frad = dl->intensity; fdist = DotProduct(dl->origin, surf->plane->normal) - surf->plane->dist; frad -= fabs(fdist); /* rad is now the highest intensity on the plane */ fminlight = DLIGHT_CUTOFF; if (frad < fminlight) { continue; } fminlight = frad - fminlight; for (i = 0; i < 3; i++) { impact[i] = dl->origin[i] - surf->plane->normal[i] * fdist; } local[0] = DotProduct(impact, tex->vecs[0]) + tex->vecs[0][3] - surf->texturemins[0]; local[1] = DotProduct(impact, tex->vecs[1]) + tex->vecs[1][3] - surf->texturemins[1]; pfBL = s_blocklights; for (t = 0, ftacc = 0; t < tmax; t++, ftacc += 16) { td = local[1] - ftacc; if (td < 0) { td = -td; } for (s = 0, fsacc = 0; s < smax; s++, fsacc += 16, pfBL += 3) { sd = Q_ftol(local[0] - fsacc); if (sd < 0) { sd = -sd; } if (sd > td) { fdist = sd + (td >> 1); } else { fdist = td + (sd >> 1); } if (fdist < fminlight) { pfBL[0] += (frad - fdist) * dl->color[0]; pfBL[1] += (frad - fdist) * dl->color[1]; pfBL[2] += (frad - fdist) * dl->color[2]; } } } } } void R_SetCacheState(msurface_t *surf) { int maps; for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { surf->cached_light[maps] = r_newrefdef.lightstyles[surf->styles[maps]].white; } } /* * Combine and scale multiple lightmaps into the floating format in blocklights */ void R_BuildLightMap(msurface_t *surf, byte *dest, int stride) { int smax, tmax; int r, g, b, a, max; int i, j, size; byte *lightmap; float scale[4]; int nummaps; float *bl; if (surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) { ri.Sys_Error(ERR_DROP, "R_BuildLightMap called for non-lit surface"); } smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; size = smax * tmax; if (size > (sizeof(s_blocklights) >> 4)) { ri.Sys_Error(ERR_DROP, "Bad s_blocklights size"); } /* set to full bright if no light data */ if (!surf->samples) { for (i = 0; i < size * 3; i++) { s_blocklights[i] = 255; } goto store; } /* count the # of maps */ for (nummaps = 0; nummaps < MAXLIGHTMAPS && surf->styles[nummaps] != 255; nummaps++) { } lightmap = surf->samples; /* add all the lightmaps */ if (nummaps == 1) { int maps; for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { bl = s_blocklights; for (i = 0; i < 3; i++) { scale[i] = gl_modulate->value * r_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; } if ((scale[0] == 1.0F) && (scale[1] == 1.0F) && (scale[2] == 1.0F)) { for (i = 0; i < size; i++, bl += 3) { bl[0] = lightmap[i * 3 + 0]; bl[1] = lightmap[i * 3 + 1]; bl[2] = lightmap[i * 3 + 2]; } } else { for (i = 0; i < size; i++, bl += 3) { bl[0] = lightmap[i * 3 + 0] * scale[0]; bl[1] = lightmap[i * 3 + 1] * scale[1]; bl[2] = lightmap[i * 3 + 2] * scale[2]; } } lightmap += size * 3; /* skip to next lightmap */ } } else { int maps; memset(s_blocklights, 0, sizeof(s_blocklights[0]) * size * 3); for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { bl = s_blocklights; for (i = 0; i < 3; i++) { scale[i] = gl_modulate->value * r_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; } if ((scale[0] == 1.0F) && (scale[1] == 1.0F) && (scale[2] == 1.0F)) { for (i = 0; i < size; i++, bl += 3) { bl[0] += lightmap[i * 3 + 0]; bl[1] += lightmap[i * 3 + 1]; bl[2] += lightmap[i * 3 + 2]; } } else { for (i = 0; i < size; i++, bl += 3) { bl[0] += lightmap[i * 3 + 0] * scale[0]; bl[1] += lightmap[i * 3 + 1] * scale[1]; bl[2] += lightmap[i * 3 + 2] * scale[2]; } } lightmap += size * 3; /* skip to next lightmap */ } } /* add all the dynamic lights */ if (surf->dlightframe == r_framecount) { R_AddDynamicLights(surf); } store: stride -= (smax << 2); bl = s_blocklights; for (i = 0; i < tmax; i++, dest += stride) { for (j = 0; j < smax; j++) { r = Q_ftol(bl[0]); g = Q_ftol(bl[1]); b = Q_ftol(bl[2]); /* catch negative lights */ if (r < 0) { r = 0; } if (g < 0) { g = 0; } if (b < 0) { b = 0; } /* determine the brightest of the three color components */ if (r > g) { max = r; } else { max = g; } if (b > max) { max = b; } /* alpha is ONLY used for the mono lightmap case. For this reason we set it to the brightest of the color components so that things don't get too dim. */ a = max; /* rescale all the color components if the intensity of the greatest channel exceeds 1.0 */ if (max > 255) { float t = 255.0F / max; r = r * t; g = g * t; b = b * t; a = a * t; } dest[0] = r; dest[1] = g; dest[2] = b; dest[3] = a; bl += 3; dest += 4; } } }