Skip to content

Commit

Permalink
Merge branch 'brushbsp' of https://github.com/ericwa/ericw-tools into…
Browse files Browse the repository at this point in the history
… brushbsp
  • Loading branch information
Paril committed Jul 21, 2023
2 parents 3f214b5 + a02a4ec commit a2ec923
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 10 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ find_package(TBB REQUIRED)

set(TEST_QUAKE_MAP_EXPORT_DIR "" CACHE PATH "When running unit tests, export Quake maps to this directory (useful for testing in game)")
set(TEST_QUAKE2_MAP_EXPORT_DIR "" CACHE PATH "When running unit tests, export Quake 2 maps to this directory (useful for testing in game)")
set(TEST_HEXEN2_MAP_EXPORT_DIR "" CACHE PATH "When running unit tests, export Hexen 2 maps to this directory (useful for testing in game)")
set(TEST_HALFLIFE_MAP_EXPORT_DIR "" CACHE PATH "When running unit tests, export Half-Life maps to this directory (useful for testing in game)")

add_subdirectory(3rdparty)
add_subdirectory(common)
Expand Down
23 changes: 16 additions & 7 deletions common/bspfile_generic.cc
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ void dmiptexlump_t::stream_read(std::istream &stream, const lump_t &lump)

// dummy texture?
if (offset < 0) {
tex.null_texture = true;
continue;
}

Expand All @@ -159,12 +160,20 @@ void dmiptexlump_t::stream_read(std::istream &stream, const lump_t &lump)
stream.seekg(lump.fileofs + offset);

// calculate the length of the data used for the individual miptex.
int32_t next_offset;

