light: wip -world_units_per_luxel and DECOUPLED_LM BSPX lump

This commit is contained in:
Eric Wasylishen 2022-11-02 09:57:44 -06:00
parent 8b524cf324
commit ca0efbf3d3
10 changed files with 274 additions and 33 deletions

View File

@ -96,6 +96,33 @@ static json serialize_bspxbrushlist(const std::vector<uint8_t> &lump)
return j; return j;
} }
static json serialize_bspx_decoupled_lm(const std::vector<uint8_t> &lump)
{
json j = json::array();
imemstream p(lump.data(), lump.size(), std::ios_base::in | std::ios_base::binary);
p >> endianness<std::endian::little>;
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) * The MIT License (MIT)
* Copyright (c) 2016 tomykaira * Copyright (c) 2016 tomykaira
@ -191,8 +218,15 @@ static std::string serialize_image(const std::optional<img::texture> &texture_op
#include "common/bsputils.hh" #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_lm_perface> &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) { if (!use_bspx) {
return {face, bsp, 16.0}; 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<const char *>(bspx.at("LMSHIFT").data())[&face - bsp.dfaces.data()])}; (float)nth_bit(reinterpret_cast<const char *>(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 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::endian::little>; bspx_lmoffset >> endianness<std::endian::little>;
} }
std::vector<bspx_decoupled_lm_perface> 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<std::endian::little>;
for (size_t i = 0; i < bsp.dfaces.size(); ++i) {
stream >= bspx_decoupled[i];
}
} else {
use_decoupled = false;
}
// make rectangles // make rectangles
for (auto &face : bsp.dfaces) { for (auto &face : bsp.dfaces) {
const ptrdiff_t face_idx = (&face - bsp.dfaces.data());
int32_t faceofs; int32_t faceofs;
if (!use_bspx) { if (use_decoupled) {
faceofs = bspx_decoupled[face_idx].offset;
} else if (!use_bspx) {
faceofs = face.lightofs; faceofs = face.lightofs;
} else { } else {
bspx_lmoffset.seekg((&face - bsp.dfaces.data()) * sizeof(int32_t)); bspx_lmoffset.seekg(face_idx * sizeof(int32_t));
bspx_lmoffset >= faceofs; 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()) { 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") { if (lump.first == "BRUSHLIST") {
entry["models"] = serialize_bspxbrushlist(lump.second); entry["models"] = serialize_bspxbrushlist(lump.second);
} else if (lump.first == "DECOUPLED_LM") {
entry["faces"] = serialize_bspx_decoupled_lm(lump.second);
} else { } else {
// unhandled BSPX lump, just write the raw data // unhandled BSPX lump, just write the raw data
entry["lumpdata"] = hex_string(lump.second.data(), lump.second.size()); 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 #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; std::ofstream(name, std::fstream::out | std::fstream::trunc) << std::setw(4) << j;
} }

View File

@ -745,6 +745,107 @@ faceextents_t::faceextents_t(const mface_t &face, const mbsp_t &bsp, float light
worldToLMMatrix = TexCoordToLMMatrix * worldToTexCoordMatrix; 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<float, 3, 3> 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<int>(lm_bounds[0][i]);
lm_extents[i] = static_cast<int>(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 int faceextents_t::width() const
{ {
return lm_extents[0] + 1; return lm_extents[0] + 1;

View File

@ -70,3 +70,15 @@ void bspxbrushes_perbrush::stream_read(std::istream &s)
{ {
s >= std::tie(bounds, contents, numfaces); 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);
}

View File

@ -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)*/ * it doesn't affect bsp complexity (actually, can simplify it a little)*/
constexpr size_t MAXDIMENSION = 255 + 1; constexpr size_t MAXDIMENSION = 255 + 1;
struct world_units_per_luxel_t {};
class faceextents_t class faceextents_t
{ {
private: public:
qvec2i lm_extents; qvec2i lm_extents;
qmat4x4f worldToTexCoordMatrix; qmat4x4f worldToTexCoordMatrix;
qmat4x4f texCoordToWorldMatrix; qmat4x4f texCoordToWorldMatrix;
qmat4x4f lmToWorldMatrix; qmat4x4f lmToWorldMatrix;
qmat4x4f worldToLMMatrix; qmat4x4f worldToLMMatrix;
public:
qvec3d origin; qvec3d origin;
vec_t radius; vec_t radius;
aabb3d bounds; aabb3d bounds;
@ -137,6 +138,8 @@ public:
faceextents_t() = default; 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, 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 width() const;
int height() const; int height() const;

View File

@ -24,6 +24,7 @@
#include <cstdint> #include <cstdint>
#include <common/aabb.hh> #include <common/aabb.hh>
#include <memory> #include <memory>
#include <common/bspfile.hh>
/* ========================================================================= */ /* ========================================================================= */
@ -98,4 +99,22 @@ struct bspxfacenormals_header
uint32_t bitangent; 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 // BSPX data

View File

@ -370,6 +370,7 @@ public:
setting_func bspxlux; setting_func bspxlux;
setting_func bspxonly; setting_func bspxonly;
setting_func bspx; setting_func bspx;
setting_scalar world_units_per_luxel;
setting_bool litonly; setting_bool litonly;
setting_bool nolights; setting_bool nolights;
setting_int32 facestyles; setting_int32 facestyles;

View File

@ -52,11 +52,12 @@ std::unordered_map<int, qvec3f> GetDirectLighting(
void SetupDirt(settings::worldspawn_keys &cfg); void SetupDirt(settings::worldspawn_keys &cfg);
float DirtAtPoint(const settings::worldspawn_keys &cfg, raystream_intersection_t *rs, const qvec3d &point, float DirtAtPoint(const settings::worldspawn_keys &cfg, raystream_intersection_t *rs, const qvec3d &point,
const qvec3d &normal, const modelinfo_t *selfshadow); const qvec3d &normal, const modelinfo_t *selfshadow);
std::unique_ptr<lightsurf_t> CreateLightmapSurface( std::unique_ptr<lightsurf_t> CreateLightmapSurface(const mbsp_t *bsp, const mface_t *face, const facesup_t *facesup,
const mbsp_t *bsp, const mface_t *face, const facesup_t *facesup, const settings::worldspawn_keys &cfg); const bspx_decoupled_lm_perface *facesup_decoupled, const settings::worldspawn_keys &cfg);
bool Face_IsLightmapped(const mbsp_t *bsp, const mface_t *face); 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 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 IndirectLightFace(const mbsp_t *bsp, lightsurf_t &lightsurf, const settings::worldspawn_keys &cfg);
void FinishLightmapSurface(const mbsp_t *bsp, lightsurf_t *lightsurf); void FinishLightmapSurface(const mbsp_t *bsp, lightsurf_t *lightsurf);
void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, lightsurf_t *lightsurf, void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup,
const faceextents_t &extents, const faceextents_t &output_extents); bspx_decoupled_lm_perface *facesup_decoupled, lightsurf_t *lightsurf, const faceextents_t &extents,
const faceextents_t &output_extents);

View File

@ -63,6 +63,7 @@ std::vector<std::unique_ptr<lightsurf_t>> &LightSurfaces()
} }
static std::vector<facesup_t> faces_sup; // lit2/bspx stuff static std::vector<facesup_t> faces_sup; // lit2/bspx stuff
static std::vector<bspx_decoupled_lm_perface> facesup_decoupled_global;
bool IsOutputtingSupplementaryData() bool IsOutputtingSupplementaryData()
{ {
@ -296,6 +297,7 @@ light_settings::light_settings()
write_luxfile = lightfile::bspx; write_luxfile = lightfile::bspx;
}, },
&experimental_group, "writes both rgb and directions data into the bsp itself"}, &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"}, 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)"}, 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"}, 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::funcheader();
logging::parallel_for(static_cast<size_t>(0), bsp->dfaces.size(), [&bsp](size_t i) { logging::parallel_for(static_cast<size_t>(0), bsp->dfaces.size(), [&bsp](size_t i) {
auto facesup = faces_sup.empty() ? nullptr : &faces_sup[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]; auto face = &bsp->dfaces[i];
/* One extra lightmap is allocated to simplify handling overflow */ /* 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++) { for (size_t i = 0; i < MAXLIGHTMAPS; i++) {
face->styles[i] = INVALID_LIGHTSTYLE_OLD; 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]; auto f = &bsp->dfaces[i];
const modelinfo_t *face_modelinfo = ModelInfoForFace(bsp, i); const modelinfo_t *face_modelinfo = ModelInfoForFace(bsp, i);
if (faces_sup.empty()) { if (!facesup_decoupled_global.empty()) {
SaveLightmapSurface(bsp, f, nullptr, surf.get(), surf->extents, surf->extents); 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) { } else if (light_options.novanilla.value() || faces_sup[i].lmscale == face_modelinfo->lightmapscale) {
if (faces_sup[i].lmscale == face_modelinfo->lightmapscale) { if (faces_sup[i].lmscale == face_modelinfo->lightmapscale) {
f->lightofs = faces_sup[i].lightofs; f->lightofs = faces_sup[i].lightofs;
} else { } else {
f->lightofs = -1; 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++) { for (int j = 0; j < MAXLIGHTMAPS; j++) {
f->styles[j] = f->styles[j] =
faces_sup[i].styles[j] == INVALID_LIGHTSTYLE ? INVALID_LIGHTSTYLE_OLD : faces_sup[i].styles[j]; faces_sup[i].styles[j] == INVALID_LIGHTSTYLE ? INVALID_LIGHTSTYLE_OLD : faces_sup[i].styles[j];
} }
} else { } else {
SaveLightmapSurface(bsp, f, nullptr, surf.get(), surf->extents, surf->vanilla_extents); SaveLightmapSurface(bsp, f, nullptr, nullptr, surf.get(), surf->extents, surf->vanilla_extents);
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);
} }
light_surfaces[i].reset(); 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); CalculateVertexNormals(&bsp);
// create lightmap surfaces // create lightmap surfaces
@ -896,6 +911,7 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale)
bspdata->bspx.entries.erase("LMSTYLE16"); bspdata->bspx.entries.erase("LMSTYLE16");
bspdata->bspx.entries.erase("LMSTYLE"); bspdata->bspx.entries.erase("LMSTYLE");
bspdata->bspx.entries.erase("LMOFFSET"); bspdata->bspx.entries.erase("LMOFFSET");
bspdata->bspx.entries.erase("DECOUPLED_LM");
if (!faces_sup.empty()) { if (!faces_sup.empty()) {
bool needoffsets = false; bool needoffsets = false;
@ -978,6 +994,20 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale)
bspdata->bspx.transfer("LMOFFSET", offsets_mem); bspdata->bspx.transfer("LMOFFSET", offsets_mem);
} }
} }
if (!facesup_decoupled_global.empty()) {
std::vector<uint8_t> 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<std::endian::little>;
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) static void LoadExtendedTexinfoFlags(const fs::path &sourcefilename, const mbsp_t *bsp)

View File

@ -413,8 +413,7 @@ static void CalcPoints(
const vec_t us = starts + s * st_step; const vec_t us = starts + s * st_step;
const vec_t ut = startt + t * st_step; const vec_t ut = startt + t * st_step;
point = surf->extents.LMCoordToWorld(qvec2f(us, ut)) + point = surf->extents.LMCoordToWorld(qvec2f(us, ut)) + surf->plane.normal; // one unit in front of face
surf->plane.normal; // one unit in front of face
// do this before correcting the point, so we can wrap around the inside of pipes // do this before correcting the point, so we can wrap around the inside of pipes
const bool phongshaded = (surf->curved && cfg.phongallowed.value()); 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_t> Lightsurf_Init(const modelinfo_t *modelinfo, const settings::worldspawn_keys &cfg, static std::unique_ptr<lightsurf_t> 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); auto spaceToWorld = TexSpaceToWorld(bsp, face);
@ -697,7 +697,12 @@ static std::unique_ptr<lightsurf_t> Lightsurf_Init(const modelinfo_t *modelinfo,
lightsurf->tnormal = -qv::normalize(tex->vecs.row(1).xyz()); lightsurf->tnormal = -qv::normalize(tex->vecs.row(1).xyz());
/* Set up the surface points */ /* 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); lightsurf->vanilla_extents = faceextents_t(*face, *bsp, 16.0);
CalcPoints(modelinfo, modelinfo->offset, lightsurf.get(), bsp, face); 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, void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup,
const faceextents_t &extents, const faceextents_t &output_extents) bspx_decoupled_lm_perface *facesup_decoupled, lightsurf_t *lightsurf, const faceextents_t &extents,
const faceextents_t &output_extents)
{ {
lightmapdict_t &lightmaps = lightsurf->lightmapsByStyle; lightmapdict_t &lightmaps = lightsurf->lightmapsByStyle;
const int actual_width = extents.width(); 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++) { for (; mapnum < MAXLIGHTMAPS; mapnum++) {
face->styles[mapnum] = INVALID_LIGHTSTYLE_OLD; 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) if (!numstyles)
@ -2714,6 +2729,9 @@ void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, l
facesup->lightofs = lightofs; facesup->lightofs = lightofs;
} else { } else {
face->lightofs = lightofs; face->lightofs = lightofs;
if (facesup_decoupled) {
facesup_decoupled->offset = lightofs;
}
} }
// sanity check that we don't save a lightmap for a non-lightmapped face // 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<lightsurf_t> CreateLightmapSurface( std::unique_ptr<lightsurf_t> CreateLightmapSurface(const mbsp_t *bsp, const mface_t *face, const facesup_t *facesup,
const mbsp_t *bsp, const mface_t *face, const facesup_t *facesup, const settings::worldspawn_keys &cfg) const bspx_decoupled_lm_perface *facesup_decoupled, const settings::worldspawn_keys &cfg)
{ {
/* Find the correct model offset */ /* Find the correct model offset */
const modelinfo_t *modelinfo = ModelInfoForFace(bsp, Face_GetNum(bsp, face)); const modelinfo_t *modelinfo = ModelInfoForFace(bsp, Face_GetNum(bsp, face));
@ -2767,7 +2785,7 @@ std::unique_ptr<lightsurf_t> CreateLightmapSurface(
if (!Q_strcasecmp(texname, "skip")) if (!Q_strcasecmp(texname, "skip"))
return nullptr; return nullptr;
return Lightsurf_Init(modelinfo, cfg, face, bsp, facesup); return Lightsurf_Init(modelinfo, cfg, face, bsp, facesup, facesup_decoupled);
} }
/* /*

View File

@ -21,9 +21,6 @@ static void LoadTestmap(const std::filesystem::path &name, std::vector<std::stri
"-path", "-path",
wal_metadata_path.string() wal_metadata_path.string()
}; };
for (auto &arg : extra_args) {
args.push_back(arg);
}
args.push_back(map_path.string()); args.push_back(map_path.string());
args.push_back(bsp_path.string()); args.push_back(bsp_path.string());
@ -36,9 +33,12 @@ static void LoadTestmap(const std::filesystem::path &name, std::vector<std::stri
{ {
std::vector<std::string> light_args{ std::vector<std::string> light_args{
"", // the exe path, which we're ignoring in this case "", // 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); light_main(light_args);
} }
@ -56,5 +56,5 @@ static void LoadTestmap(const std::filesystem::path &name, std::vector<std::stri
} }
TEST_CASE("TestLight") { TEST_CASE("TestLight") {
LoadTestmap("q2_lightmap_custom_scale.map", {}); LoadTestmap("q2_lightmap_custom_scale.map", {"-threads", "1", "-extra", "-world_units_per_luxel", "8"});
} }