Skip to content

Commit

Permalink
LIGHTMAPS: Support for loading e5bgr9 lightmaps.
Browse files Browse the repository at this point in the history
This format was added to ericw-tools spring 2024 and initial implementation
does not expose it's full range potential, but it loads, and works, which allows
mappers to target this format instead of the old rgb8 format.

Ported from QuakeSpasm-Spiked.
  • Loading branch information
dsvensson committed Jan 25, 2025
1 parent feca102 commit 9adb697
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 50 deletions.
3 changes: 3 additions & 0 deletions src/gl_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ typedef enum
#define EF_TRACER2 64 // orange split trail + rotate
#define EF_TRACER3 128 // purple trail

#define MOD_HDRLIGHTING (1u<<13) //spike -- light samples are in e5bgr9 format. int aligned.

#define MAX_SIMPLE_TEXTURES 5
#define MAX_TEXTURE_ARRAYS_PER_MODEL 64

Expand Down Expand Up @@ -542,6 +544,7 @@ typedef struct model_s {
byte* visdata;
int visdata_length;
byte* lightdata;
size_t lightdatasamplesize;

int bspversion;
qbool isworldmodel;
Expand Down
48 changes: 32 additions & 16 deletions src/r_brushmodel_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ static void SetSurfaceLighting(model_t* loadmodel, msurface_t* out, byte* styles
out->samples = NULL;
}
else {
out->samples = loadmodel->lightdata + (loadmodel->bspversion == HL_BSPVERSION ? i : i * 3);
out->samples = loadmodel->lightdata + (loadmodel->bspversion == HL_BSPVERSION ? i : i * loadmodel->lightdatasamplesize);
}
}