if (i == nummiptex - 1) {
int32_t next_offset = -1;

// scan forward (skipping -1's) to find the next valid offset
for (int j = i + 1; j < nummiptex; ++j) {
// valid?
if (offsets[j] >= 0) {
next_offset = offsets[j];
break;
}
}
if (next_offset == -1) {
// the remainder of the texures are missing, so read to the end
// of the overall lump
next_offset = lump.filelen;
} else {
next_offset = offsets[i + 1];
}

if (next_offset > offset) {
Expand All @@ -185,7 +194,7 @@ void dmiptexlump_t::stream_write(std::ostream &stream) const

// write out the miptex offsets
for (auto &texture : textures) {
if (!texture.name[0]) {
if (!texture.name[0] || texture.width == 0 || texture.height == 0) {
// dummy texture
stream <= static_cast<int32_t>(-1);
continue;
Expand All @@ -203,7 +212,7 @@ void dmiptexlump_t::stream_write(std::ostream &stream) const
}

for (auto &texture : textures) {
if (texture.name[0]) {
if (texture.name[0] && texture.width && texture.height) {
// fix up the padding to match the above conditions
if (stream.tellp() % 4) {
constexpr const char pad[4]{};
Expand Down
5 changes: 5 additions & 0 deletions common/bspinfo.cc
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,11 @@ void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const fs::path &
json &textures = (j.emplace("textures", json::array())).first.value();

for (auto &src_tex : bsp.dtex.textures) {
if (src_tex.null_texture) {
// use json null to indicate offset -1
textures.insert(textures.end(), json(nullptr));
continue;
}
json &tex = textures.insert(textures.end(), json::object()).value();

tex.push_back({"name", src_tex.name});
Expand Down
4 changes: 4 additions & 0 deletions include/common/bspfile_generic.hh
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ struct miptex_t
std::string name;
uint32_t width, height;
std::vector<uint8_t> data;
/**
* set at read time if the offset is -1
*/
bool null_texture = false;

size_t stream_size() const;

Expand Down
2 changes: 2 additions & 0 deletions testmaps.hh.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
inline const char *testmaps_dir = "@CMAKE_CURRENT_SOURCE_DIR@/testmaps";
inline const char *test_quake_maps_dir = "@TEST_QUAKE_MAP_EXPORT_DIR@";
inline const char *test_quake2_maps_dir = "@TEST_QUAKE2_MAP_EXPORT_DIR@";
inline const char *test_hexen2_maps_dir = "@TEST_HEXEN2_MAP_EXPORT_DIR@";
inline const char *test_halflife_maps_dir = "@TEST_HALFLIFE_MAP_EXPORT_DIR@";
Binary file added testmaps/deprecated/hlwad.wad
Binary file not shown.
79 changes: 79 additions & 0 deletions testmaps/hl_basic.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Game: Half-Life
// Format: Valve
// entity 0
{
"mapversion" "220"
"classname" "worldspawn"
"wad" "deprecated/hlwad.wad"
// brush 0
{
( -192 -64 -16 ) ( -192 -63 -16 ) ( -192 -64 -15 ) hltest [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -64 -128 -16 ) ( -64 -128 -15 ) ( -63 -128 -16 ) hltest [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) hltest [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) hltest [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 64 192 16 ) ( 65 192 16 ) ( 64 192 17 ) hltest [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 256 64 16 ) ( 256 64 17 ) ( 256 65 16 ) hltest [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 1
{
( -192 -384 -16 ) ( -192 -383 -16 ) ( -192 -384 -15 ) hltest [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -64 -144 -16 ) ( -64 -144 -15 ) ( -63 -144 -16 ) hltest [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 64 -256 16 ) ( 65 -256 16 ) ( 64 -255 16 ) hltest [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 64 -256 304 ) ( 64 -255 304 ) ( 65 -256 304 ) hltest [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 64 -128 16 ) ( 65 -128 16 ) ( 64 -128 17 ) hltest [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 256 -256 16 ) ( 256 -256 17 ) ( 256 -255 16 ) hltest [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 2
{
( -192 -48 -16 ) ( -192 -47 -16 ) ( -192 -48 -15 ) hltest [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1
( -64 192 -16 ) ( -64 192 -15 ) ( -63 192 -16 ) hltest [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 64 80 16 ) ( 65 80 16 ) ( 64 81 16 ) hltest [ 1 0 0 0 ] [ 0 -1 0 16 ] 0 1 1
( 64 80 304 ) ( 64 81 304 ) ( 65 80 304 ) hltest [ 1 0 0 0 ] [ 0 -1 0 16 ] 0 1 1
( 64 208 16 ) ( 65 208 16 ) ( 64 208 17 ) hltest [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 256 80 16 ) ( 256 80 17 ) ( 256 81 16 ) hltest [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 3
{
( -208 128 16 ) ( -208 129 16 ) ( -208 128 17 ) hltest [ -6.123233995736766e-17 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 48 -128 -16 ) ( 47 -128 -16 ) ( 48 -128 -15 ) hltest [ 1 -6.123233995736766e-17 0 16 ] [ 0 0 -1 0 ] 0 1 1
( -80 128 16 ) ( -80 129 16 ) ( -81 128 16 ) hltest [ 6.123233995736766e-17 1 0 0 ] [ 1 -6.123233995736766e-17 0 16 ] 90 1 1
( -80 128 304 ) ( -81 128 304 ) ( -80 129 304 ) hltest [ 6.123233995736766e-17 1 0 0 ] [ 1 -6.123233995736766e-17 0 16 ] 270 1 1
( -80 192 16 ) ( -80 192 17 ) ( -81 192 16 ) hltest [ -1 6.123233995736766e-17 0 -16 ] [ 0 0 -1 0 ] 0 1 1
( -192 0 -16 ) ( -192 0 -15 ) ( -192 1 -16 ) hltest [ 6.123233995736766e-17 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 4
{
( 256 128 16 ) ( 256 129 16 ) ( 256 128 17 ) hltest [ -6.123233995736766e-17 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 512 -128 -16 ) ( 511 -128 -16 ) ( 512 -128 -15 ) hltest [ 1 -6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 384 128 16 ) ( 384 129 16 ) ( 383 128 16 ) hltest [ 6.123233995736766e-17 1 0 0 ] [ 1 -6.123233995736766e-17 0 0 ] 90 1 1
( 384 128 304 ) ( 383 128 304 ) ( 384 129 304 ) hltest [ 6.123233995736766e-17 1 0 0 ] [ 1 -6.123233995736766e-17 0 0 ] 270 1 1
( 384 192 16 ) ( 384 192 17 ) ( 383 192 16 ) hltest [ -1 6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 272 0 -16 ) ( 272 0 -15 ) ( 272 1 -16 ) hltest [ 6.123233995736766e-17 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
}
// brush 5
{
( -192 -64 304 ) ( -192 -63 304 ) ( -192 -64 305 ) hltest [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -64 -128 304 ) ( -64 -128 305 ) ( -63 -128 304 ) hltest [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -64 -64 304 ) ( -63 -64 304 ) ( -64 -63 304 ) hltest [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 64 64 336 ) ( 64 65 336 ) ( 65 64 336 ) hltest [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 64 192 336 ) ( 65 192 336 ) ( 64 192 337 ) hltest [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 256 64 336 ) ( 256 64 337 ) ( 256 65 336 ) hltest [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
}
}
// entity 1
{
"classname" "info_player_start"
"spawnflags" "0"
"angles" "0 0 0"
"origin" "32 16 52"
}
// entity 2
{
"classname" "light"
"spawnflags" "0"
"_light" "255 255 128 200"
"style" "0"
"_fade" "1.0"
"_falloff" "0"
"origin" "40 8 200"
}
69 changes: 69 additions & 0 deletions testmaps/q1_missing_texture.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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
{
( 96 32 208 ) ( 96 -192 208 ) ( 96 32 48 ) somemissingtexture [ 0 -1 0 -16 ] [ 0 0 -1 48 ] 0 1 1
( 112 -192 48 ) ( 96 -192 48 ) ( 112 -192 208 ) somemissingtexture [ -1 0 0 -16 ] [ 0 0 -1 48 ] 180 1 1
( 112 32 48 ) ( 96 32 48 ) ( 112 -192 48 ) somemissingtexture [ 1 0 0 16 ] [ 0 -1 0 -16 ] 180 1 1
( 112 -192 208 ) ( 96 -192 208 ) ( 112 32 208 ) somemissingtexture [ -1 0 0 -16 ] [ 0 -1 0 -16 ] 180 1 1
( 112 32 208 ) ( 96 32 208 ) ( 112 32 48 ) somemissingtexture [ 1 0 0 16 ] [ 0 0 -1 48 ] 180 1 1
( 112 -192 208 ) ( 112 32 208 ) ( 112 -192 48 ) somemissingtexture [ 0 -1 0 -16 ] [ 0 0 -1 48 ] 0 1 1
}
// brush 1
{
( -144 -192 48 ) ( -144 32 48 ) ( -144 -192 208 ) somemissingtexture [ 0 1 0 16 ] [ 0 0 -1 48 ] 0 1 1
( -144 -192 208 ) ( -128 -192 208 ) ( -144 -192 48 ) somemissingtexture [ -1 0 0 -16 ] [ 0 0 -1 48 ] 180 1 1
( -144 -192 48 ) ( -128 -192 48 ) ( -144 32 48 ) somemissingtexture [ 1 0 0 16 ] [ 0 -1 0 -16 ] 180 1 1
( -144 32 208 ) ( -128 32 208 ) ( -144 -192 208 ) somemissingtexture [ -1 0 0 -16 ] [ 0 -1 0 -16 ] 180 1 1
( -144 32 48 ) ( -128 32 48 ) ( -144 32 208 ) somemissingtexture [ 1 0 0 16 ] [ 0 0 -1 48 ] 180 1 1
( -128 -192 48 ) ( -128 -192 208 ) ( -128 32 48 ) somemissingtexture [ 0 1 0 16 ] [ 0 0 -1 48 ] 0 1 1
}
// brush 2
{
( -128 32 208 ) ( -128 16 208 ) ( -128 32 48 ) somemissingtexture [ 0 1 0 16 ] [ 0 0 -1 48 ] 0 1 1
( 96 16 208 ) ( 96 16 48 ) ( -128 16 208 ) somemissingtexture [ 1 0 0 16 ] [ 0 0 -1 48 ] 180 1 1
( -128 32 48 ) ( -128 16 48 ) ( 96 32 48 ) somemissingtexture [ 1 0 0 16 ] [ 0 -1 0 -16 ] 180 1 1
( 96 32 208 ) ( 96 16 208 ) ( -128 32 208 ) somemissingtexture [ -1 0 0 -16 ] [ 0 -1 0 -16 ] 180 1 1
( 96 32 208 ) ( -128 32 208 ) ( 96 32 48 ) somemissingtexture [ 1 0 0 16 ] [ 0 0 -1 48 ] 180 1 1
( 96 32 48 ) ( 96 16 48 ) ( 96 32 208 ) somemissingtexture [ 0 -1 0 -16 ] [ 0 0 -1 48 ] 0 1 1
}
// brush 3
{
( -128 -192 48 ) ( -128 -176 48 ) ( -128 -192 208 ) somemissingtexture [ 0 1 0 16 ] [ 0 0 -1 48 ] 0 1 1
( 96 -192 48 ) ( -128 -192 48 ) ( 96 -192 208 ) somemissingtexture [ -1 0 0 -16 ] [ 0 0 -1 48 ] 180 1 1
( 96 -192 48 ) ( 96 -176 48 ) ( -128 -192 48 ) somemissingtexture [ 1 0 0 16 ] [ 0 -1 0 -16 ] 180 1 1
( -128 -192 208 ) ( -128 -176 208 ) ( 96 -192 208 ) somemissingtexture [ -1 0 0 -16 ] [ 0 -1 0 -16 ] 180 1 1
( -128 -176 48 ) ( 96 -176 48 ) ( -128 -176 208 ) somemissingtexture [ -1 0 0 -16 ] [ 0 0 -1 48 ] 180 1 1
( 96 -192 208 ) ( 96 -176 208 ) ( 96 -192 48 ) somemissingtexture [ 0 -1 0 -16 ] [ 0 0 -1 48 ] 0 1 1
}
// brush 4
{
( -128 -176 208 ) ( -128 -176 192 ) ( -128 16 208 ) somemissingtexture [ 0 1 0 16 ] [ 0 0 -1 48 ] 0 1 1
( 96 -176 208 ) ( 96 -176 192 ) ( -128 -176 208 ) somemissingtexture [ -1 0 0 -16 ] [ 0 0 -1 48 ] 180 1 1
( 96 16 192 ) ( -128 16 192 ) ( 96 -176 192 ) somemissingtexture [ -1 0 0 -16 ] [ 0 -1 0 -16 ] 180 1 1
( 96 16 208 ) ( 96 -176 208 ) ( -128 16 208 ) somemissingtexture [ -1 0 0 -16 ] [ 0 -1 0 -16 ] 180 1 1
( -128 16 208 ) ( -128 16 192 ) ( 96 16 208 ) somemissingtexture [ 1 0 0 16 ] [ 0 0 -1 48 ] 180 1 1
( 96 16 208 ) ( 96 16 192 ) ( 96 -176 208 ) somemissingtexture [ 0 -1 0 -16 ] [ 0 0 -1 48 ] 0 1 1
}
// brush 5
{
( -128 16 48 ) ( -128 16 64 ) ( -128 -176 48 ) somemissingtexture [ 0 1 0 16 ] [ 0 0 -1 48 ] 0 1 1
( -128 -176 48 ) ( -128 -176 64 ) ( 96 -176 48 ) somemissingtexture [ -1 0 0 -16 ] [ 0 0 -1 48 ] 180 1 1
( -128 16 48 ) ( -128 -176 48 ) ( 96 16 48 ) somemissingtexture [ 1 0 0 16 ] [ 0 -1 0 -16 ] 180 1 1
( -128 -176 64 ) ( -128 16 64 ) ( 96 -176 64 ) somemissingtexture [ 1 0 0 16 ] [ 0 -1 0 -16 ] 180 1 1
( 96 16 48 ) ( 96 16 64 ) ( -128 16 48 ) somemissingtexture [ 1 0 0 16 ] [ 0 0 -1 48 ] 180 1 1
( 96 -176 48 ) ( 96 -176 64 ) ( 96 16 48 ) somemissingtexture [ 0 -1 0 -16 ] [ 0 0 -1 48 ] 0 1 1
}
}
// entity 1
{
"classname" "info_player_start"
"origin" "-56 -96 120"
}
45 changes: 42 additions & 3 deletions tests/test_qbsp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ std::tuple<mbsp_t, bspxentries_t, std::optional<prtfile_t>> LoadTestmap(
destdir = test_quake2_maps_dir;
} else if (qbsp_options.target_game->id == GAME_QUAKE) {
destdir = test_quake_maps_dir;
} else if (qbsp_options.target_game->id == GAME_HEXEN_II) {
destdir = test_hexen2_maps_dir;
} else if (qbsp_options.target_game->id == GAME_HALF_LIFE) {
destdir = test_halflife_maps_dir;
}

// copy .bsp to game's basedir/maps directory, for easy in-game testing
Expand Down Expand Up @@ -1449,8 +1453,9 @@ TEST_CASE("q1_wad_mapname" * doctest::test_suite("testmaps_q1"))
CHECK(GAME_QUAKE == bsp.loadversion->game->id);

CHECK(bsp.dtex.textures.size() == 2);
CHECK(bsp.dtex.textures[0].name == "skip");
CHECK(bsp.dtex.textures[0].data.size() == sizeof(dmiptex_t)); // no texture data
CHECK(bsp.dtex.textures[0].name == ""); // skip
CHECK(bsp.dtex.textures[0].data.size() == 0); // no texture data
CHECK(bsp.dtex.textures[0].null_texture); // no texture data

CHECK(bsp.dtex.textures[1].name == "{trigger");
CHECK(bsp.dtex.textures[1].data.size() > sizeof(dmiptex_t));
Expand Down Expand Up @@ -1763,7 +1768,7 @@ TEST_CASE("textures search relative to current directory")
const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_cwd_relative_wad.map");
REQUIRE(2 == bsp.dtex.textures.size());
// FIXME: we shouldn't really be writing skip
CHECK("skip" == bsp.dtex.textures[0].name);
CHECK("" == bsp.dtex.textures[0].name);

// make sure the texture was written
CHECK("orangestuff8" == bsp.dtex.textures[1].name);
Expand Down Expand Up @@ -1882,3 +1887,37 @@ TEST_CASE("q1_liquid_software")
CHECK(inwater_undirected_edges.find(e) == inwater_undirected_edges.end());
}
}

TEST_CASE("q1_missing_texture")
{
const auto [bsp, bspx, prt] = LoadTestmap("q1_missing_texture.map");

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("" == bsp.dtex.textures[1].name);
CHECK(bsp.dtex.textures[1].null_texture);
}

TEST_CASE("hl_basic")
{
const auto [bsp, bspx, prt] = LoadTestmap("hl_basic.map", {"-hlbsp"});
CHECK(prt);

REQUIRE(2 == bsp.dtex.textures.size());

// FIXME: we shouldn't really be writing skip
CHECK(bsp.dtex.textures[0].null_texture);

CHECK("hltest" == bsp.dtex.textures[1].name);
CHECK(!bsp.dtex.textures[1].null_texture);
CHECK(64 == bsp.dtex.textures[1].width);
CHECK(64 == bsp.dtex.textures[1].height);
}

0 comments on commit a2ec923

Please sign in to comment.