diff --git a/include/common/bspfile_generic.hh b/include/common/bspfile_generic.hh index bcb052e4..382560a2 100644 --- a/include/common/bspfile_generic.hh +++ b/include/common/bspfile_generic.hh @@ -399,6 +399,7 @@ struct mtexinfo_t }; constexpr size_t MAXLIGHTMAPS = 4; +constexpr uint16_t INVALID_LIGHTSTYLE_OLD = 0xffu; struct mface_t { diff --git a/include/light/entities.hh b/include/light/entities.hh index 5e775640..efb0ea5c 100644 --- a/include/light/entities.hh +++ b/include/light/entities.hh @@ -76,7 +76,7 @@ public: {{"linear", LF_LINEAR}, {"inverse", LF_INVERSE}, {"inverse2", LF_INVERSE2}, {"infinite", LF_INFINITE}, {"localmin", LF_LOCALMIN}, {"inverse2a", LF_INVERSE2A}}}; settings::setting_scalar spotangle{this, "angle", 40.0}; settings::setting_scalar spotangle2{this, "softangle", 0.0}; - settings::setting_numeric style{this, "style", 0, 0, 254}; + settings::setting_numeric style{this, "style", 0, 0, INVALID_LIGHTSTYLE - 1}; settings::setting_scalar anglescale{this, {"anglesense", "anglescale"}, -1.0}; // fallback to worldspawn settings::setting_scalar dirtscale{this, "dirtscale", 0.0}; settings::setting_scalar dirtgain{this, "dirtgain", 0}; diff --git a/include/light/light.hh b/include/light/light.hh index a3e745ae..12f392d2 100644 --- a/include/light/light.hh +++ b/include/light/light.hh @@ -450,6 +450,7 @@ public: &experimental_group, "writes both rgb and directions data into the bsp itself"}; setting_bool litonly{this, "litonly", false, &output_group, "only write .lit file, don't modify BSP"}; setting_bool nolights{this, "nolights", false, &output_group, "ignore light entities (only sunlight/minlight)"}; + setting_int32 facestyles{this, "facestyles", 4, &output_group, "max amount of styles per face; requires BSPX lump"}; inline void CheckNoDebugModeSet() { diff --git a/include/light/litfile.hh b/include/light/litfile.hh index 30130996..ab22b4be 100644 --- a/include/light/litfile.hh +++ b/include/light/litfile.hh @@ -41,13 +41,16 @@ struct litheader_t } v2; }; +constexpr size_t MAXLIGHTMAPSSUP = 16; +constexpr uint16_t INVALID_LIGHTSTYLE = 0xffffu; + /* internal representation for bspx/lit2 */ struct facesup_t { float lmscale; - uint8_t styles[MAXLIGHTMAPS]; /* scaled styles */ + uint16_t styles[MAXLIGHTMAPSSUP]; /* scaled styles */ int32_t lightofs; /* scaled lighting */ - unsigned short extent[2]; + uint16_t extent[2]; }; void WriteLitFile(const mbsp_t *bsp, facesup_t *facesup, const fs::path &filename, int version); diff --git a/light/light.cc b/light/light.cc index 65265241..234f9acb 100644 --- a/light/light.cc +++ b/light/light.cc @@ -167,6 +167,11 @@ void light_settings::postinitialize(int argc, const char **argv) options.nolighting.setValueLocked(true); } } + + // upgrade to uint16 if facestyles is specified + if (options.facestyles.value() > MAXLIGHTMAPS && !options.compilerstyle_max.isChanged()) { + options.compilerstyle_max.setValue(INVALID_LIGHTSTYLE); + } common_settings::postinitialize(argc, argv); } @@ -318,13 +323,13 @@ static void LightThread(const mbsp_t *bsp, size_t facenum) LightFace(bsp, f, nullptr, options); else if (options.novanilla.value()) { f->lightofs = -1; - f->styles[0] = 255; + f->styles[0] = INVALID_LIGHTSTYLE_OLD; LightFace(bsp, f, faces_sup + facenum, options); } else if (faces_sup[facenum].lmscale == face_modelinfo->lightmapscale) { - LightFace(bsp, f, nullptr, options); - faces_sup[facenum].lightofs = f->lightofs; + LightFace(bsp, f, faces_sup + facenum, options); + f->lightofs = faces_sup[facenum].lightofs; for (int i = 0; i < MAXLIGHTMAPS; i++) - faces_sup[facenum].styles[i] = f->styles[i]; + f->styles[i] = faces_sup[facenum].styles[i]; } else { LightFace(bsp, f, nullptr, options); LightFace(bsp, f, faces_sup + facenum, options); @@ -449,7 +454,7 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale) auto lmshift_lump = bspdata->bspx.entries.find("LMSHIFT"); - if (lmshift_lump == bspdata->bspx.entries.end() && options.write_litfile != lightfile::lit2) + if (lmshift_lump == bspdata->bspx.entries.end() && options.write_litfile != lightfile::lit2 && options.facestyles.value() <= 4) faces_sup = nullptr; // no scales, no lit2 else { // we have scales or lit2 output. yay... faces_sup = new facesup_t[bsp.dfaces.size()]{}; @@ -518,21 +523,77 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale) logging::print("lightdatasize: {}\n", bsp.dlightdata.size()); // kill this stuff if its somehow found. + bspdata->bspx.entries.erase("LMSTYLE16"); bspdata->bspx.entries.erase("LMSTYLE"); bspdata->bspx.entries.erase("LMOFFSET"); if (faces_sup) { - std::vector styles(4 * bsp.dfaces.size()); - std::vector offsets_mem(bsp.dfaces.size() * sizeof(int32_t)); - omemstream offsets(offsets_mem.data(), std::ios_base::out | std::ios_base::binary); - offsets << endianness; + bool needoffsets = false; + bool needstyles = false; + int maxstyle = 0; + int stylesperface = 0; + for (int i = 0; i < bsp.dfaces.size(); i++) { - offsets <= faces_sup[i].lightofs; - for (int j = 0; j < MAXLIGHTMAPS; j++) - styles[i * 4 + j] = faces_sup[i].styles[j]; + if (bsp.dfaces[i].lightofs != faces_sup[i].lightofs) + needoffsets = true; + int j = 0; + for (; j < MAXLIGHTMAPSSUP; j++) { + if (faces_sup[i].styles[j] == INVALID_LIGHTSTYLE) + break; + if (bsp.dfaces[i].styles[j] != faces_sup[i].styles[j]) + needstyles = true; + if (maxstyle < faces_sup[i].styles[j]) + maxstyle = faces_sup[i].styles[j]; + } + if (stylesperface < j) + stylesperface = j; + } + + needstyles |= (stylesperface>4); + + logging::print("max {} styles per face{}\n", stylesperface, maxstyle >= INVALID_LIGHTSTYLE_OLD ? ", 16bit lightstyles" : ""); + + if (needstyles) { + if (maxstyle >= INVALID_LIGHTSTYLE_OLD) { + /*needs bigger datatype*/ + std::vector styles_mem(sizeof(uint16_t) * stylesperface * bsp.dfaces.size()); + + omemstream styles(styles_mem.data(), std::ios_base::out | std::ios_base::binary); + styles << endianness; + + for (size_t i = 0; i < bsp.dfaces.size(); i++) { + for (size_t j = 0; j < stylesperface; j++) { + styles <= faces_sup[i].styles[j]; + } + } + + bspdata->bspx.transfer("LMSTYLE16", styles_mem); + } else { + /*original LMSTYLE lump was just for different lmshift info*/ + std::vector styles_mem(stylesperface * bsp.dfaces.size()); + + for (size_t i = 0, k = 0; i < bsp.dfaces.size(); i++) { + for (size_t j = 0; j < stylesperface; j++, k++) { + styles_mem[k] = faces_sup[i].styles[j]; + } + } + + bspdata->bspx.transfer("LMSTYLE", styles_mem); + } + } + + if (needoffsets) { + std::vector offsets_mem(bsp.dfaces.size() * sizeof(int32_t)); + + omemstream offsets(offsets_mem.data(), std::ios_base::out | std::ios_base::binary); + offsets << endianness; + + for (size_t i = 0; i < bsp.dfaces.size(); i++) { + offsets <= faces_sup[i].lightofs; + } + + bspdata->bspx.transfer("LMOFFSET", offsets_mem); } - bspdata->bspx.transfer("LMSTYLE", styles); - bspdata->bspx.transfer("LMOFFSET", offsets_mem); } } diff --git a/light/ltface.cc b/light/ltface.cc index 572598b2..0130e918 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -1014,7 +1014,7 @@ static lightmap_t *Lightmap_ForStyle(lightmapdict_t *lightmaps, const int style, // no exact match, check for an unsaved one for (auto &lm : *lightmaps) { - if (lm.style == 255) { + if (lm.style == INVALID_LIGHTSTYLE) { Lightmap_AllocOrClear(&lm, lightsurf); return &lm; } @@ -1022,7 +1022,7 @@ static lightmap_t *Lightmap_ForStyle(lightmapdict_t *lightmaps, const int style, // add a new one to the vector (invalidates existing lightmap_t pointers) lightmap_t &newLightmap = lightmaps->emplace_back(); - newLightmap.style = 255; + newLightmap.style = INVALID_LIGHTSTYLE; Lightmap_AllocOrClear(&newLightmap, lightsurf); return &newLightmap; } @@ -1030,7 +1030,7 @@ static lightmap_t *Lightmap_ForStyle(lightmapdict_t *lightmaps, const int style, static void Lightmap_ClearAll(lightmapdict_t *lightmaps) { for (auto &lm : *lightmaps) { - lm.style = 255; + lm.style = INVALID_LIGHTSTYLE; } } @@ -1043,7 +1043,7 @@ static void Lightmap_ClearAll(lightmapdict_t *lightmaps) static void Lightmap_Save( lightmapdict_t *lightmaps, const lightsurf_t *lightsurf, lightmap_t *lightmap, const int style) { - if (lightmap->style == 255) { + if (lightmap->style == INVALID_LIGHTSTYLE) { lightmap->style = style; } } @@ -3151,13 +3151,26 @@ static void WriteLightmaps( return; } + int maxfstyles = facesup ? MAXLIGHTMAPSSUP : MAXLIGHTMAPS; + if (maxfstyles > options.facestyles.value()) { + maxfstyles = options.facestyles.value(); //truncate it a little + } + int maxstyle = facesup ? INVALID_LIGHTSTYLE : INVALID_LIGHTSTYLE_OLD; + // intermediate collection for sorting lightmaps std::vector> sortable; for (const lightmap_t &lightmap : *lightmaps) { // skip un-saved lightmaps - if (lightmap.style == 255) + if (lightmap.style == INVALID_LIGHTSTYLE) continue; + if (lightmap.style > maxstyle) { + logging::print("WARNING: Style {} too high\n" + " lightmap point near {}\n", + lightmap.style, + lightsurf->points[0]); + continue; + } // skip lightmaps where all samples have brightness below 1 if (bsp->loadversion->game->id != GAME_QUAKE_II) { // HACK: don't do this on Q2. seems if all styles are 0xff, @@ -3177,7 +3190,7 @@ static void WriteLightmaps( std::vector sorted; for (const auto &pair : sortable) { - if (sorted.size() == MAXLIGHTMAPS) { + if (sorted.size() == maxfstyles) { logging::print("WARNING: Too many light styles on a face\n" " lightmap point near [{}]\n", lightsurf->points[0]); @@ -3189,7 +3202,7 @@ static void WriteLightmaps( /* final number of lightmaps */ const int numstyles = static_cast(sorted.size()); - Q_assert(numstyles <= MAXLIGHTMAPS); + Q_assert(numstyles <= MAXLIGHTMAPSSUP); /* update face info (either core data or supplementary stuff) */ if (facesup) { @@ -3199,8 +3212,8 @@ static void WriteLightmaps( for (mapnum = 0; mapnum < numstyles; mapnum++) { facesup->styles[mapnum] = sorted.at(mapnum)->style; } - for (; mapnum < MAXLIGHTMAPS; mapnum++) { - facesup->styles[mapnum] = 255; + for (; mapnum < MAXLIGHTMAPSSUP; mapnum++) { + facesup->styles[mapnum] = INVALID_LIGHTSTYLE; } facesup->lmscale = lightsurf->lightmapscale; } else { @@ -3209,7 +3222,7 @@ static void WriteLightmaps( face->styles[mapnum] = sorted.at(mapnum)->style; } for (; mapnum < MAXLIGHTMAPS; mapnum++) { - face->styles[mapnum] = 255; + face->styles[mapnum] = INVALID_LIGHTSTYLE_OLD; } } @@ -3376,12 +3389,12 @@ void LightFace(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, const setti /* some surfaces don't need lightmaps */ if (facesup) { facesup->lightofs = -1; - for (int i = 0; i < MAXLIGHTMAPS; i++) - facesup->styles[i] = 255; + for (int i = 0; i < MAXLIGHTMAPSSUP; i++) + facesup->styles[i] = INVALID_LIGHTSTYLE; } else { face->lightofs = -1; for (int i = 0; i < MAXLIGHTMAPS; i++) - face->styles[i] = 255; + face->styles[i] = INVALID_LIGHTSTYLE_OLD; } } diff --git a/light/surflight.cc b/light/surflight.cc index 9a385318..677de38c 100644 --- a/light/surflight.cc +++ b/light/surflight.cc @@ -86,7 +86,7 @@ static void MakeSurfaceLightsThread(const mbsp_t *bsp, const settings::worldspaw style = surflight->epairs->get_int("style"); if (surflight->color.isChanged()) { - texture_color = surflight->color.value(); + texture_color = surflight->color.value() / 255.f; } break; }