Expand Down Expand Up @@ -243,24 +243,38 @@ static void Mod_LoadLighting(model_t* loadmodel, lump_t* l, byte* mod_base, bspx
if (load_inline) {
int threshold = (lightmode == 1 ? 255 : lightmode == 2 ? 170 : 128);
int lumpsize;
byte *rgb = Mod_BSPX_FindLump(bspx_header, "RGBLIGHTING", &lumpsize, mod_base);
// Sanity-check size if vanilla lit exists
if (rgb && lumpsize % 3 == 0 && (lumpsize == l->filelen * 3 || l->filelen <= 0)) {
loadmodel->lightdata = (byte *) Hunk_AllocName(lumpsize, loadmodel->name);
byte *rgb;

rgb = Mod_BSPX_FindLump(bspx_header, "LIGHTING_E5BGR9", &lumpsize, mod_base);
if (rgb && lumpsize % 4 == 0 && (lumpsize == l->filelen * 4 || l->filelen <= 0)) {
loadmodel->lightdata = (byte *) Hunk_AllocName (lumpsize, loadmodel->name);
loadmodel->lightdatasamplesize = 4;
memcpy(loadmodel->lightdata, rgb, lumpsize);
// we trust the inline RGB data to be bug free so we don't check it against the mono lightmap
// what we do though is prevent color wash-out in brightly lit areas
// (one day we may do it in R_BuildLightMap instead)
out = loadmodel->lightdata;
for (i = 0; i < lumpsize / 3; i++, out += 3) {
int m = max(out[0], max(out[1], out[2]));
if (m > threshold) {
out[0] = out[0] * threshold / m;
out[1] = out[1] * threshold / m;
out[2] = out[2] * threshold / m;
loadmodel->flags |= MOD_HDRLIGHTING;
for (i = 0; i < lumpsize / 4; i++) { //native endian...
((int*)loadmodel->lightdata)[i] = LittleLong(((int*)loadmodel->lightdata)[i]);
}
} else {
rgb = Mod_BSPX_FindLump(bspx_header, "RGBLIGHTING", &lumpsize, mod_base);
// Sanity-check size if vanilla lit exists
if (rgb && lumpsize % 3 == 0 && (lumpsize == l->filelen * 3 || l->filelen <= 0)) {
loadmodel->lightdata = (byte *) Hunk_AllocName(lumpsize, loadmodel->name);
loadmodel->lightdatasamplesize = 3;
memcpy(loadmodel->lightdata, rgb, lumpsize);
// we trust the inline RGB data to be bug free so we don't check it against the mono lightmap
// what we do though is prevent color wash-out in brightly lit areas
// (one day we may do it in R_BuildLightMap instead)
out = loadmodel->lightdata;
for (i = 0; i < lumpsize / 3; i++, out += 3) {
int m = max(out[0], max(out[1], out[2]));
if (m > threshold) {
out[0] = out[0] * threshold / m;
out[1] = out[1] * threshold / m;
out[2] = out[2] * threshold / m;
}
}
// all done, but we let them override it with a .lit
}
// all done, but we let them override it with a .lit
}
}

Expand All @@ -285,6 +299,7 @@ static void Mod_LoadLighting(model_t* loadmodel, lump_t* l, byte* mod_base, bspx
Com_Printf("Static coloured lighting loaded\n");
}
loadmodel->lightdata = data + 8;
loadmodel->lightdatasamplesize = 3;

in = mod_base + l->fileofs;
out = loadmodel->lightdata;
Expand Down Expand Up @@ -345,6 +360,7 @@ static void Mod_LoadLighting(model_t* loadmodel, lump_t* l, byte* mod_base, bspx

//no .lit found, expand the white lighting data to color
loadmodel->lightdata = (byte *) Hunk_AllocName (l->filelen * 3, va("%s_@lightdata", loadmodel->name));
loadmodel->lightdatasamplesize = 3;
in = mod_base + l->fileofs;
out = loadmodel->lightdata;
for (i = 0; i < l->filelen; i++, out += 3) {
Expand Down
8 changes: 8 additions & 0 deletions src/r_lighting.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ void R_RenderDlights(void);
void R_LightEntity(entity_t* ent);

extern unsigned int d_lightstylevalue[256]; // 8.8 fraction of base light value
static const float rgb9e5tab[32] = {
//multipliers for the 9-bit mantissa, according to the biased mantissa
//aka: pow(2, biasedexponent - bias-bits) where bias is 15 and bits is 9
1.0/(1<<24), 1.0/(1<<23), 1.0/(1<<22), 1.0/(1<<21), 1.0/(1<<20), 1.0/(1<<19), 1.0/(1<<18), 1.0/(1<<17),
1.0/(1<<16), 1.0/(1<<15), 1.0/(1<<14), 1.0/(1<<13), 1.0/(1<<12), 1.0/(1<<11), 1.0/(1<<10), 1.0/(1<<9),
1.0/(1<<8), 1.0/(1<<7), 1.0/(1<<6), 1.0/(1<<5), 1.0/(1<<4), 1.0/(1<<3), 1.0/(1<<2), 1.0/(1<<1),
1.0, 1.0*(1<<1), 1.0*(1<<2), 1.0*(1<<3), 1.0*(1<<4), 1.0*(1<<5), 1.0*(1<<6), 1.0*(1<<7),
};

extern cvar_t r_dynamic;
#define R_NoLighting() (r_dynamic.integer == 0)
Expand Down
81 changes: 57 additions & 24 deletions src/r_lightmaps.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,9 @@ static void R_AddDynamicLights(msurface_t *surf)
}

//Combine and scale multiple lightmaps into the 8.8 format in blocklights
static void R_BuildLightMap(msurface_t *surf, byte *dest, int stride)
static void R_BuildLightMap(msurface_t *surf, byte *dest, int stride, uint32_t flags)
{
int smax, tmax, i, j, size, blocksize, maps;
byte *lightmap;
unsigned scale, *bl;
qbool fullbright = false;

Expand All @@ -225,7 +224,6 @@ static void R_BuildLightMap(msurface_t *surf, byte *dest, int stride)
tmax = (surf->extents[1] >> surf->lmshift) + 1;
size = smax * tmax;
blocksize = size * 3;
lightmap = surf->samples;

// check for full bright or no light data
fullbright = (R_FullBrightAllowed() || !cl.worldmodel || !cl.worldmodel->lightdata);
Expand All @@ -241,16 +239,37 @@ static void R_BuildLightMap(msurface_t *surf, byte *dest, int stride)
}

// add all the lightmaps
for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) {
scale = d_lightstylevalue[surf->styles[maps]];
surf->cached_light[maps] = scale; // 8.8 fraction

if (!fullbright && lightmap) {
if (flags & MOD_HDRLIGHTING) {
uint32_t *lightmap = (uint32_t *)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
// it sucks that blocklights is an int array. we can still massively
// overbright though, just not underbright quite as accurately
// (still quite a bit more than rgb8 precision there).
bl = blocklights;
for (i = 0; i < blocksize; i++) {
*bl++ += lightmap[i] * scale;
for (i=0 ; i<size ; i++) {
uint32_t e5bgr9 = *lightmap++;
//we're converting to a scale that holds overbrights, so 1->128, its 2->255ish
float e = rgb9e5tab[e5bgr9>>27] * (1<<7) * scale;
*bl++ += e*((e5bgr9>> 0)&0x1ff); //red
*bl++ += e*((e5bgr9>> 9)&0x1ff); //green
*bl++ += e*((e5bgr9>>18)&0x1ff); //blue
}
}
} else {
byte *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

if (!fullbright && lightmap) {
bl = blocklights;
for (i = 0; i < blocksize; i++) {
*bl++ += lightmap[i] * scale;
}
lightmap += blocksize; // skip to next lightmap
}
lightmap += blocksize; // skip to next lightmap
}
}

Expand Down Expand Up @@ -392,7 +411,7 @@ void R_RenderDynamicLightmaps(msurface_t *fa, qbool world)
theRect->h = fa->light_t - theRect->t + tmax;
}
base = lm->rawdata + (fa->light_t * LIGHTMAP_WIDTH + fa->light_s) * 4;
R_BuildLightMap (fa, base, LIGHTMAP_WIDTH * 4);
R_BuildLightMap (fa, base, LIGHTMAP_WIDTH * 4, world ? cl.worldmodel->flags : 0);
}

void R_LightmapFrameInit(void)
Expand Down Expand Up @@ -749,7 +768,6 @@ static void R_BuildSurfaceDisplayList(model_t* currentmodel, msurface_t *fa)
static void R_BuildLightmapData(msurface_t* surf, int surfnum)
{
lightmap_data_t* lm = &lightmaps[surf->lightmaptexturenum];
byte* lightmap = surf->samples;
unsigned int smax = (surf->extents[0] >> surf->lmshift) + 1;
unsigned int tmax = (surf->extents[1] >> surf->lmshift) + 1;
unsigned int lightmap_flags;
Expand All @@ -775,20 +793,35 @@ static void R_BuildLightmapData(msurface_t* surf, int surfnum)
source[0] = source[1] = source[2] = 0;
source[3] = lightmap_flags;

if (lightmap) {
for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) {
size_t lightmap_index = (maps * smax * tmax + t * smax + s) * 3;

source[0] |= ((unsigned int)lightmap[lightmap_index + 0]) << (8 * maps);
source[1] |= ((unsigned int)lightmap[lightmap_index + 1]) << (8 * maps);
source[2] |= ((unsigned int)lightmap[lightmap_index + 2]) << (8 * maps);
if (surf->samples) {
if (surfnum != -1 && cl.worldmodel->flags & MOD_HDRLIGHTING) {
uint32_t* lightmap = (uint32_t *)surf->samples;
for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) {
size_t lightmap_index = (maps * smax * tmax + t * smax + s);
uint32_t e5bgr9 = lightmap[lightmap_index];
//we're converting to a scale that holds overbrights, so 1->128, its 2->255ish
float e = rgb9e5tab[e5bgr9>>27] * (1<<7);
// should not clamp here, but changing the light compute shader can be done later
source[0] += (unsigned int)bound(0, e*((e5bgr9>> 0)&0x1ff), 0xff) << (8 * maps);
source[1] += (unsigned int)bound(0, e*((e5bgr9>> 9)&0x1ff), 0xff) << (8 * maps);
source[2] += (unsigned int)bound(0, e*((e5bgr9>>18)&0x1ff), 0xff) << (8 * maps);
}
} else {
byte* lightmap = surf->samples;
for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) {
size_t lightmap_index = (maps * smax * tmax + t * smax + s) * 3;

source[0] |= ((unsigned int)lightmap[lightmap_index + 0]) << (8 * maps);
source[1] |= ((unsigned int)lightmap[lightmap_index + 1]) << (8 * maps);
source[2] |= ((unsigned int)lightmap[lightmap_index + 2]) << (8 * maps);
}
}
}
}
}
}

