2002-07-23 19:57:47 +00:00
|
|
|
/*
|
|
|
|
gl_lightmap.c
|
|
|
|
|
|
|
|
surface-related refresh code
|
|
|
|
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
Copyright (C) 2000 Joseph Carter <knghtbrd@debian.org>
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
Free Software Foundation, Inc.
|
|
|
|
59 Temple Place - Suite 330
|
|
|
|
Boston, MA 02111-1307, USA
|
|
|
|
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
2003-01-15 15:31:36 +00:00
|
|
|
|
2002-07-23 19:57:47 +00:00
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
# include <string.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_STRINGS_H
|
|
|
|
# include <strings.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "QF/cvar.h"
|
|
|
|
#include "QF/render.h"
|
|
|
|
#include "QF/sys.h"
|
2021-07-24 05:19:52 +00:00
|
|
|
|
|
|
|
#include "QF/scene/entity.h"
|
|
|
|
|
2002-07-23 19:57:47 +00:00
|
|
|
#include "QF/GL/defines.h"
|
|
|
|
#include "QF/GL/funcs.h"
|
|
|
|
#include "QF/GL/qf_lightmap.h"
|
|
|
|
#include "QF/GL/qf_rmain.h"
|
2022-03-17 04:09:20 +00:00
|
|
|
#include "QF/GL/qf_rsurf.h"
|
2002-07-23 19:57:47 +00:00
|
|
|
#include "QF/GL/qf_sky.h"
|
|
|
|
#include "QF/GL/qf_textures.h"
|
|
|
|
#include "QF/GL/qf_vid.h"
|
|
|
|
|
|
|
|
#include "compat.h"
|
2012-02-14 08:28:09 +00:00
|
|
|
#include "r_internal.h"
|
2002-07-23 19:57:47 +00:00
|
|
|
|
2022-05-10 15:27:55 +00:00
|
|
|
static scrap_t *light_scrap;
|
2012-02-17 09:33:07 +00:00
|
|
|
static int dlightdivtable[8192];
|
|
|
|
static int gl_internalformat; // 1 or 3
|
|
|
|
static int lightmap_bytes; // 1, 3, or 4
|
2022-03-15 04:29:05 +00:00
|
|
|
GLuint gl_lightmap_textures[MAX_LIGHTMAPS];
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
// keep lightmap texture data in main memory so texsubimage can update properly
|
|
|
|
// LordHavoc: changed to be allocated at runtime (typically lower memory usage)
|
2012-02-17 09:33:07 +00:00
|
|
|
static byte *lightmaps[MAX_LIGHTMAPS];
|
2002-07-23 19:57:47 +00:00
|
|
|
|
2022-05-10 15:27:55 +00:00
|
|
|
static unsigned *blocklights;
|
|
|
|
static int bl_extents[2];
|
2012-02-17 09:33:07 +00:00
|
|
|
static int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH];
|
2002-07-23 19:57:47 +00:00
|
|
|
|
2012-02-17 09:33:07 +00:00
|
|
|
qboolean gl_lightmap_modified[MAX_LIGHTMAPS];
|
2022-05-10 15:27:55 +00:00
|
|
|
instsurf_t *gl_lightmap_polys;
|
2012-02-17 09:33:07 +00:00
|
|
|
glRect_t gl_lightmap_rectchange[MAX_LIGHTMAPS];
|
2002-07-23 19:57:47 +00:00
|
|
|
|
2004-03-07 23:51:29 +00:00
|
|
|
static int lmshift = 7;
|
2004-02-29 01:49:41 +00:00
|
|
|
|
2021-07-22 06:39:28 +00:00
|
|
|
void (*gl_R_BuildLightMap) (const transform_t *transform, mod_brush_t *brush,
|
|
|
|
msurface_t *surf);
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
gl_lightmap_init (void)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
|
|
|
|
memset (&lightmaps, 0, sizeof (lightmaps));
|
|
|
|
dlightdivtable[0] = 1048576 >> 7;
|
|
|
|
for (s = 1; s < 8192; s++)
|
|
|
|
dlightdivtable[s] = 1048576 / (s << 7);
|
|
|
|
}
|
2022-03-14 06:12:33 +00:00
|
|
|
|
2003-08-11 06:05:07 +00:00
|
|
|
static inline void
|
2021-07-22 06:39:28 +00:00
|
|
|
R_AddDynamicLights_1 (const transform_t *transform, msurface_t *surf)
|
2002-07-23 19:57:47 +00:00
|
|
|
{
|
|
|
|
float dist;
|
2004-07-13 19:14:01 +00:00
|
|
|
unsigned int maxdist, maxdist2, maxdist3;
|
|
|
|
int smax, smax_bytes, tmax,
|
2003-08-07 21:05:58 +00:00
|
|
|
grey, s, t;
|
2004-02-15 00:02:04 +00:00
|
|
|
unsigned int lnum, td, i, j;
|
|
|
|
unsigned int sdtable[18];
|
2002-07-23 19:57:47 +00:00
|
|
|
unsigned int *bl;
|
|
|
|
vec3_t impact, local;
|
2021-03-19 11:18:45 +00:00
|
|
|
vec4f_t entorigin = { 0, 0, 0, 1 };
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
smax = (surf->extents[0] >> 4) + 1;
|
|
|
|
smax_bytes = smax * gl_internalformat;
|
|
|
|
tmax = (surf->extents[1] >> 4) + 1;
|
|
|
|
|
2021-07-22 06:39:28 +00:00
|
|
|
if (transform) {
|
2021-03-19 11:18:45 +00:00
|
|
|
//FIXME give world entity a transform
|
2021-07-22 06:39:28 +00:00
|
|
|
entorigin = Transform_GetWorldPosition (transform);
|
2021-03-19 11:18:45 +00:00
|
|
|
}
|
|
|
|
|
2002-07-23 19:57:47 +00:00
|
|
|
for (lnum = 0; lnum < r_maxdlights; lnum++) {
|
2012-07-21 04:58:54 +00:00
|
|
|
if (!(surf->dlightbits[lnum / 32] & (1 << (lnum % 32))))
|
2002-07-23 19:57:47 +00:00
|
|
|
continue; // not lit by this light
|
|
|
|
|
2021-03-19 11:18:45 +00:00
|
|
|
VectorSubtract (r_dlights[lnum].origin, entorigin, local);
|
2002-07-23 19:57:47 +00:00
|
|
|
dist = DotProduct (local, surf->plane->normal) - surf->plane->dist;
|
2003-08-08 15:25:53 +00:00
|
|
|
VectorMultSub (r_dlights[lnum].origin, dist, surf->plane->normal,
|
|
|
|
impact);
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
i = DotProduct (impact, surf->texinfo->vecs[0]) +
|
|
|
|
surf->texinfo->vecs[0][3] - surf->texturemins[0];
|
|
|
|
|
|
|
|
// reduce calculations
|
|
|
|
t = dist * dist;
|
|
|
|
for (s = 0; s < smax; s++, i -= 16)
|
|
|
|
sdtable[s] = i * i + t;
|
|
|
|
|
|
|
|
i = DotProduct (impact, surf->texinfo->vecs[1]) +
|
|
|
|
surf->texinfo->vecs[1][3] - surf->texturemins[1];
|
|
|
|
|
|
|
|
// for comparisons to minimum acceptable light
|
2004-03-17 04:47:55 +00:00
|
|
|
maxdist = (int) (r_dlights[lnum].radius * r_dlights[lnum].radius);
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
// clamp radius to avoid exceeding 8192 entry division table
|
|
|
|
if (maxdist > 1048576)
|
|
|
|
maxdist = 1048576;
|
|
|
|
maxdist3 = maxdist - t;
|
|
|
|
|
|
|
|
// convert to 8.8 blocklights format
|
|
|
|
grey = (r_dlights[lnum].color[0] + r_dlights[lnum].color[1] +
|
|
|
|
r_dlights[lnum].color[2]) * maxdist / 3.0;
|
|
|
|
bl = blocklights;
|
|
|
|
for (t = 0; t < tmax; t++, i -= 16) {
|
|
|
|
td = i * i;
|
|
|
|
if (td < maxdist3) { // ensure part is visible on this line
|
|
|
|
maxdist2 = maxdist - td;
|
|
|
|
for (s = 0; s < smax; s++) {
|
|
|
|
if (sdtable[s] < maxdist2) {
|
|
|
|
j = dlightdivtable[(sdtable[s] + td) >> 7];
|
|
|
|
*bl++ += (grey * j) >> 7;
|
|
|
|
} else
|
|
|
|
bl++;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
bl += smax_bytes; // skip line
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-02-14 07:39:43 +00:00
|
|
|
static inline void
|
2021-07-22 06:39:28 +00:00
|
|
|
R_AddDynamicLights_3 (const transform_t *transform, msurface_t *surf)
|
2002-07-23 19:57:47 +00:00
|
|
|
{
|
|
|
|
float dist;
|
2004-07-13 19:14:01 +00:00
|
|
|
unsigned int maxdist, maxdist2, maxdist3;
|
|
|
|
int smax, smax_bytes, tmax,
|
2003-08-07 19:58:39 +00:00
|
|
|
red, green, blue, s, t;
|
2004-02-15 00:02:04 +00:00
|
|
|
unsigned int lnum, td, i, j;
|
|
|
|
unsigned int sdtable[18];
|
2002-07-23 19:57:47 +00:00
|
|
|
unsigned int *bl;
|
|
|
|
vec3_t impact, local;
|
2021-03-19 11:18:45 +00:00
|
|
|
vec4f_t entorigin = { 0, 0, 0, 1 };
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
smax = (surf->extents[0] >> 4) + 1;
|
|
|
|
smax_bytes = smax * gl_internalformat;
|
|
|
|
tmax = (surf->extents[1] >> 4) + 1;
|
|
|
|
|
2021-07-22 06:39:28 +00:00
|
|
|
if (transform) {
|
|
|
|
entorigin = Transform_GetWorldPosition (transform);
|
2021-03-19 11:18:45 +00:00
|
|
|
}
|
|
|
|
|
2002-07-23 19:57:47 +00:00
|
|
|
for (lnum = 0; lnum < r_maxdlights; lnum++) {
|
2012-07-21 04:58:54 +00:00
|
|
|
if (!(surf->dlightbits[lnum / 32] & (1 << (lnum % 32))))
|
2002-07-23 19:57:47 +00:00
|
|
|
continue; // not lit by this light
|
|
|
|
|
2021-03-19 11:18:45 +00:00
|
|
|
VectorSubtract (r_dlights[lnum].origin, entorigin, local);
|
2002-07-23 19:57:47 +00:00
|
|
|
dist = DotProduct (local, surf->plane->normal) - surf->plane->dist;
|
2003-08-08 15:25:53 +00:00
|
|
|
VectorMultSub (r_dlights[lnum].origin, dist, surf->plane->normal,
|
|
|
|
impact);
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
i = DotProduct (impact, surf->texinfo->vecs[0]) +
|
|
|
|
surf->texinfo->vecs[0][3] - surf->texturemins[0];
|
|
|
|
|
|
|
|
// reduce calculations
|
|
|
|
t = dist * dist;
|
|
|
|
for (s = 0; s < smax; s++, i -= 16)
|
|
|
|
sdtable[s] = i * i + t;
|
|
|
|
|
|
|
|
i = DotProduct (impact, surf->texinfo->vecs[1]) +
|
|
|
|
surf->texinfo->vecs[1][3] - surf->texturemins[1];
|
|
|
|
|
|
|
|
// for comparisons to minimum acceptable light
|
2004-03-17 04:47:55 +00:00
|
|
|
maxdist = (int) (r_dlights[lnum].radius * r_dlights[lnum].radius);
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
// clamp radius to avoid exceeding 8192 entry division table
|
|
|
|
if (maxdist > 1048576)
|
|
|
|
maxdist = 1048576;
|
|
|
|
maxdist3 = maxdist - t;
|
|
|
|
|
|
|
|
// convert to 8.8 blocklights format
|
|
|
|
red = r_dlights[lnum].color[0] * maxdist;
|
|
|
|
green = r_dlights[lnum].color[1] * maxdist;
|
|
|
|
blue = r_dlights[lnum].color[2] * maxdist;
|
|
|
|
bl = blocklights;
|
|
|
|
for (t = 0; t < tmax; t++, i -= 16) {
|
|
|
|
td = i * i;
|
|
|
|
if (td < maxdist3) { // ensure part is visible on this line
|
|
|
|
maxdist2 = maxdist - td;
|
|
|
|
for (s = 0; s < smax; s++) {
|
|
|
|
if (sdtable[s] < maxdist2) {
|
|
|
|
j = dlightdivtable[(sdtable[s] + td) >> 7];
|
|
|
|
*bl++ += (red * j) >> 7;
|
|
|
|
*bl++ += (green * j) >> 7;
|
|
|
|
*bl++ += (blue * j) >> 7;
|
|
|
|
} else
|
|
|
|
bl += 3;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
bl += smax_bytes; // skip line
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-07-22 06:39:28 +00:00
|
|
|
R_BuildLightMap_1 (const transform_t *transform, mod_brush_t *brush,
|
|
|
|
msurface_t *surf)
|
2002-07-23 19:57:47 +00:00
|
|
|
{
|
|
|
|
byte *dest;
|
2022-05-10 15:27:55 +00:00
|
|
|
int maps, size, smax, tmax, i;
|
2002-07-23 19:57:47 +00:00
|
|
|
unsigned int scale;
|
|
|
|
unsigned int *bl;
|
|
|
|
|
|
|
|
surf->cached_dlight = (surf->dlightframe == r_framecount);
|
|
|
|
|
|
|
|
smax = (surf->extents[0] >> 4) + 1;
|
|
|
|
tmax = (surf->extents[1] >> 4) + 1;
|
|
|
|
size = smax * tmax * gl_internalformat;
|
|
|
|
|
|
|
|
// set to full bright if no light data
|
2021-02-01 10:31:11 +00:00
|
|
|
if (!brush->lightdata) {
|
2002-07-23 19:57:47 +00:00
|
|
|
memset (&blocklights[0], 0xff, size * sizeof(int));
|
|
|
|
goto store;
|
|
|
|
}
|
|
|
|
|
|
|
|
// clear to no light
|
|
|
|
memset (&blocklights[0], 0, size * sizeof(int));
|
|
|
|
|
|
|
|
// add all the lightmaps
|
|
|
|
if (surf->samples) {
|
|
|
|
byte *lightmap;
|
|
|
|
|
|
|
|
lightmap = surf->samples;
|
|
|
|
for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255;
|
|
|
|
maps++) {
|
|
|
|
scale = d_lightstylevalue[surf->styles[maps]];
|
|
|
|
surf->cached_light[maps] = scale; // 8.8 fraction
|
|
|
|
bl = blocklights;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
*bl++ += *lightmap++ * scale;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// add all the dynamic lights
|
|
|
|
if (surf->dlightframe == r_framecount)
|
2021-07-22 06:39:28 +00:00
|
|
|
R_AddDynamicLights_1 (transform, surf);
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
store:
|
|
|
|
// bound and shift
|
2004-02-14 05:10:29 +00:00
|
|
|
// Also, invert because we're using a diff blendfunc now
|
2002-07-23 19:57:47 +00:00
|
|
|
bl = blocklights;
|
2022-05-10 15:27:55 +00:00
|
|
|
dest = (byte *) blocklights;
|
|
|
|
for (i = 0; i < tmax * smax; i++) {
|
|
|
|
*dest++ = min (*bl >> lmshift, 255);
|
|
|
|
bl++;
|
2002-07-23 19:57:47 +00:00
|
|
|
}
|
2022-05-10 15:27:55 +00:00
|
|
|
|
|
|
|
GL_SubpicUpdate (surf->lightpic, (byte *) blocklights, 1);
|
2002-07-23 19:57:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-07-22 06:39:28 +00:00
|
|
|
R_BuildLightMap_3 (const transform_t *transform, mod_brush_t *brush,
|
|
|
|
msurface_t *surf)
|
2002-07-23 19:57:47 +00:00
|
|
|
{
|
|
|
|
byte *dest;
|
2022-05-10 15:27:55 +00:00
|
|
|
int maps, size, smax, tmax, i;
|
2002-07-23 19:57:47 +00:00
|
|
|
unsigned int scale;
|
|
|
|
unsigned int *bl;
|
|
|
|
|
|
|
|
surf->cached_dlight = (surf->dlightframe == r_framecount);
|
|
|
|
|
|
|
|
smax = (surf->extents[0] >> 4) + 1;
|
|
|
|
tmax = (surf->extents[1] >> 4) + 1;
|
|
|
|
size = smax * tmax * gl_internalformat;
|
|
|
|
|
|
|
|
// set to full bright if no light data
|
2021-02-01 10:31:11 +00:00
|
|
|
if (!brush->lightdata) {
|
2002-07-23 19:57:47 +00:00
|
|
|
memset (&blocklights[0], 0xff, size * sizeof(int));
|
|
|
|
goto store;
|
|
|
|
}
|
|
|
|
|
|
|
|
// clear to no light
|
|
|
|
memset (&blocklights[0], 0, size * sizeof(int));
|
|
|
|
|
|
|
|
// add all the lightmaps
|
|
|
|
if (surf->samples) {
|
|
|
|
byte *lightmap;
|
|
|
|
|
|
|
|
lightmap = surf->samples;
|
|
|
|
for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255;
|
|
|
|
maps++) {
|
|
|
|
scale = d_lightstylevalue[surf->styles[maps]];
|
|
|
|
surf->cached_light[maps] = scale; // 8.8 fraction
|
|
|
|
bl = blocklights;
|
2004-02-27 08:29:56 +00:00
|
|
|
for (i = 0; i < smax * tmax; i++) {
|
|
|
|
*bl++ += *lightmap++ * scale;
|
|
|
|
*bl++ += *lightmap++ * scale;
|
2002-07-23 19:57:47 +00:00
|
|
|
*bl++ += *lightmap++ * scale;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// add all the dynamic lights
|
|
|
|
if (surf->dlightframe == r_framecount)
|
2021-07-22 06:39:28 +00:00
|
|
|
R_AddDynamicLights_3 (transform, surf);
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
store:
|
|
|
|
// bound and shift
|
2004-02-14 05:10:29 +00:00
|
|
|
// and invert too
|
2002-07-23 19:57:47 +00:00
|
|
|
bl = blocklights;
|
2022-05-10 15:27:55 +00:00
|
|
|
dest = (byte *) blocklights;
|
|
|
|
for (i = 0; i < tmax * smax; i++) {
|
|
|
|
*dest++ = min (*bl >> lmshift, 255);
|
|
|
|
bl++;
|
|
|
|
*dest++ = min (*bl >> lmshift, 255);
|
|
|
|
bl++;
|
|
|
|
*dest++ = min (*bl >> lmshift, 255);
|
|
|
|
bl++;
|
2002-07-23 19:57:47 +00:00
|
|
|
}
|
2022-05-10 15:27:55 +00:00
|
|
|
|
|
|
|
GL_SubpicUpdate (surf->lightpic, (byte *) blocklights, 1);
|
2002-07-23 19:57:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-07-22 06:39:28 +00:00
|
|
|
R_BuildLightMap_4 (const transform_t *transform, mod_brush_t *brush,
|
|
|
|
msurface_t *surf)
|
2002-07-23 19:57:47 +00:00
|
|
|
{
|
|
|
|
byte *dest;
|
2022-05-10 15:27:55 +00:00
|
|
|
int maps, size, smax, tmax, i;
|
2002-07-23 19:57:47 +00:00
|
|
|
unsigned int scale;
|
|
|
|
unsigned int *bl;
|
|
|
|
|
|
|
|
surf->cached_dlight = (surf->dlightframe == r_framecount);
|
|
|
|
|
|
|
|
smax = (surf->extents[0] >> 4) + 1;
|
|
|
|
tmax = (surf->extents[1] >> 4) + 1;
|
|
|
|
size = smax * tmax * gl_internalformat;
|
|
|
|
|
|
|
|
// set to full bright if no light data
|
2021-02-01 10:31:11 +00:00
|
|
|
if (!brush->lightdata) {
|
2002-07-23 19:57:47 +00:00
|
|
|
memset (&blocklights[0], 0xff, size * sizeof(int));
|
|
|
|
goto store;
|
|
|
|
}
|
|
|
|
|
|
|
|
// clear to no light
|
|
|
|
memset (&blocklights[0], 0, size * sizeof(int));
|
|
|
|
|
|
|
|
// add all the lightmaps
|
|
|
|
if (surf->samples) {
|
|
|
|
byte *lightmap;
|
|
|
|
|
|
|
|
lightmap = surf->samples;
|
|
|
|
for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255;
|
|
|
|
maps++) {
|
|
|
|
scale = d_lightstylevalue[surf->styles[maps]];
|
|
|
|
surf->cached_light[maps] = scale; // 8.8 fraction
|
|
|
|
bl = blocklights;
|
2004-02-27 08:29:56 +00:00
|
|
|
for (i = 0; i < smax * tmax; i++) {
|
|
|
|
*bl++ += *lightmap++ * scale;
|
|
|
|
*bl++ += *lightmap++ * scale;
|
2002-07-23 19:57:47 +00:00
|
|
|
*bl++ += *lightmap++ * scale;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// add all the dynamic lights
|
|
|
|
if (surf->dlightframe == r_framecount)
|
2021-07-22 06:39:28 +00:00
|
|
|
R_AddDynamicLights_3 (transform, surf);
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
store:
|
|
|
|
// bound and shift
|
2004-02-14 05:10:29 +00:00
|
|
|
// and invert too
|
2002-07-23 19:57:47 +00:00
|
|
|
bl = blocklights;
|
2022-05-10 15:27:55 +00:00
|
|
|
dest = (byte *) blocklights;
|
|
|
|
for (i = 0; i < tmax * smax; i++) {
|
|
|
|
*dest++ = min (*bl >> lmshift, 255);
|
|
|
|
bl++;
|
|
|
|
*dest++ = min (*bl >> lmshift, 255);
|
|
|
|
bl++;
|
|
|
|
*dest++ = min (*bl >> lmshift, 255);
|
|
|
|
bl++;
|
|
|
|
*dest++ = 255;
|
2002-07-23 19:57:47 +00:00
|
|
|
}
|
2022-05-10 15:27:55 +00:00
|
|
|
|
|
|
|
GL_SubpicUpdate (surf->lightpic, (byte *) blocklights, 1);
|
2002-07-23 19:57:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// BRUSH MODELS ===============================================================
|
|
|
|
|
|
|
|
void
|
2012-02-22 07:32:34 +00:00
|
|
|
gl_R_BlendLightmaps (void)
|
2002-07-23 19:57:47 +00:00
|
|
|
{
|
|
|
|
float *v;
|
2011-12-17 12:45:52 +00:00
|
|
|
instsurf_t *sc;
|
2002-07-23 19:57:47 +00:00
|
|
|
glpoly_t *p;
|
|
|
|
|
|
|
|
qfglDepthMask (GL_FALSE); // don't bother writing Z
|
2004-02-29 01:49:41 +00:00
|
|
|
qfglBlendFunc (lm_src_blend, lm_dest_blend);
|
2003-03-20 19:58:18 +00:00
|
|
|
|
2022-05-10 15:27:55 +00:00
|
|
|
qfglBindTexture (GL_TEXTURE_2D, gl_R_LightmapTexture ());
|
|
|
|
for (sc = gl_lightmap_polys; sc; sc = sc->lm_chain) {
|
|
|
|
if (sc->transform) {
|
|
|
|
qfglPushMatrix ();
|
|
|
|
qfglLoadMatrixf (sc->transform);
|
|
|
|
}
|
|
|
|
for (p = sc->surface->polys; p; p = p->next) {
|
|
|
|
qfglBegin (GL_POLYGON);
|
|
|
|
v = p->verts[0];
|
|
|
|
for (int j = 0; j < p->numverts; j++, v += VERTEXSIZE) {
|
|
|
|
qfglTexCoord2fv (&v[5]);
|
|
|
|
qfglVertex3fv (v);
|
2003-03-20 19:58:18 +00:00
|
|
|
}
|
2022-05-10 15:27:55 +00:00
|
|
|
qfglEnd ();
|
2003-03-20 19:58:18 +00:00
|
|
|
}
|
2022-05-10 15:27:55 +00:00
|
|
|
if (sc->transform)
|
|
|
|
qfglPopMatrix ();
|
2003-03-20 19:58:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return to normal blending --KB
|
|
|
|
qfglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
qfglDepthMask (GL_TRUE); // back to normal Z buffering
|
|
|
|
}
|
|
|
|
|
2004-02-15 00:02:04 +00:00
|
|
|
void
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
gl_overbright_f (void *data, const cvar_t *cvar)
|
2004-02-15 00:02:04 +00:00
|
|
|
{
|
2021-02-01 10:31:11 +00:00
|
|
|
mod_brush_t *brush;
|
2004-02-15 00:02:04 +00:00
|
|
|
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
if (!cvar)
|
2004-02-29 01:49:41 +00:00
|
|
|
return;
|
2004-02-15 00:02:04 +00:00
|
|
|
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
if (gl_overbright) {
|
2004-03-07 23:51:29 +00:00
|
|
|
if (!gl_combine_capable && gl_mtex_capable) {
|
2007-11-06 10:17:14 +00:00
|
|
|
Sys_Printf ("Warning: gl_overbright has no effect with "
|
2004-03-07 23:51:29 +00:00
|
|
|
"gl_multitexture enabled if you don't have "
|
|
|
|
"GL_COMBINE support in your driver.\n");
|
|
|
|
lm_src_blend = GL_ZERO;
|
|
|
|
lm_dest_blend = GL_SRC_COLOR;
|
|
|
|
lmshift = 7;
|
2012-02-22 02:09:09 +00:00
|
|
|
gl_rgb_scale = 1.0;
|
2004-03-07 23:51:29 +00:00
|
|
|
} else {
|
|
|
|
lm_src_blend = GL_DST_COLOR;
|
|
|
|
lm_dest_blend = GL_SRC_COLOR;
|
|
|
|
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
switch (gl_overbright) {
|
2004-03-07 23:51:29 +00:00
|
|
|
case 2:
|
|
|
|
lmshift = 9;
|
2012-02-22 02:09:09 +00:00
|
|
|
gl_rgb_scale = 4.0;
|
2004-03-07 23:51:29 +00:00
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
lmshift = 8;
|
2012-02-22 02:09:09 +00:00
|
|
|
gl_rgb_scale = 2.0;
|
2004-03-07 23:51:29 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
lmshift = 7;
|
2012-02-22 02:09:09 +00:00
|
|
|
gl_rgb_scale = 1.0;
|
2004-03-07 23:51:29 +00:00
|
|
|
break;
|
|
|
|
}
|
2004-02-29 01:49:41 +00:00
|
|
|
}
|
2004-03-07 23:51:29 +00:00
|
|
|
} else {
|
|
|
|
lm_src_blend = GL_ZERO;
|
|
|
|
lm_dest_blend = GL_SRC_COLOR;
|
|
|
|
lmshift = 7;
|
2012-02-22 02:09:09 +00:00
|
|
|
gl_rgb_scale = 1.0;
|
2004-02-29 01:49:41 +00:00
|
|
|
}
|
|
|
|
|
2012-02-22 07:32:34 +00:00
|
|
|
if (!gl_R_BuildLightMap)
|
2004-03-08 18:42:42 +00:00
|
|
|
return;
|
|
|
|
|
2022-03-14 06:27:43 +00:00
|
|
|
brush = &r_refdef.worldmodel->brush;
|
2004-02-15 00:02:04 +00:00
|
|
|
|
2021-08-01 12:54:05 +00:00
|
|
|
for (unsigned i = 0; i < brush->numsurfaces; i++) {
|
|
|
|
msurface_t *surf = brush->surfaces + i;
|
2021-07-15 12:38:12 +00:00
|
|
|
if (surf->flags & (SURF_DRAWTURB | SURF_DRAWSKY))
|
2004-02-15 00:02:04 +00:00
|
|
|
continue;
|
|
|
|
|
2021-07-22 06:39:28 +00:00
|
|
|
gl_R_BuildLightMap (0, brush, surf);
|
2004-02-15 00:02:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-07-23 19:57:47 +00:00
|
|
|
// LIGHTMAP ALLOCATION ========================================================
|
|
|
|
|
|
|
|
static void
|
2021-02-01 10:31:11 +00:00
|
|
|
GL_CreateSurfaceLightmap (mod_brush_t *brush, msurface_t *surf)
|
2002-07-23 19:57:47 +00:00
|
|
|
{
|
|
|
|
int smax, tmax;
|
|
|
|
|
|
|
|
if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB))
|
|
|
|
return;
|
|
|
|
|
|
|
|
smax = (surf->extents[0] >> 4) + 1;
|
|
|
|
tmax = (surf->extents[1] >> 4) + 1;
|
|
|
|
|
2022-05-10 15:27:55 +00:00
|
|
|
surf->lightpic = GL_ScrapSubpic (light_scrap, smax, tmax);
|
|
|
|
if (!surf->lightpic) {
|
|
|
|
Sys_Error ("FIXME taniwha is being lazy");
|
|
|
|
}
|
|
|
|
if (smax > bl_extents[0]) {
|
|
|
|
bl_extents[0] = smax;
|
|
|
|
}
|
|
|
|
if (tmax > bl_extents[1]) {
|
|
|
|
bl_extents[1] = tmax;
|
|
|
|
}
|
2002-07-23 19:57:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
GL_BuildLightmaps
|
|
|
|
|
|
|
|
Builds the lightmap texture with all the surfaces from all brush models
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
GL_BuildLightmaps (model_t **models, int num_models)
|
|
|
|
{
|
|
|
|
model_t *m;
|
2021-02-01 10:31:11 +00:00
|
|
|
mod_brush_t *brush;
|
2002-07-23 19:57:47 +00:00
|
|
|
|
|
|
|
memset (allocated, 0, sizeof (allocated));
|
|
|
|
|
|
|
|
r_framecount = 1; // no dlightcache
|
|
|
|
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
switch (r_lightmap_components) {
|
2002-07-23 19:57:47 +00:00
|
|
|
case 1:
|
|
|
|
gl_internalformat = 1;
|
|
|
|
gl_lightmap_format = GL_LUMINANCE;
|
|
|
|
lightmap_bytes = 1;
|
2012-02-22 07:32:34 +00:00
|
|
|
gl_R_BuildLightMap = R_BuildLightMap_1;
|
2002-07-23 19:57:47 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
gl_internalformat = 3;
|
2012-02-22 02:09:09 +00:00
|
|
|
if (gl_use_bgra)
|
2004-02-24 20:50:55 +00:00
|
|
|
gl_lightmap_format = GL_BGR;
|
|
|
|
else
|
|
|
|
gl_lightmap_format = GL_RGB;
|
2002-07-23 19:57:47 +00:00
|
|
|
lightmap_bytes = 3;
|
2012-02-22 07:32:34 +00:00
|
|
|
gl_R_BuildLightMap = R_BuildLightMap_3;
|
2002-07-23 19:57:47 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
default:
|
|
|
|
gl_internalformat = 3;
|
2012-02-22 02:09:09 +00:00
|
|
|
if (gl_use_bgra)
|
2004-02-24 20:50:55 +00:00
|
|
|
gl_lightmap_format = GL_BGRA;
|
|
|
|
else
|
|
|
|
gl_lightmap_format = GL_RGBA;
|
2002-07-23 19:57:47 +00:00
|
|
|
lightmap_bytes = 4;
|
2012-02-22 07:32:34 +00:00
|
|
|
gl_R_BuildLightMap = R_BuildLightMap_4;
|
2002-07-23 19:57:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-05-10 15:27:55 +00:00
|
|
|
if (!light_scrap) {
|
2022-05-21 08:45:22 +00:00
|
|
|
light_scrap = GL_CreateScrap (4096, gl_lightmap_format, 1);
|
2022-05-10 15:27:55 +00:00
|
|
|
} else {
|
|
|
|
GL_ScrapClear (light_scrap);
|
|
|
|
}
|
|
|
|
|
|
|
|
bl_extents[1] = bl_extents[0] = 0;
|
2021-08-01 12:54:05 +00:00
|
|
|
for (int j = 1; j < num_models; j++) {
|
2002-07-23 19:57:47 +00:00
|
|
|
m = models[j];
|
|
|
|
if (!m)
|
|
|
|
break;
|
2021-02-01 10:31:11 +00:00
|
|
|
if (m->path[0] == '*' || m->type != mod_brush) {
|
2011-12-17 00:39:57 +00:00
|
|
|
// sub model surfaces are processed as part of the main model
|
2002-07-23 19:57:47 +00:00
|
|
|
continue;
|
2011-12-17 00:39:57 +00:00
|
|
|
}
|
2021-02-01 10:31:11 +00:00
|
|
|
brush = &m->brush;
|
2012-02-17 09:33:07 +00:00
|
|
|
gl_currentmodel = m;
|
2011-12-17 00:39:57 +00:00
|
|
|
// non-bsp models don't have surfaces.
|
2021-08-01 12:54:05 +00:00
|
|
|
for (unsigned i = 0; i < brush->numsurfaces; i++) {
|
[cvar] Make cvars properly typed
This is an extremely extensive patch as it hits every cvar, and every
usage of the cvars. Cvars no longer store the value they control,
instead, they use a cexpr value object to reference the value and
specify the value's type (currently, a null type is used for strings).
Non-string cvars are passed through cexpr, allowing expressions in the
cvars' settings. Also, cvars have returned to an enhanced version of the
original (id quake) registration scheme.
As a minor benefit, relevant code having direct access to the
cvar-controlled variables is probably a slight optimization as it
removed a pointer dereference, and the variables can be located for data
locality.
The static cvar descriptors are made private as an additional safety
layer, though there's nothing stopping external modification via
Cvar_FindVar (which is needed for adding listeners).
While not used yet (partly due to working out the design), cvars can
have a validation function.
Registering a cvar allows a primary listener (and its data) to be
specified: it will always be called first when the cvar is modified. The
combination of proper listeners and direct access to the controlled
variable greatly simplifies the more complex cvar interactions as much
less null checking is required, and there's no need for one cvar's
callback to call another's.
nq-x11 is known to work at least well enough for the demos. More testing
will come.
2022-04-23 03:22:45 +00:00
|
|
|
if (brush->surfaces[i].flags & (SURF_DRAWTURB | SURF_DRAWSKY))
|
2002-07-23 19:57:47 +00:00
|
|
|
continue;
|
2021-02-01 10:31:11 +00:00
|
|
|
GL_CreateSurfaceLightmap (brush, brush->surfaces + i);
|
2022-03-17 04:09:20 +00:00
|
|
|
GL_BuildSurfaceDisplayList (brush, brush->surfaces + i);
|
2002-07-23 19:57:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-10 15:27:55 +00:00
|
|
|
int size = bl_extents[0] * bl_extents[1] * 3; // * 3 for rgb support
|
|
|
|
blocklights = realloc (blocklights, size * sizeof (blocklights[0]));
|
|
|
|
|
2002-07-23 19:57:47 +00:00
|
|
|
// upload all lightmaps that were filled
|
2022-05-10 15:27:55 +00:00
|
|
|
for (int j = 1; j < num_models; j++) {
|
|
|
|
m = models[j];
|
|
|
|
if (!m)
|
|
|
|
break;
|
|
|
|
if (m->path[0] == '*' || m->type != mod_brush) {
|
|
|
|
// sub model surfaces are processed as part of the main model
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
brush = &m->brush;
|
|
|
|
// non-bsp models don't have surfaces.
|
|
|
|
for (unsigned i = 0; i < brush->numsurfaces; i++) {
|
|
|
|
msurface_t *surf = brush->surfaces + i;
|
|
|
|
if (surf->lightpic) {
|
|
|
|
gl_R_BuildLightMap (0, brush, surf);
|
|
|
|
}
|
|
|
|
}
|
2002-07-23 19:57:47 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-10 15:27:55 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
gl_R_LightmapTexture (void)
|
|
|
|
{
|
|
|
|
return GL_ScrapTexture (light_scrap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gl_R_FlushLightmaps (void)
|
|
|
|
{
|
|
|
|
GL_ScrapFlush (light_scrap);
|
|
|
|
}
|