From 8a0797e48466cc02cb053856f5543057e807175d Mon Sep 17 00:00:00 2001 From: Daniel Svensson Date: Tue, 22 Aug 2023 14:25:17 +0300 Subject: [PATCH] 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. --- src/common/header/files.h | 29 +++++- src/vk/header/model.h | 4 + src/vk/vk_light.c | 12 ++- src/vk/vk_model.c | 188 +++++++++++++++++++++++++++++++++----- src/vk/vk_rsurf.c | 4 +- 5 files changed, 201 insertions(+), 36 deletions(-) diff --git a/src/common/header/files.h b/src/common/header/files.h index b1afaab..2feb1b0 100644 --- a/src/common/header/files.h +++ b/src/common/header/files.h @@ -333,9 +333,10 @@ typedef struct m32tex_s /* .BSP file format */ #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 -/* upper design bounds: leaffaces, leafbrushes, planes, and +/* upper design bounds: leaffaces, leafbrushes, planes, and * verts are still bounded by 16 bit short limits */ #define MAX_MAP_MODELS 1024 #define MAX_MAP_BRUSHES 8192 @@ -399,6 +400,17 @@ typedef struct lump_t lumps[HEADER_LUMPS]; } 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 { float mins[3], maxs[3]; @@ -493,12 +505,12 @@ typedef struct texinfo_s { float vecs[2][4]; /* [s/t][xyz offset] */ int flags; /* miptex flags + overrides light emission, etc */ - int value; + int value; char texture[32]; /* texture name (textures*.wal) */ int nexttexinfo; /* for animations, -1 = end of chain */ } texinfo_t; -/* note that edge 0 is never used, because negative edge +/* note that edge 0 is never used, because negative edge nums are used for counterclockwise use of the edge in a face */ typedef struct @@ -521,6 +533,13 @@ typedef struct int lightofs; /* start of [numstyles*surfsize] samples */ } dface_t; +typedef struct { + unsigned short lmwidth; + unsigned short lmheight; + int lightofs; + float vecs[2][4]; +} dlminfo_t; + typedef struct { int contents; /* OR of all brushes (not needed?) */ @@ -554,8 +573,8 @@ typedef struct #define ANGLE_UP -1 #define ANGLE_DOWN -2 -/* the visibility lump consists of a header with a count, then - * byte offsets for the PVS and PHS of each cluster, then the raw +/* the visibility lump consists of a header with a count, then + * byte offsets for the PVS and PHS of each cluster, then the raw * compressed bit vectors */ #define DVIS_PVS 0 #define DVIS_PHS 1 diff --git a/src/vk/header/model.h b/src/vk/header/model.h index 115c368..9f49ea4 100644 --- a/src/vk/header/model.h +++ b/src/vk/header/model.h @@ -78,6 +78,10 @@ typedef struct msurface_s mtexinfo_t *texinfo; + /* decoupled lm */ + float lmvecs[2][4]; + float lmvlen[2]; + /* lighting info */ int dlightframe; int dlightbits; diff --git a/src/vk/vk_light.c b/src/vk/vk_light.c index b566365..390cb5c 100644 --- a/src/vk/vk_light.c +++ b/src/vk/vk_light.c @@ -367,14 +367,12 @@ R_AddDynamicLights(msurface_t *surf) int s, t; int i; int smax, tmax; - mtexinfo_t *tex; dlight_t *dl; float *plightdest; float fsacc, ftacc; smax = (surf->extents[0] >> surf->lmshift) + 1; tmax = (surf->extents[1] >> surf->lmshift) + 1; - tex = surf->texinfo; for (lnum=0 ; lnumplane->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]; + local[0] = DotProduct (impact, surf->lmvecs[0]) + surf->lmvecs[0][3] - surf->texturemins[0]; + local[1] = DotProduct (impact, surf->lmvecs[1]) + surf->lmvecs[1][3] - surf->texturemins[1]; plightdest = s_blocklights; for (t = 0, ftacc = 0; t < tmax; t++, ftacc += (1 << surf->lmshift)) { td = local[1] - ftacc; - if ( td < 0 ) + if (td < 0) td = -td; + td *= surf->lmvlen[1]; + for ( s=0, fsacc = 0 ; slmshift), plightdest += 3) { sd = Q_ftol( local[0] - fsacc ); @@ -421,6 +421,8 @@ R_AddDynamicLights(msurface_t *surf) if ( sd < 0 ) sd = -sd; + sd *= surf->lmvlen[0]; + if (sd > td) fdist = sd + (td>>1); else diff --git a/src/vk/vk_model.c b/src/vk/vk_model.c index ec63455..d17120b 100644 --- a/src/vk/vk_model.c +++ b/src/vk/vk_model.c @@ -375,17 +375,151 @@ calcTexinfoAndFacesSize(const lump_t *fl, const byte *mod_base, const lump_t *tl 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 ================= */ 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; - int i, count, surfnum; + dface_t *in; 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->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); 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->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 */ - for (i = 0; i < MAXLIGHTMAPS; i++) - { - out->styles[i] = in->styles[i]; + CalcSurfaceExtents(loadmodel, out); + + lightofs = in->lightofs; } - i = LittleLong(in->lightofs); - - if (i == -1) - { - out->samples = NULL; - } - else - { - out->samples = loadmodel->lightdata + i; - } + SetSurfaceLighting(loadmodel, out, in->styles, lightofs); /* set the drawing flags */ if (out->texinfo->flags & SURF_WARP) @@ -636,9 +771,10 @@ Mod_LoadBrushModel static void Mod_LoadBrushModel (model_t *mod, const void *buffer, int modfilelen) { - int i; - dheader_t *header; + bspx_header_t* bspx_header; byte *mod_base; + dheader_t *header; + int i; header = (dheader_t *)buffer; @@ -678,6 +814,10 @@ Mod_LoadBrushModel (model_t *mod, const void *buffer, int modfilelen) mod->extradata = Hunk_Begin(hunkSize); mod->type = mod_brush; + + /* check for BSPX extensions */ + bspx_header = Mod_LoadBSPX(modfilelen, (byte*)header); + /* load into heap */ Mod_LoadVertexes(mod->name, &mod->vertexes, &mod->numvertexes, mod_base, &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_base, &header->lumps[LUMP_SURFEDGES], 0); 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_LoadTexinfo(mod->name, &mod->texinfo, &mod->numtexinfo, mod_base, &header->lumps[LUMP_TEXINFO], (findimage_t)Vk_FindImage, 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_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_LoadNodes(mod->name, mod->planes, mod->numplanes, mod->leafs, mod->numleafs, &mod->nodes, &mod->numnodes, mod_base, diff --git a/src/vk/vk_rsurf.c b/src/vk/vk_rsurf.c index af31b18..76d433d 100644 --- a/src/vk/vk_rsurf.c +++ b/src/vk/vk_rsurf.c @@ -1172,13 +1172,13 @@ void Vk_BuildPolygonFromSurface(msurface_t *fa, model_t *currentmodel) // // 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->light_s * (1 << fa->lmshift); s += (1 << fa->lmshift) * 0.5; 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->light_t * (1 << fa->lmshift); t += (1 << fa->lmshift) * 0.5;