static void R_LightmapCreateForSurface(msurface_t *surf, int surfnum)
static void R_LightmapCreateForSurface(msurface_t *surf, int surfnum, uint32_t flags)
{
int smax, tmax;
byte *base;
Expand All @@ -811,7 +844,7 @@ static void R_LightmapCreateForSurface(msurface_t *surf, int surfnum)
base = lightmaps[surf->lightmaptexturenum].rawdata + (surf->light_t * LIGHTMAP_WIDTH + surf->light_s) * 4;
numdlights = 0;
R_BuildLightmapData(surf, surfnum);
R_BuildLightMap(surf, base, LIGHTMAP_WIDTH * 4);
R_BuildLightMap(surf, base, LIGHTMAP_WIDTH * 4, flags);
}

static int R_LightmapSurfaceSortFunction(const void* lhs_, const void* rhs_)
Expand Down Expand Up @@ -917,7 +950,7 @@ void R_BuildLightmaps(void)
qbool isTurb = (surfaces[i]->flags & SURF_DRAWTURB);

if (!isTurb) {
R_LightmapCreateForSurface(surfaces[i], m->isworldmodel ? surfaces[i]->surfacenum : -1);
R_LightmapCreateForSurface(surfaces[i], m->isworldmodel ? surfaces[i]->surfacenum : -1, m->flags);
}
R_BuildSurfaceDisplayList(m, surfaces[i]);
}
Expand All @@ -939,7 +972,7 @@ void R_BuildLightmaps(void)
}

