diff --git a/common/bspinfo.cc b/common/bspinfo.cc index 57bd070b..a1088a10 100644 --- a/common/bspinfo.cc +++ b/common/bspinfo.cc @@ -96,6 +96,33 @@ static json serialize_bspxbrushlist(const std::vector &lump) return j; } +static json serialize_bspx_decoupled_lm(const std::vector &lump) +{ + json j = json::array(); + + imemstream p(lump.data(), lump.size(), std::ios_base::in | std::ios_base::binary); + + p >> endianness; + + while (true) { + bspx_decoupled_lm_perface src_face; + p >= src_face; + + if (!p) { + break; + } + + json &model = j.insert(j.end(), json::object()).value(); + model["lmwidth"] = src_face.lmwidth; + model["lmheight"] = src_face.lmheight; + model["offset"] = src_face.offset; + model["world_to_lm_space"] = + json::array({src_face.world_to_lm_space.row(0), src_face.world_to_lm_space.row(1)}); + } + + return j; +} + /** * The MIT License (MIT) * Copyright (c) 2016 tomykaira @@ -191,8 +218,15 @@ static std::string serialize_image(const std::optional &texture_op #include "common/bsputils.hh" -static faceextents_t get_face_extents(const mbsp_t &bsp, const bspxentries_t &bspx, const mface_t &face, bool use_bspx) +static faceextents_t get_face_extents(const mbsp_t &bsp, const bspxentries_t &bspx, + const std::vector &bspx_decoupled, const mface_t &face, bool use_bspx, + bool use_decoupled) { + if (use_decoupled) { + ptrdiff_t face_idx = &face - bsp.dfaces.data(); + auto &bspx = bspx_decoupled[face_idx]; + return {face, bsp, bspx.lmwidth, bspx.lmheight, bspx.world_to_lm_space}; + } if (!use_bspx) { return {face, bsp, 16.0}; } @@ -201,7 +235,7 @@ static faceextents_t get_face_extents(const mbsp_t &bsp, const bspxentries_t &bs (float)nth_bit(reinterpret_cast(bspx.at("LMSHIFT").data())[&face - bsp.dfaces.data()])}; } -static void export_obj_and_lightmaps(const mbsp_t &bsp, const bspxentries_t &bspx, bool use_bspx, fs::path obj_path, fs::path lightmaps_path) +static void export_obj_and_lightmaps(const mbsp_t &bsp, const bspxentries_t &bspx, bool use_bspx, bool use_decoupled, fs::path obj_path, fs::path lightmaps_path) { struct face_rect { @@ -235,18 +269,38 @@ static void export_obj_and_lightmaps(const mbsp_t &bsp, const bspxentries_t &bsp bspx_lmoffset >> endianness; } + std::vector bspx_decoupled; + if (use_decoupled && (bspx.find("DECOUPLED_LM") != bspx.end())) { + bspx_decoupled.resize(bsp.dfaces.size()); + + imemstream stream(nullptr, 0); + + auto &decoupled_lm = bspx.at("DECOUPLED_LM"); + stream = imemstream(decoupled_lm.data(), decoupled_lm.size()); + stream >> endianness; + + for (size_t i = 0; i < bsp.dfaces.size(); ++i) { + stream >= bspx_decoupled[i]; + } + } else { + use_decoupled = false; + } + // make rectangles for (auto &face : bsp.dfaces) { + const ptrdiff_t face_idx = (&face - bsp.dfaces.data()); int32_t faceofs; - if (!use_bspx) { + if (use_decoupled) { + faceofs = bspx_decoupled[face_idx].offset; + } else if (!use_bspx) { faceofs = face.lightofs; } else { - bspx_lmoffset.seekg((&face - bsp.dfaces.data()) * sizeof(int32_t)); + bspx_lmoffset.seekg(face_idx * sizeof(int32_t)); bspx_lmoffset >= faceofs; } - rectangles.emplace_back(face_rect{&face, get_face_extents(bsp, bspx, face, use_bspx), faceofs}); + rectangles.emplace_back(face_rect{&face, get_face_extents(bsp, bspx, bspx_decoupled, face, use_bspx, use_decoupled), faceofs}); } if (!rectangles.size()) { @@ -684,6 +738,8 @@ void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const fs::path & if (lump.first == "BRUSHLIST") { entry["models"] = serialize_bspxbrushlist(lump.second); + } else if (lump.first == "DECOUPLED_LM") { + entry["faces"] = serialize_bspx_decoupled_lm(lump.second); } else { // unhandled BSPX lump, just write the raw data entry["lumpdata"] = hex_string(lump.second.data(), lump.second.size()); @@ -705,7 +761,7 @@ void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const fs::path & } } #endif - export_obj_and_lightmaps(bsp, bspdata.bspx.entries, false, fs::path(name).replace_extension(".geometry.obj"), fs::path(name).replace_extension(".lm.png")); + export_obj_and_lightmaps(bsp, bspdata.bspx.entries, false, true, fs::path(name).replace_extension(".geometry.obj"), fs::path(name).replace_extension(".lm.png")); std::ofstream(name, std::fstream::out | std::fstream::trunc) << std::setw(4) << j; } diff --git a/common/bsputils.cc b/common/bsputils.cc index 8a0a4a43..13aaeb95 100644 --- a/common/bsputils.cc +++ b/common/bsputils.cc @@ -745,6 +745,107 @@ faceextents_t::faceextents_t(const mface_t &face, const mbsp_t &bsp, float light worldToLMMatrix = TexCoordToLMMatrix * worldToTexCoordMatrix; } +faceextents_t::faceextents_t(const mface_t &face, const mbsp_t &bsp, uint16_t lmwidth, uint16_t lmheight, texvecf world_to_lm_space) { + if (lmwidth > 0 && lmheight > 0) { + lm_extents = {lmwidth - 1, lmheight - 1}; + } + + worldToTexCoordMatrix = WorldToTexSpace(&bsp, &face); + texCoordToWorldMatrix = TexSpaceToWorld(&bsp, &face); + + worldToLMMatrix.set_row(0, world_to_lm_space.row(0)); + worldToLMMatrix.set_row(1, world_to_lm_space.row(1)); + worldToLMMatrix.set_row(2, {0, 0, 1, 0}); + worldToLMMatrix.set_row(3, {0, 0, 0, 1}); + + lmToWorldMatrix = qv::inverse(worldToLMMatrix); + + // bounds + for (int i = 0; i < face.numedges; i++) { + const qvec3f &worldpoint = Face_PointAtIndex(&bsp, &face, i); + bounds += worldpoint; + } + + // calculate a bounding sphere for the face + qvec3d radius = (bounds.maxs() - bounds.mins()) * 0.5; + + origin = bounds.mins() + radius; + this->radius = qv::length(radius); +} + +faceextents_t::faceextents_t(const mface_t &face, const mbsp_t &bsp, world_units_per_luxel_t tag, float world_units_per_luxel) +{ + auto orig_normal = Face_Normal(&bsp, &face); + size_t axis = qv::indexOfLargestMagnitudeComponent(orig_normal); + +#if 0 + if (orig_normal == qvec3f(-1, 0, 0)) { + logging::print("-x\n"); + } +#endif + + qvec3f snapped_normal{}; + if (orig_normal[axis] > 0) { + snapped_normal[axis] = 1; + } else { + snapped_normal[axis] = -1; + } + + auto [t, b] = qv::MakeTangentAndBitangentUnnormalized(snapped_normal); + t = t * (1/world_units_per_luxel); + b = b * (1/world_units_per_luxel); + + qmat world_to_lm; + world_to_lm.set_row(0, t); + world_to_lm.set_row(1, b); + world_to_lm.set_row(2, snapped_normal); + + aabb2f lm_bounds; + for (int i = 0; i < face.numedges; i++) { + const qvec3f &worldpoint = Face_PointAtIndex(&bsp, &face, i); + const qvec2f lmcoord = world_to_lm * worldpoint; + lm_bounds += lmcoord; + } + + qvec2i lm_mins; + for (int i = 0; i < 2; i++) { + lm_bounds[0][i] = floor(lm_bounds[0][i]); + lm_bounds[1][i] = ceil(lm_bounds[1][i]); + lm_mins[i] = static_cast(lm_bounds[0][i]); + lm_extents[i] = static_cast(lm_bounds[1][i] - lm_bounds[0][i]); + } + + worldToLMMatrix.set_row(0, qvec4f(world_to_lm.row(0), -lm_mins[0])); + worldToLMMatrix.set_row(1, qvec4f(world_to_lm.row(1), -lm_mins[1])); + worldToLMMatrix.set_row(2, qvec4f(world_to_lm.row(2), 0)); + worldToLMMatrix.set_row(3, qvec4f(0, 0, 0, 1)); + + lmToWorldMatrix = qv::inverse(worldToLMMatrix); + + // world <-> tex conversions + worldToTexCoordMatrix = WorldToTexSpace(&bsp, &face); + texCoordToWorldMatrix = TexSpaceToWorld(&bsp, &face); + + // bounds + for (int i = 0; i < face.numedges; i++) { + const qvec3f &worldpoint = Face_PointAtIndex(&bsp, &face, i); + bounds += worldpoint; + + auto lm = worldToLMMatrix * qvec4f(worldpoint, 1.0f); +#if 0 + logging::print("testing world {} -> lm {}\n", + worldpoint, + lm); +#endif + } + + // calculate a bounding sphere for the face + qvec3d radius = (bounds.maxs() - bounds.mins()) * 0.5; + + origin = bounds.mins() + radius; + this->radius = qv::length(radius); +} + int faceextents_t::width() const { return lm_extents[0] + 1; diff --git a/common/bspxfile.cc b/common/bspxfile.cc index 50cc5a70..0f2b0636 100644 --- a/common/bspxfile.cc +++ b/common/bspxfile.cc @@ -70,3 +70,15 @@ void bspxbrushes_perbrush::stream_read(std::istream &s) { s >= std::tie(bounds, contents, numfaces); } + +// bspx_decoupled_lm_perface + +void bspx_decoupled_lm_perface::stream_write(std::ostream &s) const +{ + s <= std::tie(lmwidth, lmheight, offset, world_to_lm_space); +} + +void bspx_decoupled_lm_perface::stream_read(std::istream &s) +{ + s >= std::tie(lmwidth, lmheight, offset, world_to_lm_space); +} diff --git a/include/common/bsputils.hh b/include/common/bsputils.hh index c31968ce..f28cd115 100644 --- a/include/common/bsputils.hh +++ b/include/common/bsputils.hh @@ -120,16 +120,17 @@ qmat4x4f TexSpaceToWorld(const mbsp_t *bsp, const mface_t *f); * it doesn't affect bsp complexity (actually, can simplify it a little)*/ constexpr size_t MAXDIMENSION = 255 + 1; +struct world_units_per_luxel_t {}; + class faceextents_t { -private: +public: qvec2i lm_extents; qmat4x4f worldToTexCoordMatrix; qmat4x4f texCoordToWorldMatrix; qmat4x4f lmToWorldMatrix; qmat4x4f worldToLMMatrix; -public: qvec3d origin; vec_t radius; aabb3d bounds; @@ -137,6 +138,8 @@ public: faceextents_t() = default; faceextents_t(const mface_t &face, const mbsp_t &bsp, float lmshift); + faceextents_t(const mface_t &face, const mbsp_t &bsp, uint16_t lmwidth, uint16_t lmheight, texvecf world_to_lm_space); + faceextents_t(const mface_t &face, const mbsp_t &bsp, world_units_per_luxel_t tag, float world_units_per_luxel); int width() const; int height() const; diff --git a/include/common/bspxfile.hh b/include/common/bspxfile.hh index 3af2cb39..9c931031 100644 --- a/include/common/bspxfile.hh +++ b/include/common/bspxfile.hh @@ -24,6 +24,7 @@ #include #include #include +#include /* ========================================================================= */ @@ -98,4 +99,22 @@ struct bspxfacenormals_header uint32_t bitangent; }; +// DECOUPLED_LM BSPX lump (subject to change!) +struct bspx_decoupled_lm_perface +{ + uint16_t lmwidth; // pixels + uint16_t lmheight; // pixels + // offset into dlightdata lump. + // start of numstyles (from face struct) * (lmwidth * lmheight) samples + int32_t offset; + + // 2 rows * 4 column matrix, stored in row major order + // this is a world -> lightmap space transformation matrix + texvecf world_to_lm_space; + + // serialize for streams + void stream_write(std::ostream &s) const; + void stream_read(std::istream &s); +}; + // BSPX data diff --git a/include/light/light.hh b/include/light/light.hh index 2b902498..1382a352 100644 --- a/include/light/light.hh +++ b/include/light/light.hh @@ -370,6 +370,7 @@ public: setting_func bspxlux; setting_func bspxonly; setting_func bspx; + setting_scalar world_units_per_luxel; setting_bool litonly; setting_bool nolights; setting_int32 facestyles; diff --git a/include/light/ltface.hh b/include/light/ltface.hh index 52b920b1..70d8adf3 100644 --- a/include/light/ltface.hh +++ b/include/light/ltface.hh @@ -52,11 +52,12 @@ std::unordered_map GetDirectLighting( void SetupDirt(settings::worldspawn_keys &cfg); float DirtAtPoint(const settings::worldspawn_keys &cfg, raystream_intersection_t *rs, const qvec3d &point, const qvec3d &normal, const modelinfo_t *selfshadow); -std::unique_ptr CreateLightmapSurface( - const mbsp_t *bsp, const mface_t *face, const facesup_t *facesup, const settings::worldspawn_keys &cfg); +std::unique_ptr CreateLightmapSurface(const mbsp_t *bsp, const mface_t *face, const facesup_t *facesup, + const bspx_decoupled_lm_perface *facesup_decoupled, const settings::worldspawn_keys &cfg); bool Face_IsLightmapped(const mbsp_t *bsp, const mface_t *face); void DirectLightFace(const mbsp_t *bsp, lightsurf_t &lightsurf, const settings::worldspawn_keys &cfg); void IndirectLightFace(const mbsp_t *bsp, lightsurf_t &lightsurf, const settings::worldspawn_keys &cfg); void FinishLightmapSurface(const mbsp_t *bsp, lightsurf_t *lightsurf); -void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, lightsurf_t *lightsurf, - const faceextents_t &extents, const faceextents_t &output_extents); +void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, + bspx_decoupled_lm_perface *facesup_decoupled, lightsurf_t *lightsurf, const faceextents_t &extents, + const faceextents_t &output_extents); diff --git a/light/light.cc b/light/light.cc index cc9d8104..45704665 100644 --- a/light/light.cc +++ b/light/light.cc @@ -63,6 +63,7 @@ std::vector> &LightSurfaces() } static std::vector faces_sup; // lit2/bspx stuff +static std::vector facesup_decoupled_global; bool IsOutputtingSupplementaryData() { @@ -296,6 +297,7 @@ light_settings::light_settings() write_luxfile = lightfile::bspx; }, &experimental_group, "writes both rgb and directions data into the bsp itself"}, + world_units_per_luxel{this, "world_units_per_luxel", 0, 0, 1024, &output_group, "enables output of DECOUPLED_LM BSPX lump"}, litonly{this, "litonly", false, &output_group, "only write .lit file, don't modify BSP"}, nolights{this, "nolights", false, &output_group, "ignore light entities (only sunlight/minlight)"}, facestyles{this, "facestyles", 4, &output_group, "max amount of styles per face; requires BSPX lump if > 4"}, @@ -619,6 +621,7 @@ static void CreateLightmapSurfaces(mbsp_t *bsp) logging::funcheader(); logging::parallel_for(static_cast(0), bsp->dfaces.size(), [&bsp](size_t i) { auto facesup = faces_sup.empty() ? nullptr : &faces_sup[i]; + auto facesup_decoupled = facesup_decoupled_global.empty() ? nullptr : &facesup_decoupled_global[i]; auto face = &bsp->dfaces[i]; /* One extra lightmap is allocated to simplify handling overflow */ @@ -636,10 +639,14 @@ static void CreateLightmapSurfaces(mbsp_t *bsp) for (size_t i = 0; i < MAXLIGHTMAPS; i++) { face->styles[i] = INVALID_LIGHTSTYLE_OLD; } + + if (facesup_decoupled) { + facesup_decoupled->offset = -1; + } } } - light_surfaces[i] = CreateLightmapSurface(bsp, face, facesup, light_options); + light_surfaces[i] = CreateLightmapSurface(bsp, face, facesup, facesup_decoupled, light_options); }); } @@ -658,22 +665,24 @@ static void SaveLightmapSurfaces(mbsp_t *bsp) auto f = &bsp->dfaces[i]; const modelinfo_t *face_modelinfo = ModelInfoForFace(bsp, i); - if (faces_sup.empty()) { - SaveLightmapSurface(bsp, f, nullptr, surf.get(), surf->extents, surf->extents); + if (!facesup_decoupled_global.empty()) { + SaveLightmapSurface(bsp, f, nullptr, &facesup_decoupled_global[i], surf.get(), surf->extents, surf->extents); + } else if (faces_sup.empty()) { + SaveLightmapSurface(bsp, f, nullptr, nullptr, surf.get(), surf->extents, surf->extents); } else if (light_options.novanilla.value() || faces_sup[i].lmscale == face_modelinfo->lightmapscale) { if (faces_sup[i].lmscale == face_modelinfo->lightmapscale) { f->lightofs = faces_sup[i].lightofs; } else { f->lightofs = -1; } - SaveLightmapSurface(bsp, f, &faces_sup[i], surf.get(), surf->extents, surf->extents); + SaveLightmapSurface(bsp, f, &faces_sup[i], nullptr, surf.get(), surf->extents, surf->extents); for (int j = 0; j < MAXLIGHTMAPS; j++) { f->styles[j] = faces_sup[i].styles[j] == INVALID_LIGHTSTYLE ? INVALID_LIGHTSTYLE_OLD : faces_sup[i].styles[j]; } } else { - SaveLightmapSurface(bsp, f, nullptr, surf.get(), surf->extents, surf->vanilla_extents); - SaveLightmapSurface(bsp, f, &faces_sup[i], surf.get(), surf->extents, surf->extents); + SaveLightmapSurface(bsp, f, nullptr, nullptr, surf.get(), surf->extents, surf->vanilla_extents); + SaveLightmapSurface(bsp, f, &faces_sup[i], nullptr, surf.get(), surf->extents, surf->extents); } light_surfaces[i].reset(); @@ -831,6 +840,12 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale) } } + // decoupled lightmaps + facesup_decoupled_global.clear(); + if (light_options.world_units_per_luxel.isChanged()) { + facesup_decoupled_global.resize(bsp.dfaces.size()); + } + CalculateVertexNormals(&bsp); // create lightmap surfaces @@ -896,6 +911,7 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale) bspdata->bspx.entries.erase("LMSTYLE16"); bspdata->bspx.entries.erase("LMSTYLE"); bspdata->bspx.entries.erase("LMOFFSET"); + bspdata->bspx.entries.erase("DECOUPLED_LM"); if (!faces_sup.empty()) { bool needoffsets = false; @@ -978,6 +994,20 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale) bspdata->bspx.transfer("LMOFFSET", offsets_mem); } } + + if (!facesup_decoupled_global.empty()) { + std::vector mem(sizeof(bspx_decoupled_lm_perface) * bsp.dfaces.size()); + + omemstream stream(mem.data(), mem.size(), std::ios_base::out | std::ios_base::binary); + stream << endianness; + + for (size_t i = 0; i < bsp.dfaces.size(); i++) { + stream <= facesup_decoupled_global[i]; + } + + logging::print("DECOUPLED_LM BSPX lump written\n"); + bspdata->bspx.transfer("DECOUPLED_LM", mem); + } } static void LoadExtendedTexinfoFlags(const fs::path &sourcefilename, const mbsp_t *bsp) diff --git a/light/ltface.cc b/light/ltface.cc index 9b435951..7844a3ad 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -413,8 +413,7 @@ static void CalcPoints( const vec_t us = starts + s * st_step; const vec_t ut = startt + t * st_step; - point = surf->extents.LMCoordToWorld(qvec2f(us, ut)) + - surf->plane.normal; // one unit in front of face + point = surf->extents.LMCoordToWorld(qvec2f(us, ut)) + surf->plane.normal; // one unit in front of face // do this before correcting the point, so we can wrap around the inside of pipes const bool phongshaded = (surf->curved && cfg.phongallowed.value()); @@ -587,7 +586,8 @@ static void CalcPvs(const mbsp_t *bsp, lightsurf_t *lightsurf) } static std::unique_ptr Lightsurf_Init(const modelinfo_t *modelinfo, const settings::worldspawn_keys &cfg, - const mface_t *face, const mbsp_t *bsp, const facesup_t *facesup) + const mface_t *face, const mbsp_t *bsp, const facesup_t *facesup, + const bspx_decoupled_lm_perface *facesup_decoupled) { auto spaceToWorld = TexSpaceToWorld(bsp, face); @@ -697,7 +697,12 @@ static std::unique_ptr Lightsurf_Init(const modelinfo_t *modelinfo, lightsurf->tnormal = -qv::normalize(tex->vecs.row(1).xyz()); /* Set up the surface points */ - lightsurf->extents = faceextents_t(*face, *bsp, lightsurf->lightmapscale); + if (light_options.world_units_per_luxel.isChanged()) { + lightsurf->extents = faceextents_t(*face, *bsp, world_units_per_luxel_t{}, + light_options.world_units_per_luxel.value()); + } else { + lightsurf->extents = faceextents_t(*face, *bsp, lightsurf->lightmapscale); + } lightsurf->vanilla_extents = faceextents_t(*face, *bsp, 16.0); CalcPoints(modelinfo, modelinfo->offset, lightsurf.get(), bsp, face); @@ -2528,8 +2533,9 @@ static void WriteSingleLightmap(const mbsp_t *bsp, const mface_t *face, const li } } -void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, lightsurf_t *lightsurf, - const faceextents_t &extents, const faceextents_t &output_extents) +void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, + bspx_decoupled_lm_perface *facesup_decoupled, lightsurf_t *lightsurf, const faceextents_t &extents, + const faceextents_t &output_extents) { lightmapdict_t &lightmaps = lightsurf->lightmapsByStyle; const int actual_width = extents.width(); @@ -2693,6 +2699,15 @@ void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, l for (; mapnum < MAXLIGHTMAPS; mapnum++) { face->styles[mapnum] = INVALID_LIGHTSTYLE_OLD; } + + if (facesup_decoupled) { + facesup_decoupled->lmwidth = output_width; + facesup_decoupled->lmheight = output_height; + for (size_t i = 0; i < 2; ++i) { + facesup_decoupled->world_to_lm_space.set_row(i, + output_extents.worldToLMMatrix.row(i)); + } + } } if (!numstyles) @@ -2714,6 +2729,9 @@ void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, l facesup->lightofs = lightofs; } else { face->lightofs = lightofs; + if (facesup_decoupled) { + facesup_decoupled->offset = lightofs; + } } // sanity check that we don't save a lightmap for a non-lightmapped face @@ -2741,8 +2759,8 @@ void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, l } } -std::unique_ptr CreateLightmapSurface( - const mbsp_t *bsp, const mface_t *face, const facesup_t *facesup, const settings::worldspawn_keys &cfg) +std::unique_ptr CreateLightmapSurface(const mbsp_t *bsp, const mface_t *face, const facesup_t *facesup, + const bspx_decoupled_lm_perface *facesup_decoupled, const settings::worldspawn_keys &cfg) { /* Find the correct model offset */ const modelinfo_t *modelinfo = ModelInfoForFace(bsp, Face_GetNum(bsp, face)); @@ -2767,7 +2785,7 @@ std::unique_ptr CreateLightmapSurface( if (!Q_strcasecmp(texname, "skip")) return nullptr; - return Lightsurf_Init(modelinfo, cfg, face, bsp, facesup); + return Lightsurf_Init(modelinfo, cfg, face, bsp, facesup, facesup_decoupled); } /* diff --git a/tests/test_ltface.cc b/tests/test_ltface.cc index 403df549..b81f0ce9 100644 --- a/tests/test_ltface.cc +++ b/tests/test_ltface.cc @@ -21,9 +21,6 @@ static void LoadTestmap(const std::filesystem::path &name, std::vector light_args{ "", // the exe path, which we're ignoring in this case - "-extra", - bsp_path.string() }; + for (auto &arg : extra_args) { + light_args.push_back(arg); + } + light_args.push_back(bsp_path.string()); + light_main(light_args); } @@ -56,5 +56,5 @@ static void LoadTestmap(const std::filesystem::path &name, std::vector