Merge branch 'brushbsp' of https://github.com/ericwa/ericw-tools into brushbsp
This commit is contained in:
commit
328fa6ade6
|
|
@ -1 +1 @@
|
|||
Subproject commit 7bdf0628b1276379886c7f6dda2cef2b3b374f0b
|
||||
Subproject commit a33701196adfad74917046096bf5a2aa0ab0bb50
|
||||
|
|
@ -37,7 +37,7 @@ if (UNIX)
|
|||
endif (UNIX)
|
||||
|
||||
# set our C/C++ dialects
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
image:
|
||||
- Visual Studio 2019
|
||||
- Visual Studio 2022
|
||||
platform:
|
||||
- x64
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ mkdir cmakebuild
|
|||
|
||||
cd cmakebuild
|
||||
|
||||
cmake .. -T v142 -Dembree_DIR="C:\embree-3.12.1.x64.vc14.windows" -DTBB_DIR="C:\tbb\cmake" -DCMAKE_GENERATOR_PLATFORM=x64 -DENABLE_LIGHTPREVIEW=NO -DQt5Widgets_DIR="C:\Qt\5.8\msvc2013_64\lib\cmake\Qt5Widgets"
|
||||
cmake .. -T v143 -Dembree_DIR="C:\embree-3.12.1.x64.vc14.windows" -DTBB_DIR="C:\tbb\cmake" -DCMAKE_GENERATOR_PLATFORM=x64 -DENABLE_LIGHTPREVIEW=NO -DQt5Widgets_DIR="C:\Qt\5.8\msvc2013_64\lib\cmake\Qt5Widgets"
|
||||
|
||||
$cmakePlatform = "x64"
|
||||
|
||||
|
|
|
|||
|
|
@ -1693,14 +1693,14 @@ const bspversion_t bspver_qbism{Q2_QBISMIDENT, Q2_BSPVERSION, "qbism", "Quake II
|
|||
|
||||
bool surfflags_t::needs_write() const
|
||||
{
|
||||
return no_dirt || no_shadow || no_bounce || no_minlight || no_expand || light_ignore || phong_angle ||
|
||||
return no_dirt || no_shadow || no_bounce || no_minlight || no_expand || light_ignore || !surflight_rescale || phong_angle ||
|
||||
phong_angle_concave || minlight || !qv::emptyExact(minlight_color) || light_alpha || maxlight || lightcolorscale != 1.0;
|
||||
}
|
||||
|
||||
static auto as_tuple(const surfflags_t &flags)
|
||||
{
|
||||
return std::tie(flags.native, flags.is_nodraw, flags.is_hintskip, flags.is_hint, flags.no_dirt, flags.no_shadow, flags.no_bounce, flags.no_minlight, flags.no_expand,
|
||||
flags.light_ignore, flags.phong_angle, flags.phong_angle_concave, flags.minlight, flags.minlight_color, flags.light_alpha, flags.maxlight, flags.lightcolorscale);
|
||||
flags.light_ignore, flags.surflight_rescale, flags.phong_angle, flags.phong_angle_concave, flags.minlight, flags.minlight_color, flags.light_alpha, flags.maxlight, flags.lightcolorscale);
|
||||
}
|
||||
|
||||
bool surfflags_t::operator<(const surfflags_t &other) const
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ static faceextents_t get_face_extents(const mbsp_t &bsp, const bspxentries_t &bs
|
|||
return {face, bsp, bspx.lmwidth, bspx.lmheight, bspx.world_to_lm_space};
|
||||
}
|
||||
if (!use_bspx) {
|
||||
return {face, bsp, 16.0};
|
||||
return {face, bsp, LMSCALE_DEFAULT};
|
||||
}
|
||||
|
||||
return {face, bsp,
|
||||
|
|
|
|||
|
|
@ -622,6 +622,20 @@ void DecompressRow(const uint8_t *in, const int numbytes, uint8_t *decompressed)
|
|||
} while (out - decompressed < row);
|
||||
}
|
||||
|
||||
bspx_decoupled_lm_perface BSPX_DecoupledLM(const bspxentries_t &entries, int face_num)
|
||||
{
|
||||
auto &lump_bytes = entries.at("DECOUPLED_LM");
|
||||
|
||||
auto stream = imemstream(lump_bytes.data(), lump_bytes.size());
|
||||
|
||||
stream.seekg(face_num * sizeof(bspx_decoupled_lm_perface));
|
||||
stream >> endianness<std::endian::little>;
|
||||
|
||||
bspx_decoupled_lm_perface result;
|
||||
stream >= result;
|
||||
return result;
|
||||
}
|
||||
|
||||
qvec2d WorldToTexCoord(const qvec3d &world, const mtexinfo_t *tex)
|
||||
{
|
||||
/*
|
||||
|
|
@ -901,3 +915,27 @@ qvec3f faceextents_t::LMCoordToWorld(qvec2f lm) const
|
|||
const qvec4f res = lmToWorldMatrix * lmPadded;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Samples the lightmap at an integer coordinate
|
||||
*/
|
||||
qvec3b LM_Sample(const mbsp_t *bsp, const faceextents_t &faceextents, int byte_offset_of_face, qvec2i coord)
|
||||
{
|
||||
int pixel = coord[0] + (coord[1] * faceextents.width());
|
||||
|
||||
const uint8_t* data = bsp->dlightdata.data();
|
||||
|
||||
if (bsp->loadversion->game->has_rgb_lightmap) {
|
||||
return qvec3f{
|
||||
data[byte_offset_of_face + (pixel * 3) + 0],
|
||||
data[byte_offset_of_face + (pixel * 3) + 1],
|
||||
data[byte_offset_of_face + (pixel * 3) + 2]
|
||||
};
|
||||
} else {
|
||||
return qvec3f{
|
||||
data[byte_offset_of_face + pixel],
|
||||
data[byte_offset_of_face + pixel],
|
||||
data[byte_offset_of_face + pixel]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -304,16 +304,14 @@ void stat_tracker_t::print_stats()
|
|||
|
||||
stats_printed = true;
|
||||
|
||||
auto old = std::locale::global(std::locale("en_US.UTF-8"));
|
||||
// add 8 char padding just to keep it away from the left side
|
||||
size_t number_padding = number_of_digit_padding() + 4;
|
||||
|
||||
for (auto &stat : stats) {
|
||||
if (stat.show_even_if_zero || stat.count) {
|
||||
print(flag::STAT, "{}{:{}L} {}\n", stat.is_warning ? "WARNING: " : "", stat.count, stat.is_warning ? 0 : number_padding, stat.name);
|
||||
print(flag::STAT, "{}{:{}} {}\n", stat.is_warning ? "WARNING: " : "", fmt::group_digits(stat.count.load()), stat.is_warning ? 0 : number_padding, stat.name);
|
||||
}
|
||||
}
|
||||
std::locale::global(old);
|
||||
}
|
||||
|
||||
stat_tracker_t::~stat_tracker_t()
|
||||
|
|
|
|||
|
|
@ -224,11 +224,11 @@ struct fmt::formatter<aabb<T, Dim>> : formatter<qvec<T, Dim>>
|
|||
template<typename FormatContext>
|
||||
auto format(const aabb<T, Dim> &b, FormatContext &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
format_to(ctx.out(), "{{mins: ");
|
||||
formatter<qvec<T, Dim>>::format(b.mins(), ctx);
|
||||
format_to(ctx.out(), ", maxs: ");
|
||||
formatter<qvec<T, Dim>>::format(b.maxs(), ctx);
|
||||
format_to(ctx.out(), "}}");
|
||||
fmt::format_to(ctx.out(), "{{mins: ");
|
||||
fmt::formatter<qvec<T, Dim>>::format(b.mins(), ctx);
|
||||
fmt::format_to(ctx.out(), ", maxs: ");
|
||||
fmt::formatter<qvec<T, Dim>>::format(b.maxs(), ctx);
|
||||
fmt::format_to(ctx.out(), "}}");
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -185,6 +185,10 @@ struct surfflags_t
|
|||
// this face doesn't receive light
|
||||
bool light_ignore;
|
||||
|
||||
// if true, rescales any surface light emitted by these brushes to emit 50% light at 90 degrees from the surface normal
|
||||
// if false, use a more natural angle falloff of 0% at 90 degrees
|
||||
bool surflight_rescale = true;
|
||||
|
||||
// if non zero, enables phong shading and gives the angle threshold to use
|
||||
vec_t phong_angle;
|
||||
|
||||
|
|
@ -351,18 +355,18 @@ struct fmt::formatter<bspversion_t>
|
|||
auto format(const bspversion_t &v, FormatContext &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
if (v.name) {
|
||||
format_to(ctx.out(), "{} ", v.name);
|
||||
fmt::format_to(ctx.out(), "{} ", v.name);
|
||||
}
|
||||
|
||||
// Q2-esque BSPs are printed as, ex, IBSP:38
|
||||
if (v.version.has_value()) {
|
||||
char ident[5] = {(char)(v.ident & 0xFF), (char)((v.ident >> 8) & 0xFF), (char)((v.ident >> 16) & 0xFF),
|
||||
(char)((v.ident >> 24) & 0xFF), '\0'};
|
||||
return format_to(ctx.out(), "{}:{}", ident, v.version.value());
|
||||
return fmt::format_to(ctx.out(), "{}:{}", ident, v.version.value());
|
||||
}
|
||||
|
||||
// Q1-esque BSPs are printed as, ex, bsp29
|
||||
return format_to(ctx.out(), "{}", v.short_name);
|
||||
return fmt::format_to(ctx.out(), "{}", v.short_name);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -406,7 +410,7 @@ struct texvec : qmat<T, 2, 4>
|
|||
|
||||
// Fmt support
|
||||
template<class T>
|
||||
struct fmt::formatter<texvec<T>> : formatter<qmat<T, 2, 4>>
|
||||
struct fmt::formatter<texvec<T>> : fmt::formatter<qmat<T, 2, 4>>
|
||||
{
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <common/bspfile.hh>
|
||||
#include <common/bspxfile.hh>
|
||||
#include <common/mathlib.hh>
|
||||
#include <common/qvec.hh>
|
||||
#include <common/aabb.hh>
|
||||
|
|
@ -107,6 +108,8 @@ void Face_DebugPrint(const mbsp_t *bsp, const mface_t *face);
|
|||
void CompressRow(const uint8_t *vis, const size_t numbytes, std::back_insert_iterator<std::vector<uint8_t>> it);
|
||||
void DecompressRow(const uint8_t *in, const int numbytes, uint8_t *decompressed);
|
||||
|
||||
bspx_decoupled_lm_perface BSPX_DecoupledLM(const bspxentries_t &entries, int face_num);
|
||||
|
||||
/* ======================================================================== */
|
||||
|
||||
qvec2d WorldToTexCoord(const qvec3d &world, const mtexinfo_t *tex);
|
||||
|
|
@ -122,6 +125,8 @@ constexpr size_t MAXDIMENSION = 255 + 1;
|
|||
|
||||
struct world_units_per_luxel_t {};
|
||||
|
||||
constexpr float LMSCALE_DEFAULT = 16.0f;
|
||||
|
||||
class faceextents_t
|
||||
{
|
||||
public:
|
||||
|
|
@ -150,3 +155,5 @@ public:
|
|||
qvec2f worldToLMCoord(qvec3f world) const;
|
||||
qvec3f LMCoordToWorld(qvec2f lm) const;
|
||||
};
|
||||
|
||||
qvec3b LM_Sample(const mbsp_t *bsp, const faceextents_t &faceextents, int byte_offset_of_face, qvec2i coord);
|
||||
|
|
|
|||
|
|
@ -108,25 +108,8 @@ time_point I_FloatTime();
|
|||
* BYTE ORDER FUNCTIONS
|
||||
* ============================================================================
|
||||
*/
|
||||
// C++20 polyfill
|
||||
#if defined(__cpp_lib_endian) && __cpp_lib_endian >= 201907L
|
||||
#include <bit>
|
||||
#else
|
||||
namespace std
|
||||
{
|
||||
enum class endian
|
||||
{
|
||||
little = 0,
|
||||
big = 1,
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
native = big
|
||||
#else
|
||||
native = little
|
||||
#endif
|
||||
};
|
||||
} // namespace std
|
||||
#endif
|
||||
#include <bit>
|
||||
|
||||
// Binary streams; by default, streams use the native endianness
|
||||
// (unchanged bytes) but can be changed to a specific endianness
|
||||
|
|
|
|||
|
|
@ -113,6 +113,6 @@ struct fmt::formatter<fs::path>
|
|||
template<typename FormatContext>
|
||||
auto format(const fs::path &p, FormatContext &ctx)
|
||||
{
|
||||
return format_to(ctx.out(), "{}", p.string());
|
||||
return fmt::format_to(ctx.out(), "{}", p.string());
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -74,15 +74,15 @@ template<typename... Args>
|
|||
inline void print(flag type, const char *fmt, const Args &...args)
|
||||
{
|
||||
if (mask & type) {
|
||||
print(type, fmt::format(fmt, std::forward<const Args &>(args)...).c_str());
|
||||
print(type, fmt::format(fmt::runtime(fmt), std::forward<const Args &>(args)...).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// format print to default targets
|
||||
template<typename... Args>
|
||||
inline void print(const char *fmt, const Args &...args)
|
||||
inline void print(const char *formt, const Args &...args)
|
||||
{
|
||||
print(flag::DEFAULT, fmt::format(fmt, std::forward<const Args &>(args)...).c_str());
|
||||
print(flag::DEFAULT, fmt::format(fmt::runtime(formt), std::forward<const Args &>(args)...).c_str());
|
||||
}
|
||||
|
||||
void header(const char *name);
|
||||
|
|
@ -182,7 +182,7 @@ struct stat_tracker_t
|
|||
template<typename... Args>
|
||||
[[noreturn]] inline void Error(const char *fmt, const Args &...args)
|
||||
{
|
||||
auto formatted = fmt::format(fmt, std::forward<const Args &>(args)...);
|
||||
auto formatted = fmt::format(fmt::runtime(fmt), std::forward<const Args &>(args)...);
|
||||
Error(formatted.c_str());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,13 +93,13 @@ struct fmt::formatter<parser_source_location>
|
|||
auto format(const parser_source_location &v, FormatContext &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
if (v.source_name) {
|
||||
format_to(ctx.out(), "{}", *v.source_name.get());
|
||||
fmt::format_to(ctx.out(), "{}", *v.source_name.get());
|
||||
} else {
|
||||
format_to(ctx.out(), "unknown/unset location");
|
||||
fmt::format_to(ctx.out(), "unknown/unset location");
|
||||
}
|
||||
|
||||
if (v.line_number.has_value()) {
|
||||
format_to(ctx.out(), "[line {}]", v.line_number.value());
|
||||
fmt::format_to(ctx.out(), "[line {}]", v.line_number.value());
|
||||
}
|
||||
|
||||
return ctx.out();
|
||||
|
|
|
|||
|
|
@ -341,11 +341,11 @@ struct fmt::formatter<qvec<T, N>>
|
|||
auto format(const qvec<T, N> &p, FormatContext &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
for (size_t i = 0; i < N - 1; i++) {
|
||||
format_to(ctx.out(), "{}", p[i]);
|
||||
format_to(ctx.out(), " ");
|
||||
fmt::format_to(ctx.out(), "{}", p[i]);
|
||||
fmt::format_to(ctx.out(), " ");
|
||||
}
|
||||
|
||||
return format_to(ctx.out(), "{}", p[N - 1]);
|
||||
return fmt::format_to(ctx.out(), "{}", p[N - 1]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -838,9 +838,9 @@ struct fmt::formatter<qplane3<T>> : formatter<qvec<T, 3>>
|
|||
template<typename FormatContext>
|
||||
auto format(const qplane3<T> &p, FormatContext &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
format_to(ctx.out(), "{{normal: ");
|
||||
formatter<qvec<T, 3>>::format(p.normal, ctx);
|
||||
format_to(ctx.out(), ", dist: {}}}", p.dist);
|
||||
fmt::format_to(ctx.out(), "{{normal: ");
|
||||
fmt::formatter<qvec<T, 3>>::format(p.normal, ctx);
|
||||
fmt::format_to(ctx.out(), ", dist: {}}}", p.dist);
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
|
@ -1026,12 +1026,12 @@ struct fmt::formatter<qmat<T, NRow, NCol>> : formatter<qvec<T, NCol>>
|
|||
auto format(const qmat<T, NRow, NCol> &p, FormatContext &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
for (size_t i = 0; i < NRow; i++) {
|
||||
format_to(ctx.out(), "[ ");
|
||||
formatter<qvec<T, NCol>>::format(p.row(i), ctx);
|
||||
format_to(ctx.out(), " ]");
|
||||
fmt::format_to(ctx.out(), "[ ");
|
||||
fmt::formatter<qvec<T, NCol>>::format(p.row(i), ctx);
|
||||
fmt::format_to(ctx.out(), " ]");
|
||||
|
||||
if (i != NRow - 1) {
|
||||
format_to(ctx.out(), "\n");
|
||||
fmt::format_to(ctx.out(), "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include <limits>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
#include <functional>
|
||||
|
||||
namespace settings
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,5 +32,6 @@
|
|||
|
||||
// public functions
|
||||
|
||||
void ResetBounce();
|
||||
const std::vector<surfacelight_t> &BounceLights();
|
||||
void MakeBounceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp);
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ public:
|
|||
* Stores the RGB values to determine the light color
|
||||
*/
|
||||
|
||||
void ResetLightEntities();
|
||||
std::string TargetnameForLightStyle(int style);
|
||||
std::vector<std::unique_ptr<light_t>> &GetLights();
|
||||
std::vector<sun_t> &GetSuns();
|
||||
|
|
|
|||
|
|
@ -161,7 +161,8 @@ enum class debugmodes
|
|||
debugoccluded,
|
||||
debugneighbours,
|
||||
phong_tangents,
|
||||
phong_bitangents
|
||||
phong_bitangents,
|
||||
mottle
|
||||
};
|
||||
|
||||
enum class lightfile
|
||||
|
|
@ -385,6 +386,7 @@ public:
|
|||
setting_func phongdebug_obj;
|
||||
setting_func debugoccluded;
|
||||
setting_func debugneighbours;
|
||||
setting_func debugmottle;
|
||||
|
||||
light_settings();
|
||||
|
||||
|
|
@ -425,5 +427,6 @@ const modelinfo_t *ModelInfoForFace(const mbsp_t *bsp, int facenum);
|
|||
const img::texture *Face_Texture(const mbsp_t *bsp, const mface_t *face);
|
||||
const qvec3b &Face_LookupTextureColor(const mbsp_t *bsp, const mface_t *face);
|
||||
const qvec3d &Face_LookupTextureBounceColor(const mbsp_t *bsp, const mface_t *face);
|
||||
void light_reset();
|
||||
int light_main(int argc, const char **argv);
|
||||
int light_main(const std::vector<std::string> &args);
|
||||
|
|
|
|||
|
|
@ -61,3 +61,4 @@ 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);
|
||||
void ResetLtFace();
|
||||
|
|
|
|||
|
|
@ -79,3 +79,4 @@ public:
|
|||
};
|
||||
|
||||
const face_cache_t &FaceCacheForFNum(int fnum);
|
||||
void ResetPhong();
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ struct surfacelight_t
|
|||
bool rescale;
|
||||
};
|
||||
|
||||
void ResetSurflight();
|
||||
std::vector<surfacelight_t> &GetSurfaceLights();
|
||||
std::optional<std::tuple<int32_t, int32_t>> IsSurfaceLitFace(const mbsp_t *bsp, const mface_t *face);
|
||||
const std::vector<int> &SurfaceLightsForFaceNum(int facenum);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <common/polylib.hh>
|
||||
#include <vector>
|
||||
|
||||
void ResetEmbree();
|
||||
void Embree_TraceInit(const mbsp_t *bsp);
|
||||
hitresult_t Embree_TestSky(const qvec3d &start, const qvec3d &dirn, const modelinfo_t *self, const mface_t **face_out);
|
||||
hitresult_t Embree_TestLight(const qvec3d &start, const qvec3d &stop, const modelinfo_t *self);
|
||||
|
|
|
|||
|
|
@ -368,9 +368,9 @@ struct fmt::formatter<qbsp_plane_t> : formatter<qplane3d>
|
|||
template<typename FormatContext>
|
||||
auto format(const qbsp_plane_t &p, FormatContext &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
format_to(ctx.out(), "<");
|
||||
formatter<qplane3d>::format(p.get_plane(), ctx);
|
||||
format_to(ctx.out(), ", type: {}>", p.get_type());
|
||||
fmt::format_to(ctx.out(), "<");
|
||||
fmt::formatter<qplane3d>::format(p.get_plane(), ctx);
|
||||
fmt::format_to(ctx.out(), ", type: {}>", p.get_type());
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,6 +44,12 @@ mutex bouncelights_lock;
|
|||
static std::vector<surfacelight_t> bouncelights;
|
||||
static std::atomic_size_t bouncelightpoints;
|
||||
|
||||
void ResetBounce()
|
||||
{
|
||||
bouncelights.clear();
|
||||
bouncelightpoints = 0;
|
||||
}
|
||||
|
||||
static bool Face_ShouldBounce(const mbsp_t *bsp, const mface_t *face)
|
||||
{
|
||||
// make bounce light, only if this face is shadow casting
|
||||
|
|
|
|||
|
|
@ -33,6 +33,27 @@ std::vector<std::unique_ptr<light_t>> all_lights;
|
|||
std::vector<sun_t> all_suns;
|
||||
std::vector<entdict_t> entdicts;
|
||||
std::vector<entdict_t> radlights;
|
||||
static std::vector<std::pair<std::string, int>> lightstyleForTargetname;
|
||||
static std::vector<std::unique_ptr<light_t>> surfacelight_templates;
|
||||
static std::ofstream surflights_dump_file;
|
||||
static fs::path surflights_dump_filename;
|
||||
|
||||
/**
|
||||
* Resets global data in this file
|
||||
*/
|
||||
void ResetLightEntities()
|
||||
{
|
||||
all_lights.clear();
|
||||
all_suns.clear();
|
||||
entdicts.clear();
|
||||
radlights.clear();
|
||||
|
||||
lightstyleForTargetname.clear();
|
||||
|
||||
surfacelight_templates.clear();
|
||||
surflights_dump_file = {};
|
||||
surflights_dump_filename.clear();
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<light_t>> &GetLights()
|
||||
{
|
||||
|
|
@ -104,8 +125,6 @@ void light_t::expandAABB(const qvec3d &pt) { bounds += pt; }
|
|||
* ============================================================================
|
||||
*/
|
||||
|
||||
static std::vector<std::pair<std::string, int>> lightstyleForTargetname;
|
||||
|
||||
entdict_t &WorldEnt()
|
||||
{
|
||||
if (entdicts.size() == 0 || entdicts.at(0).get("classname") != "worldspawn") {
|
||||
|
|
@ -1152,16 +1171,11 @@ void WriteEntitiesToString(const settings::worldspawn_keys &cfg, mbsp_t *bsp)
|
|||
* =======================================================================
|
||||
*/
|
||||
|
||||
static std::vector<std::unique_ptr<light_t>> surfacelight_templates;
|
||||
|
||||
const std::vector<std::unique_ptr<light_t>> &GetSurfaceLightTemplates()
|
||||
{
|
||||
return surfacelight_templates;
|
||||
}
|
||||
|
||||
static std::ofstream surflights_dump_file;
|
||||
static fs::path surflights_dump_filename;
|
||||
|
||||
static void SurfLights_WriteEntityToFile(light_t *entity, const qvec3d &pos)
|
||||
{
|
||||
Q_assert(entity->epairs != nullptr);
|
||||
|
|
|
|||
|
|
@ -351,7 +351,14 @@ light_settings::light_settings()
|
|||
CheckNoDebugModeSet();
|
||||
debugmode = debugmodes::debugneighbours;
|
||||
},
|
||||
&debug_group, "save neighboring faces data to lightmap (requires -debugface)"}
|
||||
&debug_group, "save neighboring faces data to lightmap (requires -debugface)"},
|
||||
|
||||
debugmottle{this, "debugmottle",
|
||||
[&](source) {
|
||||
CheckNoDebugModeSet();
|
||||
debugmode = debugmodes::mottle;
|
||||
},
|
||||
&debug_group, "save mottle pattern to lightmap"}
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -707,10 +714,10 @@ static void FindModelInfo(const mbsp_t *bsp)
|
|||
|
||||
float lightmapscale = WorldEnt().get_int("_lightmap_scale");
|
||||
if (!lightmapscale)
|
||||
lightmapscale = 16; /* the default */
|
||||
lightmapscale = LMSCALE_DEFAULT; /* the default */
|
||||
if (lightmapscale <= 0)
|
||||
FError("lightmap scale is 0 or negative\n");
|
||||
if (light_options.lightmap_scale.isChanged() || lightmapscale != 16)
|
||||
if (light_options.lightmap_scale.isChanged() || lightmapscale != LMSCALE_DEFAULT)
|
||||
logging::print("Forcing lightmap scale of {}qu\n", lightmapscale);
|
||||
/*I'm going to do this check in the hopes that there's a benefit to cheaper scaling in engines (especially software
|
||||
* ones that might be able to just do some mip hacks). This tool doesn't really care.*/
|
||||
|
|
@ -1065,6 +1072,9 @@ static void LoadExtendedTexinfoFlags(const fs::path &sourcefilename, const mbsp_
|
|||
if (val.contains("light_ignore")) {
|
||||
flags.light_ignore = val.at("light_ignore").get<bool>();
|
||||
}
|
||||
if (val.contains("surflight_rescale")) {
|
||||
flags.surflight_rescale = val.at("surflight_rescale").get<bool>();
|
||||
}
|
||||
if (val.contains("phong_angle")) {
|
||||
flags.phong_angle = val.at("phong_angle").get<vec_t>();
|
||||
}
|
||||
|
|
@ -1458,6 +1468,51 @@ void load_textures(const mbsp_t *bsp)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets globals in this file
|
||||
*/
|
||||
static void ResetLight()
|
||||
{
|
||||
dirt_in_use = false;
|
||||
light_surfaces.clear();
|
||||
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;
|
||||
|
||||
modelinfo.clear();
|
||||
tracelist.clear();
|
||||
selfshadowlist.clear();
|
||||
shadowworldonlylist.clear();
|
||||
switchableshadowlist.clear();
|
||||
|
||||
extended_texinfo_flags.clear();
|
||||
|
||||
dump_facenum = -1;
|
||||
dump_vertnum = -1;
|
||||
}
|
||||
|
||||
void light_reset()
|
||||
{
|
||||
ResetBounce();
|
||||
ResetLightEntities();
|
||||
ResetLight();
|
||||
ResetLtFace();
|
||||
ResetPhong();
|
||||
ResetSurflight();
|
||||
ResetEmbree();
|
||||
}
|
||||
|
||||
/*
|
||||
* ==================
|
||||
* main
|
||||
|
|
@ -1466,6 +1521,8 @@ void load_textures(const mbsp_t *bsp)
|
|||
*/
|
||||
int light_main(int argc, const char **argv)
|
||||
{
|
||||
light_reset();
|
||||
|
||||
bspdata_t bspdata;
|
||||
|
||||
light_options.preinitialize(argc, argv);
|
||||
|
|
|
|||
259
light/ltface.cc
259
light/ltface.cc
|
|
@ -703,7 +703,7 @@ static std::unique_ptr<lightsurf_t> Lightsurf_Init(const modelinfo_t *modelinfo,
|
|||
} 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, LMSCALE_DEFAULT);
|
||||
|
||||
CalcPoints(modelinfo, modelinfo->offset, lightsurf.get(), bsp, face);
|
||||
|
||||
|
|
@ -1395,6 +1395,110 @@ static void LightFace_Sky(const sun_t *sun, lightsurf_t *lightsurf, lightmapdict
|
|||
}
|
||||
}
|
||||
|
||||
// Mottle
|
||||
|
||||
static int mod_round_to_neg_inf(int x, int y) {
|
||||
assert(y > 0);
|
||||
if (x >= 0) {
|
||||
return x % y;
|
||||
}
|
||||
// e.g. with mod_round_to_neg_inf(-7, 3) we want +2
|
||||
const int temp = (-x) % y;
|
||||
return y - temp;
|
||||
}
|
||||
|
||||
constexpr int mottle_texsize = 256;
|
||||
|
||||
/**
|
||||
* integers 0 through 255 shuffled with Python:
|
||||
*
|
||||
* import random
|
||||
* a = list(range(0, 256))
|
||||
* random.shuffle(a)
|
||||
*/
|
||||
static constexpr uint8_t MottlePermutation256[] = {11, 255, 250, 82, 217, 9, 144, 93, 136, 153, 55, 71, 73, 204, 96,
|
||||
180, 126, 8, 50, 46, 113, 91, 238, 143, 30, 215, 191, 243, 65, 58, 208, 33, 86, 1, 182, 118, 83, 115, 207, 52, 94,
|
||||
112, 205, 48, 99, 254, 117, 101, 157, 140, 72, 242, 244, 154, 10, 135, 155, 168, 125, 183, 148, 116, 187, 166, 25,
|
||||
156, 177, 231, 165, 57, 221, 105, 28, 211, 127, 41, 142, 253, 146, 87, 122, 229, 162, 137, 194, 174, 167, 15, 220,
|
||||
26, 235, 3, 39, 80, 88, 42, 202, 12, 97, 53, 70, 123, 170, 110, 214, 192, 173, 84, 169, 188, 64, 102, 147, 158, 100,
|
||||
69, 213, 193, 43, 20, 13, 237, 171, 103, 32, 190, 223, 150, 131, 206, 85, 124, 163, 18, 139, 132, 79, 29, 216, 232,
|
||||
178, 74, 24, 141, 201, 181, 152, 4, 7, 159, 134, 212, 226, 245, 164, 239, 47, 66, 27, 40, 197, 81, 78, 219, 228,
|
||||
241, 121, 23, 120, 230, 76, 252, 199, 184, 45, 203, 161, 89, 16, 21, 119, 5, 209, 196, 68, 130, 195, 176, 225, 233,
|
||||
128, 22, 248, 179, 249, 61, 108, 138, 145, 31, 49, 107, 56, 172, 224, 210, 6, 160, 189, 104, 200, 44, 175, 133, 77,
|
||||
62, 106, 92, 186, 227, 14, 38, 247, 37, 17, 222, 36, 75, 129, 185, 251, 240, 54, 151, 2, 98, 149, 0, 63, 218, 60,
|
||||
198, 19, 59, 90, 246, 234, 67, 51, 109, 95, 236, 35, 34, 114, 111};
|
||||
|
||||
/**
|
||||
* Return a noise texture value from 0-47.
|
||||
*
|
||||
* Vanilla Q2 tools just called (rand() % 48) per-luxel, which generates seams
|
||||
* and scales in size with lightmap scale.
|
||||
*
|
||||
* Replacement code uses "value noise", generating a tiling 3D texture
|
||||
* in world space.
|
||||
*/
|
||||
static float Mottle(const qvec3d &position)
|
||||
{
|
||||
#if 0
|
||||
return rand() % 48;
|
||||
#else
|
||||
const float world_to_tex = 1/16.0f;
|
||||
|
||||
qvec3d texspace_pos = position * world_to_tex;
|
||||
|
||||
int coord_floor_x = static_cast<int>(floor(texspace_pos[0]));
|
||||
int coord_floor_y = static_cast<int>(floor(texspace_pos[1]));
|
||||
int coord_floor_z = static_cast<int>(floor(texspace_pos[2]));
|
||||
|
||||
float coord_frac_x = static_cast<float>(texspace_pos[0] - coord_floor_x);
|
||||
float coord_frac_y = static_cast<float>(texspace_pos[1] - coord_floor_y);
|
||||
float coord_frac_z = static_cast<float>(texspace_pos[2] - coord_floor_z);
|
||||
|
||||
assert(coord_frac_x >= 0 && coord_frac_x <= 1);
|
||||
assert(coord_frac_y >= 0 && coord_frac_y <= 1);
|
||||
assert(coord_frac_z >= 0 && coord_frac_z <= 1);
|
||||
|
||||
coord_floor_x = mod_round_to_neg_inf(coord_floor_x, mottle_texsize);
|
||||
coord_floor_y = mod_round_to_neg_inf(coord_floor_y, mottle_texsize);
|
||||
coord_floor_z = mod_round_to_neg_inf(coord_floor_z, mottle_texsize);
|
||||
|
||||
assert(coord_floor_x >= 0 && coord_floor_x < mottle_texsize);
|
||||
assert(coord_floor_y >= 0 && coord_floor_y < mottle_texsize);
|
||||
assert(coord_floor_z >= 0 && coord_floor_z < mottle_texsize);
|
||||
|
||||
// look up sample in the 3d texture at an integer coordinate
|
||||
auto tex = [](int x, int y, int z) -> uint8_t {
|
||||
int v;
|
||||
v = MottlePermutation256[x % 256];
|
||||
v = MottlePermutation256[(v + y) % 256];
|
||||
v = MottlePermutation256[(v + z) % 256];
|
||||
return v;
|
||||
};
|
||||
|
||||
// 3D bilinear interpolation
|
||||
float res = mix(
|
||||
mix(
|
||||
mix(tex(coord_floor_x, coord_floor_y, coord_floor_z),
|
||||
tex(coord_floor_x + 1, coord_floor_y, coord_floor_z),
|
||||
coord_frac_x),
|
||||
mix(tex(coord_floor_x, coord_floor_y + 1, coord_floor_z),
|
||||
tex(coord_floor_x + 1, coord_floor_y + 1, coord_floor_z),
|
||||
coord_frac_x),
|
||||
coord_frac_y),
|
||||
mix(
|
||||
mix(tex(coord_floor_x, coord_floor_y, coord_floor_z + 1),
|
||||
tex(coord_floor_x + 1, coord_floor_y, coord_floor_z + 1),
|
||||
coord_frac_x),
|
||||
mix(tex(coord_floor_x, coord_floor_y + 1, coord_floor_z + 1),
|
||||
tex(coord_floor_x + 1, coord_floor_y + 1, coord_floor_z + 1),
|
||||
coord_frac_x),
|
||||
coord_frac_y),
|
||||
coord_frac_z);
|
||||
|
||||
return (res / 255.0f) * 48.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* ============
|
||||
* LightFace_Min
|
||||
|
|
@ -1425,7 +1529,7 @@ static void LightFace_Min(const mbsp_t *bsp, const mface_t *face, const qvec3d &
|
|||
sample.color += color * (value / 255.0);
|
||||
} else {
|
||||
if (lightsurf->minlightMottle) {
|
||||
value += rand() % 48;
|
||||
value += Mottle(lightsurf->points[i]);
|
||||
}
|
||||
Light_ClampMin(sample, value, color);
|
||||
}
|
||||
|
|
@ -1563,6 +1667,23 @@ static void LightFace_PhongDebug(const lightsurf_t *lightsurf, lightmapdict_t *l
|
|||
Lightmap_Save(lightmaps, lightsurf, lightmap, 0);
|
||||
}
|
||||
|
||||
static void LightFace_DebugMottle(const lightsurf_t *lightsurf, lightmapdict_t *lightmaps)
|
||||
{
|
||||
/* use a style 0 light map */
|
||||
lightmap_t *lightmap = Lightmap_ForStyle(lightmaps, 0, lightsurf);
|
||||
|
||||
/* Overwrite each point with the mottle noise for that sample... */
|
||||
for (int i = 0; i < lightsurf->points.size(); i++) {
|
||||
lightsample_t &sample = lightmap->samples[i];
|
||||
// mottle is meant to be applied on top of minlight, so add some here
|
||||
// for preview purposes.
|
||||
const float minlight = 20.0f;
|
||||
sample.color = qvec3f(minlight + Mottle(lightsurf->points[i]));
|
||||
}
|
||||
|
||||
Lightmap_Save(lightmaps, lightsurf, lightmap, 0);
|
||||
}
|
||||
|
||||
// mxd. Surface light falloff. Returns color in [0,255]
|
||||
inline qvec3f SurfaceLight_ColorAtDist(
|
||||
const settings::worldspawn_keys &cfg, const float &surf_scale, const float &intensity, const qvec3d &color, const float &dist, const float &hotspot_clamp)
|
||||
|
|
@ -1832,6 +1953,7 @@ void SetupDirt(settings::worldspawn_keys &cfg)
|
|||
|
||||
/* iterate angle */
|
||||
float angle = 0.0f;
|
||||
numDirtVectors = 0;
|
||||
for (int i = 0; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep) {
|
||||
/* iterate elevation */
|
||||
float elevation = elevationStep * 0.5f;
|
||||
|
|
@ -2534,6 +2656,80 @@ static void WriteSingleLightmap(const mbsp_t *bsp, const mface_t *face, const li
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - 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 = clamp(x, 0, lightsurf->width - 1);
|
||||
const int y_clamped = 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)
|
||||
|
|
@ -2726,13 +2922,13 @@ void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup,
|
|||
lightofs = out - filebase.data();
|
||||
}
|
||||
|
||||
if (facesup) {
|
||||
if (facesup_decoupled) {
|
||||
facesup_decoupled->offset = lightofs;
|
||||
face->lightofs = -1;
|
||||
} else if (facesup) {
|
||||
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
|
||||
|
|
@ -2758,6 +2954,37 @@ void SaveLightmapSurface(const mbsp_t *bsp, mface_t *face, facesup_t *facesup,
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<lightsurf_t> CreateLightmapSurface(const mbsp_t *bsp, const mface_t *face, const facesup_t *facesup,
|
||||
|
|
@ -2888,6 +3115,9 @@ void DirectLightFace(const mbsp_t *bsp, lightsurf_t &lightsurf, const settings::
|
|||
|
||||
if (light_options.debugmode == debugmodes::debugneighbours)
|
||||
LightFace_DebugNeighbours(&lightsurf, lightmaps);
|
||||
|
||||
if (light_options.debugmode == debugmodes::mottle)
|
||||
LightFace_DebugMottle(&lightsurf, lightmaps);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2913,3 +3143,20 @@ void IndirectLightFace(const mbsp_t *bsp, lightsurf_t &lightsurf, const settings
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResetLtFace()
|
||||
{
|
||||
total_light_rays = 0;
|
||||
total_light_ray_hits = 0;
|
||||
total_samplepoints = 0;
|
||||
|
||||
total_bounce_rays = 0;
|
||||
total_bounce_ray_hits = 0;
|
||||
total_surflight_rays = 0;
|
||||
total_surflight_ray_hits = 0;
|
||||
|
||||
fully_transparent_lightmaps = 0;
|
||||
|
||||
warned_about_light_map_overflow = false;
|
||||
warned_about_light_style_overflow = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,17 @@ static map<int, vector<const mface_t *>> planesToFaces;
|
|||
static edgeToFaceMap_t EdgeToFaceMap;
|
||||
static vector<face_cache_t> FaceCache;
|
||||
|
||||
void ResetPhong()
|
||||
{
|
||||
s_builtPhongCaches = false;
|
||||
vertex_normals = {};
|
||||
smoothFaces = {};
|
||||
vertsToFaces = {};
|
||||
planesToFaces = {};
|
||||
EdgeToFaceMap = {};
|
||||
FaceCache = {};
|
||||
}
|
||||
|
||||
vector<const mface_t *> FacesUsingVert(int vertnum)
|
||||
{
|
||||
const auto &vertsToFaces_const = vertsToFaces;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,13 @@ static std::vector<surfacelight_t> surfacelights;
|
|||
static std::map<int, std::vector<int>> surfacelightsByFacenum;
|
||||
static size_t total_surflight_points = 0;
|
||||
|
||||
void ResetSurflight()
|
||||
{
|
||||
surfacelights = {};
|
||||
surfacelightsByFacenum = {};
|
||||
total_surflight_points = {};
|
||||
}
|
||||
|
||||
std::vector<surfacelight_t> &GetSurfaceLights()
|
||||
{
|
||||
return surfacelights;
|
||||
|
|
@ -54,6 +61,8 @@ static void MakeSurfaceLight(const mbsp_t *bsp, const settings::worldspawn_keys
|
|||
auto poly = GLM_FacePoints(bsp, face);
|
||||
const float facearea = qv::PolyArea(poly.begin(), poly.end());
|
||||
|
||||
const surfflags_t &extended_flags = extended_texinfo_flags[face->texinfo];
|
||||
|
||||
// Avoid small, or zero-area faces
|
||||
if (facearea < 1)
|
||||
return;
|
||||
|
|
@ -108,7 +117,7 @@ static void MakeSurfaceLight(const mbsp_t *bsp, const settings::worldspawn_keys
|
|||
l.omnidirectional = !is_directional;
|
||||
l.points = std::move(points);
|
||||
l.style = style;
|
||||
l.rescale = true;
|
||||
l.rescale = extended_flags.surflight_rescale;
|
||||
|
||||
// Init bbox...
|
||||
if (light_options.visapprox.value() == visapprox_t::RAYS) {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,30 @@ sceneinfo skygeom; // sky. always occludes.
|
|||
sceneinfo solidgeom; // solids. always occludes.
|
||||
sceneinfo filtergeom; // conditional occluders.. needs to run ray intersection filter
|
||||
|
||||
static RTCDevice device;
|
||||
RTCScene scene;
|
||||
|
||||
static const mbsp_t *bsp_static;
|
||||
|
||||
void ResetEmbree()
|
||||
{
|
||||
skygeom = {};
|
||||
solidgeom = {};
|
||||
filtergeom = {};
|
||||
|
||||
if (scene) {
|
||||
rtcReleaseScene(scene);
|
||||
scene = nullptr;
|
||||
}
|
||||
|
||||
if (device) {
|
||||
rtcReleaseDevice(device);
|
||||
device = nullptr;
|
||||
}
|
||||
|
||||
bsp_static = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1.0 unless a custom alpha value is set.
|
||||
* The priority is: "_light_alpha" (read from extended_texinfo_flags), then "alpha", then Q2 surface flags
|
||||
|
|
@ -229,11 +253,6 @@ static void CreateGeometryFromWindings(RTCDevice g_device, RTCScene scene, const
|
|||
rtcCommitGeometry(geom_1);
|
||||
}
|
||||
|
||||
RTCDevice device;
|
||||
RTCScene scene;
|
||||
|
||||
static const mbsp_t *bsp_static;
|
||||
|
||||
void ErrorCallback(void *userptr, const RTCError code, const char *str)
|
||||
{
|
||||
fmt::print("RTC Error {}: {}\n", code, str);
|
||||
|
|
|
|||
|
|
@ -779,31 +779,24 @@ static void Brush_LoadEntity(mapentity_t &dst, mapentity_t &src, hull_index_t hu
|
|||
|
||||
/* entities in some games never use water merging */
|
||||
if (!map.is_world_entity(dst) && !qbsp_options.target_game->allow_contented_bmodels) {
|
||||
contents = qbsp_options.target_game->create_solid_contents();
|
||||
// bmodels become solid in Q1
|
||||
|
||||
/* Hack to turn bmodels with "_mirrorinside" into func_detail_fence in hull 0.
|
||||
this is to allow "_mirrorinside" to work on func_illusionary, func_wall, etc.
|
||||
Otherwise they would be CONTENTS_SOLID and the inside faces would be deleted.
|
||||
|
||||
It's CONTENTS_DETAIL_FENCE because this gets mapped to CONTENTS_SOLID just
|
||||
before writing the bsp, and bmodels normally have CONTENTS_SOLID as their
|
||||
contents type.
|
||||
*/
|
||||
if (!hullnum.value_or(0) && contents.is_mirrored(qbsp_options.target_game)) {
|
||||
contents = qbsp_options.target_game->create_detail_fence_contents(contents);
|
||||
}
|
||||
// to allow use of _mirrorinside, we'll set it to detail fence, which will get remapped back
|
||||
// to CONTENTS_SOLID at export. (we wouldn't generate inside faces if the content was CONTENTS_SOLID
|
||||
// from the start.)
|
||||
contents = qbsp_options.target_game->create_detail_fence_contents(qbsp_options.target_game->create_solid_contents());
|
||||
}
|
||||
|
||||
if (hullnum.value_or(0)) {
|
||||
/* nonsolid brushes don't show up in clipping hulls */
|
||||
if (!contents.is_any_solid(qbsp_options.target_game) && !contents.is_sky(qbsp_options.target_game)) {
|
||||
if (!contents.is_any_solid(qbsp_options.target_game)
|
||||
&& !contents.is_sky(qbsp_options.target_game)
|
||||
&& !contents.is_fence(qbsp_options.target_game)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* sky brushes are solid in the collision hulls */
|
||||
if (contents.is_sky(qbsp_options.target_game)) {
|
||||
contents = qbsp_options.target_game->create_solid_contents();
|
||||
}
|
||||
/* all used brushes are solid in the collision hulls */
|
||||
contents = qbsp_options.target_game->create_solid_contents();
|
||||
}
|
||||
|
||||
// fixme-brushbsp: function calls above can override the values below
|
||||
|
|
|
|||
|
|
@ -576,6 +576,8 @@ static surfflags_t SurfFlagsForEntity(const maptexinfo_t &texinfo, const mapenti
|
|||
flags.no_minlight = true;
|
||||
if (entity.epairs.get_int("_lightignore") == 1)
|
||||
flags.light_ignore = true;
|
||||
if (entity.epairs.has("_surflight_rescale") && entity.epairs.get_int("_surflight_rescale") == 0)
|
||||
flags.surflight_rescale = false;
|
||||
|
||||
// "_minlight_exclude", "_minlight_exclude2", "_minlight_exclude3"...
|
||||
for (int i = 0; i <= 9; i++) {
|
||||
|
|
|
|||
|
|
@ -770,7 +770,7 @@ void EmitAreaPortals(node_t *headnode)
|
|||
|
||||
struct visible_faces_stats_t : logging::stat_tracker_t
|
||||
{
|
||||
stat &sides_not_found = register_stat("sides not found", false, true);
|
||||
stat &sides_not_found = register_stat("sides not found (use -verbose to display)", false, true);
|
||||
stat &sides_visible = register_stat("sides visible");
|
||||
};
|
||||
|
||||
|
|
@ -870,6 +870,7 @@ static void FindPortalSide(portal_t *p, visible_faces_stats_t &stats)
|
|||
|
||||
if (!bestside[0] && !bestside[1]) {
|
||||
stats.sides_not_found++;
|
||||
logging::print(logging::flag::VERBOSE, "couldn't find portal side at {}\n", p->winding.center());
|
||||
}
|
||||
|
||||
p->sidefound = true;
|
||||
|
|
|
|||
|
|
@ -368,6 +368,9 @@ static void WriteExtendedTexinfoFlags(void)
|
|||
if (tx.flags.light_ignore) {
|
||||
t["light_ignore"] = tx.flags.light_ignore;
|
||||
}
|
||||
if (tx.flags.surflight_rescale == false) {
|
||||
t["surflight_rescale"] = tx.flags.surflight_rescale;
|
||||
}
|
||||
if (tx.flags.phong_angle) {
|
||||
t["phong_angle"] = tx.flags.phong_angle;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
// Game: Quake 2
|
||||
// Format: Quake2 (Valve)
|
||||
// entity 0
|
||||
{
|
||||
"classname" "worldspawn"
|
||||
"_tb_textures" "textures/e1u1"
|
||||
"_bounce" "0"
|
||||
// brush 0
|
||||
{
|
||||
( 928 -1072 880 ) ( 928 -1504 880 ) ( 928 -1504 864 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 928 -1504 880 ) ( 1120 -1504 880 ) ( 1120 -1504 864 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1504 864 ) ( 1120 -1072 864 ) ( 928 -1072 864 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 928 -1072 880 ) ( 1120 -1072 880 ) ( 1120 -1504 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1072 864 ) ( 1120 -1072 880 ) ( 928 -1072 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1504 880 ) ( 1120 -1072 880 ) ( 1120 -1072 864 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
}
|
||||
// brush 1
|
||||
{
|
||||
( 928 -1072 1040 ) ( 928 -1504 1040 ) ( 928 -1504 1024 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 928 -1504 1040 ) ( 1120 -1504 1040 ) ( 1120 -1504 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1504 1024 ) ( 1120 -1072 1024 ) ( 928 -1072 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 928 -1072 1040 ) ( 1120 -1072 1040 ) ( 1120 -1504 1040 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1072 1024 ) ( 1120 -1072 1040 ) ( 928 -1072 1040 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1504 1040 ) ( 1120 -1072 1040 ) ( 1120 -1072 1024 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
}
|
||||
// brush 2
|
||||
{
|
||||
( 1120 -1072 1024 ) ( 1120 -1504 1024 ) ( 1120 -1504 880 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1504 1024 ) ( 1136 -1504 1024 ) ( 1136 -1504 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1136 -1504 880 ) ( 1136 -1072 880 ) ( 1120 -1072 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1072 1024 ) ( 1136 -1072 1024 ) ( 1136 -1504 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 1136 -1072 880 ) ( 1136 -1072 1024 ) ( 1120 -1072 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1136 -1504 1024 ) ( 1136 -1072 1024 ) ( 1136 -1072 880 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
}
|
||||
// brush 3
|
||||
{
|
||||
( 928 -1072 1024 ) ( 928 -1504 1024 ) ( 928 -1504 880 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 928 -1504 1024 ) ( 944 -1504 1024 ) ( 944 -1504 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 944 -1504 880 ) ( 944 -1072 880 ) ( 928 -1072 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 928 -1072 1024 ) ( 944 -1072 1024 ) ( 944 -1504 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 944 -1072 880 ) ( 944 -1072 1024 ) ( 928 -1072 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 944 -1504 1024 ) ( 944 -1072 1024 ) ( 944 -1072 880 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
}
|
||||
// brush 4
|
||||
{
|
||||
( 944 -1072 1024 ) ( 944 -1088 1024 ) ( 944 -1088 880 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 944 -1088 1024 ) ( 1120 -1088 1024 ) ( 1120 -1088 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1088 880 ) ( 1120 -1072 880 ) ( 944 -1072 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 944 -1072 1024 ) ( 1120 -1072 1024 ) ( 1120 -1088 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1072 880 ) ( 1120 -1072 1024 ) ( 944 -1072 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1120 -1088 1024 ) ( 1120 -1072 1024 ) ( 1120 -1072 880 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
}
|
||||
// brush 5
|
||||
{
|
||||
( 936 -1504 1024 ) ( 936 -1520 1024 ) ( 936 -1520 880 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 936 -1520 1024 ) ( 1112 -1520 1024 ) ( 1112 -1520 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1112 -1520 880 ) ( 1112 -1504 880 ) ( 936 -1504 880 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 936 -1504 1024 ) ( 1112 -1504 1024 ) ( 1112 -1520 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 0 0
|
||||
( 1112 -1504 880 ) ( 1112 -1504 1024 ) ( 936 -1504 1024 ) e1u1/box3_7 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
( 1112 -1520 1024 ) ( 1112 -1504 1024 ) ( 1112 -1504 880 ) e1u1/box3_7 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 0 0
|
||||
}
|
||||
}
|
||||
// entity 1
|
||||
{
|
||||
"classname" "info_player_start"
|
||||
"origin" "1040 -1184 904"
|
||||
"angle" "270"
|
||||
}
|
||||
// entity 2
|
||||
{
|
||||
"classname" "func_group"
|
||||
"_surflight_rescale" "0"
|
||||
// brush 0
|
||||
{
|
||||
( 968 -1264 992 ) ( 968 -1464 992 ) ( 968 -1464 904 ) e1u1/box3_6 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 1 100
|
||||
( 968 -1464 992 ) ( 1104 -1464 992 ) ( 1104 -1464 904 ) e1u1/box3_6 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 1 100
|
||||
( 1104 -1464 904 ) ( 1104 -1264 904 ) ( 968 -1264 904 ) e1u1/box3_6 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 1 100
|
||||
( 968 -1264 992 ) ( 1104 -1264 992 ) ( 1104 -1464 992 ) e1u1/box3_6 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 0 1 100
|
||||
( 1104 -1264 904 ) ( 1104 -1264 992 ) ( 968 -1264 992 ) e1u1/box3_6 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 1 100
|
||||
( 1104 -1464 992 ) ( 1104 -1264 992 ) ( 1104 -1264 904 ) e1u1/box3_6 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 0 1 100
|
||||
}
|
||||
}
|
||||
|
|
@ -977,3 +977,10 @@
|
|||
"origin" "-398 -160 94"
|
||||
"light" "1200"
|
||||
}
|
||||
// entity 7
|
||||
{
|
||||
"classname" "light"
|
||||
"origin" "232 -248 104"
|
||||
"style" "1"
|
||||
"_color" "183 255 227"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
// Game: Quake
|
||||
// Format: Valve
|
||||
// entity 0
|
||||
{
|
||||
"mapversion" "220"
|
||||
"classname" "worldspawn"
|
||||
"wad" "deprecated/free_wad.wad;deprecated/fence.wad;deprecated/origin.wad;deprecated/hintskip.wad"
|
||||
"_wateralpha" "0.5"
|
||||
"_tb_def" "builtin:Quake.fgd"
|
||||
// brush 0
|
||||
{
|
||||
( -112 -112 96 ) ( -112 -111 96 ) ( -112 -112 97 ) orangestuff8 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
( -80 -272 80 ) ( -81 -272 80 ) ( -80 -272 81 ) orangestuff8 [ -1 0 0 16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( -80 -432 80 ) ( -80 -431 80 ) ( -81 -432 80 ) orangestuff8 [ 1 0 0 -16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -160 -112 96 ) ( -161 -112 96 ) ( -160 -111 96 ) orangestuff8 [ -1 0 0 16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -160 0 96 ) ( -160 0 97 ) ( -161 0 96 ) orangestuff8 [ 1 0 0 -16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( 64 -432 80 ) ( 64 -432 81 ) ( 64 -431 80 ) orangestuff8 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
}
|
||||
// brush 1
|
||||
{
|
||||
( -112 -96 96 ) ( -112 -95 96 ) ( -112 -96 97 ) orangestuff8 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
( -80 0 80 ) ( -81 0 80 ) ( -80 0 81 ) orangestuff8 [ -1 0 0 16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( -80 -416 80 ) ( -80 -415 80 ) ( -81 -416 80 ) orangestuff8 [ 1 0 0 -16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -160 -96 224 ) ( -161 -96 224 ) ( -160 -95 224 ) orangestuff8 [ -1 0 0 16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -160 16 96 ) ( -160 16 97 ) ( -161 16 96 ) orangestuff8 [ 1 0 0 -16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( 64 -416 80 ) ( 64 -416 81 ) ( 64 -415 80 ) orangestuff8 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
}
|
||||
// brush 2
|
||||
{
|
||||
( -112 -384 96 ) ( -112 -383 96 ) ( -112 -384 97 ) orangestuff8 [ 0 1 0 8 ] [ 0 0 -1 0 ] 0 2 2
|
||||
( -80 -288 80 ) ( -81 -288 80 ) ( -80 -288 81 ) orangestuff8 [ -1 0 0 16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( -80 -704 80 ) ( -80 -703 80 ) ( -81 -704 80 ) orangestuff8 [ 1 0 0 -16 ] [ 0 -1 0 -8 ] 180 2 2
|
||||
( -160 -384 224 ) ( -161 -384 224 ) ( -160 -383 224 ) orangestuff8 [ -1 0 0 16 ] [ 0 -1 0 -8 ] 180 2 2
|
||||
( -160 -272 96 ) ( -160 -272 97 ) ( -161 -272 96 ) orangestuff8 [ 1 0 0 -16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( 64 -704 80 ) ( 64 -704 81 ) ( 64 -703 80 ) orangestuff8 [ 0 -1 0 -8 ] [ 0 0 -1 0 ] 0 2 2
|
||||
}
|
||||
// brush 3
|
||||
{
|
||||
( -128 -112 96 ) ( -128 -111 96 ) ( -128 -112 97 ) orangestuff8 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
( -256 -272 80 ) ( -257 -272 80 ) ( -256 -272 81 ) orangestuff8 [ -1 0 0 16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( -256 -432 80 ) ( -256 -431 80 ) ( -257 -432 80 ) orangestuff8 [ 1 0 0 -16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -336 -112 224 ) ( -337 -112 224 ) ( -336 -111 224 ) orangestuff8 [ -1 0 0 16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -336 0 96 ) ( -336 0 97 ) ( -337 0 96 ) orangestuff8 [ 1 0 0 -16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( -112 -432 80 ) ( -112 -432 81 ) ( -112 -431 80 ) orangestuff8 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
}
|
||||
// brush 4
|
||||
{
|
||||
( 64 -112 96 ) ( 64 -111 96 ) ( 64 -112 97 ) orangestuff8 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
( -64 -272 80 ) ( -65 -272 80 ) ( -64 -272 81 ) orangestuff8 [ -1 0 0 16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( -64 -432 80 ) ( -64 -431 80 ) ( -65 -432 80 ) orangestuff8 [ 1 0 0 -16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -144 -112 224 ) ( -145 -112 224 ) ( -144 -111 224 ) orangestuff8 [ -1 0 0 16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -144 0 96 ) ( -144 0 97 ) ( -145 0 96 ) orangestuff8 [ 1 0 0 -16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( 80 -432 80 ) ( 80 -432 81 ) ( 80 -431 80 ) orangestuff8 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
}
|
||||
// brush 5
|
||||
{
|
||||
( -112 -112 240 ) ( -112 -111 240 ) ( -112 -112 241 ) orangestuff8 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
( -80 -272 224 ) ( -81 -272 224 ) ( -80 -272 225 ) orangestuff8 [ -1 0 0 16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( -80 -432 224 ) ( -80 -431 224 ) ( -81 -432 224 ) orangestuff8 [ 1 0 0 -16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -160 -112 240 ) ( -161 -112 240 ) ( -160 -111 240 ) orangestuff8 [ -1 0 0 16 ] [ 0 -1 0 16 ] 180 2 2
|
||||
( -160 0 240 ) ( -160 0 241 ) ( -161 0 240 ) orangestuff8 [ 1 0 0 -16 ] [ 0 0 -1 0 ] 180 2 2
|
||||
( 64 -432 224 ) ( 64 -432 225 ) ( 64 -431 224 ) orangestuff8 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 2 2
|
||||
}
|
||||
}
|
||||
// entity 1
|
||||
{
|
||||
"classname" "info_player_start"
|
||||
"origin" "-88 -64 120"
|
||||
}
|
||||
// entity 2
|
||||
{
|
||||
"classname" "func_wall"
|
||||
"_mirrorinside" "1"
|
||||
// brush 0
|
||||
{
|
||||
( -16 -76 112 ) ( -16 -75 112 ) ( -16 -76 113 ) {trigger [ 0 -1 0 -16 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( -8 -80 112 ) ( -8 -80 113 ) ( -7 -80 112 ) {trigger [ 1 0 0 16 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( -8 -76 112 ) ( -7 -76 112 ) ( -8 -75 112 ) {trigger [ -1 0 0 -16 ] [ 0 -1 0 -16 ] 0 1 1
|
||||
( 56 -28 208 ) ( 56 -27 208 ) ( 57 -28 208 ) {trigger [ 1 0 0 16 ] [ 0 -1 0 -16 ] 0 1 1
|
||||
( 56 -32 128 ) ( 57 -32 128 ) ( 56 -32 129 ) {trigger [ -1 0 0 -16 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( 32 -28 128 ) ( 32 -28 129 ) ( 32 -27 128 ) {trigger [ 0 1 0 16 ] [ 0 0 -1 16 ] 0 1 1
|
||||
}
|
||||
}
|
||||
// entity 3
|
||||
{
|
||||
"classname" "func_wall"
|
||||
"_mirrorinside" "1"
|
||||
// brush 0
|
||||
{
|
||||
( -16 -140 112 ) ( -16 -139 112 ) ( -16 -140 113 ) *swater4 [ 0 -1 0 -16 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( -8 -144 112 ) ( -8 -144 113 ) ( -7 -144 112 ) *swater4 [ 1 0 0 16 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( -8 -140 112 ) ( -7 -140 112 ) ( -8 -139 112 ) *swater4 [ -1 0 0 -16 ] [ 0 -1 0 -16 ] 0 1 1
|
||||
( 56 -92 208 ) ( 56 -91 208 ) ( 57 -92 208 ) *swater4 [ 1 0 0 16 ] [ 0 -1 0 -16 ] 0 1 1
|
||||
( 56 -96 128 ) ( 57 -96 128 ) ( 56 -96 129 ) *swater4 [ -1 0 0 -16 ] [ 0 0 -1 16 ] 0 1 1
|
||||
( 32 -92 128 ) ( 32 -92 129 ) ( 32 -91 128 ) *swater4 [ 0 1 0 16 ] [ 0 0 -1 16 ] 0 1 1
|
||||
}
|
||||
}
|
||||
|
|
@ -188,7 +188,7 @@ TEST_CASE("PolygonCentroid")
|
|||
const std::initializer_list<qvec3d> poly{{0, 0, 0}, {0, 32, 0}, // colinear
|
||||
{0, 64, 0}, {64, 64, 0}, {64, 0, 0}};
|
||||
|
||||
CHECK(qvec3f(32, 32, 0) == qv::PolyCentroid(poly.begin(), poly.end()));
|
||||
CHECK(qvec3d(32, 32, 0) == qv::PolyCentroid(poly.begin(), poly.end()));
|
||||
}
|
||||
|
||||
TEST_CASE("PolygonArea")
|
||||
|
|
|
|||
|
|
@ -5,7 +5,12 @@
|
|||
#include <qbsp/qbsp.hh>
|
||||
#include <testmaps.hh>
|
||||
|
||||
static void LoadTestmap(const std::filesystem::path &name, std::vector<std::string> extra_args)
|
||||
struct testresults_t {
|
||||
mbsp_t bsp;
|
||||
bspxentries_t bspx;
|
||||
};
|
||||
|
||||
static testresults_t LoadTestmap(const std::filesystem::path &name, std::vector<std::string> extra_args)
|
||||
{
|
||||
auto map_path = std::filesystem::path(testmaps_dir) / name;
|
||||
|
||||
|
|
@ -52,9 +57,93 @@ static void LoadTestmap(const std::filesystem::path &name, std::vector<std::stri
|
|||
// write to .json for inspection
|
||||
serialize_bsp(bspdata, std::get<mbsp_t>(bspdata.bsp),
|
||||
fs::path(qbsp_options.bsp_path).replace_extension(".bsp.json"));
|
||||
|
||||
return {std::move(std::get<mbsp_t>(bspdata.bsp)),
|
||||
std::move(bspdata.bspx.entries)};
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("TestLight") {
|
||||
LoadTestmap("q2_lightmap_custom_scale.map", {"-threads", "1", "-extra", "-world_units_per_luxel", "8"});
|
||||
TEST_CASE("-world_units_per_luxel") {
|
||||
LoadTestmap("q2_lightmap_custom_scale.map", {"-world_units_per_luxel", "8"});
|
||||
}
|
||||
|
||||
TEST_CASE("emissive cube artifacts") {
|
||||
// A cube with surface flags "light", value "100", placed in a hallway.
|
||||
//
|
||||
// Generates harsh lines on the walls/ceiling due to a hack in `light` allowing
|
||||
// surface lights to emit 50% at 90 degrees off their surface normal (when physically it should be 0%).
|
||||
//
|
||||
// It's wanted in some cases (base1.map sewer lights flush with the wall, desired for them to
|
||||
// emit some lights on to their adjacent wall faces.)
|
||||
//
|
||||
// To disable the behaviour in this case with the cube lighting a hallway we have a entity key:
|
||||
//
|
||||
// "_surflight_rescale" "0"
|
||||
//
|
||||
auto [bsp, bspx] = LoadTestmap("light_q2_emissive_cube.map", {"-threads", "1", "-world_units_per_luxel", "4", "-novanilla"});
|
||||
|
||||
const auto start = qvec3d{1044, -1244, 880};
|
||||
const auto end = qvec3d{1044, -1272, 880};
|
||||
|
||||
auto *floor = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], start, {0, 0, 1});
|
||||
auto lm_info = BSPX_DecoupledLM(bspx, Face_GetNum(&bsp, floor));
|
||||
|
||||
const faceextents_t extents(*floor, bsp, lm_info.lmwidth, lm_info.lmheight, lm_info.world_to_lm_space);
|
||||
|
||||
// sample the lightmap along the floor, approaching the glowing cube
|
||||
// should get brighter
|
||||
qvec3b previous_sample{};
|
||||
for (int y = start[1]; y >= end[1]; y -= 4) {
|
||||
qvec3d pos = start;
|
||||
pos[1] = y;
|
||||
|
||||
auto lm_coord = extents.worldToLMCoord(pos);
|
||||
|
||||
auto sample = LM_Sample(&bsp, extents, lm_info.offset, lm_coord);
|
||||
CHECK(sample[0] >= previous_sample[0]);
|
||||
|
||||
//logging::print("world: {} lm_coord: {} sample: {} lm size: {}x{}\n", pos, lm_coord, sample, lm_info.lmwidth, lm_info.lmheight);
|
||||
|
||||
previous_sample = sample;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("-novanilla + -world_units_per_luxel")
|
||||
{
|
||||
auto [bsp, bspx] = LoadTestmap("q2_lightmap_custom_scale.map", {"-novanilla", "-world_units_per_luxel", "8"});
|
||||
|
||||
for (auto &face : bsp.dfaces) {
|
||||
CHECK(face.lightofs == -1);
|
||||
}
|
||||
|
||||
// make sure no other bspx lumps are written
|
||||
CHECK(bspx.size() == 1);
|
||||
CHECK(bspx.find("DECOUPLED_LM") != bspx.end());
|
||||
|
||||
// make sure all dlightdata bytes are accounted for by the DECOUPLED_LM lump
|
||||
// and no extra was written.
|
||||
size_t expected_dlightdata_bytes = 0;
|
||||
for (auto &face : bsp.dfaces) {
|
||||
// count used styles
|
||||
size_t face_used_styles = 0;
|
||||
for (auto style : face.styles) {
|
||||
if (style != 255) {
|
||||
++face_used_styles;
|
||||
}
|
||||
}
|
||||
|
||||
// count used pixels per style
|
||||
auto lm_info = BSPX_DecoupledLM(bspx, Face_GetNum(&bsp, &face));
|
||||
const faceextents_t extents(face, bsp, lm_info.lmwidth, lm_info.lmheight, lm_info.world_to_lm_space);
|
||||
int samples_per_face = extents.numsamples() * face_used_styles;
|
||||
|
||||
// round up to multiple of 4
|
||||
if (samples_per_face % 4) {
|
||||
samples_per_face += (4 - (samples_per_face % 4));
|
||||
}
|
||||
|
||||
int bytes_per_face = 3 * samples_per_face;
|
||||
expected_dlightdata_bytes += bytes_per_face;
|
||||
}
|
||||
CHECK(bsp.dlightdata.size() == expected_dlightdata_bytes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -822,6 +822,29 @@ TEST_CASE("water_detail_illusionary" * doctest::test_suite("testmaps_q1"))
|
|||
}
|
||||
}
|
||||
|
||||
TEST_CASE("qbsp_bmodel_mirrorinside_with_liquid" * doctest::test_suite("testmaps_q1"))
|
||||
{
|
||||
const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_bmodel_mirrorinside_with_liquid.map");
|
||||
|
||||
REQUIRE(prt.has_value());
|
||||
|
||||
const qvec3d model1_fenceface{-16, -56, 168};
|
||||
const qvec3d model2_waterface{-16, -120, 168};
|
||||
|
||||
CHECK(2 == BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[1], model1_fenceface).size());
|
||||
CHECK(2 == BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[2], model2_waterface).size());
|
||||
|
||||
// both bmodels should be CONTENTS_SOLID in all hulls
|
||||
for (int model_idx = 1; model_idx <= 2; ++model_idx) {
|
||||
for (int hull = 0; hull <= 2; ++hull) {
|
||||
auto &model = bsp.dmodels[model_idx];
|
||||
|
||||
INFO("model: ", model_idx, " hull: ", hull);
|
||||
CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, {hull}, &model, (model.mins + model.maxs) / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("noclipfaces" * doctest::test_suite("testmaps_q1"))
|
||||
{
|
||||
const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_noclipfaces.map");
|
||||
|
|
@ -1123,7 +1146,7 @@ TEST_CASE("q1_cube")
|
|||
|
||||
REQUIRE_FALSE(prt.has_value());
|
||||
|
||||
const aabb3d cube_bounds {
|
||||
const aabb3f cube_bounds {
|
||||
{32, -240, 80},
|
||||
{80, -144, 112}
|
||||
};
|
||||
|
|
@ -1134,8 +1157,8 @@ TEST_CASE("q1_cube")
|
|||
|
||||
// check the solid leaf
|
||||
auto& solid_leaf = bsp.dleafs[0];
|
||||
CHECK(solid_leaf.mins == qvec3d(0,0,0));
|
||||
CHECK(solid_leaf.maxs == qvec3d(0,0,0));
|
||||
CHECK(solid_leaf.mins == qvec3f(0,0,0));
|
||||
CHECK(solid_leaf.maxs == qvec3f(0,0,0));
|
||||
|
||||
// check the empty leafs
|
||||
for (int i = 1; i < 7; ++i) {
|
||||
|
|
@ -1185,7 +1208,7 @@ TEST_CASE("q1_clip_func_wall" * doctest::test_suite("testmaps_q1"))
|
|||
|
||||
REQUIRE(prt.has_value());
|
||||
|
||||
const aabb3d cube_bounds {
|
||||
const aabb3f cube_bounds {
|
||||
{64, 64, 48},
|
||||
{128, 128, 80}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -492,8 +492,8 @@ TEST_CASE("q2_door" * doctest::test_suite("testmaps_q2")) {
|
|||
|
||||
CHECK(GAME_QUAKE_II == bsp.loadversion->game->id);
|
||||
|
||||
const aabb3d world_tight_bounds {{-64, -64, -16}, {64, 80, 128}};
|
||||
const aabb3d bmodel_tight_bounds {{-48, 48, 16}, {48, 64, 112}};
|
||||
const aabb3f world_tight_bounds {{-64, -64, -16}, {64, 80, 128}};
|
||||
const aabb3f bmodel_tight_bounds {{-48, 48, 16}, {48, 64, 112}};
|
||||
|
||||
CHECK(world_tight_bounds.mins() == bsp.dmodels[0].mins);
|
||||
CHECK(world_tight_bounds.maxs() == bsp.dmodels[0].maxs);
|
||||
|
|
|
|||
Loading…
Reference in New Issue