if (!isTurb || !(m->surfaces[i].texinfo->flags & TEX_SPECIAL)) {
R_LightmapCreateForSurface(m->surfaces + i, m->isworldmodel ? m->surfaces[i].surfacenum : -1);
R_LightmapCreateForSurface(m->surfaces + i, m->isworldmodel ? m->surfaces[i].surfacenum : -1, m->flags);
}
R_BuildSurfaceDisplayList(m, m->surfaces + i);
}
Expand Down
32 changes: 22 additions & 10 deletions src/r_rlight.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,20 +321,32 @@ static void R_LightFromSurface(msurface_t* surf, int ds, int dt, vec3_t color)
{
if (surf->samples) {
// LordHavoc: enhanced to interpolate lighting
byte *lightmap;
int maps, line3, dsfrac = ds & 15, dtfrac = dt & 15, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
float scale;
line3 = ((surf->extents[0] >> 4) + 1) * 3;

lightmap = surf->samples + ((dt >> 4) * ((surf->extents[0] >> 4) + 1) + (ds >> 4)) * 3; // LordHavoc: *3 for color

for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) {
scale = (float)d_lightstylevalue[surf->styles[maps]] * 1.0 / 256.0;
r00 += (float)lightmap[0] * scale; g00 += (float)lightmap[1] * scale; b00 += (float)lightmap[2] * scale;
r01 += (float)lightmap[3] * scale; g01 += (float)lightmap[4] * scale; b01 += (float)lightmap[5] * scale;
r10 += (float)lightmap[line3 + 0] * scale; g10 += (float)lightmap[line3 + 1] * scale; b10 += (float)lightmap[line3 + 2] * scale;
r11 += (float)lightmap[line3 + 3] * scale; g11 += (float)lightmap[line3 + 4] * scale; b11 += (float)lightmap[line3 + 5] * scale;
lightmap += ((surf->extents[0] >> 4) + 1) * ((surf->extents[1] >> 4) + 1) * 3; // LordHavoc: *3 for colored lighting
if (cl.worldmodel->flags & MOD_HDRLIGHTING) {
uint32_t *lightmap = (uint32_t*)surf->samples + (dt * (surf->extents[0]+1) + ds);
line3 = (surf->extents[0]+1);
for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) {
float e;
scale = (1<<7) * (float) d_lightstylevalue[surf->styles[maps]] * 1.0f / 256.0f;
e = rgb9e5tab[lightmap[ 0]>>27] * scale;r00 += ((lightmap[ 0]>> 0)&0x1ff) * e;g00 += ((lightmap[ 0]>> 9)&0x1ff) * e;b00 += ((lightmap[ 0]>> 9)&0x1ff) * e;
e = rgb9e5tab[lightmap[ 1]>>27] * scale;r01 += ((lightmap[ 1]>> 0)&0x1ff) * e;g01 += ((lightmap[ 1]>> 9)&0x1ff) * e;b01 += ((lightmap[ 1]>> 9)&0x1ff) * e;
e = rgb9e5tab[lightmap[line3+0]>>27] * scale;r10 += ((lightmap[line3+0]>> 0)&0x1ff) * e;g10 += ((lightmap[line3+0]>> 9)&0x1ff) * e;b10 += ((lightmap[line3+0]>> 9)&0x1ff) * e;
e = rgb9e5tab[lightmap[line3+1]>>27] * scale;r11 += ((lightmap[line3+1]>> 0)&0x1ff) * e;g11 += ((lightmap[line3+1]>> 9)&0x1ff) * e;b11 += ((lightmap[line3+1]>> 9)&0x1ff) * e;
lightmap += (surf->extents[0]+1) * (surf->extents[1]+1);
}
} else {
byte *lightmap = surf->samples + ((dt >> 4) * ((surf->extents[0] >> 4) + 1) + (ds >> 4)) * 3; // LordHavoc: *3 for color
for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) {
scale = (float)d_lightstylevalue[surf->styles[maps]] * 1.0 / 256.0;
r00 += (float)lightmap[0] * scale; g00 += (float)lightmap[1] * scale; b00 += (float)lightmap[2] * scale;
r01 += (float)lightmap[3] * scale; g01 += (float)lightmap[4] * scale; b01 += (float)lightmap[5] * scale;
r10 += (float)lightmap[line3 + 0] * scale; g10 += (float)lightmap[line3 + 1] * scale; b10 += (float)lightmap[line3 + 2] * scale;
r11 += (float)lightmap[line3 + 3] * scale; g11 += (float)lightmap[line3 + 4] * scale; b11 += (float)lightmap[line3 + 5] * scale;
lightmap += ((surf->extents[0] >> 4) + 1) * ((surf->extents[1] >> 4) + 1) * 3; // LordHavoc: *3 for colored lighting
}
}

color[0] += (float)((int)((((((((r11 - r10) * dsfrac) >> 4) + r10) - ((((r01 - r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01 - r00) * dsfrac) >> 4) + r00)));
Expand Down

0 comments on commit 9adb697

Please sign in to comment.