memory on writes
This commit is contained in:
parent
0942b9eb0c
commit
3356b5776e
|
|
@ -429,24 +429,20 @@ public:
|
|||
|
||||
extern settings::light_settings light_options;
|
||||
|
||||
extern std::vector<uint8_t> filebase;
|
||||
extern std::vector<uint8_t> lit_filebase;
|
||||
extern std::vector<uint8_t> lux_filebase;
|
||||
|
||||
const std::unordered_map<int, std::vector<uint8_t>> &UncompressedVis();
|
||||
|
||||
bool IsOutputtingSupplementaryData();
|
||||
|
||||
std::span<lightsurf_t, std::dynamic_extent> &LightSurfaces();
|
||||
std::span<lightsurf_t> &LightSurfaces();
|
||||
std::vector<lightsurf_t*> &EmissiveLightSurfaces();
|
||||
|
||||
extern std::vector<surfflags_t> extended_texinfo_flags;
|
||||
|
||||
lightmap_t *Lightmap_ForStyle(lightmapdict_t *lightmaps, const int style, const lightsurf_t *lightsurf);
|
||||
|
||||
// public functions
|
||||
|
||||
void FixupGlobalSettings();
|
||||
void GetFileSpace(uint8_t **lightdata, uint8_t **colordata, uint8_t **deluxdata, int size);
|
||||
void GetFileSpace_PreserveOffsetInBsp(uint8_t **lightdata, uint8_t **colordata, uint8_t **deluxdata, int lightofs);
|
||||
const modelinfo_t *ModelInfoForModel(const mbsp_t *bsp, int modelnum);
|
||||
/**
|
||||
* returns nullptr for "skip" faces
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ extern std::atomic<uint32_t> total_light_rays, total_light_ray_hits, total_sampl
|
|||
extern std::atomic<uint32_t> total_bounce_rays, total_bounce_ray_hits;
|
||||
extern std::atomic<uint32_t> total_surflight_rays, total_surflight_ray_hits; // mxd
|
||||
#endif
|
||||
extern std::atomic<uint32_t> fully_transparent_lightmaps;
|
||||
extern std::atomic<uint32_t> fully_transparent_lightmaps; // write.cc
|
||||
|
||||
void PrintFaceInfo(const mface_t *face, const mbsp_t *bsp);
|
||||
// FIXME: remove light param. add normal param and dir params.
|
||||
|
|
@ -55,10 +55,6 @@ bool Face_IsEmissive(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, size_t bounce_depth);
|
||||
void PostProcessLightFace(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,
|
||||
bspx_decoupled_lm_perface *facesup_decoupled, lightsurf_t *lightsurf, const faceextents_t &extents,
|
||||
const faceextents_t &output_extents);
|
||||
|
||||
struct lightgrid_sample_t
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <common/fs.hh>
|
||||
|
||||
struct mbsp_t;
|
||||
struct bspdata_t;
|
||||
|
||||
constexpr int32_t LIT_VERSION = 1;
|
||||
|
||||
|
|
@ -66,5 +67,7 @@ struct facesup_t
|
|||
twosided<uint16_t> extent;
|
||||
};
|
||||
|
||||
void WriteLitFile(const mbsp_t *bsp, const std::vector<facesup_t> &facesup, const fs::path &filename, int version);
|
||||
void WriteLuxFile(const mbsp_t *bsp, const fs::path &filename, int version);
|
||||
void WriteLitFile(const mbsp_t *bsp, const std::vector<facesup_t> &facesup, const fs::path &filename, int version, const std::vector<uint8_t> &lit_filebase, const std::vector<uint8_t> &lux_filebase);
|
||||
void WriteLuxFile(const mbsp_t *bsp, const fs::path &filename, int version, const std::vector<uint8_t> &lux_filebase);
|
||||
|
||||
void SaveLightmapSurfaces(bspdata_t *bspdata, const fs::path &source);
|
||||
|
|
@ -10,11 +10,10 @@ set(LIGHT_INCLUDES
|
|||
../include/light/surflight.hh
|
||||
../include/light/ltface.hh
|
||||
../include/light/trace.hh
|
||||
../include/light/litfile.hh)
|
||||
../include/light/write.hh)
|
||||
|
||||
set(LIGHT_SOURCES
|
||||
entities.cc
|
||||
litfile.cc
|
||||
ltface.cc
|
||||
trace.cc
|
||||
light.cc
|
||||
|
|
@ -22,6 +21,7 @@ set(LIGHT_SOURCES
|
|||
phong.cc
|
||||
bounce.cc
|
||||
surflight.cc
|
||||
write.cc
|
||||
${LIGHT_INCLUDES})
|
||||
|
||||
if (embree_FOUND)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include <common/cmdlib.hh>
|
||||
#include <common/parser.hh>
|
||||
|
||||
#include <light/litfile.hh>
|
||||
#include <light/write.hh>
|
||||
#include <light/trace.hh>
|
||||
#include <light/trace_embree.hh>
|
||||
#include <light/light.hh>
|
||||
|
|
|
|||
227
light/light.cc
227
light/light.cc
|
|
@ -29,7 +29,7 @@
|
|||
#include <light/surflight.hh> //mxd
|
||||
#include <light/entities.hh>
|
||||
#include <light/ltface.hh>
|
||||
#include <light/litfile.hh> // for facesup_t
|
||||
#include <light/write.hh> // for facesup_t
|
||||
#include <light/trace_embree.hh>
|
||||
|
||||
#include <common/log.hh>
|
||||
|
|
@ -65,7 +65,7 @@ static std::span<lightsurf_t> light_surfaces_span;
|
|||
// light_surfaces filtered down to just the emissive ones
|
||||
static std::vector<lightsurf_t*> emissive_light_surfaces;
|
||||
|
||||
std::span<lightsurf_t, std::dynamic_extent> &LightSurfaces()
|
||||
std::span<lightsurf_t> &LightSurfaces()
|
||||
{
|
||||
return light_surfaces_span;
|
||||
}
|
||||
|
|
@ -86,35 +86,14 @@ static void UpdateEmissiveLightSurfacesList()
|
|||
}
|
||||
}
|
||||
|
||||
static std::vector<facesup_t> faces_sup; // lit2/bspx stuff
|
||||
static std::vector<bspx_decoupled_lm_perface> facesup_decoupled_global;
|
||||
std::vector<facesup_t> faces_sup; // lit2/bspx stuff
|
||||
std::vector<bspx_decoupled_lm_perface> facesup_decoupled_global;
|
||||
|
||||
bool IsOutputtingSupplementaryData()
|
||||
{
|
||||
return !faces_sup.empty();
|
||||
}
|
||||
|
||||
/// start of lightmap data
|
||||
std::vector<uint8_t> filebase;
|
||||
/// offset of start of free space after data (should be kept a multiple of 4)
|
||||
static int file_p;
|
||||
/// offset of end of free space for lightmap data
|
||||
static int file_end;
|
||||
|
||||
/// start of litfile data
|
||||
std::vector<uint8_t> lit_filebase;
|
||||
/// offset of start of free space after litfile data (should be kept a multiple of 12)
|
||||
static int lit_file_p;
|
||||
/// offset of end of space for litfile data
|
||||
static int lit_file_end;
|
||||
|
||||
/// start of luxfile data
|
||||
std::vector<uint8_t> lux_filebase;
|
||||
/// offset of start of free space after luxfile data (should be kept a multiple of 12)
|
||||
static int lux_file_p;
|
||||
/// offset of end of space for luxfile data
|
||||
static int lux_file_end;
|
||||
|
||||
static std::unordered_map<int, std::vector<uint8_t>> all_uncompressed_vis;
|
||||
|
||||
const std::unordered_map<int, std::vector<uint8_t>> &UncompressedVis()
|
||||
|
|
@ -554,82 +533,6 @@ void FixupGlobalSettings()
|
|||
}
|
||||
}
|
||||
|
||||
static std::mutex light_mutex;
|
||||
|
||||
/*
|
||||
* Return space for the lightmap and colourmap at the same time so it can
|
||||
* be done in a thread-safe manner.
|
||||
*
|
||||
* size is the number of greyscale pixels = number of bytes to allocate
|
||||
* and return in *lightdata
|
||||
*/
|
||||
void GetFileSpace(uint8_t **lightdata, uint8_t **colordata, uint8_t **deluxdata, int size)
|
||||
{
|
||||
light_mutex.lock();
|
||||
|
||||
*lightdata = *colordata = *deluxdata = nullptr;
|
||||
|
||||
if (!filebase.empty()) {
|
||||
*lightdata = filebase.data() + file_p;
|
||||
}
|
||||
if (!lit_filebase.empty()) {
|
||||
*colordata = lit_filebase.data() + lit_file_p;
|
||||
}
|
||||
if (!lux_filebase.empty()) {
|
||||
*deluxdata = lux_filebase.data() + lux_file_p;
|
||||
}
|
||||
|
||||
// if size isn't a multiple of 4, round up to the next multiple of 4
|
||||
if ((size % 4) != 0) {
|
||||
size += (4 - (size % 4));
|
||||
}
|
||||
|
||||
// increment the next writing offsets, aligning them to 4 uint8_t boundaries (file_p)
|
||||
// and 12-uint8_t boundaries (lit_file_p/lux_file_p)
|
||||
if (!filebase.empty()) {
|
||||
file_p += size;
|
||||
}
|
||||
if (!lit_filebase.empty()) {
|
||||
lit_file_p += 3 * size;
|
||||
}
|
||||
if (!lux_filebase.empty()) {
|
||||
lux_file_p += 3 * size;
|
||||
}
|
||||
|
||||
light_mutex.unlock();
|
||||
|
||||
if (file_p > file_end)
|
||||
FError("overrun");
|
||||
|
||||
if (lit_file_p > lit_file_end)
|
||||
FError("overrun");
|
||||
}
|
||||
|
||||
/**
|
||||
* Special version of GetFileSpace for when we're relighting a .bsp and can't modify it.
|
||||
* In this case the offsets are already known.
|
||||
*/
|
||||
void GetFileSpace_PreserveOffsetInBsp(uint8_t **lightdata, uint8_t **colordata, uint8_t **deluxdata, int lightofs)
|
||||
{
|
||||
Q_assert(lightofs >= 0);
|
||||
|
||||
*lightdata = *colordata = *deluxdata = nullptr;
|
||||
|
||||
if (!filebase.empty()) {
|
||||
*lightdata = filebase.data() + lightofs;
|
||||
}
|
||||
|
||||
if (colordata && !lit_filebase.empty()) {
|
||||
*colordata = lit_filebase.data() + (lightofs * 3);
|
||||
}
|
||||
|
||||
if (deluxdata && !lux_filebase.empty()) {
|
||||
*deluxdata = lux_filebase.data() + (lightofs * 3);
|
||||
}
|
||||
|
||||
// NOTE: file_p et. al. are not updated, since we're not dynamically allocating the lightmaps
|
||||
}
|
||||
|
||||
const modelinfo_t *ModelInfoForModel(const mbsp_t *bsp, int modelnum)
|
||||
{
|
||||
return modelinfo.at(modelnum);
|
||||
|
|
@ -734,44 +637,6 @@ static void CreateLightmapSurfaces(mbsp_t *bsp)
|
|||
});
|
||||
}
|
||||
|
||||
static void SaveLightmapSurfaces(mbsp_t *bsp)
|
||||
{
|
||||
logging::funcheader();
|
||||
logging::parallel_for(static_cast<size_t>(0), bsp->dfaces.size(), [&bsp](size_t i) {
|
||||
auto &surf = light_surfaces[i];
|
||||
|
||||
if (surf.samples.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FinishLightmapSurface(bsp, &surf);
|
||||
|
||||
auto f = &bsp->dfaces[i];
|
||||
const modelinfo_t *face_modelinfo = ModelInfoForFace(bsp, i);
|
||||
|
||||
if (!facesup_decoupled_global.empty()) {
|
||||
SaveLightmapSurface(
|
||||
bsp, f, nullptr, &facesup_decoupled_global[i], &surf, surf.extents, surf.extents);
|
||||
} else if (faces_sup.empty()) {
|
||||
SaveLightmapSurface(bsp, f, nullptr, nullptr, &surf, 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], nullptr, &surf, 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, nullptr, &surf, surf.extents, surf.vanilla_extents);
|
||||
SaveLightmapSurface(bsp, f, &faces_sup[i], nullptr, &surf, surf.extents, surf.extents);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void ClearLightmapSurfaces()
|
||||
{
|
||||
logging::funcheader();
|
||||
|
|
@ -854,47 +719,18 @@ static void FindModelInfo(const mbsp_t *bsp)
|
|||
Q_assert(modelinfo.size() == bsp->dmodels.size());
|
||||
}
|
||||
|
||||
// FIXME: in theory can't we calculate the exact amount of
|
||||
// storage required? we'd have to expand it by 4 to account for
|
||||
// lightstyles though
|
||||
static constexpr size_t MAX_MAP_LIGHTING = 0x8000000;
|
||||
|
||||
/*
|
||||
* =============
|
||||
* LightWorld
|
||||
* =============
|
||||
*/
|
||||
static void LightWorld(bspdata_t *bspdata, bool forcedscale)
|
||||
static void LightWorld(bspdata_t *bspdata, const fs::path &source, bool forcedscale)
|
||||
{
|
||||
logging::funcheader();
|
||||
|
||||
mbsp_t &bsp = std::get<mbsp_t>(bspdata->bsp);
|
||||
|
||||
ClearLightmapSurfaces();
|
||||
filebase.clear();
|
||||
lit_filebase.clear();
|
||||
lux_filebase.clear();
|
||||
|
||||
if (!bsp.loadversion->game->has_rgb_lightmap) {
|
||||
/* greyscale data stored in a separate buffer */
|
||||
filebase.resize(MAX_MAP_LIGHTING);
|
||||
file_p = 0;
|
||||
file_end = MAX_MAP_LIGHTING;
|
||||
}
|
||||
|
||||
if (bsp.loadversion->game->has_rgb_lightmap || light_options.write_litfile) {
|
||||
/* litfile data stored in a separate buffer */
|
||||
lit_filebase.resize(MAX_MAP_LIGHTING * 3);
|
||||
lit_file_p = 0;
|
||||
lit_file_end = (MAX_MAP_LIGHTING * 3);
|
||||
}
|
||||
|
||||
if (light_options.write_luxfile) {
|
||||
/* lux data stored in a separate buffer */
|
||||
lux_filebase.resize(MAX_MAP_LIGHTING * 3);
|
||||
lux_file_p = 0;
|
||||
lux_file_end = (MAX_MAP_LIGHTING * 3);
|
||||
}
|
||||
|
||||
if (forcedscale) {
|
||||
bspdata->bspx.entries.erase("LMSHIFT");
|
||||
|
|
@ -996,23 +832,7 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale)
|
|||
});
|
||||
}
|
||||
|
||||
SaveLightmapSurfaces(&bsp);
|
||||
|
||||
logging::print("Lighting Completed.\n\n");
|
||||
|
||||
// Transfer greyscale lightmap (or color lightmap for Q2/HL) to the bsp and update lightdatasize
|
||||
if (!light_options.litonly.value()) {
|
||||
if (bsp.loadversion->game->has_rgb_lightmap) {
|
||||
bsp.dlightdata.resize(lit_file_p);
|
||||
memcpy(bsp.dlightdata.data(), lit_filebase.data(), bsp.dlightdata.size());
|
||||
} else {
|
||||
bsp.dlightdata.resize(file_p);
|
||||
memcpy(bsp.dlightdata.data(), filebase.data(), bsp.dlightdata.size());
|
||||
}
|
||||
} else {
|
||||
// NOTE: bsp.lightdatasize is already valid in the -litonly case
|
||||
}
|
||||
logging::print("lightdatasize: {}\n", bsp.dlightdata.size());
|
||||
SaveLightmapSurfaces(bspdata, source);
|
||||
|
||||
// kill this stuff if its somehow found.
|
||||
bspdata->bspx.entries.erase("LMSTYLE16");
|
||||
|
|
@ -1476,18 +1296,6 @@ static void ResetLight()
|
|||
faces_sup.clear();
|
||||
facesup_decoupled_global.clear();
|
||||
|
||||
filebase.clear();
|
||||
file_p = 0;
|
||||
file_end = 0;
|
||||
|
||||
lit_filebase.clear();
|
||||
lit_file_p = 0;
|
||||
lit_file_end = 0;
|
||||
|
||||
lux_filebase.clear();
|
||||
lux_file_p = 0;
|
||||
lux_file_end = 0;
|
||||
|
||||
all_uncompressed_vis.clear();
|
||||
modelinfo.clear();
|
||||
tracelist.clear();
|
||||
|
|
@ -1629,7 +1437,7 @@ int light_main(int argc, const char **argv)
|
|||
|
||||
SetupDirt(light_options);
|
||||
|
||||
LightWorld(&bspdata, light_options.lightmap_scale.is_changed());
|
||||
LightWorld(&bspdata, source, light_options.lightmap_scale.is_changed());
|
||||
|
||||
LightGrid(&bspdata);
|
||||
|
||||
|
|
@ -1642,30 +1450,9 @@ int light_main(int argc, const char **argv)
|
|||
WriteNormals(bsp, bspdata);
|
||||
}
|
||||
|
||||
/*invalidate any bspx lighting info early*/
|
||||
bspdata.bspx.entries.erase("RGBLIGHTING");
|
||||
bspdata.bspx.entries.erase("LIGHTINGDIR");
|
||||
|
||||
if (light_options.write_litfile == lightfile::lit2) {
|
||||
WriteLitFile(&bsp, faces_sup, source, 2);
|
||||
return 0; // run away before any files are written
|
||||
}
|
||||
|
||||
/*fixme: add a new per-surface offset+lmscale lump for compat/versitility?*/
|
||||
if (light_options.write_litfile & lightfile::external) {
|
||||
WriteLitFile(&bsp, faces_sup, source, LIT_VERSION);
|
||||
}
|
||||
if (light_options.write_litfile & lightfile::bspx) {
|
||||
lit_filebase.resize(bsp.dlightdata.size() * 3);
|
||||
bspdata.bspx.transfer("RGBLIGHTING", lit_filebase);
|
||||
}
|
||||
if (light_options.write_luxfile & lightfile::external) {
|
||||
WriteLuxFile(&bsp, source, LIT_VERSION);
|
||||
}
|
||||
if (light_options.write_luxfile & lightfile::bspx) {
|
||||
lux_filebase.resize(bsp.dlightdata.size() * 3);
|
||||
bspdata.bspx.transfer("LIGHTINGDIR", lux_filebase);
|
||||
}
|
||||
}
|
||||
|
||||
/* -novanilla + internal lighting = no grey lightmap */
|
||||
|
|
|
|||
102
light/litfile.cc
102
light/litfile.cc
|
|
@ -1,102 +0,0 @@
|
|||
/* Copyright (C) 2002-2006 Kevin Shanahan
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
#include <light/litfile.hh>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <light/light.hh>
|
||||
|
||||
#include <common/bspfile.hh>
|
||||
#include <common/cmdlib.hh>
|
||||
#include <common/fs.hh>
|
||||
|
||||
// litheader_t::v1_t
|
||||
|
||||
void litheader_t::v1_t::stream_write(std::ostream &s) const
|
||||
{
|
||||
s <= std::tie(ident, version);
|
||||
}
|
||||
|
||||
void litheader_t::v1_t::stream_read(std::istream &s)
|
||||
{
|
||||
s >= std::tie(ident, version);
|
||||
}
|
||||
|
||||
// litheader_t::v2_t
|
||||
|
||||
void litheader_t::v2_t::stream_write(std::ostream &s) const
|
||||
{
|
||||
s <= std::tie(numsurfs, lmsamples);
|
||||
}
|
||||
|
||||
void litheader_t::v2_t::stream_read(std::istream &s)
|
||||
{
|
||||
s >= std::tie(numsurfs, lmsamples);
|
||||
}
|
||||
|
||||
void WriteLitFile(const mbsp_t *bsp, const std::vector<facesup_t> &facesup, const fs::path &filename, int version)
|
||||
{
|
||||
litheader_t header;
|
||||
|
||||
fs::path litname = filename;
|
||||
litname.replace_extension("lit");
|
||||
|
||||
header.v1.version = version;
|
||||
header.v2.numsurfs = bsp->dfaces.size();
|
||||
header.v2.lmsamples = bsp->dlightdata.size();
|
||||
|
||||
logging::print("Writing {}\n", litname);
|
||||
std::ofstream litfile(litname, std::ios_base::out | std::ios_base::binary);
|
||||
litfile <= header.v1;
|
||||
if (version == 2) {
|
||||
unsigned int i, j;
|
||||
litfile <= header.v2;
|
||||
for (i = 0; i < bsp->dfaces.size(); i++) {
|
||||
litfile <= facesup[i].lightofs;
|
||||
for (int j = 0; j < 4; j++) {
|
||||
litfile <= facesup[i].styles[j];
|
||||
}
|
||||
for (int j = 0; j < 2; j++) {
|
||||
litfile <= facesup[i].extent[j];
|
||||
}
|
||||
j = 0;
|
||||
while (nth_bit(j) < facesup[i].lmscale)
|
||||
j++;
|
||||
litfile <= (uint8_t)j;
|
||||
}
|
||||
litfile.write((const char *)lit_filebase.data(), bsp->dlightdata.size() * 3);
|
||||
litfile.write((const char *)lux_filebase.data(), bsp->dlightdata.size() * 3);
|
||||
} else
|
||||
litfile.write((const char *)lit_filebase.data(), bsp->dlightdata.size() * 3);
|
||||
}
|
||||
|
||||
void WriteLuxFile(const mbsp_t *bsp, const fs::path &filename, int version)
|
||||
{
|
||||
litheader_t header;
|
||||
|
||||
fs::path luxname = filename;
|
||||
luxname.replace_extension("lux");
|
||||
|
||||
header.v1.version = version;
|
||||
|
||||
std::ofstream luxfile(luxname, std::ios_base::out | std::ios_base::binary);
|
||||
luxfile <= header.v1;
|
||||
luxfile.write((const char *)lux_filebase.data(), bsp->dlightdata.size() * 3);
|
||||
}
|
||||
753
light/ltface.cc
753
light/ltface.cc
|
|
@ -26,7 +26,7 @@
|
|||
#include <light/entities.hh>
|
||||
#include <light/lightgrid.hh>
|
||||
#include <light/trace.hh>
|
||||
#include <light/litfile.hh> // for facesup_t
|
||||
#include <light/write.hh> // for facesup_t
|
||||
|
||||
#include <common/imglib.hh>
|
||||
#include <common/log.hh>
|
||||
|
|
@ -45,8 +45,6 @@ std::atomic<uint32_t> total_light_rays, total_light_ray_hits, total_samplepoints
|
|||
std::atomic<uint32_t> total_bounce_rays, total_bounce_ray_hits;
|
||||
std::atomic<uint32_t> total_surflight_rays, total_surflight_ray_hits; // mxd
|
||||
#endif
|
||||
std::atomic<uint32_t> fully_transparent_lightmaps;
|
||||
static bool warned_about_light_map_overflow, warned_about_light_style_overflow;
|
||||
|
||||
thread_local static raystream_occlusion_t occlusion_stream;
|
||||
thread_local static raystream_intersection_t intersection_stream;
|
||||
|
|
@ -760,7 +758,7 @@ static const lightmap_t *Lightmap_ForStyle_ReadOnly(const lightsurf_t *lightsurf
|
|||
* Otherwise, return the next available map. A new map is not marked as
|
||||
* allocated since it may not be kept if no lights hit.
|
||||
*/
|
||||
static lightmap_t *Lightmap_ForStyle(lightmapdict_t *lightmaps, const int style, const lightsurf_t *lightsurf)
|
||||
lightmap_t *Lightmap_ForStyle(lightmapdict_t *lightmaps, const int style, const lightsurf_t *lightsurf)
|
||||
{
|
||||
for (auto &lm : *lightmaps) {
|
||||
if (lm.style == style)
|
||||
|
|
@ -2422,56 +2420,6 @@ static void LightFace_CalculateDirt(lightsurf_t *lightsurf)
|
|||
}
|
||||
}
|
||||
|
||||
// clamps negative values. applies gamma and rangescale. clamps values over 255
|
||||
// N.B. we want to do this before smoothing / downscaling, so huge values don't mess up the averaging.
|
||||
inline void LightFace_ScaleAndClamp(lightsurf_t *lightsurf)
|
||||
{
|
||||
const settings::worldspawn_keys &cfg = *lightsurf->cfg;
|
||||
|
||||
for (lightmap_t &lightmap : lightsurf->lightmapsByStyle) {
|
||||
for (int i = 0; i < lightsurf->samples.size(); i++) {
|
||||
qvec3f &color = lightmap.samples[i].color;
|
||||
|
||||
/* Fix any negative values */
|
||||
color = qv::max(color, {0});
|
||||
|
||||
// before any other scaling, apply maxlight
|
||||
if (lightsurf->maxlight || cfg.maxlight.value()) {
|
||||
float maxcolor = qv::max(color);
|
||||
// FIXME: for colored lighting, this doesn't seem to generate the right values...
|
||||
float maxval = (lightsurf->maxlight ? lightsurf->maxlight : cfg.maxlight.value()) * 2.0f;
|
||||
|
||||
if (maxcolor > maxval) {
|
||||
color *= (maxval / maxcolor);
|
||||
}
|
||||
}
|
||||
|
||||
// color scaling
|
||||
if (lightsurf->lightcolorscale != 1.0f) {
|
||||
qvec3f grayscale{qv::max(color)};
|
||||
color = mix(grayscale, color, lightsurf->lightcolorscale);
|
||||
}
|
||||
|
||||
/* Scale and handle gamma adjustment */
|
||||
color *= cfg.rangescale.value();
|
||||
|
||||
if (cfg.lightmapgamma.value() != 1.0f) {
|
||||
for (auto &c : color) {
|
||||
c = pow(c / 255.0f, 1.0f / cfg.lightmapgamma.value()) * 255.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// clamp
|
||||
// FIXME: should this be a brightness clamp?
|
||||
float maxcolor = qv::max(color);
|
||||
|
||||
if (maxcolor > 255.0f) {
|
||||
color *= (255.0f / maxcolor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above LightFace_ScaleAndClamp, but for lightgrid
|
||||
* TODO: share code with above
|
||||
|
|
@ -2521,34 +2469,6 @@ static void LightPoint_ScaleAndClamp(lightgrid_samples_t &result)
|
|||
}
|
||||
}
|
||||
|
||||
void FinishLightmapSurface(const mbsp_t *bsp, lightsurf_t *lightsurf)
|
||||
{
|
||||
/* Apply gamma, rangescale, and clamp */
|
||||
LightFace_ScaleAndClamp(lightsurf);
|
||||
}
|
||||
|
||||
static float Lightmap_AvgBrightness(const lightmap_t *lm, const lightsurf_t *lightsurf)
|
||||
{
|
||||
float avgb = 0;
|
||||
for (int j = 0; j < lightsurf->samples.size(); j++) {
|
||||
avgb += LightSample_Brightness(lm->samples[j].color);
|
||||
}
|
||||
avgb /= lightsurf->samples.size();
|
||||
return avgb;
|
||||
}
|
||||
|
||||
static float Lightmap_MaxBrightness(const lightmap_t *lm, const lightsurf_t *lightsurf)
|
||||
{
|
||||
float maxb = 0;
|
||||
for (int j = 0; j < lightsurf->samples.size(); j++) {
|
||||
const float b = LightSample_Brightness(lm->samples[j].color);
|
||||
if (b > maxb) {
|
||||
maxb = b;
|
||||
}
|
||||
}
|
||||
return maxb;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void WritePPM(const fs::path &fname, int width, int height, const uint8_t *rgbdata)
|
||||
{
|
||||
|
|
@ -2616,251 +2536,6 @@ static void DumpDownscaledLightmap(const mbsp_t *bsp, const mface_t *face, int w
|
|||
}
|
||||
#endif
|
||||
|
||||
static std::vector<qvec4f> LightmapColorsToGLMVector(const lightsurf_t *lightsurf, const lightmap_t *lm)
|
||||
{
|
||||
std::vector<qvec4f> res;
|
||||
for (int i = 0; i < lightsurf->samples.size(); i++) {
|
||||
const qvec3f &color = lm->samples[i].color;
|
||||
const float alpha = lightsurf->samples[i].occluded ? 0.0f : 1.0f;
|
||||
res.emplace_back(color[0], color[1], color[2], alpha);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::vector<qvec4f> LightmapNormalsToGLMVector(const lightsurf_t *lightsurf, const lightmap_t *lm)
|
||||
{
|
||||
std::vector<qvec4f> res;
|
||||
for (int i = 0; i < lightsurf->samples.size(); i++) {
|
||||
const qvec3f &color = lm->samples[i].direction;
|
||||
const float alpha = lightsurf->samples[i].occluded ? 0.0f : 1.0f;
|
||||
res.emplace_back(color[0], color[1], color[2], alpha);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Special handling of alpha channel:
|
||||
// - "alpha channel" is expected to be 0 or 1. This gets set to 0 if the sample
|
||||
// point is occluded (bmodel sticking outside of the world, or inside a shadow-
|
||||
// casting bmodel that is overlapping a world face), otherwise it's 1.
|
||||
//
|
||||
// - If alpha is 0 the sample doesn't contribute to the filter kernel.
|
||||
// - If all the samples in the filter kernel have alpha=0, write a sample with alpha=0
|
||||
// (but still average the colors, important so that minlight still works properly
|
||||
// for bmodels that go outside of the world).
|
||||
static std::vector<qvec4f> IntegerDownsampleImage(const std::vector<qvec4f> &input, int w, int h, int factor)
|
||||
{
|
||||
Q_assert(factor >= 1);
|
||||
if (factor == 1)
|
||||
return input;
|
||||
|
||||
const int outw = w / factor;
|
||||
const int outh = h / factor;
|
||||
|
||||
std::vector<qvec4f> res(static_cast<size_t>(outw * outh));
|
||||
|
||||
for (int y = 0; y < outh; y++) {
|
||||
for (int x = 0; x < outw; x++) {
|
||||
|
||||
float totalWeight = 0.0f;
|
||||
qvec3f totalColor{};
|
||||
|
||||
// These are only used if all the samples in the kernel have alpha = 0
|
||||
float totalWeightIgnoringOcclusion = 0.0f;
|
||||
qvec3f totalColorIgnoringOcclusion{};
|
||||
|
||||
const int extraradius = 0;
|
||||
const int kernelextent = factor + (2 * extraradius);
|
||||
|
||||
for (int y0 = 0; y0 < kernelextent; y0++) {
|
||||
for (int x0 = 0; x0 < kernelextent; x0++) {
|
||||
const int x1 = (x * factor) - extraradius + x0;
|
||||
const int y1 = (y * factor) - extraradius + y0;
|
||||
|
||||
// check if the kernel goes outside of the source image
|
||||
if (x1 < 0 || x1 >= w)
|
||||
continue;
|
||||
if (y1 < 0 || y1 >= h)
|
||||
continue;
|
||||
|
||||
// read the input sample
|
||||
const float weight = 1.0f;
|
||||
const qvec4f &inSample = input.at((y1 * w) + x1);
|
||||
|
||||
totalColorIgnoringOcclusion += qvec3f(inSample) * weight;
|
||||
totalWeightIgnoringOcclusion += weight;
|
||||
|
||||
// Occluded sample points don't contribute to the filter
|
||||
if (inSample[3] == 0.0f)
|
||||
continue;
|
||||
|
||||
totalColor += qvec3f(inSample) * weight;
|
||||
totalWeight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
const int outIndex = (y * outw) + x;
|
||||
if (totalWeight > 0.0f) {
|
||||
const qvec3f tmp = totalColor / totalWeight;
|
||||
const qvec4f resultColor = qvec4f(tmp[0], tmp[1], tmp[2], 1.0f);
|
||||
res[outIndex] = resultColor;
|
||||
} else {
|
||||
const qvec3f tmp = totalColorIgnoringOcclusion / totalWeightIgnoringOcclusion;
|
||||
const qvec4f resultColor = qvec4f(tmp[0], tmp[1], tmp[2], 0.0f);
|
||||
res[outIndex] = resultColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::vector<qvec4f> FloodFillTransparent(const std::vector<qvec4f> &input, int w, int h)
|
||||
{
|
||||
// transparent pixels take the average of their neighbours.
|
||||
|
||||
std::vector<qvec4f> res(input);
|
||||
|
||||
while (1) {
|
||||
int unhandled_pixels = 0;
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
const int i = (y * w) + x;
|
||||
const qvec4f &inSample = res.at(i);
|
||||
|
||||
if (inSample[3] == 0) {
|
||||
// average the neighbouring non-transparent samples
|
||||
|
||||
int opaque_neighbours = 0;
|
||||
qvec3f neighbours_sum{};
|
||||
for (int y0 = -1; y0 <= 1; y0++) {
|
||||
for (int x0 = -1; x0 <= 1; x0++) {
|
||||
const int x1 = x + x0;
|
||||
const int y1 = y + y0;
|
||||
|
||||
if (x1 < 0 || x1 >= w)
|
||||
continue;
|
||||
if (y1 < 0 || y1 >= h)
|
||||
continue;
|
||||
|
||||
const qvec4f neighbourSample = res.at((y1 * w) + x1);
|
||||
if (neighbourSample[3] == 1) {
|
||||
opaque_neighbours++;
|
||||
neighbours_sum += qvec3f(neighbourSample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opaque_neighbours > 0) {
|
||||
neighbours_sum *= (1.0f / (float)opaque_neighbours);
|
||||
res.at(i) = qvec4f(neighbours_sum[0], neighbours_sum[1], neighbours_sum[2], 1.0f);
|
||||
|
||||
// this sample is now opaque
|
||||
} else {
|
||||
unhandled_pixels++;
|
||||
|
||||
// all neighbours are transparent. need to perform more iterations (or the whole lightmap is
|
||||
// transparent).
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unhandled_pixels == input.size()) {
|
||||
// logging::funcprint("warning, fully transparent lightmap\n");
|
||||
fully_transparent_lightmaps++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (unhandled_pixels == 0)
|
||||
break; // all done
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::vector<qvec4f> HighlightSeams(const std::vector<qvec4f> &input, int w, int h)
|
||||
{
|
||||
std::vector<qvec4f> res(input);
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
const int i = (y * w) + x;
|
||||
const qvec4f &inSample = res.at(i);
|
||||
|
||||
if (inSample[3] == 0) {
|
||||
res.at(i) = qvec4f(255, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static std::vector<qvec4f> BoxBlurImage(const std::vector<qvec4f> &input, int w, int h, int radius)
|
||||
{
|
||||
std::vector<qvec4f> res(input.size());
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
|
||||
float totalWeight = 0.0f;
|
||||
qvec3f totalColor{};
|
||||
|
||||
// These are only used if all the samples in the kernel have alpha = 0
|
||||
float totalWeightIgnoringOcclusion = 0.0f;
|
||||
qvec3f totalColorIgnoringOcclusion{};
|
||||
|
||||
for (int y0 = -radius; y0 <= radius; y0++) {
|
||||
for (int x0 = -radius; x0 <= radius; x0++) {
|
||||
const int x1 = std::clamp(x + x0, 0, w - 1);
|
||||
const int y1 = std::clamp(y + y0, 0, h - 1);
|
||||
|
||||
// check if the kernel goes outside of the source image
|
||||
|
||||
// 2017-09-16: this is a hack, but clamping the
|
||||
// x/y instead of discarding the samples outside of the
|
||||
// kernel looks better in some cases:
|
||||
// https://github.com/ericwa/ericw-tools/issues/171
|
||||
#if 0
|
||||
if (x1 < 0 || x1 >= w)
|
||||
continue;
|
||||
if (y1 < 0 || y1 >= h)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// read the input sample
|
||||
const float weight = 1.0f;
|
||||
const qvec4f &inSample = input.at((y1 * w) + x1);
|
||||
|
||||
totalColorIgnoringOcclusion += qvec3f(inSample) * weight;
|
||||
totalWeightIgnoringOcclusion += weight;
|
||||
|
||||
// Occluded sample points don't contribute to the filter
|
||||
if (inSample[3] == 0.0f)
|
||||
continue;
|
||||
|
||||
totalColor += qvec3f(inSample) * weight;
|
||||
totalWeight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
const int outIndex = (y * w) + x;
|
||||
if (totalWeight > 0.0f) {
|
||||
const qvec3f tmp = totalColor / totalWeight;
|
||||
const qvec4f resultColor = qvec4f(tmp[0], tmp[1], tmp[2], 1.0f);
|
||||
res[outIndex] = resultColor;
|
||||
} else {
|
||||
const qvec3f tmp = totalColorIgnoringOcclusion / totalWeightIgnoringOcclusion;
|
||||
const qvec4f resultColor = qvec4f(tmp[0], tmp[1], tmp[2], 0.0f);
|
||||
res[outIndex] = resultColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// check if the given face can actually store luxel data
|
||||
bool Face_IsLightmapped(const mbsp_t *bsp, const mface_t *face)
|
||||
{
|
||||
|
|
@ -2892,425 +2567,6 @@ bool Face_IsEmissive(const mbsp_t *bsp, const mface_t *face)
|
|||
return bsp->loadversion->game->surf_is_emissive(texinfo->flags, texname);
|
||||
}
|
||||
|
||||
/**
|
||||
* - Writes (actual_width * actual_height) bytes to `out`
|
||||
* - Writes (actual_width * actual_height * 3) bytes to `lit`
|
||||
* - Writes (actual_width * actual_height * 3) bytes to `lux`
|
||||
*/
|
||||
static void WriteSingleLightmap(const mbsp_t *bsp, const mface_t *face, const lightsurf_t *lightsurf,
|
||||
const lightmap_t *lm, const int actual_width, const int actual_height, uint8_t *out, uint8_t *lit, uint8_t *lux,
|
||||
const faceextents_t &output_extents)
|
||||
{
|
||||
const int oversampled_width = actual_width * light_options.extra.value();
|
||||
const int oversampled_height = actual_height * light_options.extra.value();
|
||||
|
||||
// allocate new float buffers for the output colors and directions
|
||||
// these are the actual output width*height, without oversampling.
|
||||
|
||||
std::vector<qvec4f> fullres = LightmapColorsToGLMVector(lightsurf, lm);
|
||||
|
||||
if (light_options.highlightseams.value()) {
|
||||
fullres = HighlightSeams(fullres, oversampled_width, oversampled_height);
|
||||
}
|
||||
|
||||
// removes all transparent pixels by averaging from adjacent pixels
|
||||
fullres = FloodFillTransparent(fullres, oversampled_width, oversampled_height);
|
||||
|
||||
if (light_options.soft.value() > 0) {
|
||||
fullres = BoxBlurImage(fullres, oversampled_width, oversampled_height, light_options.soft.value());
|
||||
}
|
||||
|
||||
const std::vector<qvec4f> output_color =
|
||||
IntegerDownsampleImage(fullres, oversampled_width, oversampled_height, light_options.extra.value());
|
||||
std::optional<std::vector<qvec4f>> output_dir;
|
||||
|
||||
if (lux) {
|
||||
output_dir = IntegerDownsampleImage(LightmapNormalsToGLMVector(lightsurf, lm), oversampled_width,
|
||||
oversampled_height, light_options.extra.value());
|
||||
}
|
||||
|
||||
// copy from the float buffers to byte buffers in .bsp / .lit / .lux
|
||||
const int output_width = output_extents.width();
|
||||
const int output_height = output_extents.height();
|
||||
|
||||
for (int t = 0; t < output_height; t++) {
|
||||
for (int s = 0; s < output_width; s++) {
|
||||
const int input_sample_s = (s / (float)output_width) * actual_width;
|
||||
const int input_sample_t = (t / (float)output_height) * actual_height;
|
||||
const int sampleindex = (input_sample_t * actual_width) + input_sample_s;
|
||||
|
||||
if (lit || out) {
|
||||
const qvec4f &color = output_color.at(sampleindex);
|
||||
|
||||
if (lit) {
|
||||
*lit++ = color[0];
|
||||
*lit++ = color[1];
|
||||
*lit++ = color[2];
|
||||
}
|
||||
|
||||
if (out) {
|
||||
/* Take the max() of the 3 components to get the value to write to the
|
||||
.bsp lightmap. this avoids issues with some engines
|
||||
that require the lit and internal lightmap to have the same
|
||||
intensity. (MarkV, some QW engines)
|
||||
|
||||
This must be max(), see LightNormalize in MarkV 1036.
|
||||
*/
|
||||
float light = std::max({color[0], color[1], color[2]});
|
||||
if (light < 0)
|
||||
light = 0;
|
||||
if (light > 255)
|
||||
light = 255;
|
||||
*out++ = light;
|
||||
}
|
||||
}
|
||||
|
||||
if (lux) {
|
||||
qvec3f direction = output_dir->at(sampleindex).xyz();
|
||||
qvec3f temp = {qv::dot(direction, lightsurf->snormal), qv::dot(direction, lightsurf->tnormal),
|
||||
qv::dot(direction, lightsurf->plane.normal)};
|
||||
|
||||
if (qv::emptyExact(temp))
|
||||
temp = {0, 0, 1};
|
||||
else
|
||||
qv::normalizeInPlace(temp);
|
||||
|
||||
int v = (temp[0] + 1) * 128;
|
||||
*lux++ = (v > 255) ? 255 : v;
|
||||
v = (temp[1] + 1) * 128;
|
||||
*lux++ = (v > 255) ? 255 : v;
|
||||
v = (temp[2] + 1) * 128;
|
||||
*lux++ = (v > 255) ? 255 : v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - Writes (output_width * output_height) bytes to `out`
|
||||
* - Writes (output_width * output_height * 3) bytes to `lit`
|
||||
* - Writes (output_width * output_height * 3) bytes to `lux`
|
||||
*/
|
||||
static void WriteSingleLightmap_FromDecoupled(const mbsp_t *bsp, const mface_t *face, const lightsurf_t *lightsurf,
|
||||
const lightmap_t *lm, const int output_width, const int output_height, uint8_t *out, uint8_t *lit, uint8_t *lux)
|
||||
{
|
||||
// this is the lightmap data in the "decoupled" coordinate system
|
||||
std::vector<qvec4f> fullres = LightmapColorsToGLMVector(lightsurf, lm);
|
||||
|
||||
// maps a luxel in the vanilla lightmap to the corresponding position in the decoupled lightmap
|
||||
const qmat4x4f vanillaLMToDecoupled =
|
||||
lightsurf->extents.worldToLMMatrix * lightsurf->vanilla_extents.lmToWorldMatrix;
|
||||
|
||||
// samples the "decoupled" lightmap at an integer coordinate, with clamping
|
||||
auto tex = [&lightsurf, &fullres](int x, int y) -> qvec4f {
|
||||
const int x_clamped = std::clamp(x, 0, lightsurf->width - 1);
|
||||
const int y_clamped = std::clamp(y, 0, lightsurf->height - 1);
|
||||
|
||||
const int sampleindex = (y_clamped * lightsurf->width) + x_clamped;
|
||||
assert(sampleindex >= 0);
|
||||
assert(sampleindex < fullres.size());
|
||||
|
||||
return fullres[sampleindex];
|
||||
};
|
||||
|
||||
for (int t = 0; t < output_height; t++) {
|
||||
for (int s = 0; s < output_width; s++) {
|
||||
// convert from vanilla lm coord to decoupled lm coord
|
||||
qvec2f decoupled_lm_coord = vanillaLMToDecoupled * qvec4f(s, t, 0, 1);
|
||||
|
||||
decoupled_lm_coord = decoupled_lm_coord * light_options.extra.value();
|
||||
|
||||
// split into integer/fractional part for bilinear interpolation
|
||||
const int coord_floor_x = (int)decoupled_lm_coord[0];
|
||||
const int coord_floor_y = (int)decoupled_lm_coord[1];
|
||||
|
||||
const float coord_frac_x = decoupled_lm_coord[0] - coord_floor_x;
|
||||
const float coord_frac_y = decoupled_lm_coord[1] - coord_floor_y;
|
||||
|
||||
// 2D bilinear interpolation
|
||||
const qvec4f color =
|
||||
mix(mix(tex(coord_floor_x, coord_floor_y), tex(coord_floor_x + 1, coord_floor_y), coord_frac_x),
|
||||
mix(tex(coord_floor_x, coord_floor_y + 1), tex(coord_floor_x + 1, coord_floor_y + 1), coord_frac_x),
|
||||
coord_frac_y);
|
||||
|
||||
if (lit || out) {
|
||||
if (lit) {
|
||||
*lit++ = color[0];
|
||||
*lit++ = color[1];
|
||||
*lit++ = color[2];
|
||||
}
|
||||
|
||||
if (out) {
|
||||
// FIXME: implement
|
||||
*out++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (lux) {
|
||||
// FIXME: implement
|
||||
*lux++ = 0;
|
||||
*lux++ = 0;
|
||||
*lux++ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
const int actual_height = extents.height();
|
||||
const int output_width = output_extents.width();
|
||||
const int output_height = output_extents.height();
|
||||
const int size = output_extents.numsamples();
|
||||
|
||||
if (light_options.litonly.value()) {
|
||||
// special case for writing a .lit for a bsp without modifying the bsp.
|
||||
// involves looking at which styles were written to the bsp in the previous lighting run, and then
|
||||
// writing the same styles to the same offsets in the .lit file.
|
||||
|
||||
if (face->lightofs == -1) {
|
||||
// nothing to write for this face
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *out, *lit, *lux;
|
||||
GetFileSpace_PreserveOffsetInBsp(&out, &lit, &lux, face->lightofs);
|
||||
|
||||
for (int mapnum = 0; mapnum < MAXLIGHTMAPS; mapnum++) {
|
||||
const int style = face->styles[mapnum];
|
||||
|
||||
if (style == 255) {
|
||||
break; // all done for this face
|
||||
}
|
||||
|
||||
// see if we have computed lighting for this style
|
||||
for (const lightmap_t &lm : lightmaps) {
|
||||
if (lm.style == style) {
|
||||
WriteSingleLightmap(
|
||||
bsp, face, lightsurf, &lm, actual_width, actual_height, out, lit, lux, output_extents);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if we didn't find a matching lightmap, just don't write anything
|
||||
|
||||
if (out) {
|
||||
out += size;
|
||||
}
|
||||
if (lit) {
|
||||
lit += (size * 3);
|
||||
}
|
||||
if (lux) {
|
||||
lux += (size * 3);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
size_t maxfstyles = std::min((size_t)light_options.facestyles.value(), facesup ? MAXLIGHTMAPSSUP : MAXLIGHTMAPS);
|
||||
int maxstyle = facesup ? INVALID_LIGHTSTYLE : INVALID_LIGHTSTYLE_OLD;
|
||||
|
||||
// intermediate collection for sorting lightmaps
|
||||
std::vector<std::pair<float, const lightmap_t *>> sortable;
|
||||
|
||||
for (const lightmap_t &lightmap : lightmaps) {
|
||||
// skip un-saved lightmaps
|
||||
if (lightmap.style == INVALID_LIGHTSTYLE)
|
||||
continue;
|
||||
if (lightmap.style > maxstyle || (facesup && lightmap.style > INVALID_LIGHTSTYLE_OLD)) {
|
||||
if (!warned_about_light_style_overflow) {
|
||||
if (IsOutputtingSupplementaryData()) {
|
||||
logging::print(
|
||||
"INFO: a face has exceeded max light style id ({});\n LMSTYLE16 will be output to hold the non-truncated data.\n Use -verbose to find which faces.\n",
|
||||
maxstyle, lightsurf->samples[0].point);
|
||||
} else {
|
||||
logging::print(
|
||||
"WARNING: a face has exceeded max light style id ({}). Use -verbose to find which faces.\n",
|
||||
maxstyle, lightsurf->samples[0].point);
|
||||
}
|
||||
warned_about_light_style_overflow = true;
|
||||
}
|
||||
logging::print(logging::flag::VERBOSE, "WARNING: Style {} too high on face near {}\n", lightmap.style,
|
||||
lightsurf->samples[0].point);
|
||||
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,
|
||||
// the face is drawn fullbright instead of black (Q1)
|
||||
const float maxb = Lightmap_MaxBrightness(&lightmap, lightsurf);
|
||||
if (maxb < 1)
|
||||
continue;
|
||||
}
|
||||
|
||||
const float avgb = Lightmap_AvgBrightness(&lightmap, lightsurf);
|
||||
sortable.emplace_back(avgb, &lightmap);
|
||||
}
|
||||
|
||||
// HACK: in Q2, if lightofs is -1, then it's drawn fullbright,
|
||||
// so we can't optimize away unused portions of the lightmap.
|
||||
if (bsp->loadversion->game->id == GAME_QUAKE_II) {
|
||||
if (!sortable.size()) {
|
||||
lightmap_t *lm = Lightmap_ForStyle(&lightmaps, 0, lightsurf);
|
||||
lm->style = 0;
|
||||
for (auto &sample : lightsurf->samples) {
|
||||
sample.occluded = false;
|
||||
}
|
||||
sortable.emplace_back(0, lm);
|
||||
}
|
||||
}
|
||||
|
||||
// sort in descending order of average brightness
|
||||
std::sort(sortable.begin(), sortable.end());
|
||||
std::reverse(sortable.begin(), sortable.end());
|
||||
|
||||
std::vector<const lightmap_t *> sorted;
|
||||
for (const auto &pair : sortable) {
|
||||
if (sorted.size() == maxfstyles) {
|
||||
if (!warned_about_light_map_overflow) {
|
||||
if (IsOutputtingSupplementaryData()) {
|
||||
logging::print(
|
||||
"INFO: a face has exceeded max light styles ({});\n LMSTYLE/LMSTYLE16 will be output to hold the non-truncated data.\n Use -verbose to find which faces.\n",
|
||||
maxfstyles, lightsurf->samples[0].point);
|
||||
} else {
|
||||
logging::print(
|
||||
"WARNING: a face has exceeded max light styles ({}). Use -verbose to find which faces.\n",
|
||||
maxfstyles, lightsurf->samples[0].point);
|
||||
}
|
||||
warned_about_light_map_overflow = true;
|
||||
}
|
||||
logging::print(logging::flag::VERBOSE,
|
||||
"WARNING: {} light styles (max {}) on face near {}; styles: ", sortable.size(), maxfstyles,
|
||||
lightsurf->samples[0].point);
|
||||
for (auto &p : sortable) {
|
||||
logging::print(logging::flag::VERBOSE, "{} ", p.second->style);
|
||||
}
|
||||
logging::print(logging::flag::VERBOSE, "\n");
|
||||
break;
|
||||
}
|
||||
|
||||
sorted.push_back(pair.second);
|
||||
}
|
||||
|
||||
/* final number of lightmaps */
|
||||
const int numstyles = static_cast<int>(sorted.size());
|
||||
Q_assert(numstyles <= MAXLIGHTMAPSSUP);
|
||||
|
||||
if (bsp->loadversion->game->id == GAME_QUAKE_II) {
|
||||
Q_assert(numstyles > 0);
|
||||
}
|
||||
|
||||
/* update face info (either core data or supplementary stuff) */
|
||||
if (facesup) {
|
||||
facesup->extent[0] = output_width;
|
||||
facesup->extent[1] = output_height;
|
||||
int mapnum;
|
||||
for (mapnum = 0; mapnum < numstyles && mapnum < MAXLIGHTMAPSSUP; mapnum++) {
|
||||
facesup->styles[mapnum] = sorted.at(mapnum)->style;
|
||||
}
|
||||
for (; mapnum < MAXLIGHTMAPSSUP; mapnum++) {
|
||||
facesup->styles[mapnum] = INVALID_LIGHTSTYLE;
|
||||
}
|
||||
facesup->lmscale = lightsurf->lightmapscale;
|
||||
} else {
|
||||
int mapnum;
|
||||
for (mapnum = 0; mapnum < numstyles && mapnum < MAXLIGHTMAPS; mapnum++) {
|
||||
face->styles[mapnum] = sorted.at(mapnum)->style;
|
||||
}
|
||||
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)
|
||||
return;
|
||||
|
||||
uint8_t *out, *lit, *lux;
|
||||
GetFileSpace(&out, &lit, &lux, size * numstyles);
|
||||
|
||||
int lightofs;
|
||||
|
||||
// Q2/HL native colored lightmaps
|
||||
if (bsp->loadversion->game->has_rgb_lightmap) {
|
||||
lightofs = lit - lit_filebase.data();
|
||||
} else {
|
||||
lightofs = out - filebase.data();
|
||||
}
|
||||
|
||||
if (facesup_decoupled) {
|
||||
facesup_decoupled->offset = lightofs;
|
||||
face->lightofs = -1;
|
||||
} else if (facesup) {
|
||||
facesup->lightofs = lightofs;
|
||||
} else {
|
||||
face->lightofs = lightofs;
|
||||
}
|
||||
|
||||
// sanity check that we don't save a lightmap for a non-lightmapped face
|
||||
{
|
||||
Q_assert(Face_IsLightmapped(bsp, face));
|
||||
}
|
||||
|
||||
for (int mapnum = 0; mapnum < numstyles; mapnum++) {
|
||||
const lightmap_t *lm = sorted.at(mapnum);
|
||||
|
||||
WriteSingleLightmap(bsp, face, lightsurf, lm, actual_width, actual_height, out, lit, lux, output_extents);
|
||||
|
||||
if (out) {
|
||||
out += size;
|
||||
}
|
||||
if (lit) {
|
||||
lit += (size * 3);
|
||||
}
|
||||
if (lux) {
|
||||
lux += (size * 3);
|
||||
}
|
||||
}
|
||||
|
||||
// write vanilla lightmap if -world_units_per_luxel is in use but not -novanilla
|
||||
if (facesup_decoupled && !light_options.novanilla.value()) {
|
||||
// FIXME: duplicates some code from above
|
||||
GetFileSpace(&out, &lit, &lux, lightsurf->vanilla_extents.numsamples() * numstyles);
|
||||
|
||||
// Q2/HL native colored lightmaps
|
||||
if (bsp->loadversion->game->has_rgb_lightmap) {
|
||||
lightofs = lit - lit_filebase.data();
|
||||
} else {
|
||||
lightofs = out - filebase.data();
|
||||
}
|
||||
face->lightofs = lightofs;
|
||||
|
||||
for (int mapnum = 0; mapnum < numstyles; mapnum++) {
|
||||
const lightmap_t *lm = sorted.at(mapnum);
|
||||
|
||||
WriteSingleLightmap_FromDecoupled(bsp, face, lightsurf, lm, lightsurf->vanilla_extents.width(),
|
||||
lightsurf->vanilla_extents.height(), out, lit, lux);
|
||||
|
||||
if (out) {
|
||||
out += lightsurf->vanilla_extents.numsamples();
|
||||
}
|
||||
if (lit) {
|
||||
lit += (lightsurf->vanilla_extents.numsamples() * 3);
|
||||
}
|
||||
if (lux) {
|
||||
lux += (lightsurf->vanilla_extents.numsamples() * 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lightsurf_t 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)
|
||||
{
|
||||
|
|
@ -3707,9 +2963,4 @@ void ResetLtFace()
|
|||
total_surflight_rays = 0;
|
||||
total_surflight_ray_hits = 0;
|
||||
#endif
|
||||
|
||||
fully_transparent_lightmaps = 0;
|
||||
|
||||
warned_about_light_map_overflow = false;
|
||||
warned_about_light_style_overflow = false;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue