From f0c8d9299301bd26d15176d44189b3eacbd7c2ac Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Thu, 21 Dec 2023 00:05:40 -0700 Subject: [PATCH] qbsp: add -missing_textures_as_zero_size flag to allow writing 0x0 textures when a texture is missing this allows us to write the texture name, but it's non-standard. light: warn when a surface light template doesn't match any faces in the bsp --- .gitignore | 3 ++- common/bspfile_generic.cc | 4 ++-- include/qbsp/qbsp.hh | 1 + light/entities.cc | 9 +++++++++ qbsp/qbsp.cc | 11 +++++++++++ tests/test_qbsp.cc | 22 ++++++++++++++++++++++ 6 files changed, 47 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index dba36282..389341f8 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,8 @@ testmaps/quake_map_source/*.prt testmaps/quake_map_source/*.pts testmaps/quake_map_source/*.vis testmaps/quake_map_source/*.texinfo -testmaps/quake_map_source/*.bsp.json +testmaps/quake_map_source/*.json +testmaps/quake_map_source/*.obj **/autosave CMakeSettings.json sphinx-venv diff --git a/common/bspfile_generic.cc b/common/bspfile_generic.cc index 4891bbba..879f7e5d 100644 --- a/common/bspfile_generic.cc +++ b/common/bspfile_generic.cc @@ -195,7 +195,7 @@ void dmiptexlump_t::stream_write(std::ostream &stream) const // write out the miptex offsets for (auto &texture : textures) { - if (!texture.name[0] || texture.width == 0 || texture.height == 0) { + if (texture.null_texture) { // dummy texture stream <= static_cast(-1); continue; @@ -213,7 +213,7 @@ void dmiptexlump_t::stream_write(std::ostream &stream) const } for (auto &texture : textures) { - if (texture.name[0] && texture.width && texture.height) { + if (!texture.null_texture) { // fix up the padding to match the above conditions if (stream.tellp() % 4) { constexpr const char pad[4]{}; diff --git a/include/qbsp/qbsp.hh b/include/qbsp/qbsp.hh index 6196ae3f..350413f2 100644 --- a/include/qbsp/qbsp.hh +++ b/include/qbsp/qbsp.hh @@ -187,6 +187,7 @@ public: setting_invertible_bool transwater; setting_bool transsky; setting_bool notextures; + setting_bool missing_textures_as_zero_size; setting_enum convertmapformat; setting_invertible_bool oldaxis; setting_bool forcegoodtree; diff --git a/light/entities.cc b/light/entities.cc index 7bdf01d8..193073dc 100644 --- a/light/entities.cc +++ b/light/entities.cc @@ -1511,6 +1511,15 @@ static void MakeSurfaceLights(const mbsp_t *bsp) logging::print("Creating surface lights for texture \"{}\" from template at ({})\n", tex, entity->epairs->get("origin")); + + // Warning if no faces exist matching the texture + const bool found_face = std::any_of(bsp->dfaces.begin(), bsp->dfaces.end(), [&](const mface_t &face) -> bool { + return !Q_strcasecmp(Face_TextureName(bsp, &face), entity->epairs->get("_surface")); + }); + if (!found_face) { + logging::print("WARNING: no faces found with texture {} (qbsp may have been run with .wad's missing?)\n", + entity->epairs->get("_surface")); + } } } diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index e65b1ba6..8bd5680b 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -479,6 +479,8 @@ qbsp_settings::qbsp_settings() transsky{this, "transsky", false, &map_development_group, "compute portal information for transparent sky"}, notextures{this, "notex", false, &common_format_group, "write only placeholder textures to depend upon replacements, keep file sizes down, or to skirt copyrights"}, + missing_textures_as_zero_size{this, "missing_textures_as_zero_size", false, &common_format_group, + "write missing textures as 0x0"}, convertmapformat{this, "convert", conversion_t::none, {{"quake", conversion_t::quake}, {"quake2", conversion_t::quake2}, {"valve", conversion_t::valve}, {"bp", conversion_t::bp}}, @@ -1539,6 +1541,15 @@ static void LoadTextureData() header.height = miptex.height; header.offsets = {0, 0, 0, 0}; + if (!miptex.name[0]) + miptex.null_texture = true; + + if (!qbsp_options.missing_textures_as_zero_size.value()) { + if (miptex.width == 0 || miptex.height == 0) { + miptex.null_texture = true; + } + } + omemstream stream(miptex.data.data(), miptex.data.size()); stream <= header; } diff --git a/tests/test_qbsp.cc b/tests/test_qbsp.cc index 19813ff1..8a012025 100644 --- a/tests/test_qbsp.cc +++ b/tests/test_qbsp.cc @@ -1969,6 +1969,28 @@ TEST_CASE("q1_missing_texture") CHECK(6 == bsp.dfaces.size()); } +TEST_CASE("q1_missing_texture, -missing_textures_as_zero_size") +{ + const auto [bsp, bspx, prt] = LoadTestmap("q1_missing_texture.map", {"-missing_textures_as_zero_size"}); + + REQUIRE(2 == bsp.dtex.textures.size()); + + // FIXME: we shouldn't really be writing skip + // (our test data includes an actual "skip" texture, + // so that gets included in the bsp.) + CHECK("skip" == bsp.dtex.textures[0].name); + CHECK(!bsp.dtex.textures[0].null_texture); + CHECK(64 == bsp.dtex.textures[0].width); + CHECK(64 == bsp.dtex.textures[0].height); + + CHECK("somemissingtext" == bsp.dtex.textures[1].name); + CHECK(!bsp.dtex.textures[1].null_texture); + CHECK(0 == bsp.dtex.textures[1].width); + CHECK(0 == bsp.dtex.textures[1].height); + + CHECK(6 == bsp.dfaces.size()); +} + TEST_CASE("q1 notex") { const auto [bsp, bspx, prt] = LoadTestmap("q1_cube.map", {"-notex"});