LIGHTMAPS: Add support for DECOUPLEDLM.

Decouples texture coordinates from lightmap coordinates to allow for
higher resolution lightmaps, and also avoids lightmap misalignment when
shadows span surfaces with differing texture scale.
This commit is contained in:
Daniel Svensson 2023-08-22 14:25:17 +03:00 committed by Denis Pauk
parent 366c0efc0f
commit 8a0797e484
5 changed files with 201 additions and 36 deletions

View file

@ -333,6 +333,7 @@ typedef struct m32tex_s
/* .BSP file format */ /* .BSP file format */
#define IDBSPHEADER (('P' << 24) + ('S' << 16) + ('B' << 8) + 'I') /* little-endian "IBSP" */ #define IDBSPHEADER (('P' << 24) + ('S' << 16) + ('B' << 8) + 'I') /* little-endian "IBSP" */
#define BSPXHEADER (('X' << 24) + ('P' << 16) + ('S' << 8) + 'B') /* little-endian "BSPX" */
#define BSPVERSION 38 #define BSPVERSION 38
/* upper design bounds: leaffaces, leafbrushes, planes, and /* upper design bounds: leaffaces, leafbrushes, planes, and
@ -399,6 +400,17 @@ typedef struct
lump_t lumps[HEADER_LUMPS]; lump_t lumps[HEADER_LUMPS];
} dheader_t; } dheader_t;
typedef struct bspx_header_s {
int ident; // 'BSPX'
int numlumps;
} bspx_header_t;
typedef struct {
char lumpname[24];
int fileofs;
int filelen;
} bspx_lump_t;
typedef struct typedef struct
{ {
float mins[3], maxs[3]; float mins[3], maxs[3];
@ -521,6 +533,13 @@ typedef struct
int lightofs; /* start of [numstyles*surfsize] samples */ int lightofs; /* start of [numstyles*surfsize] samples */
} dface_t; } dface_t;
typedef struct {
unsigned short lmwidth;
unsigned short lmheight;
int lightofs;
float vecs[2][4];
} dlminfo_t;
typedef struct typedef struct
{ {
int contents; /* OR of all brushes (not needed?) */ int contents; /* OR of all brushes (not needed?) */

View file

@ -78,6 +78,10 @@ typedef struct msurface_s
mtexinfo_t *texinfo; mtexinfo_t *texinfo;
/* decoupled lm */
float lmvecs[2][4];
float lmvlen[2];
/* lighting info */ /* lighting info */
int dlightframe; int dlightframe;
int dlightbits; int dlightbits;

View file

@ -367,14 +367,12 @@ R_AddDynamicLights(msurface_t *surf)
int s, t; int s, t;
int i; int i;
int smax, tmax; int smax, tmax;
mtexinfo_t *tex;
dlight_t *dl; dlight_t *dl;
float *plightdest; float *plightdest;
float fsacc, ftacc; float fsacc, ftacc;
smax = (surf->extents[0] >> surf->lmshift) + 1; smax = (surf->extents[0] >> surf->lmshift) + 1;
tmax = (surf->extents[1] >> surf->lmshift) + 1; tmax = (surf->extents[1] >> surf->lmshift) + 1;
tex = surf->texinfo;
for (lnum=0 ; lnum<r_newrefdef.num_dlights ; lnum++) for (lnum=0 ; lnum<r_newrefdef.num_dlights ; lnum++)
{ {
@ -404,16 +402,18 @@ R_AddDynamicLights(msurface_t *surf)
surf->plane->normal[i]*fdist; surf->plane->normal[i]*fdist;
} }
local[0] = DotProduct(impact, tex->vecs[0]) + tex->vecs[0][3] - surf->texturemins[0]; local[0] = DotProduct (impact, surf->lmvecs[0]) + surf->lmvecs[0][3] - surf->texturemins[0];
local[1] = DotProduct(impact, tex->vecs[1]) + tex->vecs[1][3] - surf->texturemins[1]; local[1] = DotProduct (impact, surf->lmvecs[1]) + surf->lmvecs[1][3] - surf->texturemins[1];
plightdest = s_blocklights; plightdest = s_blocklights;
for (t = 0, ftacc = 0; t < tmax; t++, ftacc += (1 << surf->lmshift)) for (t = 0, ftacc = 0; t < tmax; t++, ftacc += (1 << surf->lmshift))
{ {
td = local[1] - ftacc; td = local[1] - ftacc;
if ( td < 0 ) if (td < 0)
td = -td; td = -td;
td *= surf->lmvlen[1];
for ( s=0, fsacc = 0 ; s<smax ; s++, fsacc += (1 << surf->lmshift), plightdest += 3) for ( s=0, fsacc = 0 ; s<smax ; s++, fsacc += (1 << surf->lmshift), plightdest += 3)
{ {
sd = Q_ftol( local[0] - fsacc ); sd = Q_ftol( local[0] - fsacc );
@ -421,6 +421,8 @@ R_AddDynamicLights(msurface_t *surf)
if ( sd < 0 ) if ( sd < 0 )
sd = -sd; sd = -sd;
sd *= surf->lmvlen[0];
if (sd > td) if (sd > td)
fdist = sd + (td>>1); fdist = sd + (td>>1);
else else

View file

@ -375,17 +375,151 @@ calcTexinfoAndFacesSize(const lump_t *fl, const byte *mod_base, const lump_t *tl
return ret; return ret;
} }
// Extension to support lightmaps that aren't tied to texture scale.
static int
Mod_LoadDecoupledLM(const dlminfo_t* lminfos, int surfnum, msurface_t *out)
{
const dlminfo_t *lminfo;
unsigned short lmwidth, lmheight;
if (lminfos == NULL) {
return -1;
}
lminfo = lminfos + surfnum;
lmwidth = LittleShort(lminfo->lmwidth);
lmheight = LittleShort(lminfo->lmheight);
if (lmwidth <= 0 || lmheight <= 0) {
return -1;
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 4; j++) {
out->lmvecs[i][j] = LittleFloat(lminfo->vecs[i][j]);
}
}
out->extents[0] = (short)(lmwidth - 1);
out->extents[1] = (short)(lmheight - 1);
out->lmshift = 0;
out->texturemins[0] = 0;
out->texturemins[1] = 0;
float v0 = VectorLength(out->lmvecs[0]);
out->lmvlen[0] = v0 > 0.0f ? 1.0f / v0 : 0.0f;
float v1 = VectorLength(out->lmvecs[1]);
out->lmvlen[1] = v1 > 0.0f ? 1.0f / v1 : 0.0f;
return LittleLong(lminfo->lightofs);
}
const void *
Mod_BSPX_FindLump(bspx_header_t* bspx_header, char* lumpname, int* plumpsize, const byte* mod_base)
{
int i;
bspx_lump_t* lump;
if (!bspx_header) {
return NULL;
}
lump = (bspx_lump_t*)(bspx_header + 1);
for (i = 0; i < bspx_header->numlumps; i++, lump++) {
if (!strcmp(lump->lumpname, lumpname)) {
if (plumpsize) {
*plumpsize = lump->filelen;
}
return mod_base + lump->fileofs;
}
}
return NULL;
}
bspx_header_t *
Mod_LoadBSPX(int filesize, byte* mod_base)
{
dheader_t* header;
bspx_header_t* xheader;
bspx_lump_t* lump;
int i;
int xofs;
// find end of last lump
header = (dheader_t*)mod_base;
xofs = 0;
for (i = 0; i < HEADER_LUMPS; i++) {
xofs = max(xofs, header->lumps[i].fileofs + header->lumps[i].filelen);
}
if (xofs + sizeof(bspx_header_t) > filesize) {
return NULL;
}
xheader = (bspx_header_t*)(mod_base + xofs);
if (LittleLong(xheader->ident) != BSPXHEADER)
{
return NULL;
}
xheader->numlumps = LittleLong(xheader->numlumps);
if (xheader->numlumps < 0 || xofs + sizeof(bspx_header_t) + xheader->numlumps * sizeof(bspx_lump_t) > filesize) {
return NULL;
}
// byte-swap and check sanity
lump = (bspx_lump_t*)(xheader + 1); // lumps immediately follow the header
for (i = 0; i < xheader->numlumps; i++, lump++) {
lump->lumpname[sizeof(lump->lumpname) - 1] = '\0'; // make sure it ends with zero
lump->fileofs = LittleLong(lump->fileofs);
lump->filelen = LittleLong(lump->filelen);
if (lump->fileofs < 0 || lump->filelen < 0 || (unsigned)(lump->fileofs + lump->filelen) >(unsigned)filesize) {
return NULL;
}
}
// success
return xheader;
}
static void
SetSurfaceLighting(model_t* loadmodel, msurface_t* out, byte* styles, int lightofs)
{
int i;
/* lighting info */
for (i = 0; i < MAXLIGHTMAPS; i++)
{
out->styles[i] = styles[i];
}
i = LittleLong(lightofs);
if (i == -1 || loadmodel->lightdata == NULL)
{
out->samples = NULL;
}
else
{
out->samples = loadmodel->lightdata + i;
}
}
/* /*
================= =================
Mod_LoadFaces Mod_LoadFaces
================= =================
*/ */
static void static void
Mod_LoadFaces (model_t *loadmodel, const byte *mod_base, const lump_t *l) Mod_LoadFaces(model_t *loadmodel, const byte *mod_base, const lump_t *l, bspx_header_t *bspx_header)
{ {
dface_t *in; int i, count, surfnum, lminfosize, lightofs;
const dlminfo_t *lminfos;
msurface_t *out; msurface_t *out;
int i, count, surfnum; dface_t *in;
in = (void *)(mod_base + l->fileofs); in = (void *)(mod_base + l->fileofs);
@ -401,6 +535,13 @@ Mod_LoadFaces (model_t *loadmodel, const byte *mod_base, const lump_t *l)
loadmodel->surfaces = out; loadmodel->surfaces = out;
loadmodel->numsurfaces = count; loadmodel->numsurfaces = count;
lminfos = Mod_BSPX_FindLump(bspx_header, "DECOUPLED_LM", &lminfosize, mod_base);
if (lminfos != NULL && lminfosize / sizeof(dlminfo_t) != loadmodel->numsurfaces) {
R_Printf(PRINT_ALL, "%s: [%s] decoupled_lm size %ld does not match surface count %d\n",
__func__, loadmodel->name, lminfosize / sizeof(dlminfo_t), loadmodel->numsurfaces);
lminfos = NULL;
}
Vk_BeginBuildingLightmaps(loadmodel); Vk_BeginBuildingLightmaps(loadmodel);
for (surfnum = 0; surfnum < count; surfnum++, in++, out++) for (surfnum = 0; surfnum < count; surfnum++, in++, out++)
@ -443,26 +584,20 @@ Mod_LoadFaces (model_t *loadmodel, const byte *mod_base, const lump_t *l)
} }
out->texinfo = loadmodel->texinfo + ti; out->texinfo = loadmodel->texinfo + ti;
out->lmshift = DEFAULT_LMSHIFT;
CalcSurfaceExtents(loadmodel, out); lightofs = Mod_LoadDecoupledLM(lminfos, surfnum, out);
if (lightofs < 0) {
memcpy(out->lmvecs, out->texinfo->vecs, sizeof(out->lmvecs));
out->lmshift = DEFAULT_LMSHIFT;
out->lmvlen[0] = 1.0f;
out->lmvlen[1] = 1.0f;
/* lighting info */ CalcSurfaceExtents(loadmodel, out);
for (i = 0; i < MAXLIGHTMAPS; i++)
{ lightofs = in->lightofs;
out->styles[i] = in->styles[i];
} }
i = LittleLong(in->lightofs); SetSurfaceLighting(loadmodel, out, in->styles, lightofs);
if (i == -1)
{
out->samples = NULL;
}
else
{
out->samples = loadmodel->lightdata + i;
}
/* set the drawing flags */ /* set the drawing flags */
if (out->texinfo->flags & SURF_WARP) if (out->texinfo->flags & SURF_WARP)
@ -636,9 +771,10 @@ Mod_LoadBrushModel
static void static void
Mod_LoadBrushModel (model_t *mod, const void *buffer, int modfilelen) Mod_LoadBrushModel (model_t *mod, const void *buffer, int modfilelen)
{ {
int i; bspx_header_t* bspx_header;
dheader_t *header;
byte *mod_base; byte *mod_base;
dheader_t *header;
int i;
header = (dheader_t *)buffer; header = (dheader_t *)buffer;
@ -678,6 +814,10 @@ Mod_LoadBrushModel (model_t *mod, const void *buffer, int modfilelen)
mod->extradata = Hunk_Begin(hunkSize); mod->extradata = Hunk_Begin(hunkSize);
mod->type = mod_brush; mod->type = mod_brush;
/* check for BSPX extensions */
bspx_header = Mod_LoadBSPX(modfilelen, (byte*)header);
/* load into heap */ /* load into heap */
Mod_LoadVertexes(mod->name, &mod->vertexes, &mod->numvertexes, mod_base, Mod_LoadVertexes(mod->name, &mod->vertexes, &mod->numvertexes, mod_base,
&header->lumps[LUMP_VERTEXES], 0); &header->lumps[LUMP_VERTEXES], 0);
@ -686,14 +826,14 @@ Mod_LoadBrushModel (model_t *mod, const void *buffer, int modfilelen)
Mod_LoadSurfedges(mod->name, &mod->surfedges, &mod->numsurfedges, Mod_LoadSurfedges(mod->name, &mod->surfedges, &mod->numsurfedges,
mod_base, &header->lumps[LUMP_SURFEDGES], 0); mod_base, &header->lumps[LUMP_SURFEDGES], 0);
Mod_LoadLighting(&mod->lightdata, mod_base, &header->lumps[LUMP_LIGHTING]); Mod_LoadLighting(&mod->lightdata, mod_base, &header->lumps[LUMP_LIGHTING]);
Mod_LoadPlanes (mod->name, &mod->planes, &mod->numplanes, Mod_LoadPlanes(mod->name, &mod->planes, &mod->numplanes,
mod_base, &header->lumps[LUMP_PLANES], 0); mod_base, &header->lumps[LUMP_PLANES], 0);
Mod_LoadTexinfo(mod->name, &mod->texinfo, &mod->numtexinfo, Mod_LoadTexinfo(mod->name, &mod->texinfo, &mod->numtexinfo,
mod_base, &header->lumps[LUMP_TEXINFO], (findimage_t)Vk_FindImage, mod_base, &header->lumps[LUMP_TEXINFO], (findimage_t)Vk_FindImage,
r_notexture, 0); r_notexture, 0);
Mod_LoadFaces(mod, mod_base, &header->lumps[LUMP_FACES]); Mod_LoadFaces(mod, mod_base, &header->lumps[LUMP_FACES], bspx_header);
Mod_LoadMarksurfaces(mod, mod_base, &header->lumps[LUMP_LEAFFACES]); Mod_LoadMarksurfaces(mod, mod_base, &header->lumps[LUMP_LEAFFACES]);
Mod_LoadVisibility (&mod->vis, mod_base, &header->lumps[LUMP_VISIBILITY]); Mod_LoadVisibility(&mod->vis, mod_base, &header->lumps[LUMP_VISIBILITY]);
Mod_LoadLeafs(mod, mod_base, &header->lumps[LUMP_LEAFS]); Mod_LoadLeafs(mod, mod_base, &header->lumps[LUMP_LEAFS]);
Mod_LoadNodes(mod->name, mod->planes, mod->numplanes, mod->leafs, Mod_LoadNodes(mod->name, mod->planes, mod->numplanes, mod->leafs,
mod->numleafs, &mod->nodes, &mod->numnodes, mod_base, mod->numleafs, &mod->nodes, &mod->numnodes, mod_base,

View file

@ -1172,13 +1172,13 @@ void Vk_BuildPolygonFromSurface(msurface_t *fa, model_t *currentmodel)
// //
// lightmap texture coordinates // lightmap texture coordinates
// //
s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s = DotProduct(vec, fa->lmvecs[0]) + fa->lmvecs[0][3];
s -= fa->texturemins[0]; s -= fa->texturemins[0];
s += fa->light_s * (1 << fa->lmshift); s += fa->light_s * (1 << fa->lmshift);
s += (1 << fa->lmshift) * 0.5; s += (1 << fa->lmshift) * 0.5;
s /= BLOCK_WIDTH * (1 << fa->lmshift); s /= BLOCK_WIDTH * (1 << fa->lmshift);
t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t = DotProduct(vec, fa->lmvecs[1]) + fa->lmvecs[1][3];
t -= fa->texturemins[1]; t -= fa->texturemins[1];
t += fa->light_t * (1 << fa->lmshift); t += fa->light_t * (1 << fa->lmshift);
t += (1 << fa->lmshift) * 0.5; t += (1 << fa->lmshift) * 0.5;