From a093037ed33f50db704889d55eb9424e8d3ab12e Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 23 Jun 2024 12:35:00 -0600 Subject: [PATCH 01/13] common: fix roundtrip of Q2_CONTENTS_NO_WATERJUMP --- common/bspfile.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/bspfile.cc b/common/bspfile.cc index 5d8d47ee..13c315f6 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -866,6 +866,7 @@ struct gamedef_q2_t : public gamedef_t if (native & Q2_CONTENTS_MONSTERCLIP) result |= EWT_INVISCONTENTS_MONSTERCLIP; if (native & Q2_CONTENTS_PROJECTILECLIP) result |= EWT_INVISCONTENTS_PROJECTILECLIP; if (native & Q2_CONTENTS_ORIGIN) result |= EWT_INVISCONTENTS_ORIGIN; + if (native & Q2_CONTENTS_NO_WATERJUMP) result |= EWT_INVISCONTENTS_NO_WATERJUMP; // contents flags if (native & Q2_CONTENTS_CURRENT_0) result |= EWT_CFLAG_CURRENT_0; @@ -908,6 +909,7 @@ struct gamedef_q2_t : public gamedef_t if (contents.flags & EWT_VISCONTENTS_WATER) result |= Q2_CONTENTS_WATER; if (contents.flags & EWT_VISCONTENTS_MIST) result |= Q2_CONTENTS_MIST; if (contents.flags & EWT_INVISCONTENTS_ORIGIN) result |= Q2_CONTENTS_ORIGIN; + if (contents.flags & EWT_INVISCONTENTS_NO_WATERJUMP) result |= Q2_CONTENTS_NO_WATERJUMP; if (contents.flags & EWT_INVISCONTENTS_PLAYERCLIP) result |= Q2_CONTENTS_PLAYERCLIP; if (contents.flags & EWT_INVISCONTENTS_MONSTERCLIP) result |= Q2_CONTENTS_MONSTERCLIP; if (contents.flags & EWT_INVISCONTENTS_PROJECTILECLIP) result |= Q2_CONTENTS_PROJECTILECLIP; From 04604b18cd78d9e552ff2f2e920a2cc798476a52 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 23 Jun 2024 21:31:02 -0600 Subject: [PATCH 02/13] tests: replace doctest with googletest (#431) * tests: replace doctest with googletest - googletest command-line output lists a nice summary of failed tests at the end, doctest's doesn't - string test case names in doctest make IDE file structure view useless - googletest has VS support - doctest development stalled other changes: - get rid of doctest::skip(), all tests run now. (was only applied to 3 tests: "winding", "mountain", "base1") * check for test failure * Revert "check for test failure" This reverts commit a71d020c5af3fc0e0c3fae681cd083afc981e381. * fix test names to comply with gtest rules --- .gitmodules | 3 - 3rdparty/CMakeLists.txt | 11 +- 3rdparty/doctest | 1 - build-appveyor.ps1 | 2 +- build-linux-64.sh | 6 +- build-osx.sh | 6 +- build-windows.ps1 | 2 +- tests/CMakeLists.txt | 6 +- tests/benchmark.cc | 34 +- tests/test.cc | 861 +++++++++++---------- tests/test_bsputil.cc | 89 ++- tests/test_common.cc | 613 +++++++-------- tests/test_entities.cc | 27 +- tests/test_light.cc | 1612 +++++++++++++++++++-------------------- tests/test_ltface.cc | 580 +++++++------- tests/test_main.cc | 15 +- tests/test_qbsp.cc | 1394 ++++++++++++++++----------------- tests/test_qbsp_q2.cc | 799 ++++++++++--------- tests/test_vis.cc | 38 +- tests/testutils.hh | 8 +- 20 files changed, 3032 insertions(+), 3075 deletions(-) delete mode 160000 3rdparty/doctest diff --git a/.gitmodules b/.gitmodules index ec5d67c7..33367a32 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,3 @@ [submodule "3rdparty/pareto"] path = 3rdparty/pareto url = https://github.com/alandefreitas/pareto.git -[submodule "3rdparty/doctest"] - path = 3rdparty/doctest - url = https://github.com/doctest/doctest diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index fb140abc..49d73422 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,7 +1,16 @@ add_subdirectory(fmt EXCLUDE_FROM_ALL) -add_subdirectory(doctest EXCLUDE_FROM_ALL) add_subdirectory(json EXCLUDE_FROM_ALL) add_subdirectory(nanobench EXCLUDE_FROM_ALL) set(BUILD_WITH_PEDANTIC_WARNINGS OFF CACHE BOOL "prevent pareto from adding /WX" FORCE) add_subdirectory(pareto EXCLUDE_FROM_ALL) + +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG f8d7d77c06936315286eb55f8de22cd23c188571 # v1.14.0 +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) diff --git a/3rdparty/doctest b/3rdparty/doctest deleted file mode 160000 index b7c21ec5..00000000 --- a/3rdparty/doctest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b7c21ec5ceeadb4951b00396fc1e4642dd347e5f diff --git a/build-appveyor.ps1 b/build-appveyor.ps1 index 77378462..6f0ba57b 100644 --- a/build-appveyor.ps1 +++ b/build-appveyor.ps1 @@ -28,7 +28,7 @@ if ( $? -eq $false ) { throw "package failed" } -.\tests\Release\tests.exe --no-skip +.\tests\Release\tests.exe if ( $? -eq $false ) { throw "tests failed" diff --git a/build-linux-64.sh b/build-linux-64.sh index d91f8ec9..fd3c5598 100755 --- a/build-linux-64.sh +++ b/build-linux-64.sh @@ -36,11 +36,7 @@ export ASAN_OPTIONS=detect_leaks=false make -j8 VERBOSE=1 package || exit 1 # run tests -if [ "$USE_ASAN" != "YES" ]; then - ./tests/tests --no-skip || exit 1 # run hidden tests (releaseonly) -else - ./tests/tests || exit 1 -fi +./tests/tests || exit 1 # check rpath readelf -d ./light/light diff --git a/build-osx.sh b/build-osx.sh index 0d72ec16..addfb72b 100755 --- a/build-osx.sh +++ b/build-osx.sh @@ -48,8 +48,4 @@ otool -L ./bspinfo/bspinfo otool -L ./bsputil/bsputil # run tests -if [ "$USE_ASAN" != "YES" ]; then - ./tests/tests --no-skip || exit 1 # run hidden tests (releaseonly) -else - ./tests/tests || exit 1 -fi +./tests/tests || exit 1 diff --git a/build-windows.ps1 b/build-windows.ps1 index 68369204..8de720af 100644 --- a/build-windows.ps1 +++ b/build-windows.ps1 @@ -27,7 +27,7 @@ if ( $? -eq $false ) { throw "build failed" } -.\tests\tests.exe --no-skip +.\tests\tests.exe if ( $? -eq $false ) { throw "tests failed" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a81c45ee..5f32ff2f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,3 @@ -enable_testing() - add_executable(tests test.cc test_main.cc @@ -29,9 +27,7 @@ if (NOT EMBREE_TBB_DLL STREQUAL EMBREE_TBB_DLL-NOTFOUND) message(STATUS "Found embree EMBREE_TBB_DLL: ${EMBREE_TBB_DLL}") endif() -target_link_libraries(tests libqbsp liblight libvis libbsputil common TBB::tbb TBB::tbbmalloc doctest::doctest fmt::fmt nanobench::nanobench) - -target_compile_definitions(tests PRIVATE DOCTEST_CONFIG_SUPER_FAST_ASSERTS) +target_link_libraries(tests libqbsp liblight libvis libbsputil common TBB::tbb TBB::tbbmalloc GTest::gtest fmt::fmt nanobench::nanobench) # HACK: copy .dll dependencies add_custom_command(TARGET tests POST_BUILD diff --git a/tests/benchmark.cc b/tests/benchmark.cc index 41262208..fb93d975 100644 --- a/tests/benchmark.cc +++ b/tests/benchmark.cc @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -7,7 +7,7 @@ #include #include -TEST_CASE("winding" * doctest::test_suite("benchmark") * doctest::skip()) +TEST(benchmark, winding) { ankerl::nanobench::Bench bench; @@ -52,27 +52,27 @@ static void test_polylib(bool check_results) ankerl::nanobench::doNotOptimizeAway(back); if (check_results) { - REQUIRE(front); - REQUIRE(back); + ASSERT_TRUE(front); + ASSERT_TRUE(back); - CHECK(front->size() == 4); - CHECK(back->size() == 4); + EXPECT_EQ(front->size(), 4); + EXPECT_EQ(back->size(), 4); // check front polygon - CHECK(front->at(0) == qvec3d{0, 64, 16}); - CHECK(front->at(1) == qvec3d{64, 64, 16}); - CHECK(front->at(2) == qvec3d{64, -64, 16}); - CHECK(front->at(3) == qvec3d{0, -64, 16}); + EXPECT_EQ(front->at(0), qvec3d(0, 64, 16)); + EXPECT_EQ(front->at(1), qvec3d(64, 64, 16)); + EXPECT_EQ(front->at(2), qvec3d(64, -64, 16)); + EXPECT_EQ(front->at(3), qvec3d(0, -64, 16)); // check back polygon - CHECK(back->at(0) == qvec3d{-64, 64, 16}); - CHECK(back->at(1) == qvec3d{0, 64, 16}); - CHECK(back->at(2) == qvec3d{0, -64, 16}); - CHECK(back->at(3) == qvec3d{-64, -64, 16}); + EXPECT_EQ(back->at(0), qvec3d(-64, 64, 16)); + EXPECT_EQ(back->at(1), qvec3d(0, 64, 16)); + EXPECT_EQ(back->at(2), qvec3d(0, -64, 16)); + EXPECT_EQ(back->at(3), qvec3d(-64, -64, 16)); } } -TEST_CASE("SplitFace" * doctest::test_suite("benchmark")) +TEST(benchmark, SplitFace) { ankerl::nanobench::Bench().run("create and split a face (polylib)", [&]() { test_polylib(false); }); @@ -80,7 +80,7 @@ TEST_CASE("SplitFace" * doctest::test_suite("benchmark")) test_polylib(true); } -TEST_CASE("vis windings") +TEST(benchmark, visWindings) { ankerl::nanobench::Bench b; b.run("create pstack_t", [&]() { @@ -143,7 +143,7 @@ TEST_CASE("vis windings") }); } -TEST_CASE("vector math") +TEST(benchmark, vectorMath) { ankerl::nanobench::Bench b; ankerl::nanobench::Rng rng; diff --git a/tests/test.cc b/tests/test.cc index 570258bc..c8b74073 100644 --- a/tests/test.cc +++ b/tests/test.cc @@ -1,538 +1,529 @@ -#include +#include #include "common/settings.hh" #include -TEST_SUITE("settings") +// test booleans +TEST(settings, booleanFlagImplicit) { + settings::setting_container settings; + settings::setting_bool boolSetting(&settings, "locked", false); + const char *arguments[] = {"qbsp.exe", "-locked"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(boolSetting.value(), true); +} - // test booleans - TEST_CASE("booleanFlagImplicit") - { - settings::setting_container settings; - settings::setting_bool boolSetting(&settings, "locked", false); - const char *arguments[] = {"qbsp.exe", "-locked"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(boolSetting.value() == true); - } - - TEST_CASE("booleanFlagExplicit") - { - settings::setting_container settings; - settings::setting_bool boolSetting(&settings, "locked", false); - const char *arguments[] = {"qbsp.exe", "-locked", "1"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(boolSetting.value() == true); - } +TEST(settings, booleanFlagExplicit) +{ + settings::setting_container settings; + settings::setting_bool boolSetting(&settings, "locked", false); + const char *arguments[] = {"qbsp.exe", "-locked", "1"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(boolSetting.value(), true); +} - TEST_CASE("booleanFlagStray") - { - settings::setting_container settings; - settings::setting_bool boolSetting(&settings, "locked", false); - const char *arguments[] = {"qbsp.exe", "-locked", "stray"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(boolSetting.value() == true); - } +TEST(settings, booleanFlagStray) +{ + settings::setting_container settings; + settings::setting_bool boolSetting(&settings, "locked", false); + const char *arguments[] = {"qbsp.exe", "-locked", "stray"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(boolSetting.value(), true); +} - // test scalars - TEST_CASE("scalarSimple") - { - settings::setting_container settings; - settings::setting_scalar scalarSetting(&settings, "scale", 1.0); - const char *arguments[] = {"qbsp.exe", "-scale", "1.25"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(scalarSetting.value() == 1.25f); - } +// test scalars +TEST(settings, scalarSimple) +{ + settings::setting_container settings; + settings::setting_scalar scalarSetting(&settings, "scale", 1.0); + const char *arguments[] = {"qbsp.exe", "-scale", "1.25"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(scalarSetting.value(), 1.25f); +} - TEST_CASE("scalarNegative") - { - settings::setting_container settings; - settings::setting_scalar scalarSetting(&settings, "scale", 1.0); - const char *arguments[] = {"qbsp.exe", "-scale", "-0.25"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(scalarSetting.value() == -0.25f); - } +TEST(settings, scalarNegative) +{ + settings::setting_container settings; + settings::setting_scalar scalarSetting(&settings, "scale", 1.0); + const char *arguments[] = {"qbsp.exe", "-scale", "-0.25"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(scalarSetting.value(), -0.25f); +} - TEST_CASE("scalarInfinity") - { - settings::setting_container settings; - settings::setting_scalar scalarSetting(&settings, "scale", 1.0, 0.0, std::numeric_limits::infinity()); - const char *arguments[] = {"qbsp.exe", "-scale", "INFINITY"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(scalarSetting.value() == std::numeric_limits::infinity()); - } +TEST(settings, scalarInfinity) +{ + settings::setting_container settings; + settings::setting_scalar scalarSetting(&settings, "scale", 1.0, 0.0, std::numeric_limits::infinity()); + const char *arguments[] = {"qbsp.exe", "-scale", "INFINITY"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(scalarSetting.value(), std::numeric_limits::infinity()); +} - TEST_CASE("scalarNAN") - { - settings::setting_container settings; - settings::setting_scalar scalarSetting(&settings, "scale", 1.0); - const char *arguments[] = {"qbsp.exe", "-scale", "NAN"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(std::isnan(scalarSetting.value())); - } +TEST(settings, scalarNAN) +{ + settings::setting_container settings; + settings::setting_scalar scalarSetting(&settings, "scale", 1.0); + const char *arguments[] = {"qbsp.exe", "-scale", "NAN"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_TRUE(std::isnan(scalarSetting.value())); +} - TEST_CASE("scalarScientific") - { - settings::setting_container settings; - settings::setting_scalar scalarSetting(&settings, "scale", 1.0); - const char *arguments[] = {"qbsp.exe", "-scale", "1.54334E-34"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(scalarSetting.value() == 1.54334E-34f); - } +TEST(settings, scalarScientific) +{ + settings::setting_container settings; + settings::setting_scalar scalarSetting(&settings, "scale", 1.0); + const char *arguments[] = {"qbsp.exe", "-scale", "1.54334E-34"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(scalarSetting.value(), 1.54334E-34f); +} - TEST_CASE("scalarEOF") - { - settings::setting_container settings; - settings::setting_scalar scalarSetting(&settings, "scale", 1.0); - const char *arguments[] = {"qbsp.exe", "-scale"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - REQUIRE_THROWS_AS(settings.parse(p), settings::parse_exception); - } +TEST(settings, scalarEOF) +{ + settings::setting_container settings; + settings::setting_scalar scalarSetting(&settings, "scale", 1.0); + const char *arguments[] = {"qbsp.exe", "-scale"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + ASSERT_THROW(settings.parse(p), settings::parse_exception); +} - TEST_CASE("scalarStray") - { - settings::setting_container settings; - settings::setting_scalar scalarSetting(&settings, "scale", 1.0); - const char *arguments[] = {"qbsp.exe", "-scale", "stray"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - REQUIRE_THROWS_AS(settings.parse(p), settings::parse_exception); - } +TEST(settings, scalarStray) +{ + settings::setting_container settings; + settings::setting_scalar scalarSetting(&settings, "scale", 1.0); + const char *arguments[] = {"qbsp.exe", "-scale", "stray"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + ASSERT_THROW(settings.parse(p), settings::parse_exception); +} - // test int32 with implicit default - TEST_CASE("int32CanOmitArgumentDefault") - { - settings::setting_container settings; - settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, - settings::can_omit_argument_tag(), 1); - REQUIRE(setting.value() == 0); - } +// test int32 with implicit default +TEST(settings, int32CanOmitArgumentDefault) +{ + settings::setting_container settings; + settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, + settings::can_omit_argument_tag(), 1); + ASSERT_EQ(setting.value(), 0); +} - TEST_CASE("int32CanOmitArgumentSimple") - { - settings::setting_container settings; - settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, - settings::can_omit_argument_tag(), 1); - const char *arguments[] = {"qbsp.exe", "-bounce", "2"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(setting.value() == 2); - } +TEST(settings, int32CanOmitArgumentSimple) +{ + settings::setting_container settings; + settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, + settings::can_omit_argument_tag(), 1); + const char *arguments[] = {"qbsp.exe", "-bounce", "2"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(setting.value(), 2); +} - TEST_CASE("int32CanOmitArgumentWithFollingSetting") - { - settings::setting_container settings; - settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, - settings::can_omit_argument_tag(), 1); - settings::setting_scalar scalarSetting(&settings, "scale", 1.0); - const char *arguments[] = {"qbsp.exe", "-bounce", "-scale", "0.25"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(setting.value() == 1); - REQUIRE(scalarSetting.value() == 0.25); - } +TEST(settings, int32CanOmitArgumentWithFollingSetting) +{ + settings::setting_container settings; + settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, + settings::can_omit_argument_tag(), 1); + settings::setting_scalar scalarSetting(&settings, "scale", 1.0); + const char *arguments[] = {"qbsp.exe", "-bounce", "-scale", "0.25"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(setting.value(), 1); + ASSERT_EQ(scalarSetting.value(), 0.25); +} - TEST_CASE("int32CanOmitArgumentEOF") - { - settings::setting_container settings; - settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, - settings::can_omit_argument_tag(), 1); - settings::setting_scalar scalarSetting(&settings, "scale", 1.0); - const char *arguments[] = {"qbsp.exe", "-bounce"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(setting.value() == 1); - } +TEST(settings, int32CanOmitArgumentEOF) +{ + settings::setting_container settings; + settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, + settings::can_omit_argument_tag(), 1); + settings::setting_scalar scalarSetting(&settings, "scale", 1.0); + const char *arguments[] = {"qbsp.exe", "-bounce"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(setting.value(), 1); +} - TEST_CASE("int32CanOmitArgumentRemainder") - { - settings::setting_container settings; - settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, - settings::can_omit_argument_tag(), 1); - settings::setting_scalar scalarSetting(&settings, "scale", 1.0); - const char *arguments[] = {"qbsp.exe", "-bounce", "remainder"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - auto remainder = settings.parse(p); - REQUIRE(remainder == std::vector{"remainder"});\ - } +TEST(settings, int32CanOmitArgumentRemainder) +{ + settings::setting_container settings; + settings::setting_int32 setting(&settings, "bounce", 0, 0, 100, + settings::can_omit_argument_tag(), 1); + settings::setting_scalar scalarSetting(&settings, "scale", 1.0); + const char *arguments[] = {"qbsp.exe", "-bounce", "remainder"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + auto remainder = settings.parse(p); + ASSERT_EQ(remainder, std::vector{"remainder"});\ +} - // test vec3 - TEST_CASE("vec3Simple") - { - settings::setting_container settings; - settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); - const char *arguments[] = {"qbsp.exe", "-origin", "1", "2", "3"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(scalarSetting.value() == (qvec3f{1, 2, 3})); - } +// test vec3 +TEST(settings, vec3Simple) +{ + settings::setting_container settings; + settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); + const char *arguments[] = {"qbsp.exe", "-origin", "1", "2", "3"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(scalarSetting.value(), (qvec3f{1, 2, 3})); +} - TEST_CASE("vec3Complex") - { - settings::setting_container settings; - settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); - const char *arguments[] = {"qbsp.exe", "-origin", "-12.5", "-INFINITY", "NAN"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(scalarSetting.value()[0] == -12.5f); - REQUIRE(scalarSetting.value()[1] == -std::numeric_limits::infinity()); - REQUIRE(std::isnan(scalarSetting.value()[2])); - } +TEST(settings, vec3Complex) +{ + settings::setting_container settings; + settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); + const char *arguments[] = {"qbsp.exe", "-origin", "-12.5", "-INFINITY", "NAN"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(scalarSetting.value()[0], -12.5f); + ASSERT_EQ(scalarSetting.value()[1], -std::numeric_limits::infinity()); + ASSERT_TRUE(std::isnan(scalarSetting.value()[2])); +} - TEST_CASE("vec3Incomplete") - { - settings::setting_container settings; - settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); - const char *arguments[] = {"qbsp.exe", "-origin", "1", "2"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - REQUIRE_THROWS_AS(settings.parse(p), settings::parse_exception); - } +TEST(settings, vec3Incomplete) +{ + settings::setting_container settings; + settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); + const char *arguments[] = {"qbsp.exe", "-origin", "1", "2"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + ASSERT_THROW(settings.parse(p), settings::parse_exception); +} - TEST_CASE("vec3Stray") - { - settings::setting_container settings; - settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); - const char *arguments[] = {"qbsp.exe", "-origin", "1", "2", "abc"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - REQUIRE_THROWS_AS(settings.parse(p), settings::parse_exception); - } +TEST(settings, vec3Stray) +{ + settings::setting_container settings; + settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); + const char *arguments[] = {"qbsp.exe", "-origin", "1", "2", "abc"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + ASSERT_THROW(settings.parse(p), settings::parse_exception); +} - // test string formatting - TEST_CASE("stringSimple") - { - settings::setting_container settings; - settings::setting_string stringSetting(&settings, "name", ""); - const char *arguments[] = {"qbsp.exe", "-name", "i am a string with spaces in it"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(stringSetting.value() == arguments[2]); - } +// test string formatting +TEST(settings, stringSimple) +{ + settings::setting_container settings; + settings::setting_string stringSetting(&settings, "name", ""); + const char *arguments[] = {"qbsp.exe", "-name", "i am a string with spaces in it"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(stringSetting.value(), arguments[2]); +} - // test remainder - TEST_CASE("remainder") - { - settings::setting_container settings; - settings::setting_string stringSetting(&settings, "name", ""); - settings::setting_bool flagSetting(&settings, "flag", false); - const char *arguments[] = {"qbsp.exe", "-name", "string", "-flag", "remainder one", "remainder two"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - auto remainder = settings.parse(p); - REQUIRE(remainder[0] == "remainder one"); - REQUIRE(remainder[1] == "remainder two"); - } +// test remainder +TEST(settings, remainder) +{ + settings::setting_container settings; + settings::setting_string stringSetting(&settings, "name", ""); + settings::setting_bool flagSetting(&settings, "flag", false); + const char *arguments[] = {"qbsp.exe", "-name", "string", "-flag", "remainder one", "remainder two"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + auto remainder = settings.parse(p); + ASSERT_EQ(remainder[0], "remainder one"); + ASSERT_EQ(remainder[1], "remainder two"); +} - // test double-hyphens - TEST_CASE("doubleHyphen") - { - settings::setting_container settings; - settings::setting_bool boolSetting(&settings, "locked", false); - settings::setting_string stringSetting(&settings, "name", ""); - const char *arguments[] = {"qbsp.exe", "--locked", "--name", "my name!"}; - token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); - REQUIRE(boolSetting.value() == true); - REQUIRE(stringSetting.value() == "my name!"); - } +// test double-hyphens +TEST(settings, doubleHyphen) +{ + settings::setting_container settings; + settings::setting_bool boolSetting(&settings, "locked", false); + settings::setting_string stringSetting(&settings, "name", ""); + const char *arguments[] = {"qbsp.exe", "--locked", "--name", "my name!"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + settings.parse(p); + ASSERT_EQ(boolSetting.value(), true); + ASSERT_EQ(stringSetting.value(), "my name!"); +} - // test groups; ensure that performance is the first group - TEST_CASE("grouping") - { - settings::setting_container settings; - settings::setting_group performance{"Performance", -1000}; - settings::setting_group others{"Others", 1000}; - settings::setting_scalar scalarSetting( - &settings, "threads", 0, &performance, "number of threads; zero for automatic"); - settings::setting_bool boolSetting( - &settings, "fast", false, &performance, "use faster algorithm, for quick compiles"); - settings::setting_string stringSetting( - &settings, "filename", "filename.bat", "file.bat", &others, "some batch file"); - REQUIRE(settings.grouped().begin()->first == &performance); - // settings.printHelp(); - } +// test groups; ensure that performance is the first group +TEST(settings, grouping) +{ + settings::setting_container settings; + settings::setting_group performance{"Performance", -1000}; + settings::setting_group others{"Others", 1000}; + settings::setting_scalar scalarSetting( + &settings, "threads", 0, &performance, "number of threads; zero for automatic"); + settings::setting_bool boolSetting( + &settings, "fast", false, &performance, "use faster algorithm, for quick compiles"); + settings::setting_string stringSetting( + &settings, "filename", "filename.bat", "file.bat", &others, "some batch file"); + ASSERT_EQ(settings.grouped().begin()->first, &performance); + // settings.printHelp(); +} - TEST_CASE("copy") - { - settings::setting_container settings; - settings::setting_scalar scaleSetting(&settings, "scale", 1.5); - settings::setting_scalar waitSetting(&settings, "wait", 0.0); - settings::setting_string stringSetting(&settings, "string", "test"); - - CHECK(settings::source::DEFAULT == scaleSetting.get_source()); - CHECK(settings::source::DEFAULT == waitSetting.get_source()); - CHECK(0 == waitSetting.value()); - - CHECK(waitSetting.copy_from(scaleSetting)); - CHECK(settings::source::DEFAULT == waitSetting.get_source()); - CHECK(1.5 == waitSetting.value()); - - // if copy fails, the value remains unchanged - CHECK_FALSE(waitSetting.copy_from(stringSetting)); - CHECK(settings::source::DEFAULT == waitSetting.get_source()); - CHECK(1.5 == waitSetting.value()); - - scaleSetting.set_value(2.5, settings::source::MAP); - CHECK(settings::source::MAP == scaleSetting.get_source()); - - // source is also copied - CHECK(waitSetting.copy_from(scaleSetting)); - CHECK(settings::source::MAP == waitSetting.get_source()); - CHECK(2.5 == waitSetting.value()); - } +TEST(settings, copy) +{ + settings::setting_container settings; + settings::setting_scalar scaleSetting(&settings, "scale", 1.5); + settings::setting_scalar waitSetting(&settings, "wait", 0.0); + settings::setting_string stringSetting(&settings, "string", "test"); + + EXPECT_EQ(settings::source::DEFAULT, scaleSetting.get_source()); + EXPECT_EQ(settings::source::DEFAULT, waitSetting.get_source()); + EXPECT_EQ(0, waitSetting.value()); + + EXPECT_TRUE(waitSetting.copy_from(scaleSetting)); + EXPECT_EQ(settings::source::DEFAULT, waitSetting.get_source()); + EXPECT_EQ(1.5, waitSetting.value()); + + // if copy fails, the value remains unchanged + EXPECT_FALSE(waitSetting.copy_from(stringSetting)); + EXPECT_EQ(settings::source::DEFAULT, waitSetting.get_source()); + EXPECT_EQ(1.5, waitSetting.value()); + + scaleSetting.set_value(2.5, settings::source::MAP); + EXPECT_EQ(settings::source::MAP, scaleSetting.get_source()); + + // source is also copied + EXPECT_TRUE(waitSetting.copy_from(scaleSetting)); + EXPECT_EQ(settings::source::MAP, waitSetting.get_source()); + EXPECT_EQ(2.5, waitSetting.value()); +} - TEST_CASE("copyMangle") - { - settings::setting_container settings; - settings::setting_mangle sunvec{&settings, {"sunlight_mangle"}, 0.0, 0.0, 0.0}; +TEST(settings, copyMangle) +{ + settings::setting_container settings; + settings::setting_mangle sunvec{&settings, {"sunlight_mangle"}, 0.0, 0.0, 0.0}; - parser_t p(std::string_view("0.0 -90.0 0.0"), {}); - CHECK(sunvec.parse("", p, settings::source::COMMANDLINE)); - CHECK(doctest::Approx(0) == sunvec.value()[0]); - CHECK(doctest::Approx(0) == sunvec.value()[1]); - CHECK(doctest::Approx(-1) == sunvec.value()[2]); + parser_t p(std::string_view("0.0 -90.0 0.0"), {}); + EXPECT_TRUE(sunvec.parse("", p, settings::source::COMMANDLINE)); + EXPECT_NEAR(0, sunvec.value()[0], 1e-7); + EXPECT_NEAR(0, sunvec.value()[1], 1e-7); + EXPECT_NEAR(-1, sunvec.value()[2], 1e-7); - settings::setting_mangle sunvec2{&settings, {"sunlight_mangle2"}, 0.0, 0.0, 0.0}; - sunvec2.copy_from(sunvec); + settings::setting_mangle sunvec2{&settings, {"sunlight_mangle2"}, 0.0, 0.0, 0.0}; + sunvec2.copy_from(sunvec); - CHECK(doctest::Approx(0) == sunvec2.value()[0]); - CHECK(doctest::Approx(0) == sunvec2.value()[1]); - CHECK(doctest::Approx(-1) == sunvec2.value()[2]); - } + EXPECT_NEAR(0, sunvec2.value()[0], 1e-7); + EXPECT_NEAR(0, sunvec2.value()[1], 1e-7); + EXPECT_NEAR(-1, sunvec2.value()[2], 1e-7); +} - TEST_CASE("copyContainer") - { - settings::setting_container settings1; - settings::setting_bool boolSetting1(&settings1, "boolSetting", false); - CHECK_FALSE(boolSetting1.value()); - CHECK(settings::source::DEFAULT == boolSetting1.get_source()); +TEST(settings, copyContainer) +{ + settings::setting_container settings1; + settings::setting_bool boolSetting1(&settings1, "boolSetting", false); + EXPECT_FALSE(boolSetting1.value()); + EXPECT_EQ(settings::source::DEFAULT, boolSetting1.get_source()); - boolSetting1.set_value(true, settings::source::MAP); - CHECK(boolSetting1.value()); - CHECK(settings::source::MAP == boolSetting1.get_source()); + boolSetting1.set_value(true, settings::source::MAP); + EXPECT_TRUE(boolSetting1.value()); + EXPECT_EQ(settings::source::MAP, boolSetting1.get_source()); - { - settings::setting_container settings2; - settings::setting_bool boolSetting2(&settings2, "boolSetting", false); - CHECK_FALSE(boolSetting2.value()); + { + settings::setting_container settings2; + settings::setting_bool boolSetting2(&settings2, "boolSetting", false); + EXPECT_FALSE(boolSetting2.value()); - settings2.copy_from(settings1); - CHECK(boolSetting2.value()); - CHECK(settings::source::MAP == boolSetting2.get_source()); - } + settings2.copy_from(settings1); + EXPECT_TRUE(boolSetting2.value()); + EXPECT_EQ(settings::source::MAP, boolSetting2.get_source()); } +} - const settings::setting_group test_group{"Test", 0, settings::expected_source::commandline}; +const settings::setting_group test_group{"Test", 0, settings::expected_source::commandline}; - TEST_CASE("copyContainerSubclass") +TEST(settings, copyContainerSubclass) +{ + struct my_settings : public settings::setting_container { - struct my_settings : public settings::setting_container - { - settings::setting_bool boolSetting{this, "boolSetting", false, &test_group}; - settings::setting_string stringSetting{this, "stringSetting", "default", "\"str\"", &test_group}; - }; - - static_assert(!std::is_copy_constructible_v); - static_assert(!std::is_copy_constructible_v); - static_assert(!std::is_copy_constructible_v); - - my_settings s1; - CHECK(&s1.boolSetting == s1.find_setting("boolSetting")); - CHECK(&s1.stringSetting == s1.find_setting("stringSetting")); - CHECK(1 == s1.grouped().size()); - CHECK((std::set{&s1.boolSetting, &s1.stringSetting}) == s1.grouped().at(&test_group)); - s1.boolSetting.set_value(true, settings::source::MAP); - CHECK(settings::source::MAP == s1.boolSetting.get_source()); - - my_settings s2; - s2.copy_from(s1); - CHECK(&s2.boolSetting == s2.find_setting("boolSetting")); - CHECK(s2.grouped().size() == 1); - CHECK((std::set{&s2.boolSetting, &s2.stringSetting}) == s2.grouped().at(&test_group)); - CHECK(s2.boolSetting.value()); - CHECK(settings::source::MAP == s2.boolSetting.get_source()); - - // s2.stringSetting is still at its default - CHECK("default" == s2.stringSetting.value()); - CHECK(settings::source::DEFAULT == s2.stringSetting.get_source()); - } + settings::setting_bool boolSetting{this, "boolSetting", false, &test_group}; + settings::setting_string stringSetting{this, "stringSetting", "default", "\"str\"", &test_group}; + }; + + static_assert(!std::is_copy_constructible_v); + static_assert(!std::is_copy_constructible_v); + static_assert(!std::is_copy_constructible_v); + + my_settings s1; + EXPECT_EQ(&s1.boolSetting, s1.find_setting("boolSetting")); + EXPECT_EQ(&s1.stringSetting, s1.find_setting("stringSetting")); + EXPECT_EQ(1, s1.grouped().size()); + EXPECT_EQ((std::set{&s1.boolSetting, &s1.stringSetting}), s1.grouped().at(&test_group)); + s1.boolSetting.set_value(true, settings::source::MAP); + EXPECT_EQ(settings::source::MAP, s1.boolSetting.get_source()); + + my_settings s2; + s2.copy_from(s1); + EXPECT_EQ(&s2.boolSetting, s2.find_setting("boolSetting")); + EXPECT_EQ(s2.grouped().size(), 1); + EXPECT_EQ((std::set{&s2.boolSetting, &s2.stringSetting}), s2.grouped().at(&test_group)); + EXPECT_TRUE(s2.boolSetting.value()); + EXPECT_EQ(settings::source::MAP, s2.boolSetting.get_source()); + + // s2.stringSetting is still at its default + EXPECT_EQ("default", s2.stringSetting.value()); + EXPECT_EQ(settings::source::DEFAULT, s2.stringSetting.get_source()); +} - TEST_CASE("resetBool") - { - settings::setting_container settings; - settings::setting_bool boolSetting1(&settings, "boolSetting", false); +TEST(settings, resetBool) +{ + settings::setting_container settings; + settings::setting_bool boolSetting1(&settings, "boolSetting", false); - boolSetting1.set_value(true, settings::source::MAP); - CHECK(settings::source::MAP == boolSetting1.get_source()); - CHECK(boolSetting1.value()); + boolSetting1.set_value(true, settings::source::MAP); + EXPECT_EQ(settings::source::MAP, boolSetting1.get_source()); + EXPECT_TRUE(boolSetting1.value()); - boolSetting1.reset(); - CHECK(settings::source::DEFAULT == boolSetting1.get_source()); - CHECK_FALSE(boolSetting1.value()); - } + boolSetting1.reset(); + EXPECT_EQ(settings::source::DEFAULT, boolSetting1.get_source()); + EXPECT_FALSE(boolSetting1.value()); +} - TEST_CASE("resetScalar") - { - settings::setting_container settings; - settings::setting_scalar scalarSetting1(&settings, "scalarSetting", 12.34); +TEST(settings, resetScalar) +{ + settings::setting_container settings; + settings::setting_scalar scalarSetting1(&settings, "scalarSetting", 12.34); - scalarSetting1.set_value(-2, settings::source::MAP); - CHECK(settings::source::MAP == scalarSetting1.get_source()); - CHECK(-2.0f == scalarSetting1.value()); + scalarSetting1.set_value(-2, settings::source::MAP); + EXPECT_EQ(settings::source::MAP, scalarSetting1.get_source()); + EXPECT_EQ(-2.0f, scalarSetting1.value()); - scalarSetting1.reset(); - CHECK(settings::source::DEFAULT == scalarSetting1.get_source()); - CHECK(12.34f == scalarSetting1.value()); - } - - TEST_CASE("resetContainer") - { - settings::setting_container settings; - settings::setting_vec3 vec3Setting1(&settings, "vec", 3, 4, 5); - settings::setting_string stringSetting1(&settings, "name", "abc"); + scalarSetting1.reset(); + EXPECT_EQ(settings::source::DEFAULT, scalarSetting1.get_source()); + EXPECT_EQ(12.34f, scalarSetting1.value()); +} - vec3Setting1.set_value(qvec3d(-1, -2, -3), settings::source::MAP); - stringSetting1.set_value("test", settings::source::MAP); - settings.reset(); +TEST(settings, resetContainer) +{ + settings::setting_container settings; + settings::setting_vec3 vec3Setting1(&settings, "vec", 3, 4, 5); + settings::setting_string stringSetting1(&settings, "name", "abc"); - CHECK(settings::source::DEFAULT == vec3Setting1.get_source()); - CHECK(qvec3f(3, 4, 5) == vec3Setting1.value()); + vec3Setting1.set_value(qvec3d(-1, -2, -3), settings::source::MAP); + stringSetting1.set_value("test", settings::source::MAP); + settings.reset(); - CHECK(settings::source::DEFAULT == stringSetting1.get_source()); - CHECK("abc" == stringSetting1.value()); - } + EXPECT_EQ(settings::source::DEFAULT, vec3Setting1.get_source()); + EXPECT_EQ(qvec3f(3, 4, 5), vec3Setting1.value()); -} // settings + EXPECT_EQ(settings::source::DEFAULT, stringSetting1.get_source()); + EXPECT_EQ("abc", stringSetting1.value()); +} #include "common/polylib.hh" struct winding_check_t : polylib::winding_base_t> { public: - inline size_t vector_size() { return storage.vector_size(); } +inline size_t vector_size() { return storage.vector_size(); } }; -TEST_SUITE("winding_base_t") +TEST(polylib, windingIterators) { + winding_check_t winding; - TEST_CASE("winding iterators") - { - winding_check_t winding; - - CHECK(winding.begin() == winding.end()); + EXPECT_EQ(winding.begin(), winding.end()); - winding.emplace_back(0, 0, 0); + winding.emplace_back(0, 0, 0); - CHECK(winding.begin() != winding.end()); + EXPECT_NE(winding.begin(), winding.end()); - winding.emplace_back(1, 1, 1); - winding.emplace_back(2, 2, 2); - winding.emplace_back(3, 3, 3); + winding.emplace_back(1, 1, 1); + winding.emplace_back(2, 2, 2); + winding.emplace_back(3, 3, 3); - CHECK(winding.size() == 4); + EXPECT_EQ(winding.size(), 4); - CHECK(winding.vector_size() == 0); - - // check that iterators match up before expansion - { - auto it = winding.begin(); + EXPECT_EQ(winding.vector_size(), 0); - for (size_t i = 0; i < winding.size(); i++) { - CHECK((*it)[0] == i); + // check that iterators match up before expansion + { + auto it = winding.begin(); - CHECK(it == (winding.begin() + i)); + for (size_t i = 0; i < winding.size(); i++) { + EXPECT_EQ((*it)[0], i); - it++; - } + EXPECT_EQ(it, (winding.begin() + i)); - CHECK(it == winding.end()); + it++; } - winding.emplace_back(4, 4, 4); - winding.emplace_back(5, 5, 5); + EXPECT_EQ(it, winding.end()); + } - // check that iterators match up after expansion - { - auto it = winding.begin(); + winding.emplace_back(4, 4, 4); + winding.emplace_back(5, 5, 5); - for (size_t i = 0; i < winding.size(); i++) { - CHECK((*it)[0] == i); + // check that iterators match up after expansion + { + auto it = winding.begin(); - auto composed_it = winding.begin() + i; - CHECK(it == composed_it); + for (size_t i = 0; i < winding.size(); i++) { + EXPECT_EQ((*it)[0], i); - it++; - } + auto composed_it = winding.begin() + i; + EXPECT_EQ(it, composed_it); - CHECK(it == winding.end()); + it++; } - // check that constructors work - { - polylib::winding_base_t> winding_other(winding.begin(), winding.end()); + EXPECT_EQ(it, winding.end()); + } - { - auto it = winding_other.begin(); + // check that constructors work + { + polylib::winding_base_t> winding_other(winding.begin(), winding.end()); - for (size_t i = 0; i < winding_other.size(); i++) { - CHECK((*it)[0] == i); + { + auto it = winding_other.begin(); - auto composed_it = winding_other.begin() + i; - CHECK(it == composed_it); + for (size_t i = 0; i < winding_other.size(); i++) { + EXPECT_EQ((*it)[0], i); - it++; - } + auto composed_it = winding_other.begin() + i; + EXPECT_EQ(it, composed_it); - CHECK(it == winding_other.end()); + it++; } - } - { - polylib::winding_base_t> winding_other( - {{0, 0, 0}, {1, 1, 1}, {2, 2, 2}, {3, 3, 3}, {4, 4, 4}}); + EXPECT_EQ(it, winding_other.end()); + } + } - { - auto it = winding_other.begin(); + { + polylib::winding_base_t> winding_other( + {{0, 0, 0}, {1, 1, 1}, {2, 2, 2}, {3, 3, 3}, {4, 4, 4}}); - for (size_t i = 0; i < winding_other.size(); i++) { - CHECK((*it)[0] == i); + { + auto it = winding_other.begin(); - auto composed_it = winding_other.begin() + i; - CHECK(it == composed_it); + for (size_t i = 0; i < winding_other.size(); i++) { + EXPECT_EQ((*it)[0], i); - it++; - } + auto composed_it = winding_other.begin() + i; + EXPECT_EQ(it, composed_it); - CHECK(it == winding_other.end()); + it++; } - } - { - polylib::winding_base_t> winding_other(std::move(winding)); + EXPECT_EQ(it, winding_other.end()); + } + } - CHECK(winding.size() == 0); - CHECK(winding.begin() == winding.end()); + { + polylib::winding_base_t> winding_other(std::move(winding)); - { - auto it = winding_other.begin(); + EXPECT_EQ(winding.size(), 0); + EXPECT_EQ(winding.begin(), winding.end()); - for (size_t i = 0; i < winding_other.size(); i++) { - CHECK((*it)[0] == i); + { + auto it = winding_other.begin(); - auto composed_it = winding_other.begin() + i; - CHECK(it == composed_it); + for (size_t i = 0; i < winding_other.size(); i++) { + EXPECT_EQ((*it)[0], i); - it++; - } + auto composed_it = winding_other.begin() + i; + EXPECT_EQ(it, composed_it); - CHECK(it == winding_other.end()); + it++; } + + EXPECT_EQ(it, winding_other.end()); } } } diff --git a/tests/test_bsputil.cc b/tests/test_bsputil.cc index 268102dd..d8dfdfa4 100644 --- a/tests/test_bsputil.cc +++ b/tests/test_bsputil.cc @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -11,66 +11,63 @@ #include "testmaps.hh" #include "test_qbsp.hh" -TEST_SUITE("bsputil") + +TEST(bsputil, q1DecompilerTest) { - TEST_CASE("q1_decompiler_test") - { - const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_decompiler_test.map"); + const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_decompiler_test.map"); - auto path = std::filesystem::path(testmaps_dir) / "q1_decompiler_test-decompile.map"; - std::ofstream f(path); + auto path = std::filesystem::path(testmaps_dir) / "q1_decompiler_test-decompile.map"; + std::ofstream f(path); - decomp_options options; - DecompileBSP(&bsp, options, f); + decomp_options options; + DecompileBSP(&bsp, options, f); - f.close(); + f.close(); - // checks on the .map file - auto &entity = LoadMapPath(path); - CHECK(entity.mapbrushes.size() == 7); // two floor brushes + // checks on the .map file + auto &entity = LoadMapPath(path); + EXPECT_EQ(entity.mapbrushes.size(), 7); // two floor brushes - // qbsp the decompiled map - const auto [bsp2, bspx2, prt2] = LoadTestmapQ1("q1_decompiler_test-decompile.map"); + // qbsp the decompiled map + const auto [bsp2, bspx2, prt2] = LoadTestmapQ1("q1_decompiler_test-decompile.map"); - CHECK(bsp2.dmodels.size() == bsp.dmodels.size()); - CHECK(bsp2.dleafs.size() == bsp.dleafs.size()); - CHECK(bsp2.dnodes.size() == bsp.dnodes.size()); + EXPECT_EQ(bsp2.dmodels.size(), bsp.dmodels.size()); + EXPECT_EQ(bsp2.dleafs.size(), bsp.dleafs.size()); + EXPECT_EQ(bsp2.dnodes.size(), bsp.dnodes.size()); - for (int i = 0; i < bsp.dmodels[0].numfaces; ++i) { - auto *face = &bsp.dfaces[bsp.dmodels[0].firstface + i]; - auto *face_texinfo = Face_Texinfo(&bsp, face); - const qvec3d face_centroid = Face_Centroid(&bsp, face); - const qvec3d face_normal = Face_Normal(&bsp, face); + for (int i = 0; i < bsp.dmodels[0].numfaces; ++i) { + auto *face = &bsp.dfaces[bsp.dmodels[0].firstface + i]; + auto *face_texinfo = Face_Texinfo(&bsp, face); + const qvec3d face_centroid = Face_Centroid(&bsp, face); + const qvec3d face_normal = Face_Normal(&bsp, face); - auto *face2 = BSP_FindFaceAtPoint(&bsp2, &bsp2.dmodels[0], face_centroid, face_normal); - REQUIRE(face2); + auto *face2 = BSP_FindFaceAtPoint(&bsp2, &bsp2.dmodels[0], face_centroid, face_normal); + ASSERT_TRUE(face2); - auto *face2_texinfo = Face_Texinfo(&bsp2, face2); - CHECK(face2_texinfo->vecs == face_texinfo->vecs); - } + auto *face2_texinfo = Face_Texinfo(&bsp2, face2); + EXPECT_EQ(face2_texinfo->vecs, face_texinfo->vecs); } +} - TEST_CASE("extract-textures") - { - const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_extract_textures.map"); +TEST(bsputil, extractTextures) +{ + const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_extract_textures.map"); - // extract .bsp textures to test.wad - std::ofstream wadfile("test.wad", std::ios::binary); - ExportWad(wadfile, &bsp); + // extract .bsp textures to test.wad + std::ofstream wadfile("test.wad", std::ios::binary); + ExportWad(wadfile, &bsp); - // reload .wad - fs::clear(); - img::clear(); + // reload .wad + fs::clear(); + img::clear(); - auto ar = fs::addArchive("test.wad"); - REQUIRE(ar); + auto ar = fs::addArchive("test.wad"); + ASSERT_TRUE(ar); - for (std::string texname : {"*swater4", "bolt14", "sky3", "brownlight"}) { - INFO(texname); - fs::data data = ar->load(texname); - REQUIRE(data); - auto loaded_tex = img::load_mip(texname, data, false, bspver_q1.game); - CHECK(loaded_tex); - } + for (std::string texname : {"*swater4", "bolt14", "sky3", "brownlight"}) { + fs::data data = ar->load(texname); + ASSERT_TRUE(data); + auto loaded_tex = img::load_mip(texname, data, false, bspver_q1.game); + EXPECT_TRUE(loaded_tex); } } diff --git a/tests/test_common.cc b/tests/test_common.cc index 7d31b59a..136d1bf3 100644 --- a/tests/test_common.cc +++ b/tests/test_common.cc @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -8,375 +8,378 @@ #include #include -TEST_SUITE("common") +TEST(common, StripFilename) { + ASSERT_EQ("/home/foo", fs::path("/home/foo/bar.txt").parent_path()); + ASSERT_EQ("", fs::path("bar.txt").parent_path()); +} + +TEST(common, q1Contents) +{ + auto *game_q1 = bspver_q1.game; + + const auto solid = game_q1->create_solid_contents(); + const auto detail_solid = game_q1->create_detail_solid_contents(solid); + const auto detail_wall = game_q1->create_detail_wall_contents(solid); + const auto detail_fence = game_q1->create_detail_fence_contents(solid); + const auto detail_illusionary = game_q1->create_detail_illusionary_contents(solid); + + const std::array test_contents{game_q1->create_contents_from_native(CONTENTS_EMPTY), + game_q1->create_contents_from_native(CONTENTS_SOLID), + game_q1->create_contents_from_native(CONTENTS_WATER), + game_q1->create_contents_from_native(CONTENTS_SLIME), + game_q1->create_contents_from_native(CONTENTS_LAVA), + game_q1->create_contents_from_native(CONTENTS_SKY), + detail_solid, detail_wall, detail_fence, detail_illusionary}; - TEST_CASE("StripFilename") { - REQUIRE("/home/foo" == fs::path("/home/foo/bar.txt").parent_path()); - REQUIRE("" == fs::path("bar.txt").parent_path()); + SCOPED_TRACE("solid combined with others"); + + EXPECT_EQ(game_q1->contents_to_native(solid), CONTENTS_SOLID); + + for (const auto &c : test_contents) { + auto combined = game_q1->combine_contents(solid, c); + + EXPECT_EQ(game_q1->contents_to_native(combined), CONTENTS_SOLID); + EXPECT_TRUE(combined.is_solid(game_q1)); + + EXPECT_FALSE(combined.is_any_detail(game_q1)); + } } - TEST_CASE("q1 contents") { - auto *game_q1 = bspver_q1.game; - - const auto solid = game_q1->create_solid_contents(); - const auto detail_solid = game_q1->create_detail_solid_contents(solid); - const auto detail_wall = game_q1->create_detail_wall_contents(solid); - const auto detail_fence = game_q1->create_detail_fence_contents(solid); - const auto detail_illusionary = game_q1->create_detail_illusionary_contents(solid); - - const std::array test_contents{game_q1->create_contents_from_native(CONTENTS_EMPTY), - game_q1->create_contents_from_native(CONTENTS_SOLID), - game_q1->create_contents_from_native(CONTENTS_WATER), - game_q1->create_contents_from_native(CONTENTS_SLIME), - game_q1->create_contents_from_native(CONTENTS_LAVA), - game_q1->create_contents_from_native(CONTENTS_SKY), - detail_solid, detail_wall, detail_fence, detail_illusionary}; - - SUBCASE("solid combined with others") - { - CHECK(game_q1->contents_to_native(solid) == CONTENTS_SOLID); + SCOPED_TRACE("detail_illusionary plus water"); + auto combined = game_q1->combine_contents(detail_illusionary, game_q1->create_contents_from_native(CONTENTS_WATER)); - for (const auto &c : test_contents) { - auto combined = game_q1->combine_contents(solid, c); + EXPECT_EQ(game_q1->contents_to_native(combined), CONTENTS_WATER); + EXPECT_TRUE(combined.is_detail_illusionary(game_q1)); + } - CHECK(game_q1->contents_to_native(combined) == CONTENTS_SOLID); - CHECK(combined.is_solid(game_q1)); + { + SCOPED_TRACE("detail_solid plus water"); + auto combined = game_q1->combine_contents(detail_solid, game_q1->create_contents_from_native(CONTENTS_WATER)); - CHECK(!combined.is_any_detail(game_q1)); - } - } + EXPECT_TRUE(combined.is_any_solid(game_q1)); + EXPECT_TRUE(combined.is_detail_solid(game_q1)); + EXPECT_FALSE(combined.is_liquid(game_q1)); + EXPECT_FALSE(combined.is_solid(game_q1)); + } - SUBCASE("detail_illusionary plus water") - { - auto combined = game_q1->combine_contents(detail_illusionary, game_q1->create_contents_from_native(CONTENTS_WATER)); + { + SCOPED_TRACE("detail_solid plus sky"); + auto combined = game_q1->combine_contents(detail_solid, game_q1->create_contents_from_native(CONTENTS_SKY)); - CHECK(game_q1->contents_to_native(combined) == CONTENTS_WATER); - CHECK(combined.is_detail_illusionary(game_q1)); - } + EXPECT_FALSE(combined.is_detail_solid(game_q1)); + EXPECT_TRUE(combined.is_sky(game_q1)); + EXPECT_FALSE(combined.is_solid(game_q1)); + } +} + +TEST(common, clusterContents) +{ + for (auto *bspver : bspversions) { + auto *game = bspver->game; + if (!game) + continue; - SUBCASE("detail_solid plus water") { - auto combined = game_q1->combine_contents(detail_solid, game_q1->create_contents_from_native(CONTENTS_WATER)); + SCOPED_TRACE(bspver->name); - CHECK(combined.is_any_solid(game_q1)); - CHECK(combined.is_detail_solid(game_q1)); - CHECK(!combined.is_liquid(game_q1)); - CHECK(!combined.is_solid(game_q1)); - } + const auto solid = game->create_solid_contents(); + const auto solid_detail = game->create_detail_solid_contents(solid); + const auto empty = game->create_empty_contents(); - SUBCASE("detail_solid plus sky") - { - auto combined = game_q1->combine_contents(detail_solid, game_q1->create_contents_from_native(CONTENTS_SKY)); + auto solid_solid_cluster = game->cluster_contents(solid_detail, solid_detail); + SCOPED_TRACE(solid_solid_cluster.to_string(game)); + EXPECT_TRUE(solid_solid_cluster.is_detail_solid(game)); + + auto solid_empty_cluster = game->cluster_contents(solid_detail, empty); + SCOPED_TRACE(solid_empty_cluster.to_string(game)); + + // it's empty because of the rule that: + // - if all leaves in the cluster are solid, it means you can't see in, and there's no visportal + // - otherwise, you can see in, and it needs a visportal + EXPECT_TRUE(solid_empty_cluster.is_empty(game)); + // this is a bit weird... + EXPECT_TRUE(solid_empty_cluster.is_any_detail(game)); - CHECK(!combined.is_detail_solid(game_q1)); - CHECK(combined.is_sky(game_q1)); - CHECK(!combined.is_solid(game_q1)); + // check portal_can_see_through + EXPECT_FALSE(game->portal_can_see_through(empty, solid_detail, true)); } } +} - TEST_CASE("cluster_contents") - { - for (auto *bspver : bspversions) { - auto *game = bspver->game; - if (!game) - continue; +TEST(common, q1Origin) +{ + auto *game = bspver_q1.game; + + auto origin = game->face_get_contents("origin", {}, {}); + + EXPECT_TRUE(origin.is_origin(game)); + EXPECT_FALSE(origin.is_empty(game)); +} + +TEST(common, q2Origin) +{ + auto *game = bspver_q2.game; + + auto origin = game->face_get_contents("", {}, game->create_contents_from_native(Q2_CONTENTS_ORIGIN)); + + EXPECT_TRUE(origin.is_origin(game)); + EXPECT_FALSE(origin.is_empty(game)); +} + +TEST(common, sharedContentFlagTests) +{ + for (auto *bspver : bspversions) { + auto *game = bspver->game; + if (!game) + continue; + + { + SCOPED_TRACE(bspver->name); + + const auto solid = game->create_solid_contents(); + const auto detail_solid = game->create_detail_solid_contents(solid); + const auto detail_wall = game->create_detail_wall_contents(solid); + const auto detail_fence = game->create_detail_fence_contents(solid); + const auto detail_illusionary = game->create_detail_illusionary_contents(solid); + + SCOPED_TRACE(solid.to_string(game)); + SCOPED_TRACE(detail_solid.to_string(game)); + SCOPED_TRACE(detail_wall.to_string(game)); + SCOPED_TRACE(detail_fence.to_string(game)); + SCOPED_TRACE(detail_illusionary.to_string(game)); - SUBCASE(bspver->name) { - const auto solid = game->create_solid_contents(); - const auto solid_detail = game->create_detail_solid_contents(solid); - const auto empty = game->create_empty_contents(); - - auto solid_solid_cluster = game->cluster_contents(solid_detail, solid_detail); - CAPTURE(solid_solid_cluster.to_string(game)); - CHECK(solid_solid_cluster.is_detail_solid(game)); - - auto solid_empty_cluster = game->cluster_contents(solid_detail, empty); - CAPTURE(solid_empty_cluster.to_string(game)); - - // it's empty because of the rule that: - // - if all leaves in the cluster are solid, it means you can't see in, and there's no visportal - // - otherwise, you can see in, and it needs a visportal - CHECK(solid_empty_cluster.is_empty(game)); - // this is a bit weird... - CHECK(solid_empty_cluster.is_any_detail(game)); - - // check portal_can_see_through - CHECK(!game->portal_can_see_through(empty, solid_detail, true)); + SCOPED_TRACE("is_empty"); + + EXPECT_TRUE(game->create_empty_contents().is_empty(game)); + EXPECT_FALSE(solid.is_empty(game)); + EXPECT_FALSE(detail_solid.is_empty(game)); + EXPECT_FALSE(detail_wall.is_empty(game)); + EXPECT_FALSE(detail_fence.is_empty(game)); + EXPECT_FALSE(detail_illusionary.is_empty(game)); } - } - } - TEST_CASE("q1 origin") - { - auto *game = bspver_q1.game; + { + SCOPED_TRACE("is_any_detail"); - auto origin = game->face_get_contents("origin", {}, {}); + EXPECT_FALSE(solid.is_any_detail(game)); + EXPECT_TRUE(detail_solid.is_any_detail(game)); + EXPECT_TRUE(detail_wall.is_any_detail(game)); + EXPECT_TRUE(detail_fence.is_any_detail(game)); + EXPECT_TRUE(detail_illusionary.is_any_detail(game)); + } - CHECK(origin.is_origin(game)); - CHECK(!origin.is_empty(game)); - } + { + SCOPED_TRACE("is_any_solid"); - TEST_CASE("q2 origin") - { - auto *game = bspver_q2.game; + EXPECT_TRUE(solid.is_any_solid(game)); + EXPECT_TRUE(detail_solid.is_any_solid(game)); + EXPECT_FALSE(detail_wall.is_any_solid(game)); + EXPECT_FALSE(detail_fence.is_any_solid(game)); + EXPECT_FALSE(detail_illusionary.is_any_solid(game)); + } - auto origin = game->face_get_contents("", {}, game->create_contents_from_native(Q2_CONTENTS_ORIGIN)); + { + SCOPED_TRACE("is_detail_solid"); - CHECK(origin.is_origin(game)); - CHECK(!origin.is_empty(game)); - } + EXPECT_FALSE(solid.is_detail_solid(game)); + EXPECT_TRUE(detail_solid.is_detail_solid(game)); + EXPECT_FALSE(detail_wall.is_detail_solid(game)); + EXPECT_FALSE(detail_fence.is_detail_solid(game)); + EXPECT_FALSE(detail_illusionary.is_detail_solid(game)); + } - TEST_CASE("shared content flag tests") - { - for (auto *bspver : bspversions) { - auto *game = bspver->game; - if (!game) - continue; + { + SCOPED_TRACE("is_detail_wall"); + + EXPECT_FALSE(solid.is_detail_wall(game)); + EXPECT_FALSE(detail_solid.is_detail_wall(game)); + EXPECT_TRUE(detail_wall.is_detail_wall(game)); + EXPECT_FALSE(detail_fence.is_detail_wall(game)); + EXPECT_FALSE(detail_illusionary.is_detail_wall(game)); + } + + { + SCOPED_TRACE("is_detail_fence"); + + EXPECT_FALSE(solid.is_detail_fence(game)); + EXPECT_FALSE(detail_solid.is_detail_fence(game)); + EXPECT_FALSE(detail_wall.is_detail_fence(game)); + EXPECT_TRUE(detail_fence.is_detail_fence(game)); + EXPECT_FALSE(detail_illusionary.is_detail_fence(game)); + } - SUBCASE(bspver->name) { - const auto solid = game->create_solid_contents(); - const auto detail_solid = game->create_detail_solid_contents(solid); - const auto detail_wall = game->create_detail_wall_contents(solid); - const auto detail_fence = game->create_detail_fence_contents(solid); - const auto detail_illusionary = game->create_detail_illusionary_contents(solid); - - CAPTURE(solid.to_string(game)); - CAPTURE(detail_solid.to_string(game)); - CAPTURE(detail_wall.to_string(game)); - CAPTURE(detail_fence.to_string(game)); - CAPTURE(detail_illusionary.to_string(game)); - - SUBCASE("is_empty") - { - CHECK(game->create_empty_contents().is_empty(game)); - CHECK(!solid.is_empty(game)); - CHECK(!detail_solid.is_empty(game)); - CHECK(!detail_wall.is_empty(game)); - CHECK(!detail_fence.is_empty(game)); - CHECK(!detail_illusionary.is_empty(game)); - } - - SUBCASE("is_any_detail") - { - CHECK(!solid.is_any_detail(game)); - CHECK(detail_solid.is_any_detail(game)); - CHECK(detail_wall.is_any_detail(game)); - CHECK(detail_fence.is_any_detail(game)); - CHECK(detail_illusionary.is_any_detail(game)); - } - - SUBCASE("is_any_solid") - { - CHECK(solid.is_any_solid(game)); - CHECK(detail_solid.is_any_solid(game)); - CHECK(!detail_wall.is_any_solid(game)); - CHECK(!detail_fence.is_any_solid(game)); - CHECK(!detail_illusionary.is_any_solid(game)); - } - - SUBCASE("is_detail_solid") - { - CHECK(!solid.is_detail_solid(game)); - CHECK(detail_solid.is_detail_solid(game)); - CHECK(!detail_wall.is_detail_solid(game)); - CHECK(!detail_fence.is_detail_solid(game)); - CHECK(!detail_illusionary.is_detail_solid(game)); - } - - SUBCASE("is_detail_wall") - { - CHECK(!solid.is_detail_wall(game)); - CHECK(!detail_solid.is_detail_wall(game)); - CHECK(detail_wall.is_detail_wall(game)); - CHECK(!detail_fence.is_detail_wall(game)); - CHECK(!detail_illusionary.is_detail_wall(game)); - } - - SUBCASE("is_detail_fence") - { - CHECK(!solid.is_detail_fence(game)); - CHECK(!detail_solid.is_detail_fence(game)); - CHECK(!detail_wall.is_detail_fence(game)); - CHECK(detail_fence.is_detail_fence(game)); - CHECK(!detail_illusionary.is_detail_fence(game)); - } - - SUBCASE("is_detail_illusionary") - { - CHECK(!solid.is_detail_illusionary(game)); - CHECK(!detail_solid.is_detail_illusionary(game)); - CHECK(!detail_wall.is_detail_illusionary(game)); - CHECK(!detail_fence.is_detail_illusionary(game)); - CHECK(detail_illusionary.is_detail_illusionary(game)); - } + SCOPED_TRACE("is_detail_illusionary"); + + EXPECT_FALSE(solid.is_detail_illusionary(game)); + EXPECT_FALSE(detail_solid.is_detail_illusionary(game)); + EXPECT_FALSE(detail_wall.is_detail_illusionary(game)); + EXPECT_FALSE(detail_fence.is_detail_illusionary(game)); + EXPECT_TRUE(detail_illusionary.is_detail_illusionary(game)); } } } +} - TEST_CASE("q2 contents") - { - auto *game_q2 = bspver_q2.game; +TEST(common, q2Contents) +{ + auto *game_q2 = bspver_q2.game; + + struct before_after_t { + int32_t before; + int32_t after; + }; - struct before_after_t { - int32_t before; - int32_t after; + { + SCOPED_TRACE("solid combined with others"); + const std::vector before_after_adding_solid { + {Q2_CONTENTS_EMPTY, Q2_CONTENTS_SOLID}, + {Q2_CONTENTS_SOLID, Q2_CONTENTS_SOLID}, + {Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER, Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER}, + {Q2_CONTENTS_WINDOW, Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW}, + {Q2_CONTENTS_AUX, Q2_CONTENTS_SOLID | Q2_CONTENTS_AUX}, + {Q2_CONTENTS_LAVA, Q2_CONTENTS_SOLID | Q2_CONTENTS_LAVA}, + {Q2_CONTENTS_SLIME, Q2_CONTENTS_SOLID | Q2_CONTENTS_SLIME}, + {Q2_CONTENTS_WATER, Q2_CONTENTS_SOLID | Q2_CONTENTS_WATER}, + {Q2_CONTENTS_MIST, Q2_CONTENTS_SOLID | Q2_CONTENTS_MIST}, + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID, Q2_CONTENTS_SOLID}, // detail flag gets erased + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW, Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW}, // detail flag gets erased + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_AUX, Q2_CONTENTS_SOLID |Q2_CONTENTS_AUX}, // detail flag gets erased + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA, Q2_CONTENTS_SOLID |Q2_CONTENTS_LAVA}, // detail flag gets erased + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_SLIME, Q2_CONTENTS_SOLID |Q2_CONTENTS_SLIME}, // detail flag gets erased + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_WATER, Q2_CONTENTS_SOLID | Q2_CONTENTS_WATER}, // detail flag gets erased + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_MIST, Q2_CONTENTS_SOLID | Q2_CONTENTS_MIST} // detail flag gets erased }; - SUBCASE("solid combined with others") - { - const std::vector before_after_adding_solid { - {Q2_CONTENTS_EMPTY, Q2_CONTENTS_SOLID}, - {Q2_CONTENTS_SOLID, Q2_CONTENTS_SOLID}, - {Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER, Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER}, - {Q2_CONTENTS_WINDOW, Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW}, - {Q2_CONTENTS_AUX, Q2_CONTENTS_SOLID | Q2_CONTENTS_AUX}, - {Q2_CONTENTS_LAVA, Q2_CONTENTS_SOLID | Q2_CONTENTS_LAVA}, - {Q2_CONTENTS_SLIME, Q2_CONTENTS_SOLID | Q2_CONTENTS_SLIME}, - {Q2_CONTENTS_WATER, Q2_CONTENTS_SOLID | Q2_CONTENTS_WATER}, - {Q2_CONTENTS_MIST, Q2_CONTENTS_SOLID | Q2_CONTENTS_MIST}, - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID, Q2_CONTENTS_SOLID}, // detail flag gets erased - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW, Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW}, // detail flag gets erased - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_AUX, Q2_CONTENTS_SOLID |Q2_CONTENTS_AUX}, // detail flag gets erased - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA, Q2_CONTENTS_SOLID |Q2_CONTENTS_LAVA}, // detail flag gets erased - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_SLIME, Q2_CONTENTS_SOLID |Q2_CONTENTS_SLIME}, // detail flag gets erased - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_WATER, Q2_CONTENTS_SOLID | Q2_CONTENTS_WATER}, // detail flag gets erased - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_MIST, Q2_CONTENTS_SOLID | Q2_CONTENTS_MIST} // detail flag gets erased - }; - - auto solid = game_q2->create_solid_contents(); - CHECK(game_q2->contents_to_native(solid) == Q2_CONTENTS_SOLID); - - for (const auto &[before, after] : before_after_adding_solid) { - - auto combined = game_q2->contents_remap_for_export( - game_q2->combine_contents(game_q2->create_contents_from_native(before), - solid), gamedef_t::remap_type_t::leaf); - - CHECK(game_q2->contents_to_native(combined) == after); - CHECK(combined.is_solid(game_q2)); - CHECK(!combined.is_any_detail(game_q2)); - } + auto solid = game_q2->create_solid_contents(); + EXPECT_EQ(game_q2->contents_to_native(solid), Q2_CONTENTS_SOLID); + + for (const auto &[before, after] : before_after_adding_solid) { + + auto combined = game_q2->contents_remap_for_export( + game_q2->combine_contents(game_q2->create_contents_from_native(before), + solid), gamedef_t::remap_type_t::leaf); + + EXPECT_EQ(game_q2->contents_to_native(combined), after); + EXPECT_TRUE(combined.is_solid(game_q2)); + EXPECT_FALSE(combined.is_any_detail(game_q2)); } + } - SUBCASE("water combined with others") - { - contentflags_t water = game_q2->create_contents_from_native(Q2_CONTENTS_WATER); - - const std::vector before_after_adding_water { - {Q2_CONTENTS_EMPTY, Q2_CONTENTS_WATER}, - {Q2_CONTENTS_SOLID, Q2_CONTENTS_WATER | Q2_CONTENTS_SOLID}, - {Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER, Q2_CONTENTS_WATER | Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER}, - {Q2_CONTENTS_WINDOW, Q2_CONTENTS_WATER | Q2_CONTENTS_WINDOW}, - {Q2_CONTENTS_AUX, Q2_CONTENTS_WATER | Q2_CONTENTS_AUX}, - {Q2_CONTENTS_LAVA, Q2_CONTENTS_WATER | Q2_CONTENTS_LAVA}, - {Q2_CONTENTS_SLIME, Q2_CONTENTS_WATER | Q2_CONTENTS_SLIME}, - {Q2_CONTENTS_WATER, Q2_CONTENTS_WATER}, - {Q2_CONTENTS_MIST, Q2_CONTENTS_WATER | Q2_CONTENTS_MIST}, - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID}, - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW}, - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_AUX, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_AUX}, - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA}, - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_SLIME, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_SLIME}, - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_WATER, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL}, - {Q2_CONTENTS_DETAIL | Q2_CONTENTS_MIST, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_MIST} - }; - for (const auto &[before, after] : before_after_adding_water) { - auto combined = game_q2->combine_contents(game_q2->create_contents_from_native(before), water); - - SUBCASE(fmt::format("water combined with {}", game_q2->create_contents_from_native(before).to_string(game_q2)).c_str()) - { - CHECK(game_q2->contents_to_native(combined) == after); - } + { + SCOPED_TRACE("water combined with others"); + contentflags_t water = game_q2->create_contents_from_native(Q2_CONTENTS_WATER); + + const std::vector before_after_adding_water { + {Q2_CONTENTS_EMPTY, Q2_CONTENTS_WATER}, + {Q2_CONTENTS_SOLID, Q2_CONTENTS_WATER | Q2_CONTENTS_SOLID}, + {Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER, Q2_CONTENTS_WATER | Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER}, + {Q2_CONTENTS_WINDOW, Q2_CONTENTS_WATER | Q2_CONTENTS_WINDOW}, + {Q2_CONTENTS_AUX, Q2_CONTENTS_WATER | Q2_CONTENTS_AUX}, + {Q2_CONTENTS_LAVA, Q2_CONTENTS_WATER | Q2_CONTENTS_LAVA}, + {Q2_CONTENTS_SLIME, Q2_CONTENTS_WATER | Q2_CONTENTS_SLIME}, + {Q2_CONTENTS_WATER, Q2_CONTENTS_WATER}, + {Q2_CONTENTS_MIST, Q2_CONTENTS_WATER | Q2_CONTENTS_MIST}, + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID}, + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW}, + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_AUX, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_AUX}, + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA}, + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_SLIME, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_SLIME}, + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_WATER, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL}, + {Q2_CONTENTS_DETAIL | Q2_CONTENTS_MIST, Q2_CONTENTS_WATER | Q2_CONTENTS_DETAIL | Q2_CONTENTS_MIST} + }; + for (const auto &[before, after] : before_after_adding_water) { + auto combined = game_q2->combine_contents(game_q2->create_contents_from_native(before), water); + + { + SCOPED_TRACE(fmt::format("water combined with {}", game_q2->create_contents_from_native(before).to_string(game_q2)).c_str()); + EXPECT_EQ(game_q2->contents_to_native(combined), after); } } } +} - TEST_CASE("q1 contents roundtrip") - { - auto *game_q1 = bspver_q1.game; +TEST(common, q1ContentsRoundtrip) +{ + auto *game_q1 = bspver_q1.game; - for (int i = CONTENTS_EMPTY; i >= CONTENTS_MIN; --i) { - contentflags_t test_internal = game_q1->create_contents_from_native(i); + for (int i = CONTENTS_EMPTY; i >= CONTENTS_MIN; --i) { + contentflags_t test_internal = game_q1->create_contents_from_native(i); - uint32_t test_out = game_q1->contents_to_native(test_internal); + uint32_t test_out = game_q1->contents_to_native(test_internal); - INFO("contents " << i); - CHECK(test_out == i); - } + SCOPED_TRACE(fmt::format("contents {}", i)); + EXPECT_EQ(test_out, i); } +} - TEST_CASE("q2 contents roundtrip") - { - auto *game_q2 = bspver_q2.game; +TEST(common, q2ContentsRoundtrip) +{ + auto *game_q2 = bspver_q2.game; - CHECK(game_q2->contents_to_native(game_q2->create_contents_from_native(0)) == 0); + EXPECT_EQ(game_q2->contents_to_native(game_q2->create_contents_from_native(0)), 0); - for (int i = 0; i <= 31; ++i) { - uint32_t test_in = nth_bit(i); + for (int i = 0; i <= 31; ++i) { + uint32_t test_in = nth_bit(i); - contentflags_t test_internal = game_q2->create_contents_from_native(test_in); + contentflags_t test_internal = game_q2->create_contents_from_native(test_in); - uint32_t test_out = game_q2->contents_to_native(test_internal); + uint32_t test_out = game_q2->contents_to_native(test_internal); - INFO("contents bit " << i); - CHECK(test_out == test_in); - } + SCOPED_TRACE(fmt::format("contents bit {}", i)); + EXPECT_EQ(test_out, test_in); } +} - TEST_CASE("q2 portal_can_see_through") - { - auto *game_q2 = bspver_q2.game; +TEST(common, q2PortalCanSeeThrough) +{ + auto *game_q2 = bspver_q2.game; - CHECK(game_q2->portal_can_see_through(contentflags_t::make(EWT_VISCONTENTS_DETAIL_WALL | EWT_CFLAG_DETAIL), - contentflags_t::make(EWT_INVISCONTENTS_PLAYERCLIP), false)); - } + EXPECT_TRUE(game_q2->portal_can_see_through(contentflags_t::make(EWT_VISCONTENTS_DETAIL_WALL | EWT_CFLAG_DETAIL), + contentflags_t::make(EWT_INVISCONTENTS_PLAYERCLIP), false)); +} - TEST_CASE("imglib png loader") - { - auto *game = bspver_q2.game; - auto wal_metadata_path = std::filesystem::path(testmaps_dir) / "q2_wal_metadata"; +TEST(imglib, png) +{ + auto *game = bspver_q2.game; + auto wal_metadata_path = std::filesystem::path(testmaps_dir) / "q2_wal_metadata"; - settings::common_settings settings; - settings.paths.add_value(wal_metadata_path.string(), settings::source::COMMANDLINE); + settings::common_settings settings; + settings.paths.add_value(wal_metadata_path.string(), settings::source::COMMANDLINE); - game->init_filesystem("placeholder.map", settings); + game->init_filesystem("placeholder.map", settings); - auto [texture, resolve, data] = img::load_texture("e1u1/yellow32x32", false, game, settings); - REQUIRE(texture); + auto [texture, resolve, data] = img::load_texture("e1u1/yellow32x32", false, game, settings); + ASSERT_TRUE(texture); - CHECK(texture->meta.name == "e1u1/yellow32x32"); - CHECK(texture->meta.width == 32); - CHECK(texture->meta.height == 32); - CHECK(texture->meta.extension.value() == img::ext::STB); - CHECK(!texture->meta.color_override); + EXPECT_EQ(texture->meta.name, "e1u1/yellow32x32"); + EXPECT_EQ(texture->meta.width, 32); + EXPECT_EQ(texture->meta.height, 32); + EXPECT_EQ(texture->meta.extension.value(), img::ext::STB); + EXPECT_FALSE(texture->meta.color_override); - CHECK(texture->width == 32); - CHECK(texture->height == 32); + EXPECT_EQ(texture->width, 32); + EXPECT_EQ(texture->height, 32); - CHECK(texture->width_scale == 1); - CHECK(texture->height_scale == 1); - } + EXPECT_EQ(texture->width_scale, 1); + EXPECT_EQ(texture->height_scale, 1); } -TEST_SUITE("qmat") +TEST(qmat, transpose) { - TEST_CASE("transpose") - { - // clang-format off - auto in = qmat::row_major( - {1.0, 2.0, 3.0, - 4.0, 5.0, 6.0}); - auto exp = qmat::row_major( - {1.0, 4.0, - 2.0, 5.0, - 3.0, 6.0}); - // clang-format on - - CHECK(in.transpose() == exp); - } + // clang-format off + auto in = qmat::row_major( + {1.0, 2.0, 3.0, + 4.0, 5.0, 6.0}); + auto exp = qmat::row_major( + {1.0, 4.0, + 2.0, 5.0, + 3.0, 6.0}); + // clang-format on + + EXPECT_EQ(in.transpose(), exp); } diff --git a/tests/test_entities.cc b/tests/test_entities.cc index c1efc53d..352daff6 100644 --- a/tests/test_entities.cc +++ b/tests/test_entities.cc @@ -1,21 +1,18 @@ -#include +#include #include -TEST_SUITE("entities") +TEST(entities, checkEmptyValues) { - TEST_CASE("CheckEmptyValues") - { - entdict_t good1{}; - entdict_t good2{{"foo", "bar"}}; - entdict_t bad1{{"foo", ""}}; - entdict_t bad2{{"", "bar"}}; - entdict_t bad3{{"", ""}}; + entdict_t good1{}; + entdict_t good2{{"foo", "bar"}}; + entdict_t bad1{{"foo", ""}}; + entdict_t bad2{{"", "bar"}}; + entdict_t bad3{{"", ""}}; - CHECK(EntDict_CheckNoEmptyValues(nullptr, good1)); - CHECK(EntDict_CheckNoEmptyValues(nullptr, good2)); - CHECK_FALSE(EntDict_CheckNoEmptyValues(nullptr, bad1)); - CHECK_FALSE(EntDict_CheckNoEmptyValues(nullptr, bad2)); - CHECK_FALSE(EntDict_CheckNoEmptyValues(nullptr, bad3)); - } + EXPECT_TRUE(EntDict_CheckNoEmptyValues(nullptr, good1)); + EXPECT_TRUE(EntDict_CheckNoEmptyValues(nullptr, good2)); + EXPECT_FALSE(EntDict_CheckNoEmptyValues(nullptr, bad1)); + EXPECT_FALSE(EntDict_CheckNoEmptyValues(nullptr, bad2)); + EXPECT_FALSE(EntDict_CheckNoEmptyValues(nullptr, bad3)); } diff --git a/tests/test_light.cc b/tests/test_light.cc index ab076c9c..a8b742e9 100644 --- a/tests/test_light.cc +++ b/tests/test_light.cc @@ -1,4 +1,4 @@ -#include +#include #include #include // for clamp_texcoord @@ -12,528 +12,521 @@ #include #include -TEST_SUITE("mathlib") + +TEST(mathlib, MakeCDF) { + std::vector pdfUnnormzlied{25, 50, 25}; + std::vector cdf = MakeCDF(pdfUnnormzlied); + + ASSERT_EQ(3u, cdf.size()); + ASSERT_FLOAT_EQ(0.25, cdf.at(0)); + ASSERT_FLOAT_EQ(0.75, cdf.at(1)); + ASSERT_FLOAT_EQ(1.0, cdf.at(2)); + + // TODO: return pdf + ASSERT_EQ(0, SampleCDF(cdf, 0)); + ASSERT_EQ(0, SampleCDF(cdf, 0.1)); + ASSERT_EQ(0, SampleCDF(cdf, 0.25)); + ASSERT_EQ(1, SampleCDF(cdf, 0.26)); + ASSERT_EQ(1, SampleCDF(cdf, 0.75)); + ASSERT_EQ(2, SampleCDF(cdf, 0.76)); + ASSERT_EQ(2, SampleCDF(cdf, 1)); +} - TEST_CASE("MakeCDF") - { +static void checkBox(const std::vector &edges, const std::vector &poly) +{ + EXPECT_TRUE(EdgePlanes_PointInside(edges, qvec3f(0, 0, 0))); + EXPECT_TRUE(EdgePlanes_PointInside(edges, qvec3f(64, 0, 0))); + EXPECT_TRUE(EdgePlanes_PointInside(edges, qvec3f(32, 32, 0))); + EXPECT_TRUE(EdgePlanes_PointInside(edges, qvec3f(32, 32, 32))); // off plane + + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(-0.1, 0, 0))); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(64.1, 0, 0))); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, -0.1, 0))); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, 64.1, 0))); +} - std::vector pdfUnnormzlied{25, 50, 25}; - std::vector cdf = MakeCDF(pdfUnnormzlied); - - REQUIRE(3u == cdf.size()); - REQUIRE(doctest::Approx(0.25) == cdf.at(0)); - REQUIRE(doctest::Approx(0.75) == cdf.at(1)); - REQUIRE(doctest::Approx(1.0) == cdf.at(2)); - - // TODO: return pdf - REQUIRE(0 == SampleCDF(cdf, 0)); - REQUIRE(0 == SampleCDF(cdf, 0.1)); - REQUIRE(0 == SampleCDF(cdf, 0.25)); - REQUIRE(1 == SampleCDF(cdf, 0.26)); - REQUIRE(1 == SampleCDF(cdf, 0.75)); - REQUIRE(2 == SampleCDF(cdf, 0.76)); - REQUIRE(2 == SampleCDF(cdf, 1)); - } +TEST(mathlib, EdgePlanesOfNonConvexPoly) +{ + // hourglass, non-convex + const std::vector poly{{0, 0, 0}, {64, 64, 0}, {0, 64, 0}, {64, 0, 0}}; - static void checkBox(const std::vector &edges, const std::vector &poly) - { - CHECK(EdgePlanes_PointInside(edges, qvec3f(0, 0, 0))); - CHECK(EdgePlanes_PointInside(edges, qvec3f(64, 0, 0))); - CHECK(EdgePlanes_PointInside(edges, qvec3f(32, 32, 0))); - CHECK(EdgePlanes_PointInside(edges, qvec3f(32, 32, 32))); // off plane - - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(-0.1, 0, 0))); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(64.1, 0, 0))); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, -0.1, 0))); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, 64.1, 0))); - } + const auto edges = MakeInwardFacingEdgePlanes(poly); + // EXPECT_EQ(vector(), edges); +} - TEST_CASE("EdgePlanesOfNonConvexPoly") - { - // hourglass, non-convex - const std::vector poly{{0, 0, 0}, {64, 64, 0}, {0, 64, 0}, {64, 0, 0}}; +TEST(mathlib, SlightlyConcavePoly) +{ + const std::vector poly{qvec3f(225.846161, -1744, 1774), qvec3f(248, -1744, 1798), + qvec3f(248, -1763.82605, 1799.65222), qvec3f(248, -1764, 1799.66663), qvec3f(248, -1892, 1810.33337), + qvec3f(248, -1893.21741, 1810.43481), qvec3f(248, -1921.59998, 1812.80005), qvec3f(248, -1924, 1813), + qvec3f(80, -1924, 1631), qvec3f(80, -1744, 1616)}; + + const auto edges = MakeInwardFacingEdgePlanes(poly); + ASSERT_FALSE(edges.empty()); + EXPECT_TRUE(EdgePlanes_PointInside(edges, qvec3f(152.636963, -1814, 1702))); +} - const auto edges = MakeInwardFacingEdgePlanes(poly); - // CHECK(vector() == edges); - } +TEST(polylib, PointInPolygonBasic) +{ + // clockwise + const std::vector poly{{0, 0, 0}, {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; - TEST_CASE("SlightlyConcavePoly") - { - const std::vector poly{qvec3f(225.846161, -1744, 1774), qvec3f(248, -1744, 1798), - qvec3f(248, -1763.82605, 1799.65222), qvec3f(248, -1764, 1799.66663), qvec3f(248, -1892, 1810.33337), - qvec3f(248, -1893.21741, 1810.43481), qvec3f(248, -1921.59998, 1812.80005), qvec3f(248, -1924, 1813), - qvec3f(80, -1924, 1631), qvec3f(80, -1744, 1616)}; - - const auto edges = MakeInwardFacingEdgePlanes(poly); - REQUIRE_FALSE(edges.empty()); - CHECK(EdgePlanes_PointInside(edges, qvec3f(152.636963, -1814, 1702))); - } + const auto edges = MakeInwardFacingEdgePlanes(poly); + checkBox(edges, poly); +} - TEST_CASE("PointInPolygon") - { - // clockwise - const std::vector poly{{0, 0, 0}, {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; +TEST(polylib, PointInPolygonDegenerateEdgeHandling) +{ + // clockwise + const std::vector poly{{0, 0, 0}, {0, 64, 0}, {0, 64, 0}, // repeat of last point + {64, 64, 0}, {64, 0, 0}}; - const auto edges = MakeInwardFacingEdgePlanes(poly); - checkBox(edges, poly); - } + const auto edges = MakeInwardFacingEdgePlanes(poly); + checkBox(edges, poly); +} - TEST_CASE("PointInPolygon_DegenerateEdgeHandling") - { - // clockwise - const std::vector poly{{0, 0, 0}, {0, 64, 0}, {0, 64, 0}, // repeat of last point - {64, 64, 0}, {64, 0, 0}}; +TEST(polylib, PointInPolygonDegenerateFaceHandling1) +{ + const std::vector poly{}; - const auto edges = MakeInwardFacingEdgePlanes(poly); - checkBox(edges, poly); - } + const auto edges = MakeInwardFacingEdgePlanes(poly); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, 0, 0))); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(10, 10, 10))); +} - TEST_CASE("PointInPolygon_DegenerateFaceHandling1") - { - const std::vector poly{}; +TEST(polylib, PointInPolygonDegenerateFaceHandling2) +{ + const std::vector poly{ + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }; - const auto edges = MakeInwardFacingEdgePlanes(poly); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, 0, 0))); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(10, 10, 10))); - } + const auto edges = MakeInwardFacingEdgePlanes(poly); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, 0, 0))); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(10, 10, 10))); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(-10, -10, -10))); +} - TEST_CASE("PointInPolygon_DegenerateFaceHandling2") - { - const std::vector poly{ - {0, 0, 0}, - {0, 0, 0}, - {0, 0, 0}, - }; - - const auto edges = MakeInwardFacingEdgePlanes(poly); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, 0, 0))); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(10, 10, 10))); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(-10, -10, -10))); - } +TEST(polylib, PointInPolygonDegenerateFaceHandling3) +{ + const std::vector poly{ + {0, 0, 0}, + {10, 10, 10}, + {20, 20, 20}, + }; - TEST_CASE("PointInPolygon_DegenerateFaceHandling3") - { - const std::vector poly{ - {0, 0, 0}, - {10, 10, 10}, - {20, 20, 20}, - }; - - const auto edges = MakeInwardFacingEdgePlanes(poly); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, 0, 0))); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(10, 10, 10))); - CHECK_FALSE(EdgePlanes_PointInside(edges, qvec3f(-10, -10, -10))); - } + const auto edges = MakeInwardFacingEdgePlanes(poly); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(0, 0, 0))); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(10, 10, 10))); + EXPECT_FALSE(EdgePlanes_PointInside(edges, qvec3f(-10, -10, -10))); +} - TEST_CASE("PointInPolygon_ColinearPointHandling") - { - // clockwise - const std::vector poly{{0, 0, 0}, {0, 32, 0}, // colinear - {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; +TEST(polylib, PointInPolygonColinearPointHandling) +{ + // clockwise + const std::vector poly{{0, 0, 0}, {0, 32, 0}, // colinear + {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; - const auto edges = MakeInwardFacingEdgePlanes(poly); + const auto edges = MakeInwardFacingEdgePlanes(poly); - checkBox(edges, poly); - } + checkBox(edges, poly); +} - TEST_CASE("ClosestPointOnLineSegment_Degenerate") - { - CHECK(qvec3f(0, 0, 0) == ClosestPointOnLineSegment(qvec3f(0, 0, 0), qvec3f(0, 0, 0), qvec3f(10, 10, 10))); - } +TEST(mathlib, ClosestPointOnLineSegmentDegenerate) +{ + EXPECT_EQ(qvec3f(0, 0, 0), ClosestPointOnLineSegment(qvec3f(0, 0, 0), qvec3f(0, 0, 0), qvec3f(10, 10, 10))); +} - TEST_CASE("ClosestPointOnPolyBoundary") - { - // clockwise - const std::vector poly{ - {0, 0, 0}, // edge 0 start, edge 3 end - {0, 64, 0}, // edge 1 start, edge 0 end - {64, 64, 0}, // edge 2 start, edge 1 end - {64, 0, 0} // edge 3 start, edge 2 end - }; +TEST(polylib, ClosestPointOnPolyBoundary) +{ + // clockwise + const std::vector poly{ + {0, 0, 0}, // edge 0 start, edge 3 end + {0, 64, 0}, // edge 1 start, edge 0 end + {64, 64, 0}, // edge 2 start, edge 1 end + {64, 0, 0} // edge 3 start, edge 2 end + }; - CHECK(std::make_pair(0, qvec3f(0, 0, 0)) == ClosestPointOnPolyBoundary(poly, qvec3f(0, 0, 0))); + EXPECT_EQ(std::make_pair(0, qvec3f(0, 0, 0)), ClosestPointOnPolyBoundary(poly, qvec3f(0, 0, 0))); - // Either edge 1 or 2 contain the point qvec3f(64,64,0), but we expect the first edge to be returned - CHECK(std::make_pair(1, qvec3f(64, 64, 0)) == ClosestPointOnPolyBoundary(poly, qvec3f(100, 100, 100))); - CHECK(std::make_pair(2, qvec3f(64, 32, 0)) == ClosestPointOnPolyBoundary(poly, qvec3f(100, 32, 0))); + // Either edge 1 or 2 contain the point qvec3f(64,64,0), but we expect the first edge to be returned + EXPECT_EQ(std::make_pair(1, qvec3f(64, 64, 0)), ClosestPointOnPolyBoundary(poly, qvec3f(100, 100, 100))); + EXPECT_EQ(std::make_pair(2, qvec3f(64, 32, 0)), ClosestPointOnPolyBoundary(poly, qvec3f(100, 32, 0))); - CHECK(std::make_pair(0, qvec3f(0, 0, 0)) == ClosestPointOnPolyBoundary(poly, qvec3f(-1, -1, 0))); - } + EXPECT_EQ(std::make_pair(0, qvec3f(0, 0, 0)), ClosestPointOnPolyBoundary(poly, qvec3f(-1, -1, 0))); +} - TEST_CASE("PolygonCentroid_empty") - { - const std::initializer_list empty{}; - const qvec3f res = qv::PolyCentroid(empty.begin(), empty.end()); +TEST(polylib, PolygonCentroidEmpty) +{ + const std::initializer_list empty{}; + const qvec3f res = qv::PolyCentroid(empty.begin(), empty.end()); - for (int i = 0; i < 3; i++) { - CHECK(std::isnan(res[i])); - } + for (int i = 0; i < 3; i++) { + EXPECT_TRUE(std::isnan(res[i])); } +} - TEST_CASE("PolygonCentroid_point") - { - const std::initializer_list point{{1, 1, 1}}; - CHECK(*point.begin() == qv::PolyCentroid(point.begin(), point.end())); - } +TEST(polylib, PolygonCentroidPoint) +{ + const std::initializer_list point{{1, 1, 1}}; + EXPECT_EQ(*point.begin(), qv::PolyCentroid(point.begin(), point.end())); +} - TEST_CASE("PolygonCentroid_line") - { - const std::initializer_list line{{0, 0, 0}, {2, 2, 2}}; - CHECK(qvec3d(1, 1, 1) == qv::PolyCentroid(line.begin(), line.end())); - } +TEST(polylib, PolygonCentroidLine) +{ + const std::initializer_list line{{0, 0, 0}, {2, 2, 2}}; + EXPECT_EQ(qvec3d(1, 1, 1), qv::PolyCentroid(line.begin(), line.end())); +} - TEST_CASE("PolygonCentroid") - { - // poor test.. but at least checks that the colinear point is treated correctly - const std::initializer_list poly{{0, 0, 0}, {0, 32, 0}, // colinear - {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; +TEST(polylib, PolygonCentroid) +{ + // poor test.. but at least checks that the colinear point is treated correctly + const std::initializer_list poly{{0, 0, 0}, {0, 32, 0}, // colinear + {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; - CHECK(qvec3d(32, 32, 0) == qv::PolyCentroid(poly.begin(), poly.end())); - } + EXPECT_EQ(qvec3d(32, 32, 0), qv::PolyCentroid(poly.begin(), poly.end())); +} - TEST_CASE("PolygonArea") - { - // poor test.. but at least checks that the colinear point is treated correctly - const std::initializer_list poly{{0, 0, 0}, {0, 32, 0}, // colinear - {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; +TEST(mathlib, PolygonArea) +{ + // poor test.. but at least checks that the colinear point is treated correctly + const std::initializer_list poly{{0, 0, 0}, {0, 32, 0}, // colinear + {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; - CHECK(64.0f * 64.0f == qv::PolyArea(poly.begin(), poly.end())); + EXPECT_EQ(64.0f * 64.0f, qv::PolyArea(poly.begin(), poly.end())); - // 0, 1, or 2 vertices return 0 area - CHECK(0.0f == qv::PolyArea(poly.begin(), poly.begin())); - CHECK(0.0f == qv::PolyArea(poly.begin(), poly.begin() + 1)); - CHECK(0.0f == qv::PolyArea(poly.begin(), poly.begin() + 2)); - } + // 0, 1, or 2 vertices return 0 area + EXPECT_EQ(0.0f, qv::PolyArea(poly.begin(), poly.begin())); + EXPECT_EQ(0.0f, qv::PolyArea(poly.begin(), poly.begin() + 1)); + EXPECT_EQ(0.0f, qv::PolyArea(poly.begin(), poly.begin() + 2)); +} - TEST_CASE("BarycentricFromPoint") - { - // clockwise - const std::array tri{qvec3f{0, 0, 0}, {0, 64, 0}, {64, 0, 0}}; +TEST(mathlib, BarycentricFromPoint) +{ + // clockwise + const std::array tri{qvec3f{0, 0, 0}, {0, 64, 0}, {64, 0, 0}}; - CHECK(qvec3f(1, 0, 0) == qv::Barycentric_FromPoint(tri[0], tri[0], tri[1], tri[2])); - CHECK(qvec3f(0, 1, 0) == qv::Barycentric_FromPoint(tri[1], tri[0], tri[1], tri[2])); - CHECK(qvec3f(0, 0, 1) == qv::Barycentric_FromPoint(tri[2], tri[0], tri[1], tri[2])); + EXPECT_EQ(qvec3f(1, 0, 0), qv::Barycentric_FromPoint(tri[0], tri[0], tri[1], tri[2])); + EXPECT_EQ(qvec3f(0, 1, 0), qv::Barycentric_FromPoint(tri[1], tri[0], tri[1], tri[2])); + EXPECT_EQ(qvec3f(0, 0, 1), qv::Barycentric_FromPoint(tri[2], tri[0], tri[1], tri[2])); - CHECK(qvec3f(0.5, 0.5, 0.0) == qv::Barycentric_FromPoint({0, 32, 0}, tri[0], tri[1], tri[2])); - CHECK(qvec3f(0.0, 0.5, 0.5) == qv::Barycentric_FromPoint({32, 32, 0}, tri[0], tri[1], tri[2])); - CHECK(qvec3f(0.5, 0.0, 0.5) == qv::Barycentric_FromPoint({32, 0, 0}, tri[0], tri[1], tri[2])); - } + EXPECT_EQ(qvec3f(0.5, 0.5, 0.0), qv::Barycentric_FromPoint({0, 32, 0}, tri[0], tri[1], tri[2])); + EXPECT_EQ(qvec3f(0.0, 0.5, 0.5), qv::Barycentric_FromPoint({32, 32, 0}, tri[0], tri[1], tri[2])); + EXPECT_EQ(qvec3f(0.5, 0.0, 0.5), qv::Barycentric_FromPoint({32, 0, 0}, tri[0], tri[1], tri[2])); +} - TEST_CASE("BarycentricToPoint") - { - // clockwise - const std::array tri{qvec3f{0, 0, 0}, {0, 64, 0}, {64, 0, 0}}; +TEST(mathlib, BarycentricToPoint) +{ + // clockwise + const std::array tri{qvec3f{0, 0, 0}, {0, 64, 0}, {64, 0, 0}}; - CHECK(tri[0] == qv::Barycentric_ToPoint({1, 0, 0}, tri[0], tri[1], tri[2])); - CHECK(tri[1] == qv::Barycentric_ToPoint({0, 1, 0}, tri[0], tri[1], tri[2])); - CHECK(tri[2] == qv::Barycentric_ToPoint({0, 0, 1}, tri[0], tri[1], tri[2])); + EXPECT_EQ(tri[0], qv::Barycentric_ToPoint({1, 0, 0}, tri[0], tri[1], tri[2])); + EXPECT_EQ(tri[1], qv::Barycentric_ToPoint({0, 1, 0}, tri[0], tri[1], tri[2])); + EXPECT_EQ(tri[2], qv::Barycentric_ToPoint({0, 0, 1}, tri[0], tri[1], tri[2])); - CHECK(qvec3f(0, 32, 0) == qv::Barycentric_ToPoint({0.5, 0.5, 0.0}, tri[0], tri[1], tri[2])); - CHECK(qvec3f(32, 32, 0) == qv::Barycentric_ToPoint({0.0, 0.5, 0.5}, tri[0], tri[1], tri[2])); - CHECK(qvec3f(32, 0, 0) == qv::Barycentric_ToPoint({0.5, 0.0, 0.5}, tri[0], tri[1], tri[2])); - } + EXPECT_EQ(qvec3f(0, 32, 0), qv::Barycentric_ToPoint({0.5, 0.5, 0.0}, tri[0], tri[1], tri[2])); + EXPECT_EQ(qvec3f(32, 32, 0), qv::Barycentric_ToPoint({0.0, 0.5, 0.5}, tri[0], tri[1], tri[2])); + EXPECT_EQ(qvec3f(32, 0, 0), qv::Barycentric_ToPoint({0.5, 0.0, 0.5}, tri[0], tri[1], tri[2])); +} - TEST_CASE("BarycentricRandom") - { - // clockwise - const std::array tri{qvec3f{0, 0, 0}, {0, 64, 0}, {64, 0, 0}}; +TEST(mathlib, BarycentricRandom) +{ + // clockwise + const std::array tri{qvec3f{0, 0, 0}, {0, 64, 0}, {64, 0, 0}}; - const auto triAsVec = std::vector{tri.begin(), tri.end()}; - const auto edges = MakeInwardFacingEdgePlanes(triAsVec); - const auto plane = PolyPlane(triAsVec); + const auto triAsVec = std::vector{tri.begin(), tri.end()}; + const auto edges = MakeInwardFacingEdgePlanes(triAsVec); + const auto plane = PolyPlane(triAsVec); - for (int i = 0; i < 100; i++) { - const float r0 = Random(); - const float r1 = Random(); + for (int i = 0; i < 100; i++) { + const float r0 = Random(); + const float r1 = Random(); - REQUIRE(r0 >= 0); - REQUIRE(r1 >= 0); - REQUIRE(r0 <= 1); - REQUIRE(r1 <= 1); + ASSERT_GE(r0, 0); + ASSERT_GE(r1, 0); + ASSERT_LE(r0, 1); + ASSERT_LE(r1, 1); - const auto bary = qv::Barycentric_Random(r0, r1); - CHECK(doctest::Approx(1.0f) == bary[0] + bary[1] + bary[2]); + const auto bary = qv::Barycentric_Random(r0, r1); + EXPECT_FLOAT_EQ(1.0f, bary[0] + bary[1] + bary[2]); - const qvec3f point = qv::Barycentric_ToPoint(bary, tri[0], tri[1], tri[2]); - CHECK(EdgePlanes_PointInside(edges, point)); + const qvec3f point = qv::Barycentric_ToPoint(bary, tri[0], tri[1], tri[2]); + EXPECT_TRUE(EdgePlanes_PointInside(edges, point)); - CHECK(doctest::Approx(0.0f) == DistAbovePlane(plane, point)); - } + EXPECT_FLOAT_EQ(0.0f, DistAbovePlane(plane, point)); } +} - TEST_CASE("RotateFromUpToSurfaceNormal") - { - std::mt19937 engine(0); - std::uniform_real_distribution dis(-4096, 4096); - - for (int i = 0; i < 100; i++) { - const qvec3f randvec = qv::normalize(qvec3f(dis(engine), dis(engine), dis(engine))); - const qmat3x3f m = RotateFromUpToSurfaceNormal(randvec); - - const qvec3f roundtrip = m * qvec3f(0, 0, 1); - REQUIRE(qv::epsilonEqual(randvec, roundtrip, 0.01f)); - } - } +TEST(mathlib, RotateFromUpToSurfaceNormal) +{ + std::mt19937 engine(0); + std::uniform_real_distribution dis(-4096, 4096); - TEST_CASE("MakePlane") - { - CHECK(qvec4f(0, 0, 1, 10) == MakePlane(qvec3f(0, 0, 1), qvec3f(0, 0, 10))); - CHECK(qvec4f(0, 0, 1, 10) == MakePlane(qvec3f(0, 0, 1), qvec3f(100, 100, 10))); - } + for (int i = 0; i < 100; i++) { + const qvec3f randvec = qv::normalize(qvec3f(dis(engine), dis(engine), dis(engine))); + const qmat3x3f m = RotateFromUpToSurfaceNormal(randvec); - TEST_CASE("DistAbovePlane") - { - qvec4f plane(0, 0, 1, 10); - qvec3f point(100, 100, 100); - CHECK(doctest::Approx(90) == DistAbovePlane(plane, point)); + const qvec3f roundtrip = m * qvec3f(0, 0, 1); + ASSERT_TRUE(qv::epsilonEqual(randvec, roundtrip, 0.01f)); } +} - TEST_CASE("InterpolateNormalsDegenerate") - { - CHECK_FALSE(InterpolateNormal({}, std::vector{}, qvec3f(0, 0, 0)).first); - CHECK_FALSE(InterpolateNormal({qvec3f(0, 0, 0)}, {qvec3f(0, 0, 1)}, qvec3f(0, 0, 0)).first); - CHECK_FALSE( - InterpolateNormal({qvec3f(0, 0, 0), qvec3f(10, 0, 0)}, {qvec3f(0, 0, 1), qvec3f(0, 0, 1)}, qvec3f(0, 0, 0)) - .first); - } +TEST(mathlib, MakePlane) +{ + EXPECT_EQ(qvec4f(0, 0, 1, 10), MakePlane(qvec3f(0, 0, 1), qvec3f(0, 0, 10))); + EXPECT_EQ(qvec4f(0, 0, 1, 10), MakePlane(qvec3f(0, 0, 1), qvec3f(100, 100, 10))); +} - TEST_CASE("InterpolateNormals") - { - // This test relies on the way InterpolateNormal is implemented +TEST(mathlib, DistAbovePlane) +{ + qvec4f plane(0, 0, 1, 10); + qvec3f point(100, 100, 100); + EXPECT_FLOAT_EQ(90, DistAbovePlane(plane, point)); +} - // o--o--o - // | / / | - // |// | - // o-----o +TEST(mathlib, InterpolateNormalsDegenerate) +{ + EXPECT_FALSE(InterpolateNormal({}, std::vector{}, qvec3f(0, 0, 0)).first); + EXPECT_FALSE(InterpolateNormal({qvec3f(0, 0, 0)}, {qvec3f(0, 0, 1)}, qvec3f(0, 0, 0)).first); + EXPECT_FALSE( + InterpolateNormal({qvec3f(0, 0, 0), qvec3f(10, 0, 0)}, {qvec3f(0, 0, 1), qvec3f(0, 0, 1)}, qvec3f(0, 0, 0)) + .first); +} - const std::vector poly{{0, 0, 0}, {0, 64, 0}, {32, 64, 0}, // colinear - {64, 64, 0}, {64, 0, 0}}; +TEST(mathlib, InterpolateNormals) +{ + // This test relies on the way InterpolateNormal is implemented - const std::vector normals{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}, // colinear - {0, 0, 0}, {-1, 0, 0}}; + // o--o--o + // | / / | + // |// | + // o-----o - // First try all the known points - for (int i = 0; i < poly.size(); i++) { - const auto res = InterpolateNormal(poly, normals, poly.at(i)); - CHECK(true == res.first); - CHECK(qv::epsilonEqual(normals.at(i), res.second, static_cast(POINT_EQUAL_EPSILON))); - } + const std::vector poly{{0, 0, 0}, {0, 64, 0}, {32, 64, 0}, // colinear + {64, 64, 0}, {64, 0, 0}}; - { - const qvec3f firstTriCentroid = (poly[0] + poly[1] + poly[2]) / 3.0f; - const auto res = InterpolateNormal(poly, normals, firstTriCentroid); - CHECK(true == res.first); - CHECK(qv::epsilonEqual(qvec3f(1 / 3.0f), res.second, static_cast(POINT_EQUAL_EPSILON))); - } + const std::vector normals{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}, // colinear + {0, 0, 0}, {-1, 0, 0}}; - // Outside poly - CHECK_FALSE(InterpolateNormal(poly, normals, qvec3f(-0.1, 0, 0)).first); + // First try all the known points + for (int i = 0; i < poly.size(); i++) { + const auto res = InterpolateNormal(poly, normals, poly.at(i)); + EXPECT_EQ(true, res.first); + EXPECT_TRUE(qv::epsilonEqual(normals.at(i), res.second, static_cast(POINT_EQUAL_EPSILON))); } - static bool polysEqual(const std::vector &p1, const std::vector &p2) { - if (p1.size() != p2.size()) + const qvec3f firstTriCentroid = (poly[0] + poly[1] + poly[2]) / 3.0f; + const auto res = InterpolateNormal(poly, normals, firstTriCentroid); + EXPECT_EQ(true, res.first); + EXPECT_TRUE(qv::epsilonEqual(qvec3f(1 / 3.0f), res.second, static_cast(POINT_EQUAL_EPSILON))); + } + + // Outside poly + EXPECT_FALSE(InterpolateNormal(poly, normals, qvec3f(-0.1, 0, 0)).first); +} + +static bool polysEqual(const std::vector &p1, const std::vector &p2) +{ + if (p1.size() != p2.size()) + return false; + for (int i = 0; i < p1.size(); i++) { + if (!qv::epsilonEqual(p1[i], p2[i], static_cast(POINT_EQUAL_EPSILON))) return false; - for (int i = 0; i < p1.size(); i++) { - if (!qv::epsilonEqual(p1[i], p2[i], static_cast(POINT_EQUAL_EPSILON))) - return false; - } - return true; } + return true; +} - TEST_CASE("ClipPoly1") - { - const std::vector poly{{0, 0, 0}, {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; +TEST(polylib, ClipPoly1) +{ + const std::vector poly{{0, 0, 0}, {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; - const std::vector frontRes{{0, 0, 0}, {0, 64, 0}, {32, 64, 0}, {32, 0, 0}}; + const std::vector frontRes{{0, 0, 0}, {0, 64, 0}, {32, 64, 0}, {32, 0, 0}}; - const std::vector backRes{{32, 64, 0}, {64, 64, 0}, {64, 0, 0}, {32, 0, 0}}; + const std::vector backRes{{32, 64, 0}, {64, 64, 0}, {64, 0, 0}, {32, 0, 0}}; - auto clipRes = ClipPoly(poly, qvec4f(-1, 0, 0, -32)); + auto clipRes = ClipPoly(poly, qvec4f(-1, 0, 0, -32)); - CHECK(polysEqual(frontRes, clipRes.first)); - CHECK(polysEqual(backRes, clipRes.second)); - } + EXPECT_TRUE(polysEqual(frontRes, clipRes.first)); + EXPECT_TRUE(polysEqual(backRes, clipRes.second)); +} - TEST_CASE("ShrinkPoly1") - { - const std::vector poly{{0, 0, 0}, {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; +TEST(polylib, ShrinkPoly1) +{ + const std::vector poly{{0, 0, 0}, {0, 64, 0}, {64, 64, 0}, {64, 0, 0}}; - const std::vector shrunkPoly{{1, 1, 0}, {1, 63, 0}, {63, 63, 0}, {63, 1, 0}}; + const std::vector shrunkPoly{{1, 1, 0}, {1, 63, 0}, {63, 63, 0}, {63, 1, 0}}; - const auto actualShrunk = ShrinkPoly(poly, 1.0f); + const auto actualShrunk = ShrinkPoly(poly, 1.0f); - CHECK(polysEqual(shrunkPoly, actualShrunk)); - } + EXPECT_TRUE(polysEqual(shrunkPoly, actualShrunk)); +} - TEST_CASE("ShrinkPoly2") - { - const std::vector poly{{0, 0, 0}, {64, 64, 0}, {64, 0, 0}}; +TEST(polylib, ShrinkPoly2) +{ + const std::vector poly{{0, 0, 0}, {64, 64, 0}, {64, 0, 0}}; - const std::vector shrunkPoly{ - {1.0f + sqrtf(2.0f), 1.0f, 0.0f}, - {63.0f, 63.0f - sqrtf(2.0f), 0.0f}, - {63, 1, 0}, - }; + const std::vector shrunkPoly{ + {1.0f + sqrtf(2.0f), 1.0f, 0.0f}, + {63.0f, 63.0f - sqrtf(2.0f), 0.0f}, + {63, 1, 0}, + }; - const auto actualShrunk = ShrinkPoly(poly, 1.0f); + const auto actualShrunk = ShrinkPoly(poly, 1.0f); - CHECK(polysEqual(shrunkPoly, actualShrunk)); - } + EXPECT_TRUE(polysEqual(shrunkPoly, actualShrunk)); +} - TEST_CASE("SignedDegreesBetweenUnitVectors") - { - const qvec3f up{0, 0, 1}; - const qvec3f fwd{0, 1, 0}; - const qvec3f right{1, 0, 0}; +TEST(mathlib, SignedDegreesBetweenUnitVectors) +{ + const qvec3f up{0, 0, 1}; + const qvec3f fwd{0, 1, 0}; + const qvec3f right{1, 0, 0}; - CHECK(doctest::Approx(-90) == SignedDegreesBetweenUnitVectors(right, fwd, up)); - CHECK(doctest::Approx(90) == SignedDegreesBetweenUnitVectors(fwd, right, up)); - CHECK(doctest::Approx(0) == SignedDegreesBetweenUnitVectors(right, right, up)); - } + EXPECT_FLOAT_EQ(-90, SignedDegreesBetweenUnitVectors(right, fwd, up)); + EXPECT_FLOAT_EQ(90, SignedDegreesBetweenUnitVectors(fwd, right, up)); + EXPECT_FLOAT_EQ(0, SignedDegreesBetweenUnitVectors(right, right, up)); +} - TEST_CASE("ConcavityTest_concave") - { - const qvec3f face1center{0, 0, 10}; - const qvec3f face2center{10, 0, 200}; +TEST(mathlib, ConcavityTestConcave) +{ + const qvec3f face1center{0, 0, 10}; + const qvec3f face2center{10, 0, 200}; - const qvec3f face1normal{0, 0, 1}; - const qvec3f face2normal{-1, 0, 0}; + const qvec3f face1normal{0, 0, 1}; + const qvec3f face2normal{-1, 0, 0}; - CHECK(concavity_t::Concave == FacePairConcavity(face1center, face1normal, face2center, face2normal)); - } + EXPECT_EQ(concavity_t::Concave, FacePairConcavity(face1center, face1normal, face2center, face2normal)); +} - TEST_CASE("ConcavityTest_concave2") - { - const qvec3f face1center{0, 0, 10}; - const qvec3f face2center{-10, 0, 200}; +TEST(mathlib, ConcavityTestConcave2) +{ + const qvec3f face1center{0, 0, 10}; + const qvec3f face2center{-10, 0, 200}; - const qvec3f face1normal{0, 0, 1}; - const qvec3f face2normal{1, 0, 0}; + const qvec3f face1normal{0, 0, 1}; + const qvec3f face2normal{1, 0, 0}; - CHECK(concavity_t::Concave == FacePairConcavity(face1center, face1normal, face2center, face2normal)); - } + EXPECT_EQ(concavity_t::Concave, FacePairConcavity(face1center, face1normal, face2center, face2normal)); +} - TEST_CASE("ConcavityTest_convex") - { - const qvec3f face1center{0, 0, 10}; - const qvec3f face2center{10, 0, 5}; +TEST(mathlib, ConcavityTestConvex) +{ + const qvec3f face1center{0, 0, 10}; + const qvec3f face2center{10, 0, 5}; - const qvec3f face1normal{0, 0, 1}; - const qvec3f face2normal{1, 0, 0}; + const qvec3f face1normal{0, 0, 1}; + const qvec3f face2normal{1, 0, 0}; - CHECK(concavity_t::Convex == FacePairConcavity(face1center, face1normal, face2center, face2normal)); - } + EXPECT_EQ(concavity_t::Convex, FacePairConcavity(face1center, face1normal, face2center, face2normal)); +} - TEST_CASE("ConcavityTest_convex2") - { - const qvec3f face1center{0, 0, 10}; - const qvec3f face2center{-10, 0, 5}; +TEST(mathlib, ConcavityTestConvex2) +{ + const qvec3f face1center{0, 0, 10}; + const qvec3f face2center{-10, 0, 5}; - const qvec3f face1normal{0, 0, 1}; - const qvec3f face2normal{-1, 0, 0}; + const qvec3f face1normal{0, 0, 1}; + const qvec3f face2normal{-1, 0, 0}; - CHECK(concavity_t::Convex == FacePairConcavity(face1center, face1normal, face2center, face2normal)); - } + EXPECT_EQ(concavity_t::Convex, FacePairConcavity(face1center, face1normal, face2center, face2normal)); +} - TEST_CASE("ConcavityTest_coplanar") - { - const qvec3f face1center{0, 0, 10}; - const qvec3f face2center{100, 100, 10}; +TEST(mathlib, ConcavityTestCoplanar) +{ + const qvec3f face1center{0, 0, 10}; + const qvec3f face2center{100, 100, 10}; - const qvec3f face1normal{0, 0, 1}; - const qvec3f face2normal{0, 0, 1}; + const qvec3f face1normal{0, 0, 1}; + const qvec3f face2normal{0, 0, 1}; - CHECK(concavity_t::Coplanar == FacePairConcavity(face1center, face1normal, face2center, face2normal)); - } + EXPECT_EQ(concavity_t::Coplanar, FacePairConcavity(face1center, face1normal, face2center, face2normal)); } static const float MANGLE_EPSILON = 0.1f; -TEST_SUITE("light") +TEST(mathlib, vecFromMangle) { + EXPECT_TRUE(qv::epsilonEqual(qvec3f(1, 0, 0), qv::vec_from_mangle(qvec3f(0, 0, 0)), MANGLE_EPSILON)); + EXPECT_TRUE(qv::epsilonEqual(qvec3f(-1, 0, 0), qv::vec_from_mangle(qvec3f(180, 0, 0)), MANGLE_EPSILON)); + EXPECT_TRUE(qv::epsilonEqual(qvec3f(0, 0, 1), qv::vec_from_mangle(qvec3f(0, 90, 0)), MANGLE_EPSILON)); + EXPECT_TRUE(qv::epsilonEqual(qvec3f(0, 0, -1), qv::vec_from_mangle(qvec3f(0, -90, 0)), MANGLE_EPSILON)); +} - TEST_CASE("vec_from_mangle") - { - CHECK(qv::epsilonEqual(qvec3f(1, 0, 0), qv::vec_from_mangle(qvec3f(0, 0, 0)), MANGLE_EPSILON)); - CHECK(qv::epsilonEqual(qvec3f(-1, 0, 0), qv::vec_from_mangle(qvec3f(180, 0, 0)), MANGLE_EPSILON)); - CHECK(qv::epsilonEqual(qvec3f(0, 0, 1), qv::vec_from_mangle(qvec3f(0, 90, 0)), MANGLE_EPSILON)); - CHECK(qv::epsilonEqual(qvec3f(0, 0, -1), qv::vec_from_mangle(qvec3f(0, -90, 0)), MANGLE_EPSILON)); - } - - TEST_CASE("mangle_from_vec") - { - CHECK(qv::epsilonEqual(qvec3f(0, 0, 0), qv::mangle_from_vec(qvec3f(1, 0, 0)), MANGLE_EPSILON)); - CHECK(qv::epsilonEqual(qvec3f(180, 0, 0), qv::mangle_from_vec(qvec3f(-1, 0, 0)), MANGLE_EPSILON)); - CHECK(qv::epsilonEqual(qvec3f(0, 90, 0), qv::mangle_from_vec(qvec3f(0, 0, 1)), MANGLE_EPSILON)); - CHECK(qv::epsilonEqual(qvec3f(0, -90, 0), qv::mangle_from_vec(qvec3f(0, 0, -1)), MANGLE_EPSILON)); - - for (int yaw = -179; yaw <= 179; yaw++) { - for (int pitch = -89; pitch <= 89; pitch++) { - const qvec3f origMangle = qvec3f(yaw, pitch, 0); - const qvec3f vec = qv::vec_from_mangle(origMangle); - const qvec3f roundtrip = qv::mangle_from_vec(vec); - CHECK(qv::epsilonEqual(origMangle, roundtrip, MANGLE_EPSILON)); - } +TEST(mathlib, mangleFromVec) +{ + EXPECT_TRUE(qv::epsilonEqual(qvec3f(0, 0, 0), qv::mangle_from_vec(qvec3f(1, 0, 0)), MANGLE_EPSILON)); + EXPECT_TRUE(qv::epsilonEqual(qvec3f(180, 0, 0), qv::mangle_from_vec(qvec3f(-1, 0, 0)), MANGLE_EPSILON)); + EXPECT_TRUE(qv::epsilonEqual(qvec3f(0, 90, 0), qv::mangle_from_vec(qvec3f(0, 0, 1)), MANGLE_EPSILON)); + EXPECT_TRUE(qv::epsilonEqual(qvec3f(0, -90, 0), qv::mangle_from_vec(qvec3f(0, 0, -1)), MANGLE_EPSILON)); + + for (int yaw = -179; yaw <= 179; yaw++) { + for (int pitch = -89; pitch <= 89; pitch++) { + const qvec3f origMangle = qvec3f(yaw, pitch, 0); + const qvec3f vec = qv::vec_from_mangle(origMangle); + const qvec3f roundtrip = qv::mangle_from_vec(vec); + EXPECT_TRUE(qv::epsilonEqual(origMangle, roundtrip, MANGLE_EPSILON)); } } +} - TEST_CASE("bilinearInterpolate") - { - const qvec4f v1(0, 1, 2, 3); - const qvec4f v2(4, 5, 6, 7); - const qvec4f v3(1, 1, 1, 1); - const qvec4f v4(2, 2, 2, 2); - - CHECK(v1 == bilinearInterpolate(v1, v2, v3, v4, 0.0f, 0.0f)); - CHECK(v2 == bilinearInterpolate(v1, v2, v3, v4, 1.0f, 0.0f)); - CHECK(v3 == bilinearInterpolate(v1, v2, v3, v4, 0.0f, 1.0f)); - CHECK(v4 == bilinearInterpolate(v1, v2, v3, v4, 1.0f, 1.0f)); - - CHECK(qvec4f(1.5, 1.5, 1.5, 1.5) == bilinearInterpolate(v1, v2, v3, v4, 0.5f, 1.0f)); - CHECK(qvec4f(2, 3, 4, 5) == bilinearInterpolate(v1, v2, v3, v4, 0.5f, 0.0f)); - CHECK(qvec4f(1.75, 2.25, 2.75, 3.25) == bilinearInterpolate(v1, v2, v3, v4, 0.5f, 0.5f)); - } +TEST(mathlib, bilinearInterpolate) +{ + const qvec4f v1(0, 1, 2, 3); + const qvec4f v2(4, 5, 6, 7); + const qvec4f v3(1, 1, 1, 1); + const qvec4f v4(2, 2, 2, 2); + + EXPECT_EQ(v1, bilinearInterpolate(v1, v2, v3, v4, 0.0f, 0.0f)); + EXPECT_EQ(v2, bilinearInterpolate(v1, v2, v3, v4, 1.0f, 0.0f)); + EXPECT_EQ(v3, bilinearInterpolate(v1, v2, v3, v4, 0.0f, 1.0f)); + EXPECT_EQ(v4, bilinearInterpolate(v1, v2, v3, v4, 1.0f, 1.0f)); + + EXPECT_EQ(qvec4f(1.5, 1.5, 1.5, 1.5), bilinearInterpolate(v1, v2, v3, v4, 0.5f, 1.0f)); + EXPECT_EQ(qvec4f(2, 3, 4, 5), bilinearInterpolate(v1, v2, v3, v4, 0.5f, 0.0f)); + EXPECT_EQ(qvec4f(1.75, 2.25, 2.75, 3.25), bilinearInterpolate(v1, v2, v3, v4, 0.5f, 0.5f)); +} - TEST_CASE("bilinearWeightsAndCoords") - { - const auto res = bilinearWeightsAndCoords(qvec2f(0.5, 0.25), qvec2i(2, 2)); +TEST(mathlib, bilinearWeightsAndCoords) +{ + const auto res = bilinearWeightsAndCoords(qvec2f(0.5, 0.25), qvec2i(2, 2)); - qvec2f sum{}; - for (int i = 0; i < 4; i++) { - const float weight = res[i].second; - const qvec2i intPos = res[i].first; - sum += qvec2f(intPos) * weight; - } - CHECK(qvec2f(0.5, 0.25) == sum); + qvec2f sum{}; + for (int i = 0; i < 4; i++) { + const float weight = res[i].second; + const qvec2i intPos = res[i].first; + sum += qvec2f(intPos) * weight; } + EXPECT_EQ(qvec2f(0.5, 0.25), sum); +} - TEST_CASE("bilinearWeightsAndCoords2") - { - const auto res = bilinearWeightsAndCoords(qvec2f(1.5, 0.5), qvec2i(2, 2)); +TEST(mathlib, bilinearWeightsAndCoords2) +{ + const auto res = bilinearWeightsAndCoords(qvec2f(1.5, 0.5), qvec2i(2, 2)); - qvec2f sum{}; - for (int i = 0; i < 4; i++) { - const float weight = res[i].second; - const qvec2i intPos = res[i].first; - sum += qvec2f(intPos) * weight; - } - CHECK(qvec2f(1.0, 0.5) == sum); + qvec2f sum{}; + for (int i = 0; i < 4; i++) { + const float weight = res[i].second; + const qvec2i intPos = res[i].first; + sum += qvec2f(intPos) * weight; } + EXPECT_EQ(qvec2f(1.0, 0.5), sum); +} - TEST_CASE("pointsAlongLine") - { - const auto res = PointsAlongLine(qvec3f(1, 0, 0), qvec3f(3.5, 0, 0), 1.5f); +TEST(mathlib, pointsAlongLine) +{ + const auto res = PointsAlongLine(qvec3f(1, 0, 0), qvec3f(3.5, 0, 0), 1.5f); - REQUIRE(2 == res.size()); - REQUIRE(qv::epsilonEqual(qvec3f(1, 0, 0), res[0], static_cast(POINT_EQUAL_EPSILON))); - REQUIRE(qv::epsilonEqual(qvec3f(2.5, 0, 0), res[1], static_cast(POINT_EQUAL_EPSILON))); - } + ASSERT_EQ(2, res.size()); + ASSERT_TRUE(qv::epsilonEqual(qvec3f(1, 0, 0), res[0], static_cast(POINT_EQUAL_EPSILON))); + ASSERT_TRUE(qv::epsilonEqual(qvec3f(2.5, 0, 0), res[1], static_cast(POINT_EQUAL_EPSILON))); +} // FIXME: this is failing #if 0 -TEST_CASE("RandomPointInPoly") { +TEST(RandomPointInPoly) { const vector poly { { 0,0,0 }, { 0,32,0 }, // colinear point @@ -553,7 +546,7 @@ TEST_CASE("RandomPointInPoly") { const int N=100; for (int i=0; i 60); - REQUIRE(max[1] > 60); - REQUIRE(max[2] == 0); + ASSERT_GT(max[0], 60); + ASSERT_GT(max[1], 60); + ASSERT_EQ(max[2], 0); - REQUIRE(qv::length(avg - qvec3f(32, 32, 0)) < 4); + ASSERT_LT(qv::length(avg - qvec3f(32, 32, 0)), 4); } #endif - TEST_CASE("FractionOfLine") - { - REQUIRE(doctest::Approx(0) == FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0, 0, 0))); - REQUIRE(doctest::Approx(0.5) == FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0.5, 0.5, 0.5))); - REQUIRE(doctest::Approx(1) == FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(1, 1, 1))); - REQUIRE(doctest::Approx(2) == FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(2, 2, 2))); - REQUIRE(doctest::Approx(-1) == FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(-1, -1, -1))); +TEST(mathlib, FractionOfLine) +{ + ASSERT_FLOAT_EQ(0, FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0, 0, 0))); + ASSERT_FLOAT_EQ(0.5, FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0.5, 0.5, 0.5))); + ASSERT_FLOAT_EQ(1, FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(1, 1, 1))); + ASSERT_FLOAT_EQ(2, FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(2, 2, 2))); + ASSERT_FLOAT_EQ(-1, FractionOfLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(-1, -1, -1))); - REQUIRE(doctest::Approx(0) == FractionOfLine(qvec3f(0, 0, 0), qvec3f(0, 0, 0), qvec3f(0, 0, 0))); - } + ASSERT_FLOAT_EQ(0, FractionOfLine(qvec3f(0, 0, 0), qvec3f(0, 0, 0), qvec3f(0, 0, 0))); +} - TEST_CASE("DistToLine") - { - const float epsilon = 0.001; +TEST(mathlib, DistToLine) +{ + const float epsilon = 0.001; - REQUIRE(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0, 0, 0))) < epsilon); - REQUIRE(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0.5, 0.5, 0.5))) < epsilon); - REQUIRE(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(1, 1, 1))) < epsilon); - REQUIRE(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(2, 2, 2))) < epsilon); - REQUIRE(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(-1, -1, -1))) < epsilon); + ASSERT_LT(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0, 0, 0))), epsilon); + ASSERT_LT(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0.5, 0.5, 0.5))), epsilon); + ASSERT_LT(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(1, 1, 1))), epsilon); + ASSERT_LT(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(2, 2, 2))), epsilon); + ASSERT_LT(fabs(0 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(-1, -1, -1))), epsilon); - REQUIRE(fabs(sqrt(2) / 2 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 0), qvec3f(0, 1, 0))) < epsilon); - REQUIRE(fabs(sqrt(2) / 2 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 0), qvec3f(1, 0, 0))) < epsilon); + ASSERT_LT(fabs(sqrt(2) / 2 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 0), qvec3f(0, 1, 0))), epsilon); + ASSERT_LT(fabs(sqrt(2) / 2 - DistToLine(qvec3f(0, 0, 0), qvec3f(1, 1, 0), qvec3f(1, 0, 0))), epsilon); - REQUIRE(fabs(0.5 - DistToLine(qvec3f(10, 0, 0), qvec3f(10, 0, 100), qvec3f(9.5, 0, 0))) < epsilon); - } + ASSERT_LT(fabs(0.5 - DistToLine(qvec3f(10, 0, 0), qvec3f(10, 0, 100), qvec3f(9.5, 0, 0))), epsilon); +} - TEST_CASE("DistToLineSegment") - { - const float epsilon = 0.001; +TEST(mathlib, DistToLineSegment) +{ + const float epsilon = 0.001; - REQUIRE(fabs(0 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0, 0, 0))) < epsilon); - REQUIRE(fabs(0 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0.5, 0.5, 0.5))) < epsilon); - REQUIRE(fabs(0 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(1, 1, 1))) < epsilon); - REQUIRE(fabs(sqrt(3) - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(2, 2, 2))) < epsilon); - REQUIRE(fabs(sqrt(3) - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(-1, -1, -1))) < epsilon); + ASSERT_LT(fabs(0 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0, 0, 0))), epsilon); + ASSERT_LT(fabs(0 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(0.5, 0.5, 0.5))), epsilon); + ASSERT_LT(fabs(0 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(1, 1, 1))), epsilon); + ASSERT_LT(fabs(sqrt(3) - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(2, 2, 2))), epsilon); + ASSERT_LT(fabs(sqrt(3) - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 1), qvec3f(-1, -1, -1))), epsilon); - REQUIRE(fabs(sqrt(2) / 2 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 0), qvec3f(0, 1, 0))) < epsilon); - REQUIRE(fabs(sqrt(2) / 2 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 0), qvec3f(1, 0, 0))) < epsilon); + ASSERT_LT(fabs(sqrt(2) / 2 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 0), qvec3f(0, 1, 0))), epsilon); + ASSERT_LT(fabs(sqrt(2) / 2 - DistToLineSegment(qvec3f(0, 0, 0), qvec3f(1, 1, 0), qvec3f(1, 0, 0))), epsilon); - REQUIRE(fabs(0.5 - DistToLineSegment(qvec3f(10, 0, 0), qvec3f(10, 0, 100), qvec3f(9.5, 0, 0))) < epsilon); - } + ASSERT_LT(fabs(0.5 - DistToLineSegment(qvec3f(10, 0, 0), qvec3f(10, 0, 100), qvec3f(9.5, 0, 0))), epsilon); +} - TEST_CASE("linesOverlap_points") - { - REQUIRE(LinesOverlap({0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0})); - } +TEST(mathlib, linesOverlapPoints) +{ + ASSERT_TRUE(LinesOverlap({0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0})); +} - TEST_CASE("linesOverlap_point_line") - { - REQUIRE(LinesOverlap({0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 1})); - } +TEST(mathlib, linesOverlapPointLine) +{ + ASSERT_TRUE(LinesOverlap({0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 1})); +} - TEST_CASE("linesOverlap_same") - { - REQUIRE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 0}, {0, 0, 1})); - } +TEST(mathlib, linesOverlapSame) +{ + ASSERT_TRUE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 0}, {0, 0, 1})); +} - TEST_CASE("linesOverlap_same_opposite_dir") - { - REQUIRE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 1}, {0, 0, 0})); - } +TEST(mathlib, linesOverlapSameOppositeDir) +{ + ASSERT_TRUE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 1}, {0, 0, 0})); +} - TEST_CASE("linesOverlap_overlap") - { - REQUIRE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 0.5}, {0, 0, 1.5})); - } +TEST(mathlib, linesOverlapOverlap) +{ + ASSERT_TRUE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 0.5}, {0, 0, 1.5})); +} - TEST_CASE("linesOverlap_overlap_opposite_dir") - { - REQUIRE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 1.5}, {0, 0, 0.5})); - } +TEST(mathlib, linesOverlapOverlapOppositeDir) +{ + ASSERT_TRUE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 1.5}, {0, 0, 0.5})); +} - TEST_CASE("linesOverlap_only_tips_touching") - { - REQUIRE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 1}, {0, 0, 2})); - } +TEST(mathlib, linesOverlapOnlyTipsTouching) +{ + ASSERT_TRUE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 1}, {0, 0, 2})); +} - TEST_CASE("linesOverlap_non_colinear") - { - REQUIRE_FALSE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {5, 0, 0}, {5, 0, 1})); - } +TEST(mathlib, linesOverlapNonColinear) +{ + ASSERT_FALSE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {5, 0, 0}, {5, 0, 1})); +} - TEST_CASE("linesOverlap_colinear_not_touching") - { - REQUIRE_FALSE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 2}, {0, 0, 3})); - } +TEST(mathlib, linesOverlapColinearNotTouching) +{ + ASSERT_FALSE(LinesOverlap({0, 0, 0}, {0, 0, 1}, {0, 0, 2}, {0, 0, 3})); +} - // qvec +// qvec - TEST_CASE("qvec_expand") - { - const qvec2f test(1, 2); - const qvec4f test2(test); +TEST(mathlib, qvecExpand) +{ + const qvec2f test(1, 2); + const qvec4f test2(test); - CHECK(1 == test2[0]); - CHECK(2 == test2[1]); - CHECK(0 == test2[2]); - CHECK(0 == test2[3]); - } + EXPECT_EQ(1, test2[0]); + EXPECT_EQ(2, test2[1]); + EXPECT_EQ(0, test2[2]); + EXPECT_EQ(0, test2[3]); +} - TEST_CASE("qvec_contract") - { - const qvec4f test(1, 2, 0, 0); - const qvec2f test2(test); +TEST(mathlib, qvecContract) +{ + const qvec4f test(1, 2, 0, 0); + const qvec2f test2(test); - CHECK(1 == test2[0]); - CHECK(2 == test2[1]); - } + EXPECT_EQ(1, test2[0]); + EXPECT_EQ(2, test2[1]); +} - TEST_CASE("qvec_copy") - { - const qvec2f test(1, 2); - const qvec2f test2(test); +TEST(mathlib, qvecCopy) +{ + const qvec2f test(1, 2); + const qvec2f test2(test); - CHECK(1 == test2[0]); - CHECK(2 == test2[1]); - } + EXPECT_EQ(1, test2[0]); + EXPECT_EQ(2, test2[1]); +} - TEST_CASE("qvec_constructor_init") - { - const qvec2f test{}; - CHECK(0 == test[0]); - CHECK(0 == test[1]); - } +TEST(mathlib, qvecConstructorInit) +{ + const qvec2f test{}; + EXPECT_EQ(0, test[0]); + EXPECT_EQ(0, test[1]); +} - TEST_CASE("qvec_constructor_1") - { - const qvec2f test(42); - CHECK(42 == test[0]); - CHECK(42 == test[1]); - } +TEST(mathlib, qvecConstructor1) +{ + const qvec2f test(42); + EXPECT_EQ(42, test[0]); + EXPECT_EQ(42, test[1]); +} - TEST_CASE("qvec_constructor_fewer") - { - const qvec4f test(1, 2, 3); - CHECK(1 == test[0]); - CHECK(2 == test[1]); - CHECK(3 == test[2]); - CHECK(0 == test[3]); - } +TEST(mathlib, qvecConstructorFewer) +{ + const qvec4f test(1, 2, 3); + EXPECT_EQ(1, test[0]); + EXPECT_EQ(2, test[1]); + EXPECT_EQ(3, test[2]); + EXPECT_EQ(0, test[3]); +} - TEST_CASE("qvec_constructor_extra") - { - const qvec2f test(1, 2, 3); - CHECK(1 == test[0]); - CHECK(2 == test[1]); - } +TEST(mathlib, qvecConstructorExtra) +{ + const qvec2f test(1, 2, 3); + EXPECT_EQ(1, test[0]); + EXPECT_EQ(2, test[1]); +} - // aabb3f +// aabb3f - TEST_CASE("aabb_basic") - { - const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); +TEST(mathlib, aabbBasic) +{ + const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); - CHECK(qvec3f(1, 1, 1) == b1.mins()); - CHECK(qvec3f(10, 10, 10) == b1.maxs()); - CHECK(qvec3f(9, 9, 9) == b1.size()); - } + EXPECT_EQ(qvec3f(1, 1, 1), b1.mins()); + EXPECT_EQ(qvec3f(10, 10, 10), b1.maxs()); + EXPECT_EQ(qvec3f(9, 9, 9), b1.size()); +} - TEST_CASE("aabb_grow") - { - const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); +TEST(mathlib, aabbGrow) +{ + const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); - CHECK(aabb3f(qvec3f(0, 0, 0), qvec3f(11, 11, 11)) == b1.grow(qvec3f(1, 1, 1))); - } + EXPECT_EQ(aabb3f(qvec3f(0, 0, 0), qvec3f(11, 11, 11)), b1.grow(qvec3f(1, 1, 1))); +} - TEST_CASE("aabb_unionwith") - { - const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); - const aabb3f b2(qvec3f(11, 11, 11), qvec3f(12, 12, 12)); +TEST(mathlib, aabbUnionwith) +{ + const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); + const aabb3f b2(qvec3f(11, 11, 11), qvec3f(12, 12, 12)); - CHECK(aabb3f(qvec3f(1, 1, 1), qvec3f(12, 12, 12)) == b1.unionWith(b2)); - } + EXPECT_EQ(aabb3f(qvec3f(1, 1, 1), qvec3f(12, 12, 12)), b1.unionWith(b2)); +} - TEST_CASE("aabb_expand") - { - const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); +TEST(mathlib, aabbExpand) +{ + const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); - CHECK(b1 == b1.expand(qvec3f(1, 1, 1))); - CHECK(b1 == b1.expand(qvec3f(5, 5, 5))); - CHECK(b1 == b1.expand(qvec3f(10, 10, 10))); + EXPECT_EQ(b1, b1.expand(qvec3f(1, 1, 1))); + EXPECT_EQ(b1, b1.expand(qvec3f(5, 5, 5))); + EXPECT_EQ(b1, b1.expand(qvec3f(10, 10, 10))); - const aabb3f b2(qvec3f(1, 1, 1), qvec3f(100, 10, 10)); - CHECK(b2 == b1.expand(qvec3f(100, 10, 10))); + const aabb3f b2(qvec3f(1, 1, 1), qvec3f(100, 10, 10)); + EXPECT_EQ(b2, b1.expand(qvec3f(100, 10, 10))); - const aabb3f b3(qvec3f(0, 1, 1), qvec3f(10, 10, 10)); - CHECK(b3 == b1.expand(qvec3f(0, 1, 1))); - } + const aabb3f b3(qvec3f(0, 1, 1), qvec3f(10, 10, 10)); + EXPECT_EQ(b3, b1.expand(qvec3f(0, 1, 1))); +} - TEST_CASE("aabb_disjoint") - { - const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); +TEST(mathlib, aabbDisjoint) +{ + const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); - const aabb3f yes1(qvec3f(-1, -1, -1), qvec3f(0, 0, 0)); - const aabb3f yes2(qvec3f(11, 1, 1), qvec3f(12, 10, 10)); + const aabb3f yes1(qvec3f(-1, -1, -1), qvec3f(0, 0, 0)); + const aabb3f yes2(qvec3f(11, 1, 1), qvec3f(12, 10, 10)); - const aabb3f no1(qvec3f(-1, -1, -1), qvec3f(1, 1, 1)); - const aabb3f no2(qvec3f(10, 10, 10), qvec3f(10.5, 10.5, 10.5)); - const aabb3f no3(qvec3f(5, 5, 5), qvec3f(100, 6, 6)); + const aabb3f no1(qvec3f(-1, -1, -1), qvec3f(1, 1, 1)); + const aabb3f no2(qvec3f(10, 10, 10), qvec3f(10.5, 10.5, 10.5)); + const aabb3f no3(qvec3f(5, 5, 5), qvec3f(100, 6, 6)); - CHECK(b1.disjoint(yes1)); - CHECK(b1.disjoint(yes2)); - CHECK_FALSE(b1.disjoint(no1)); - CHECK_FALSE(b1.disjoint(no2)); - CHECK_FALSE(b1.disjoint(no3)); + EXPECT_TRUE(b1.disjoint(yes1)); + EXPECT_TRUE(b1.disjoint(yes2)); + EXPECT_FALSE(b1.disjoint(no1)); + EXPECT_FALSE(b1.disjoint(no2)); + EXPECT_FALSE(b1.disjoint(no3)); - CHECK_FALSE(b1.intersectWith(yes1)); - CHECK_FALSE(b1.intersectWith(yes2)); + EXPECT_FALSE(b1.intersectWith(yes1)); + EXPECT_FALSE(b1.intersectWith(yes2)); - // these intersections are single points - CHECK(aabb3f::intersection_t(aabb3f(qvec3f(1, 1, 1), qvec3f(1, 1, 1))) == b1.intersectWith(no1)); - CHECK(aabb3f::intersection_t(aabb3f(qvec3f(10, 10, 10), qvec3f(10, 10, 10))) == b1.intersectWith(no2)); + // these intersections are single points + EXPECT_EQ(aabb3f::intersection_t(aabb3f(qvec3f(1, 1, 1), qvec3f(1, 1, 1))), b1.intersectWith(no1)); + EXPECT_EQ(aabb3f::intersection_t(aabb3f(qvec3f(10, 10, 10), qvec3f(10, 10, 10))), b1.intersectWith(no2)); - // an intersection with a volume - CHECK(aabb3f::intersection_t(aabb3f(qvec3f(5, 5, 5), qvec3f(10, 6, 6))) == b1.intersectWith(no3)); + // an intersection with a volume + EXPECT_EQ(aabb3f::intersection_t(aabb3f(qvec3f(5, 5, 5), qvec3f(10, 6, 6))), b1.intersectWith(no3)); - CHECK(b1.disjoint_or_touching(aabb3f(qvec3f(10, 1, 1), qvec3f(20, 10, 10)))); - CHECK(b1.disjoint_or_touching(aabb3f(qvec3f(11, 1, 1), qvec3f(20, 10, 10)))); - CHECK_FALSE(b1.disjoint_or_touching(aabb3f(qvec3f(9.99, 1, 1), qvec3f(20, 10, 10)))); - } + EXPECT_TRUE(b1.disjoint_or_touching(aabb3f(qvec3f(10, 1, 1), qvec3f(20, 10, 10)))); + EXPECT_TRUE(b1.disjoint_or_touching(aabb3f(qvec3f(11, 1, 1), qvec3f(20, 10, 10)))); + EXPECT_FALSE(b1.disjoint_or_touching(aabb3f(qvec3f(9.99, 1, 1), qvec3f(20, 10, 10)))); +} - TEST_CASE("aabb_contains") - { - const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); +TEST(mathlib, aabbContains) +{ + const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); - const aabb3f yes1(qvec3f(1, 1, 1), qvec3f(2, 2, 2)); - const aabb3f yes2(qvec3f(9, 9, 9), qvec3f(10, 10, 10)); + const aabb3f yes1(qvec3f(1, 1, 1), qvec3f(2, 2, 2)); + const aabb3f yes2(qvec3f(9, 9, 9), qvec3f(10, 10, 10)); - const aabb3f no1(qvec3f(-1, 1, 1), qvec3f(2, 2, 2)); - const aabb3f no2(qvec3f(9, 9, 9), qvec3f(10.5, 10, 10)); + const aabb3f no1(qvec3f(-1, 1, 1), qvec3f(2, 2, 2)); + const aabb3f no2(qvec3f(9, 9, 9), qvec3f(10.5, 10, 10)); - CHECK(b1.contains(yes1)); - CHECK(b1.contains(yes2)); - CHECK_FALSE(b1.contains(no1)); - CHECK_FALSE(b1.contains(no2)); - } + EXPECT_TRUE(b1.contains(yes1)); + EXPECT_TRUE(b1.contains(yes2)); + EXPECT_FALSE(b1.contains(no1)); + EXPECT_FALSE(b1.contains(no2)); +} - TEST_CASE("aabb_containsPoint") - { - const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); - - const qvec3f yes1(1, 1, 1); - const qvec3f yes2(2, 2, 2); - const qvec3f yes3(10, 10, 10); - - const qvec3f no1(0, 0, 0); - const qvec3f no2(1, 1, 0); - const qvec3f no3(10.1, 10.1, 10.1); - - CHECK(b1.containsPoint(yes1)); - CHECK(b1.containsPoint(yes2)); - CHECK(b1.containsPoint(yes3)); - CHECK_FALSE(b1.containsPoint(no1)); - CHECK_FALSE(b1.containsPoint(no2)); - CHECK_FALSE(b1.containsPoint(no3)); - } +TEST(mathlib, aabbContainsPoint) +{ + const aabb3f b1(qvec3f(1, 1, 1), qvec3f(10, 10, 10)); + + const qvec3f yes1(1, 1, 1); + const qvec3f yes2(2, 2, 2); + const qvec3f yes3(10, 10, 10); + + const qvec3f no1(0, 0, 0); + const qvec3f no2(1, 1, 0); + const qvec3f no3(10.1, 10.1, 10.1); + + EXPECT_TRUE(b1.containsPoint(yes1)); + EXPECT_TRUE(b1.containsPoint(yes2)); + EXPECT_TRUE(b1.containsPoint(yes3)); + EXPECT_FALSE(b1.containsPoint(no1)); + EXPECT_FALSE(b1.containsPoint(no2)); + EXPECT_FALSE(b1.containsPoint(no3)); +} - TEST_CASE("aabb_create_invalid") - { - const aabb3f b1(qvec3f(1, 1, 1), qvec3f(-1, -1, -1)); - const aabb3f fixed(qvec3f(1, 1, 1), qvec3f(1, 1, 1)); +TEST(mathlib, aabbCreateInvalid) +{ + const aabb3f b1(qvec3f(1, 1, 1), qvec3f(-1, -1, -1)); + const aabb3f fixed(qvec3f(1, 1, 1), qvec3f(1, 1, 1)); - CHECK(fixed == b1); - CHECK(qvec3f(0, 0, 0) == b1.size()); - } + EXPECT_EQ(fixed, b1); + EXPECT_EQ(qvec3f(0, 0, 0), b1.size()); } -TEST_SUITE("qvec") +TEST(qmat, matrix2x2inv) { - - TEST_CASE("matrix2x2inv") - { - std::mt19937 engine(0); - std::uniform_real_distribution dis(-4096, 4096); - - qmat2x2f randMat; - for (int i = 0; i < 2; i++) - for (int j = 0; j < 2; j++) - randMat.at(i, j) = dis(engine); - - qmat2x2f randInv = qv::inverse(randMat); - REQUIRE_FALSE(std::isnan(randInv.at(0, 0))); - - qmat2x2f prod = randMat * randInv; - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - float exp = (i == j) ? 1.0f : 0.0f; - REQUIRE(fabs(exp - prod.at(i, j)) < 0.001); - } + std::mt19937 engine(0); + std::uniform_real_distribution dis(-4096, 4096); + + qmat2x2f randMat; + for (int i = 0; i < 2; i++) + for (int j = 0; j < 2; j++) + randMat.at(i, j) = dis(engine); + + qmat2x2f randInv = qv::inverse(randMat); + ASSERT_FALSE(std::isnan(randInv.at(0, 0))); + + qmat2x2f prod = randMat * randInv; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + float exp = (i == j) ? 1.0f : 0.0f; + ASSERT_LT(fabs(exp - prod.at(i, j)), 0.001); } - - // check non-invertible gives nan - qmat2x2f nanMat = qv::inverse(qmat2x2f(0)); - REQUIRE(std::isnan(nanMat.at(0, 0))); } - TEST_CASE("matrix3x3inv") - { - std::mt19937 engine(0); - std::uniform_real_distribution dis(-4096, 4096); - - qmat3x3f randMat; - for (int i = 0; i < 3; i++) - for (int j = 0; j < 3; j++) - randMat.at(i, j) = dis(engine); - - qmat3x3f randInv = qv::inverse(randMat); - REQUIRE_FALSE(std::isnan(randInv.at(0, 0))); - - qmat3x3f prod = randMat * randInv; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - float exp = (i == j) ? 1.0f : 0.0f; - REQUIRE(fabs(exp - prod.at(i, j)) < 0.001); - } - } - - // check non-invertible gives nan - qmat3x3f nanMat = qv::inverse(qmat3x3f(0)); - REQUIRE(std::isnan(nanMat.at(0, 0))); - } + // check non-invertible gives nan + qmat2x2f nanMat = qv::inverse(qmat2x2f(0)); + ASSERT_TRUE(std::isnan(nanMat.at(0, 0))); +} - TEST_CASE("matrix4x4inv") - { - std::mt19937 engine(0); - std::uniform_real_distribution dis(-4096, 4096); - - qmat4x4f randMat; - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) - randMat.at(i, j) = dis(engine); - - qmat4x4f randInv = qv::inverse(randMat); - REQUIRE_FALSE(std::isnan(randInv.at(0, 0))); - - qmat4x4f prod = randMat * randInv; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - float exp = (i == j) ? 1.0f : 0.0f; - REQUIRE(fabs(exp - prod.at(i, j)) < 0.001); - } +TEST(qmat, matrix3x3inv) +{ + std::mt19937 engine(0); + std::uniform_real_distribution dis(-4096, 4096); + + qmat3x3f randMat; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + randMat.at(i, j) = dis(engine); + + qmat3x3f randInv = qv::inverse(randMat); + ASSERT_FALSE(std::isnan(randInv.at(0, 0))); + + qmat3x3f prod = randMat * randInv; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + float exp = (i == j) ? 1.0f : 0.0f; + ASSERT_LT(fabs(exp - prod.at(i, j)), 0.001); } - - // check non-invertible gives nan - qmat4x4f nanMat = qv::inverse(qmat4x4f(0)); - REQUIRE(std::isnan(nanMat.at(0, 0))); } - TEST_CASE("qmat_construct_initialize") - { - const qmat2x2f test{1, 2, 3, 4}; // column major + // check non-invertible gives nan + qmat3x3f nanMat = qv::inverse(qmat3x3f(0)); + ASSERT_TRUE(std::isnan(nanMat.at(0, 0))); +} - CHECK(qvec2f{1, 3} == test.row(0)); - CHECK(qvec2f{2, 4} == test.row(1)); +TEST(qmat, matrix4x4inv) +{ + std::mt19937 engine(0); + std::uniform_real_distribution dis(-4096, 4096); + + qmat4x4f randMat; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + randMat.at(i, j) = dis(engine); + + qmat4x4f randInv = qv::inverse(randMat); + ASSERT_FALSE(std::isnan(randInv.at(0, 0))); + + qmat4x4f prod = randMat * randInv; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + float exp = (i == j) ? 1.0f : 0.0f; + ASSERT_LT(fabs(exp - prod.at(i, j)), 0.001); + } } - TEST_CASE("qmat_construct_row_major") - { - const qmat2x2f test = qmat2x2f::row_major({1, 2, 3, 4}); - - CHECK(qvec2f{1, 2} == test.row(0)); - CHECK(qvec2f{3, 4} == test.row(1)); - } + // check non-invertible gives nan + qmat4x4f nanMat = qv::inverse(qmat4x4f(0)); + ASSERT_TRUE(std::isnan(nanMat.at(0, 0))); } -TEST_SUITE("trace") -{ - TEST_CASE("clamp_texcoord_small") - { - // positive - CHECK(0 == clamp_texcoord(0.0f, 2)); - CHECK(0 == clamp_texcoord(0.5f, 2)); - CHECK(1 == clamp_texcoord(1.0f, 2)); - CHECK(1 == clamp_texcoord(1.5f, 2)); - CHECK(0 == clamp_texcoord(2.0f, 2)); - CHECK(0 == clamp_texcoord(2.5f, 2)); - - // negative - CHECK(1 == clamp_texcoord(-0.5f, 2)); - CHECK(1 == clamp_texcoord(-1.0f, 2)); - CHECK(0 == clamp_texcoord(-1.5f, 2)); - CHECK(0 == clamp_texcoord(-2.0f, 2)); - CHECK(1 == clamp_texcoord(-2.5f, 2)); - } +TEST(qmat, qmatConstructInitialize) +{ + const qmat2x2f test{1, 2, 3, 4}; // column major - TEST_CASE("clamp_texcoord") - { - // positive - CHECK(0 == clamp_texcoord(0.0f, 128)); - CHECK(64 == clamp_texcoord(64.0f, 128)); - CHECK(64 == clamp_texcoord(64.5f, 128)); - CHECK(127 == clamp_texcoord(127.0f, 128)); - CHECK(0 == clamp_texcoord(128.0f, 128)); - CHECK(1 == clamp_texcoord(129.0f, 128)); - - // negative - CHECK(127 == clamp_texcoord(-0.5f, 128)); - CHECK(127 == clamp_texcoord(-1.0f, 128)); - CHECK(1 == clamp_texcoord(-127.0f, 128)); - CHECK(0 == clamp_texcoord(-127.5f, 128)); - CHECK(0 == clamp_texcoord(-128.0f, 128)); - CHECK(127 == clamp_texcoord(-129.0f, 128)); - } + EXPECT_EQ(qvec2f(1, 3), test.row(0)); + EXPECT_EQ(qvec2f(2, 4), test.row(1)); } -TEST_SUITE("settings") +TEST(qmat, qmatConstructRowMajor) { + const qmat2x2f test = qmat2x2f::row_major({1, 2, 3, 4}); - TEST_CASE("delayDefault") - { - light_t light; - CHECK(LF_LINEAR == light.formula.value()); - } + EXPECT_EQ(qvec2f(1, 2), test.row(0)); + EXPECT_EQ(qvec2f(3, 4), test.row(1)); +} - TEST_CASE("delayParseInt") - { - light_t light; - parser_t p("2", {}); - CHECK(light.formula.parse(light.formula.primary_name(), p, settings::source::MAP)); - CHECK(LF_INVERSE2 == light.formula.value()); - } +TEST(mathlib, clampTexcoordSmall) +{ + // positive + EXPECT_EQ(0, clamp_texcoord(0.0f, 2)); + EXPECT_EQ(0, clamp_texcoord(0.5f, 2)); + EXPECT_EQ(1, clamp_texcoord(1.0f, 2)); + EXPECT_EQ(1, clamp_texcoord(1.5f, 2)); + EXPECT_EQ(0, clamp_texcoord(2.0f, 2)); + EXPECT_EQ(0, clamp_texcoord(2.5f, 2)); + + // negative + EXPECT_EQ(1, clamp_texcoord(-0.5f, 2)); + EXPECT_EQ(1, clamp_texcoord(-1.0f, 2)); + EXPECT_EQ(0, clamp_texcoord(-1.5f, 2)); + EXPECT_EQ(0, clamp_texcoord(-2.0f, 2)); + EXPECT_EQ(1, clamp_texcoord(-2.5f, 2)); +} - TEST_CASE("delayParseIntUnknown") - { - light_t light; - parser_t p("500", {}); - CHECK(light.formula.parse(light.formula.primary_name(), p, settings::source::MAP)); - // not sure if we should be strict and reject parsing this? - CHECK(500 == light.formula.value()); - } +TEST(mathlib, clampTexcoord) +{ + // positive + EXPECT_EQ(0, clamp_texcoord(0.0f, 128)); + EXPECT_EQ(64, clamp_texcoord(64.0f, 128)); + EXPECT_EQ(64, clamp_texcoord(64.5f, 128)); + EXPECT_EQ(127, clamp_texcoord(127.0f, 128)); + EXPECT_EQ(0, clamp_texcoord(128.0f, 128)); + EXPECT_EQ(1, clamp_texcoord(129.0f, 128)); + + // negative + EXPECT_EQ(127, clamp_texcoord(-0.5f, 128)); + EXPECT_EQ(127, clamp_texcoord(-1.0f, 128)); + EXPECT_EQ(1, clamp_texcoord(-127.0f, 128)); + EXPECT_EQ(0, clamp_texcoord(-127.5f, 128)); + EXPECT_EQ(0, clamp_texcoord(-128.0f, 128)); + EXPECT_EQ(127, clamp_texcoord(-129.0f, 128)); +} - TEST_CASE("delayParseFloat") - { - light_t light; - parser_t p("2.0", {}); - CHECK(light.formula.parse(light.formula.primary_name(), p, settings::source::MAP)); - CHECK(LF_INVERSE2 == light.formula.value()); - } +TEST(Settings, delayDefault) +{ + light_t light; + EXPECT_EQ(LF_LINEAR, light.formula.value()); +} - TEST_CASE("delayParseString") - { - light_t light; - parser_t p("inverse2", {}); - CHECK(light.formula.parse(light.formula.primary_name(), p, settings::source::MAP)); - CHECK(LF_INVERSE2 == light.formula.value()); - } +TEST(Settings, delayParseInt) +{ + light_t light; + parser_t p("2", {}); + EXPECT_TRUE(light.formula.parse(light.formula.primary_name(), p, settings::source::MAP)); + EXPECT_EQ(LF_INVERSE2, light.formula.value()); } +TEST(Settings, delayParseIntUnknown) +{ + light_t light; + parser_t p("500", {}); + EXPECT_TRUE(light.formula.parse(light.formula.primary_name(), p, settings::source::MAP)); + // not sure if we should be strict and reject parsing this? + EXPECT_EQ(500, light.formula.value()); +} +TEST(Settings, delayParseFloat) +{ + light_t light; + parser_t p("2.0", {}); + EXPECT_TRUE(light.formula.parse(light.formula.primary_name(), p, settings::source::MAP)); + EXPECT_EQ(LF_INVERSE2, light.formula.value()); +} -TEST_SUITE("light formats") +TEST(Settings, delayParseString) { - TEST_CASE("e5bgr9 pack (511, 1, 0)") - { - uint32_t packed = HDR_PackE5BRG9(qvec3f{511.0f, 1.0f, 0.0f}); + light_t light; + parser_t p("inverse2", {}); + EXPECT_TRUE(light.formula.parse(light.formula.primary_name(), p, settings::source::MAP)); + EXPECT_EQ(LF_INVERSE2, light.formula.value()); +} - // e | b | g | r - uint32_t expected = (24 << 27) | (0 << 18) | (1 << 9) | (511 << 0); - CHECK(packed == expected); +TEST(LightFormats, e5bgr9pack1) +{ + uint32_t packed = HDR_PackE5BRG9(qvec3f{511.0f, 1.0f, 0.0f}); - qvec3f roundtripped = HDR_UnpackE5BRG9(expected); - CHECK(roundtripped[0] == 511); - CHECK(roundtripped[1] == 1); - CHECK(roundtripped[2] == 0); - } + // e | b | g | r + uint32_t expected = (24 << 27) | (0 << 18) | (1 << 9) | (511 << 0); + EXPECT_EQ(packed, expected); - TEST_CASE("e5bgr9 pack (1'000'000, 0, 0)") - { - uint32_t packed = HDR_PackE5BRG9(qvec3f{1'000'000.0f, 0.0f, 0.0f}); + qvec3f roundtripped = HDR_UnpackE5BRG9(expected); + EXPECT_EQ(roundtripped[0], 511); + EXPECT_EQ(roundtripped[1], 1); + EXPECT_EQ(roundtripped[2], 0); +} - // e | b | g | r - uint32_t expected = (0x1f << 27) | (0 << 18) | (0 << 9) | (0x1ff << 0); - CHECK(packed == expected); +TEST(LightFormats, e5bgr9pack2) +{ + uint32_t packed = HDR_PackE5BRG9(qvec3f{1'000'000.0f, 0.0f, 0.0f}); - qvec3f roundtripped = HDR_UnpackE5BRG9(packed); - CHECK(roundtripped[0] == 65408.0f); - CHECK(roundtripped[1] == 0.0f); - CHECK(roundtripped[2] == 0.0f); - } + // e | b | g | r + uint32_t expected = (0x1f << 27) | (0 << 18) | (0 << 9) | (0x1ff << 0); + EXPECT_EQ(packed, expected); - TEST_CASE("e5bgr9 pack (0.1, 0.01, 0.001)") - { - qvec3f in = qvec3f{0.1, 0.01, 0.001}; - uint32_t packed = HDR_PackE5BRG9(in); + qvec3f roundtripped = HDR_UnpackE5BRG9(packed); + EXPECT_EQ(roundtripped[0], 65408.0f); + EXPECT_EQ(roundtripped[1], 0.0f); + EXPECT_EQ(roundtripped[2], 0.0f); +} - qvec3f roundtripped = HDR_UnpackE5BRG9(packed); - qvec3f error = qv::abs(in - roundtripped); +TEST(LightFormats, e5bgr9pack3) +{ + qvec3f in = qvec3f{0.1, 0.01, 0.001}; + uint32_t packed = HDR_PackE5BRG9(in); - CHECK(error[0] < 0.000098); - CHECK(error[1] < 0.00001); - CHECK(error[2] < 0.000025); - } + qvec3f roundtripped = HDR_UnpackE5BRG9(packed); + qvec3f error = qv::abs(in - roundtripped); + + EXPECT_LT(error[0], 0.000098); + EXPECT_LT(error[1], 0.00001); + EXPECT_LT(error[2], 0.000025); } diff --git a/tests/test_ltface.cc b/tests/test_ltface.cc index ff11c219..8d8e3492 100644 --- a/tests/test_ltface.cc +++ b/tests/test_ltface.cc @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -90,7 +90,7 @@ static testresults_t QbspVisLight_Common(const std::filesystem::path &name, std: if (is_q2) { auto lit_check_path = bsp_path; lit_check_path.replace_extension(".lit"); - CHECK(!fs::exists(lit_check_path)); + EXPECT_FALSE(fs::exists(lit_check_path)); } } @@ -137,88 +137,87 @@ testresults_t QbspVisLight_HL( return QbspVisLight_Common(name, {"-hlbsp"}, extra_light_args, run_vis); } -TEST_CASE("lightgrid_sample_t equality") +TEST(lightgridsample, styleEquality) { - SUBCASE("style equality") { lightgrid_sample_t a {.used = true, .style = 4, .color = {}}; lightgrid_sample_t b = a; - CHECK(a == b); + EXPECT_EQ(a, b); b.style = 6; - CHECK(a != b); - } + EXPECT_NE(a, b); +} - SUBCASE("color equality") { - lightgrid_sample_t a {.used = true, .style = 4, .color = {1,2,3}}; - lightgrid_sample_t b = a; - CHECK(a == b); +TEST(lightgridsample, colorEquality) { + lightgrid_sample_t a {.used = true, .style = 4, .color = {1,2,3}}; + lightgrid_sample_t b = a; + EXPECT_EQ(a, b); - b.color = {6,5,4}; - CHECK(a != b); - } + b.color = {6,5,4}; + EXPECT_NE(a, b); +} - SUBCASE("nan colors") { - lightgrid_sample_t a {.used = true, .style = 4, .color = {std::numeric_limits::quiet_NaN(), 1.0, 1.0}}; - lightgrid_sample_t b = a; - CHECK(a == b); +TEST(lightgridsample, nanColors) { + lightgrid_sample_t a {.used = true, .style = 4, .color = {std::numeric_limits::quiet_NaN(), 1.0, 1.0}}; + lightgrid_sample_t b = a; + EXPECT_EQ(a, b); - b.color = { 0,0,0}; - CHECK(a != b); - } + b.color = { 0,0,0}; + EXPECT_NE(a, b); +} - SUBCASE("unused equality doesn't consider other attributes") { - lightgrid_sample_t a, b; - CHECK(!a.used); - CHECK(a == b); +TEST(lightgridsample, unusedEqualityDoesntConsiderOtherAttributes) { + lightgrid_sample_t a, b; + EXPECT_FALSE(a.used); + EXPECT_EQ(a, b); - b.style = 5; - CHECK(a == b); + b.style = 5; + EXPECT_EQ(a, b); - b.color = {1, 0, 0}; - CHECK(a == b); - } + b.color = {1, 0, 0}; + EXPECT_EQ(a, b); } -TEST_CASE("-world_units_per_luxel, -lightgrid") + +TEST(worldunitsperluxel, lightgrid) { auto [bsp, bspx] = QbspVisLight_Q2("q2_lightmap_custom_scale.map", {"-lightgrid"}); { - INFO("back wall has texture scale 8 but still gets a luxel every 8 units"); + SCOPED_TRACE("back wall has texture scale 8 but still gets a luxel every 8 units"); auto *back_wall = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {448, -84, 276}, {-1, 0, 0}); auto back_wall_info = BSPX_DecoupledLM(bspx, Face_GetNum(&bsp, back_wall)); auto back_wall_extents = faceextents_t( *back_wall, bsp, back_wall_info.lmwidth, back_wall_info.lmheight, back_wall_info.world_to_lm_space); // NOTE: the exact values are not critical (depends on BSP splitting) but they should be relatively large - CHECK(75 == back_wall_extents.width()); - CHECK(43 == back_wall_extents.height()); + EXPECT_EQ(75, back_wall_extents.width()); + EXPECT_EQ(43, back_wall_extents.height()); } { - INFO("side wall func_group has _world_units_per_luxel 48, small lightmap"); + SCOPED_TRACE("side wall func_group has _world_units_per_luxel 48, small lightmap"); auto *side_wall = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {384, 240, 84}, {0, -1, 0}); auto side_wall_info = BSPX_DecoupledLM(bspx, Face_GetNum(&bsp, side_wall)); auto side_wall_extents = faceextents_t( *side_wall, bsp, side_wall_info.lmwidth, side_wall_info.lmheight, side_wall_info.world_to_lm_space); - CHECK(4 == side_wall_extents.width()); - CHECK(5 == side_wall_extents.height()); + EXPECT_EQ(4, side_wall_extents.width()); + EXPECT_EQ(5, side_wall_extents.height()); } { - INFO("sky gets an optimized lightmap"); + SCOPED_TRACE("sky gets an optimized lightmap"); auto *sky_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {256, 240, 84}, {0, -1, 0}); - CHECK(sky_face->styles[0] == 255); + EXPECT_EQ(sky_face->styles[0], 255); auto sky_face_info = BSPX_DecoupledLM(bspx, Face_GetNum(&bsp, sky_face)); - CHECK(sky_face_info.lmwidth == 0); - CHECK(sky_face_info.lmheight == 0); + EXPECT_EQ(sky_face_info.lmwidth, 0); + EXPECT_EQ(sky_face_info.lmheight, 0); } } -TEST_CASE("emissive cube artifacts") +TEST(ltfaceQ2, emissiveCubeArtifacts) { // A cube with surface flags "light", value "100", placed in a hallway. // @@ -253,7 +252,7 @@ TEST_CASE("emissive cube artifacts") auto lm_coord = extents.worldToLMCoord(pos); auto sample = LM_Sample(&bsp, floor, nullptr, extents, lm_info.offset, lm_coord); - CHECK(sample[0] >= previous_sample[0]); + EXPECT_GE(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); @@ -262,17 +261,17 @@ TEST_CASE("emissive cube artifacts") } } -TEST_CASE("-novanilla + -world_units_per_luxel") +TEST(ltfaceQ2, novanillaWorldUnitsPerLuxel) { auto [bsp, bspx] = QbspVisLight_Q2("q2_lightmap_custom_scale.map", {"-novanilla"}); for (auto &face : bsp.dfaces) { - CHECK(face.lightofs == -1); + EXPECT_EQ(face.lightofs, -1); } // make sure no other bspx lumps are written - CHECK(bspx.size() == 1); - CHECK(bspx.find("DECOUPLED_LM") != bspx.end()); + EXPECT_EQ(bspx.size(), 1); + EXPECT_NE(bspx.find("DECOUPLED_LM"), bspx.end()); // make sure all dlightdata bytes are accounted for by the DECOUPLED_LM lump // and no extra was written. @@ -293,7 +292,7 @@ TEST_CASE("-novanilla + -world_units_per_luxel") int bytes_per_face = 3 * samples_per_face; expected_dlightdata_bytes += bytes_per_face; } - CHECK(bsp.dlightdata.size() == expected_dlightdata_bytes); + EXPECT_EQ(bsp.dlightdata.size(), expected_dlightdata_bytes); } template @@ -307,7 +306,7 @@ static void CheckFaceLuxels( for (int x = 0; x < extents.width(); ++x) { for (int y = 0; y < extents.height(); ++y) { const qvec3b sample = LM_Sample(&bsp, &face, lit, extents, face.lightofs, {x, y}); - INFO("sample ", x, ", ", y); + SCOPED_TRACE(fmt::format("sample {}, {}", x, y)); lambda(sample); } } @@ -315,7 +314,7 @@ static void CheckFaceLuxels( static void CheckFaceLuxelsNonBlack(const mbsp_t &bsp, const mface_t &face) { - CheckFaceLuxels(bsp, face, [](qvec3b sample) { CHECK(sample[0] > 0); }); + CheckFaceLuxels(bsp, face, [](qvec3b sample) { EXPECT_GT(sample[0], 0); }); } static void CheckFaceLuxelAtPoint(const mbsp_t *bsp, const dmodelh2_t *model, const qvec3b &expected_color, @@ -323,7 +322,7 @@ static void CheckFaceLuxelAtPoint(const mbsp_t *bsp, const dmodelh2_t *model, co const bspxentries_t *bspx = nullptr) { auto *face = BSP_FindFaceAtPoint(bsp, model, point, normal); - REQUIRE(face); + ASSERT_TRUE(face); faceextents_t extents; int offset; @@ -342,16 +341,16 @@ static void CheckFaceLuxelAtPoint(const mbsp_t *bsp, const dmodelh2_t *model, co const auto int_coord = qvec2i(round(coord[0]), round(coord[1])); const qvec3b sample = LM_Sample(bsp, face, lit, extents, offset, int_coord); - INFO("world point: ", point); - INFO("lm coord: ", coord[0], ", ", coord[1]); - INFO("lm int_coord: ", int_coord[0], ", ", int_coord[1]); - INFO("face num: ", Face_GetNum(bsp, face)); - INFO("actual sample: ", sample[0], " ", sample[1], " ", sample[2]); + SCOPED_TRACE(fmt::format("world point: {}", point)); + SCOPED_TRACE(fmt::format("lm coord: {}", coord)); + SCOPED_TRACE(fmt::format("lm int_coord: {}", int_coord)); + SCOPED_TRACE(fmt::format("face num: {}", Face_GetNum(bsp, face))); + SCOPED_TRACE(fmt::format("actual sample: {}", sample)); qvec3i delta = qv::abs(qvec3i{sample} - qvec3i{expected_color}); - CHECK(delta[0] <= 1); - CHECK(delta[1] <= 1); - CHECK(delta[2] <= 1); + EXPECT_LE(delta[0], 1); + EXPECT_LE(delta[1], 1); + EXPECT_LE(delta[2], 1); } static void CheckFaceLuxelAtPoint_HDR(const mbsp_t *bsp, const dmodelh2_t *model, const qvec3f &expected_color, @@ -360,7 +359,7 @@ static void CheckFaceLuxelAtPoint_HDR(const mbsp_t *bsp, const dmodelh2_t *model const bspxentries_t *bspx = nullptr) { auto *face = BSP_FindFaceAtPoint(bsp, model, point, normal); - REQUIRE(face); + ASSERT_TRUE(face); faceextents_t extents; int offset; @@ -379,132 +378,132 @@ static void CheckFaceLuxelAtPoint_HDR(const mbsp_t *bsp, const dmodelh2_t *model const auto int_coord = qvec2i(round(coord[0]), round(coord[1])); const qvec3f sample = LM_Sample_HDR(bsp, face, extents, offset, int_coord, lit, bspx); - INFO("world point: ", point); - INFO("lm coord: ", coord[0], ", ", coord[1]); - INFO("lm int_coord: ", int_coord[0], ", ", int_coord[1]); - INFO("face num: ", Face_GetNum(bsp, face)); - INFO("actual sample: ", sample[0], " ", sample[1], " ", sample[2]); + SCOPED_TRACE(fmt::format("world point: {}", point)); + SCOPED_TRACE(fmt::format("lm coord: {}", coord)); + SCOPED_TRACE(fmt::format("lm int_coord: {}", int_coord)); + SCOPED_TRACE(fmt::format("face num: {}", Face_GetNum(bsp, face))); + SCOPED_TRACE(fmt::format("actual sample: {}", sample)); qvec3f delta = qv::abs(sample - expected_color); - CHECK(delta[0] <= allowed_delta[0]); - CHECK(delta[1] <= allowed_delta[1]); - CHECK(delta[2] <= allowed_delta[2]); + EXPECT_LE(delta[0], allowed_delta[0]); + EXPECT_LE(delta[1], allowed_delta[1]); + EXPECT_LE(delta[2], allowed_delta[2]); } -TEST_CASE("emissive lights") +TEST(ltfaceQ2, emissiveLights) { auto [bsp, bspx] = QbspVisLight_Q2("q2_light_flush.map", {}); - REQUIRE(bspx.empty()); + ASSERT_TRUE(bspx.empty()); { - INFO("the angled face on the right should not have any full black luxels"); + SCOPED_TRACE("the angled face on the right should not have any full black luxels"); auto *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {244, -92, 92}); - REQUIRE(face); + ASSERT_TRUE(face); CheckFaceLuxelsNonBlack(bsp, *face); } { - INFO("the angled face on the left should not have any full black luxels"); + SCOPED_TRACE("the angled face on the left should not have any full black luxels"); auto *left_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {470.4, 16, 112}); - REQUIRE(left_face); + ASSERT_TRUE(left_face); CheckFaceLuxelsNonBlack(bsp, *left_face); } } -TEST_CASE("q2_phong_doesnt_cross_contents") +TEST(ltfaceQ2, phongDoesntCrossContents) { auto [bsp, bspx] = QbspVisLight_Q2("q2_phong_doesnt_cross_contents.map", {"-wrnormals"}); } -TEST_CASE("q2_minlight_nomottle") +TEST(ltfaceQ2, minlightNomottle) { - INFO("_minlightMottle 0 works on worldspawn"); + SCOPED_TRACE("_minlightMottle 0 works on worldspawn"); auto [bsp, bspx] = QbspVisLight_Q2("q2_minlight_nomottle.map", {}); auto *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {276, 84, 32}); - REQUIRE(face); + ASSERT_TRUE(face); - CheckFaceLuxels(bsp, *face, [](qvec3b sample) { CHECK(sample == qvec3b(33, 33, 33)); }); + CheckFaceLuxels(bsp, *face, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(33, 33, 33)); }); } -TEST_CASE("q2_dirt") +TEST(ltfaceQ2, dirt) { - INFO("liquids don't cast dirt"); + SCOPED_TRACE("liquids don't cast dirt"); auto [bsp, bspx] = QbspVisLight_Q2("q2_dirt.map", {}); auto *face_under_lava = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {104, 112, 48}); - REQUIRE(face_under_lava); + ASSERT_TRUE(face_under_lava); - CheckFaceLuxels(bsp, *face_under_lava, [](qvec3b sample) { CHECK(sample == qvec3b(96)); }); + CheckFaceLuxels(bsp, *face_under_lava, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(96)); }); } -TEST_CASE("q2_dirtdebug") +TEST(ltfaceQ2, dirtDebug) { - INFO("dirtdebug works in q2"); + SCOPED_TRACE("dirtdebug works in q2"); auto [bsp, bspx] = QbspVisLight_Q2("q2_dirt.map", {"-dirtdebug"}); auto *face_under_lava = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {104, 112, 48}); - REQUIRE(face_under_lava); + ASSERT_TRUE(face_under_lava); - CheckFaceLuxels(bsp, *face_under_lava, [](qvec3b sample) { CHECK(sample == qvec3b(255)); }); + CheckFaceLuxels(bsp, *face_under_lava, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(255)); }); // check floor in the corner CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, {-124, 300, 32}); } -TEST_CASE("q2_light_translucency") +TEST(ltfaceQ2, lightTranslucency) { - INFO("liquids cast translucent colored shadows (sampling texture) by default"); + SCOPED_TRACE("liquids cast translucent colored shadows (sampling texture) by default"); auto [bsp, bspx] = QbspVisLight_Q2("q2_light_translucency.map", {}); { auto *face_under_water = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {152, -96, 32}); - REQUIRE(face_under_water); + ASSERT_TRUE(face_under_water); CheckFaceLuxels(bsp, *face_under_water, [](qvec3b sample) { - INFO("green color from the texture"); - CHECK(sample == qvec3b(100, 150, 100)); + SCOPED_TRACE("green color from the texture"); + EXPECT_EQ(sample, qvec3b(100, 150, 100)); }); } { - INFO("under _light_alpha 0 is not tinted"); + SCOPED_TRACE("under _light_alpha 0 is not tinted"); auto *under_alpha_0_glass = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-296, -96, 40}); - REQUIRE(under_alpha_0_glass); + ASSERT_TRUE(under_alpha_0_glass); - CheckFaceLuxels(bsp, *under_alpha_0_glass, [](qvec3b sample) { CHECK(sample == qvec3b(150)); }); + CheckFaceLuxels(bsp, *under_alpha_0_glass, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(150)); }); } { - INFO("under _light_alpha 1 is fully tinted"); + SCOPED_TRACE("under _light_alpha 1 is fully tinted"); auto *under_alpha_1_glass = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-616, -96, 40}); - REQUIRE(under_alpha_1_glass); + ASSERT_TRUE(under_alpha_1_glass); - CheckFaceLuxels(bsp, *under_alpha_1_glass, [](qvec3b sample) { CHECK(sample == qvec3b(0, 150, 0)); }); + CheckFaceLuxels(bsp, *under_alpha_1_glass, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(0, 150, 0)); }); } { - INFO("alpha test works"); + SCOPED_TRACE("alpha test works"); auto *in_light = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-976, -316, 184}); - REQUIRE(in_light); + ASSERT_TRUE(in_light); - CheckFaceLuxels(bsp, *in_light, [](qvec3b sample) { CHECK(sample == qvec3b(150)); }); + CheckFaceLuxels(bsp, *in_light, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(150)); }); auto *in_shadow = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-976, -316, 88}); - REQUIRE(in_shadow); + ASSERT_TRUE(in_shadow); - CheckFaceLuxels(bsp, *in_shadow, [](qvec3b sample) { CHECK(sample == qvec3b(0)); }); + CheckFaceLuxels(bsp, *in_shadow, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(0)); }); } { - INFO("opaque liquids are lit twosided"); + SCOPED_TRACE("opaque liquids are lit twosided"); const qvec3d point {-616, 592, 224}; @@ -513,10 +512,10 @@ TEST_CASE("q2_light_translucency") } } -TEST_CASE("-visapprox vis with opaque liquids") +TEST(ltfaceQ2, visapproxVisWithOpaqueLiquids) { - INFO("opaque liquids block vis, but don't cast shadows by default."); - INFO("make sure '-visapprox vis' doesn't wrongly cull rays that should illuminate the level."); + SCOPED_TRACE("opaque liquids block vis, but don't cast shadows by default."); + SCOPED_TRACE("make sure '-visapprox vis' doesn't wrongly cull rays that should illuminate the level."); const std::vector maps{ "q2_light_visapprox.map", // light in liquid @@ -524,201 +523,199 @@ TEST_CASE("-visapprox vis with opaque liquids") }; for (const auto &map : maps) { - SUBCASE(map.c_str()) - { - auto [bsp, bspx] = QbspVisLight_Q2(map, {"-visapprox", "vis"}, runvis_t::yes); + SCOPED_TRACE(map); + auto [bsp, bspx] = QbspVisLight_Q2(map, {"-visapprox", "vis"}, runvis_t::yes); - auto *ceil_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {968, 1368, 1248}); - REQUIRE(ceil_face); + auto *ceil_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {968, 1368, 1248}); + ASSERT_TRUE(ceil_face); - CheckFaceLuxels(bsp, *ceil_face, [](qvec3b sample) { - INFO("ceiling above player start receiving light"); - REQUIRE(sample[0] > 200); - }); - } + CheckFaceLuxels(bsp, *ceil_face, [](qvec3b sample) { + SCOPED_TRACE("ceiling above player start receiving light"); + ASSERT_GT(sample[0], 200); + }); } } -TEST_CASE("negative lights work") +TEST(ltfaceQ2, negativeLightsWork) { const std::vector maps{"q2_light_negative.map", "q2_light_negative_bounce.map"}; for (const auto &map : maps) { - SUBCASE(map.c_str()) - { - auto [bsp, bspx] = QbspVisLight_Q2(map, {}); + SCOPED_TRACE(map); + auto [bsp, bspx] = QbspVisLight_Q2(map, {}); - auto *face_under_negative_light = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {632, 1304, 960}); - REQUIRE(face_under_negative_light); + auto *face_under_negative_light = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {632, 1304, 960}); + ASSERT_TRUE(face_under_negative_light); - CheckFaceLuxels(bsp, *face_under_negative_light, [](qvec3b sample) { CHECK(sample == qvec3b(0)); }); - } + CheckFaceLuxels(bsp, *face_under_negative_light, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(0)); }); } } -TEST_CASE("light channel mask (_object_channel_mask, _light_channel_mask, _shadow_channel_mask)") +TEST(ltfaceQ2, lightChannelMask) { auto [bsp, bspx] = QbspVisLight_Q2("q2_light_group.map", {}); - REQUIRE(4 == bsp.dmodels.size()); + ASSERT_EQ(4, bsp.dmodels.size()); { - INFO("world doesn't receive light from the light ent with _light_channel_mask 2"); + SCOPED_TRACE("world doesn't receive light from the light ent with _light_channel_mask 2"); auto *face_under_light = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {680, 1224, 944}); - REQUIRE(face_under_light); + ASSERT_TRUE(face_under_light); - CheckFaceLuxels(bsp, *face_under_light, [](qvec3b sample) { CHECK(sample == qvec3b(64)); }); + CheckFaceLuxels(bsp, *face_under_light, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(64)); }); } { - INFO("pillar with _object_channel_mask 2 is receiving light"); + SCOPED_TRACE("pillar with _object_channel_mask 2 is receiving light"); auto *face_on_pillar = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[1], {680, 1248, 1000}); - REQUIRE(face_on_pillar); + ASSERT_TRUE(face_on_pillar); CheckFaceLuxels(bsp, *face_on_pillar, [](qvec3b sample) { - CHECK(sample[0] >= 254); - CHECK(sample[1] == 0); - CHECK(sample[2] == 0); + EXPECT_GE(sample[0], 254); + EXPECT_EQ(sample[1], 0); + EXPECT_EQ(sample[2], 0); }); } { - INFO("_object_channel_mask 2 implicitly makes bmodels cast shadow in channel 2"); + SCOPED_TRACE("_object_channel_mask 2 implicitly makes bmodels cast shadow in channel 2"); auto *occluded_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[1], {680, 1280, 1000}); - REQUIRE(occluded_face); + ASSERT_TRUE(occluded_face); - CheckFaceLuxels(bsp, *occluded_face, [](qvec3b sample) { CHECK(sample == qvec3b(0)); }); + CheckFaceLuxels(bsp, *occluded_face, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(0)); }); } { - INFO("ensure AABB culling isn't breaking light channels"); + SCOPED_TRACE("ensure AABB culling isn't breaking light channels"); auto *unoccluded_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[1], {680, 1280, 1088}); - REQUIRE(unoccluded_face); + ASSERT_TRUE(unoccluded_face); - CheckFaceLuxels(bsp, *unoccluded_face, [](qvec3b sample) { CHECK(sample[0] > 100); }); + CheckFaceLuxels(bsp, *unoccluded_face, [](qvec3b sample) { EXPECT_GT(sample[0], 100); }); } { - INFO("sunlight doesn't cast on _object_channel_mask 4 bmodel"); + SCOPED_TRACE("sunlight doesn't cast on _object_channel_mask 4 bmodel"); auto *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[2], {904, 1248, 1016}); - REQUIRE(face); + ASSERT_TRUE(face); CheckFaceLuxels(bsp, *face, [](qvec3b sample) { - CHECK(sample[0] == 0); - CHECK(sample[1] >= 254); - CHECK(sample[2] == 0); + EXPECT_EQ(sample[0], 0); + EXPECT_GE(sample[1], 254); + EXPECT_EQ(sample[2], 0); }); } { - INFO("surface light doesn't cast on _object_channel_mask 8 bmodel"); + SCOPED_TRACE("surface light doesn't cast on _object_channel_mask 8 bmodel"); auto *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[3], {1288, 1248, 1016}); - REQUIRE(face); + ASSERT_TRUE(face); CheckFaceLuxels(bsp, *face, [](qvec3b sample) { - CHECK(sample[0] == 0); - CHECK(sample[1] == 0); - CHECK(sample[2] >= 254); + EXPECT_EQ(sample[0], 0); + EXPECT_EQ(sample[1], 0); + EXPECT_GE(sample[2], 254); }); } { - INFO("_object_channel_mask 8 bmodel doesn't occlude luxels of a (channel 1) worldspawn brush touching it"); + SCOPED_TRACE("_object_channel_mask 8 bmodel doesn't occlude luxels of a (channel 1) worldspawn brush touching it"); auto *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {1290, 1264, 1014}); - REQUIRE(face); + ASSERT_TRUE(face); - INFO("should be receiving orange light from surface light"); + SCOPED_TRACE("should be receiving orange light from surface light"); CheckFaceLuxels(bsp, *face, [](qvec3b sample) { qvec3i delta = qv::abs(qvec3i(sample) - qvec3i{255, 127, 64}); - CHECK(delta[0] <= 2); - CHECK(delta[1] <= 2); - CHECK(delta[2] <= 2); + EXPECT_LE(delta[0], 2); + EXPECT_LE(delta[1], 2); + EXPECT_LE(delta[2], 2); }); } { - INFO("check that _object_channel_mask 8 func_group receives _light_channel_mask 8"); + SCOPED_TRACE("check that _object_channel_mask 8 func_group receives _light_channel_mask 8"); auto *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {1480, 1248, 1004}); - REQUIRE(face); + ASSERT_TRUE(face); CheckFaceLuxels(bsp, *face, [](qvec3b sample) { - CHECK(sample[0] == 0); - CHECK(sample[1] == 0); - CHECK(sample[2] >= 254); + EXPECT_EQ(sample[0], 0); + EXPECT_EQ(sample[1], 0); + EXPECT_GE(sample[2], 254); }); } { - INFO("_object_channel_mask 8 func_group doesn't cast shadow on default channel"); + SCOPED_TRACE("_object_channel_mask 8 func_group doesn't cast shadow on default channel"); auto *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {1484, 1280, 1016}); - REQUIRE(face); + ASSERT_TRUE(face); CheckFaceLuxels(bsp, *face, [](qvec3b sample) { qvec3i delta = qv::abs(qvec3i(sample) - qvec3i{255, 127, 64}); - CHECK(delta[0] <= 2); - CHECK(delta[1] <= 2); - CHECK(delta[2] <= 2); + EXPECT_LE(delta[0], 2); + EXPECT_LE(delta[1], 2); + EXPECT_LE(delta[2], 2); }); } } -TEST_CASE("light channel mask / dirt interaction") +TEST(ltfaceQ2, lightChannelMaskDirtInteraction) { auto [bsp, bspx] = QbspVisLight_Q2("q2_light_group_dirt.map", {}); - REQUIRE(2 == bsp.dmodels.size()); + ASSERT_EQ(2, bsp.dmodels.size()); - INFO("worldspawn has dirt in the corner"); + SCOPED_TRACE("worldspawn has dirt in the corner"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {26, 26, 26}, {1432, 1480, 944}); - INFO("worldspawn not receiving dirt from func_wall on different channel"); + SCOPED_TRACE("worldspawn not receiving dirt from func_wall on different channel"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {60, 60, 60}, {1212, 1272, 1014}); - INFO("func_wall on different channel not receiving dirt from worldspawn"); + SCOPED_TRACE("func_wall on different channel not receiving dirt from worldspawn"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[1], {64, 64, 64}, {1216, 1266, 1014}); - INFO("func_wall on different channel is receiving dirt from itself"); + SCOPED_TRACE("func_wall on different channel is receiving dirt from itself"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[1], {19, 19, 19}, {1236, 1308, 960}); } -TEST_CASE("surface lights minlight" * doctest::may_fail()) +TEST(ltfaceQ2, surfaceLightsMinlight) { + GTEST_SKIP(); + auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_surflight_minlight.map", {}); { - INFO("there's a point entity in the void, but it has _nofill 1 so it should be ignored by filling"); + SCOPED_TRACE("there's a point entity in the void, but it has _nofill 1 so it should be ignored by filling"); CheckFilled(bsp); } auto *surflight = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-3264, -1664, -560}); - REQUIRE(surflight); + ASSERT_TRUE(surflight); const auto l = [](qvec3b sample) { // "light" key is 100, color is (1, 0.5, 0), but values get halved due to overbright - CHECK(sample[0] <= 75); - CHECK(sample[0] >= 50); + EXPECT_LE(sample[0], 75); + EXPECT_GE(sample[0], 50); - CHECK(sample[1] <= 35); - CHECK(sample[1] >= 25); + EXPECT_LE(sample[1], 35); + EXPECT_GE(sample[1], 25); - CHECK(sample[2] == 0); + EXPECT_EQ(sample[2], 0); }; CheckFaceLuxels(bsp, *surflight, l, &lit); - INFO("same but with liquid"); + SCOPED_TRACE("same but with liquid"); auto *liquid_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-3264, -1456, -560}, {-1, 0, 0}); - REQUIRE(liquid_face); + ASSERT_TRUE(liquid_face); CheckFaceLuxels(bsp, *liquid_face, l, &lit); } @@ -729,7 +726,7 @@ static void CheckSpotCutoff(const mbsp_t &bsp, const qvec3d &position) CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {243, 243, 243}, position - qvec3d{16, 0, 0}); } -TEST_CASE("q2_light_cone") +TEST(ltfaceQ2, lightCone) { auto [bsp, bspx] = QbspVisLight_Q2("q2_light_cone.map", {}); @@ -742,11 +739,11 @@ TEST_CASE("q2_light_cone") CheckSpotCutoff(bsp, {1236, 1472, 952}); } -TEST_CASE("q2_light_sunlight_default_mangle") +TEST(ltfaceQ2, lightSunlightDefaultMangle) { auto [bsp, bspx] = QbspVisLight_Q2("q2_light_sunlight_default_mangle.map", {}); - INFO("sunlight should be shining directly down if unspecified"); + SCOPED_TRACE("sunlight should be shining directly down if unspecified"); const qvec3d shadow_pos{1112, 1248, 944}; CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, shadow_pos); @@ -754,25 +751,24 @@ TEST_CASE("q2_light_sunlight_default_mangle") CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {100, 100, 100}, shadow_pos + qvec3d{-48, 0, 0}); } -TEST_CASE("q2_light_sun") +TEST(ltfaceQ2, lightSun) { const std::vector maps{"q2_light_sun.map", "q2_light_sun_mangle.map"}; for (const auto &map : maps) { - SUBCASE(map.c_str()) { - auto [bsp, bspx] = QbspVisLight_Q2(map, {}); + SCOPED_TRACE(map); + auto [bsp, bspx] = QbspVisLight_Q2(map, {}); - INFO("sun entity shines at target (q2_light_sun.map) or uses given mangle (q2_light_sun_mangle.map)"); - const qvec3d shadow_pos{1084, 1284, 944}; - CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, shadow_pos); + SCOPED_TRACE("sun entity shines at target (q2_light_sun.map) or uses given mangle (q2_light_sun_mangle.map)"); + const qvec3d shadow_pos{1084, 1284, 944}; + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, shadow_pos); - CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {220, 0, 0}, shadow_pos + qvec3d{128, 0, 0}); - CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {220, 0, 0}, shadow_pos + qvec3d{-128, 0, 0}); - } + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {220, 0, 0}, shadow_pos + qvec3d{128, 0, 0}); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {220, 0, 0}, shadow_pos + qvec3d{-128, 0, 0}); } } -TEST_CASE("q2_light_origin_brush_shadow") +TEST(ltfaceQ2, lightOriginBrushShadow) { auto [bsp, bspx] = QbspVisLight_Q2("q2_light_origin_brush_shadow.map", {}); @@ -784,326 +780,332 @@ TEST_CASE("q2_light_origin_brush_shadow") const qvec3d at_origin{0, 0, 1}; - INFO("ensure expected shadow"); + SCOPED_TRACE("ensure expected shadow"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, under_shadow_bmodel); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, under_nodraw_shadow_bmodel); - INFO("ensure no spurious shadow under non-_shadow 1 bmodel"); + SCOPED_TRACE("ensure no spurious shadow under non-_shadow 1 bmodel"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {100, 100, 100}, under_nonshadow_bmodel); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {100, 100, 100}, under_nodraw_nonshadow_bmodel); - INFO("ensure no spurious shadow at the world origin (would happen if we didn't apply model offset)"); + SCOPED_TRACE("ensure no spurious shadow at the world origin (would happen if we didn't apply model offset)"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {100, 100, 100}, at_origin); } -TEST_CASE("q2_surface_lights_culling" * doctest::may_fail()) +TEST(ltfaceQ2, surfaceLightsCulling) { + GTEST_SKIP(); + auto [bsp, bspx] = QbspVisLight_Q2("q2_surface_lights_culling.map", {}); - CHECK(7 == GetSurflightPoints()); + EXPECT_EQ(7, GetSurflightPoints()); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {155, 78, 39}, {-480, 168, 64}); } -TEST_CASE("q1_lightignore" * doctest::may_fail()) +TEST(ltfaceQ1, lightignore) { + GTEST_SKIP(); + auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_lightignore.map", {"-bounce"}); { - INFO("func_wall"); + SCOPED_TRACE("func_wall"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[1], {0, 0, 0}, {-48, 144, 48}, {0, 0, 1}, &lit); } { - INFO("func_detail"); + SCOPED_TRACE("func_detail"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, {72, 144, 48}, {0, 0, 1}, &lit); } { - INFO("worldspawn (receives light)"); + SCOPED_TRACE("worldspawn (receives light)"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {55, 69, 83}, {-128, 144, 32}, {0, 0, 1}, &lit); } } -TEST_CASE("q2_light_low_luxel_res") +TEST(ltfaceQ2, lowLuxelRes) { auto [bsp, bspx] = QbspVisLight_Q2( "q2_light_low_luxel_res.map", {"-world_units_per_luxel", "32", "-dirt", "-debugface", "2164", "712", "-968"}); { - INFO("non-sloped cube"); + SCOPED_TRACE("non-sloped cube"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {232, 185, 0}, {2138, 712, -968}, {0, 1, 0}, nullptr, &bspx); } { - INFO("sloped cube"); + SCOPED_TRACE("sloped cube"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {232, 185, 0}, {2164, 712, -968}, {0, 1, 0}, nullptr, &bspx); } } -TEST_CASE("q2_light_low_luxel_res2" * doctest::may_fail()) +TEST(ltfaceQ2, lowLuxelRes2) { + GTEST_SKIP(); + auto [bsp, bspx] = QbspVisLight_Q2( "q2_light_low_luxel_res2.map", {"-world_units_per_luxel", "32", "-debugface", "2964", "1020", "-696"}); - INFO("should be a smooth transition across these points"); + SCOPED_TRACE("should be a smooth transition across these points"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {49, 49, 49}, {2964, 1046, -694}, {-1, 0, 0}, nullptr, &bspx); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {25, 25, 25}, {2964, 1046, -706}, {-1, 0, 0}, nullptr, &bspx); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {1, 1, 1}, {2964, 1046, -716}, {-1, 0, 0}, nullptr, &bspx); } -TEST_CASE("q2_minlight_inherited") +TEST(ltfaceQ2, minlightInherited) { auto [bsp, bspx] = QbspVisLight_Q2("q2_minlight_inherited.map", {}); { - INFO("check worldspawn minlight"); + SCOPED_TRACE("check worldspawn minlight"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {64, 0, 0}, {456, 196, 0}, {0, 0, 1}, nullptr, &bspx); } { - INFO("check that func_group inherits worldspawn minlight"); + SCOPED_TRACE("check that func_group inherits worldspawn minlight"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {64, 0, 0}, {360, 72, 16}, {0, 0, 1}, nullptr, &bspx); } { - INFO("check that func_wall inherits worldspawn minlight"); + SCOPED_TRACE("check that func_wall inherits worldspawn minlight"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[1], {64, 0, 0}, {208, 72, 16}, {0, 0, 1}, nullptr, &bspx); } { - INFO("check that func_group can override worldspawn minlight"); + SCOPED_TRACE("check that func_group can override worldspawn minlight"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {128, 0, 0}, {360, -84, 16}, {0, 0, 1}, nullptr, &bspx); } { - INFO("check that func_wall can override worldspawn minlight"); + SCOPED_TRACE("check that func_wall can override worldspawn minlight"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[2], {128, 0, 0}, {208, -84, 16}, {0, 0, 1}, nullptr, &bspx); } { - INFO("check that func_group can override worldspawn minlight color"); + SCOPED_TRACE("check that func_group can override worldspawn minlight color"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 64, 0}, {360, -248, 16}, {0, 0, 1}, nullptr, &bspx); } { - INFO("check that func_wall can override worldspawn minlight color"); + SCOPED_TRACE("check that func_wall can override worldspawn minlight color"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[3], {0, 64, 0}, {208, -248, 16}, {0, 0, 1}, nullptr, &bspx); } } -TEST_CASE("q2_minlight_inherited + -noextendedsurfflags") +TEST(ltfaceQ2, minlightInheritedAndNoextendedsurfflags) { auto [bsp, bspx] = QbspVisLight_Common("q2_minlight_inherited.map", {"-q2bsp", "-noextendedsurfflags"}, {}, runvis_t::no); { - INFO("check that func_wall inherits worldspawn minlight"); + SCOPED_TRACE("check that func_wall inherits worldspawn minlight"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[1], {64, 0, 0}, {208, 72, 16}, {0, 0, 1}, nullptr, &bspx); } { - INFO("check that func_wall can override worldspawn minlight"); + SCOPED_TRACE("check that func_wall can override worldspawn minlight"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[2], {128, 0, 0}, {208, -84, 16}, {0, 0, 1}, nullptr, &bspx); } { - INFO("check that func_wall can override worldspawn minlight color"); + SCOPED_TRACE("check that func_wall can override worldspawn minlight color"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[3], {0, 64, 0}, {208, -248, 16}, {0, 0, 1}, nullptr, &bspx); } } -TEST_CASE("lit water") +TEST(ltfaceQ1, litWater) { auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_litwater.map", {}); { - INFO("cube 1: lava has blue lightmap"); + SCOPED_TRACE("cube 1: lava has blue lightmap"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 10, 171}, {-288, 120, 128}, {0, 0, 1}, &lit); } { - INFO("cube 2: non-lightmapped via _splitturb 0 func_group key"); + SCOPED_TRACE("cube 2: non-lightmapped via _splitturb 0 func_group key"); auto *f = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-160, 120, 128}, {0, 0, 1}); auto *ti = Face_Texinfo(&bsp, f); - CHECK(ti->flags.native == TEX_SPECIAL); + EXPECT_EQ(ti->flags.native, TEX_SPECIAL); } { - INFO("cube 3: lightmapped, but using minlight only via _lightignore and _minlight func_group keys"); + SCOPED_TRACE("cube 3: lightmapped, but using minlight only via _lightignore and _minlight func_group keys"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {50, 50, 50}, {-32, 120, 128}, {0, 0, 1}, &lit); } } -TEST_CASE("lit water opt-in") +TEST(ltfaceQ1, litWaterOptIn) { auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_litwater_opt_in.map", {}); { - INFO("cube 1: lava has blue lightmap (opt-in via _litwater 1)"); + SCOPED_TRACE("cube 1: lava has blue lightmap (opt-in via _litwater 1)"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 162}, {-288, 120, 128}, {0, 0, 1}, &lit); } { - INFO("cube 2: non-lightmapped"); + SCOPED_TRACE("cube 2: non-lightmapped"); auto *f = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-160, 120, 128}, {0, 0, 1}); auto *ti = Face_Texinfo(&bsp, f); - CHECK(ti->flags.native == TEX_SPECIAL); + EXPECT_EQ(ti->flags.native, TEX_SPECIAL); } } -TEST_CASE("q2_light_divzero") +TEST(ltfaceQ2, lightDivZero) { auto [bsp, bspx] = QbspVisLight_Q2("q2_light_divzero.map", {"-world_units_per_luxel", "8"}); - INFO("should not have a black spot in the center of the light face"); + SCOPED_TRACE("should not have a black spot in the center of the light face"); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {255, 127, 63}, {-992, 0, -480}, {0, 0, -1}, nullptr, &bspx); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {255, 127, 63}, {-984, 8, -480}, {0, 0, -1}, nullptr, &bspx); } -TEST_CASE("minlight doesn't bounce") +TEST(ltfaceQ1, minlightDoesntBounce) { auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_minlight_nobounce.map", {"-lit"}); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {50, 50, 50}, {0, 0, 0}, {0, 0, 1}, &lit); } -TEST_CASE("q1_sunlight") +TEST(ltfaceQ1, sunlight) { auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_sunlight.map", {"-lit"}); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {49, 49, 49}, {0, 0, 0}, {0, 0, 1}, &lit); } -TEST_CASE("q1_light_suntexture") +TEST(ltfaceQ1, suntexture) { - INFO("different _sun 1 entities can emit from specific texture names using _suntexture"); + SCOPED_TRACE("different _sun 1 entities can emit from specific texture names using _suntexture"); auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_suntexture.map", {}); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {35, 0, 0}, {504, 1288, 944}, {0, 0, 1}, &lit); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 142}, {1000, 1288, 944}, {0, 0, 1}, &lit); } -TEST_CASE("q1_light_sun_artifact") +TEST(ltfaceQ1, lightSunArtifact) { - INFO("sun rays can hit cracks if RTC_SCENE_FLAG_ROBUST is not used"); + SCOPED_TRACE("sun rays can hit cracks if RTC_SCENE_FLAG_ROBUST is not used"); auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_sun_artifact.map", {"-lit"}); for (const auto &face : bsp.dfaces) { if (Face_Normal(&bsp, &face) == qvec3d(0, 0, 1)) { - CheckFaceLuxels(bsp, face, [](qvec3b sample) { CHECK(sample == qvec3b(128, 0, 0)); }, &lit); + CheckFaceLuxels(bsp, face, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(128, 0, 0)); }, &lit); } } } -TEST_CASE("q1_light_invalid_delay") +TEST(ltfaceQ1, lightInvalidDelay) { - INFO("invalid light formulas are ignored, not a fatal error"); + SCOPED_TRACE("invalid light formulas are ignored, not a fatal error"); auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_invalid_delay.map", {"-lit"}); for (const auto &face : bsp.dfaces) { - CheckFaceLuxels(bsp, face, [](qvec3b sample) { CHECK(sample == qvec3b(0, 0, 0)); }, &lit); + CheckFaceLuxels(bsp, face, [](qvec3b sample) { EXPECT_EQ(sample, qvec3b(0, 0, 0)); }, &lit); } } -TEST_CASE("q1_light_bounce_litwater without the water") +TEST(ltfaceQ1, bounceLitwaterWithoutTheWater) { auto [bsp, bspx] = QbspVisLight_Common("q1_light_bounce_litwater.map", {"-omitdetail"}, {"-lit", "-bounce", "4"}, runvis_t::no); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {118, 118, 118}, {128, 12, 156}, {-1, 0, 0}); } -TEST_CASE("q1_light_bounce_litwater") +TEST(ltfaceQ1, bounceLitwater) { - INFO("adding a water plane should not affect the amount of light bounced on to the walls"); + SCOPED_TRACE("adding a water plane should not affect the amount of light bounced on to the walls"); auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_bounce_litwater.map", {"-lit", "-bounce", "4"}); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {118, 118, 118}, {128, 12, 156}, {-1, 0, 0}); } -TEST_CASE("q1_light_bounce_noshadow") +TEST(ltfaceQ1, bounceNoshadow) { - INFO("make sure light doesn't both pass through and bounce off of a face with _shadow -1"); + SCOPED_TRACE("make sure light doesn't both pass through and bounce off of a face with _shadow -1"); auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_bounce_noshadow.map", {"-lit", "-bounce", "4"}); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {118, 118, 118}, {128, 12, 156}, {-1, 0, 0}); } -TEST_CASE("q2_light_black") +TEST(ltfaceQ2, lightBlack) { auto [bsp, bspx] = QbspVisLight_Q2("q2_light_black.map", {}); const qvec3d point {1056, 1300, 972}; - INFO("ensure completely black lightmaps are written out as style 0 in Q2 mode"); + SCOPED_TRACE("ensure completely black lightmaps are written out as style 0 in Q2 mode"); const mface_t *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point, {-1, 0, 0}); - REQUIRE(face); - CHECK(face->styles[0] == 0); - CHECK(face->styles[1] == 255); - CHECK(face->styles[2] == 255); - CHECK(face->styles[3] == 255); + ASSERT_TRUE(face); + EXPECT_EQ(face->styles[0], 0); + EXPECT_EQ(face->styles[1], 255); + EXPECT_EQ(face->styles[2], 255); + EXPECT_EQ(face->styles[3], 255); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, point, {-1, 0, 0}); } -TEST_CASE("q1_light_black") +TEST(ltfaceQ1, lightBlack) { auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_black.map", {"-lit"}); { const qvec3d point{1056, 1300, 972}; - INFO("ensure completely black lightmaps are written out as style 255 / lightofs -1 in Q1 mode"); + SCOPED_TRACE("ensure completely black lightmaps are written out as style 255 / lightofs -1 in Q1 mode"); const mface_t *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point, {-1, 0, 0}); - REQUIRE(face); - CHECK(face->styles[0] == 255); - CHECK(face->styles[1] == 255); - CHECK(face->styles[2] == 255); - CHECK(face->styles[3] == 255); - CHECK(face->lightofs == -1); + ASSERT_TRUE(face); + EXPECT_EQ(face->styles[0], 255); + EXPECT_EQ(face->styles[1], 255); + EXPECT_EQ(face->styles[2], 255); + EXPECT_EQ(face->styles[3], 255); + EXPECT_EQ(face->lightofs, -1); // this is consistent with original tools, see: // https://github.com/id-Software/Quake-Tools/blob/master/qutils/LIGHT/LTFACE.C#L542 } { - INFO("ensure lit water receiving no light is also written out as style 255 / lightofs -1"); + SCOPED_TRACE("ensure lit water receiving no light is also written out as style 255 / lightofs -1"); const qvec3d point{568, 1288, 976}; const mface_t *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point, {0, 0, 1}); - REQUIRE(face); + ASSERT_TRUE(face); auto *texinfo = Face_Texinfo(&bsp, face); - REQUIRE(texinfo); + ASSERT_TRUE(texinfo); - CHECK(texinfo->flags.native == 0); // i.e. TEX_SPECIAL is not set because it's lit water - CHECK(face->styles[0] == 255); - CHECK(face->styles[1] == 255); - CHECK(face->styles[2] == 255); - CHECK(face->styles[3] == 255); - CHECK(face->lightofs == -1); + EXPECT_EQ(texinfo->flags.native, 0); // i.e. TEX_SPECIAL is not set because it's lit water + EXPECT_EQ(face->styles[0], 255); + EXPECT_EQ(face->styles[1], 255); + EXPECT_EQ(face->styles[2], 255); + EXPECT_EQ(face->styles[3], 255); + EXPECT_EQ(face->lightofs, -1); // Note, this liquid face is rendering as fullbright (incorrect) in: QS 0.96.0 and Ironwail 0.7.0 // and rendering as solid black (correct) in vkQuake 1.30.1, FTEQW Mar 1 2022 } } -TEST_CASE("hl_light_black") +TEST(ltfaceHL, lightBlack) { auto [bsp, bspx] = QbspVisLight_HL("hl_light_black.map", {}); { const qvec3d point{1056, 1300, 972}; - INFO("ensure completely black lightmaps are written out as style 255 / lightofs -1 in HL mode"); + SCOPED_TRACE("ensure completely black lightmaps are written out as style 255 / lightofs -1 in HL mode"); const mface_t *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point, {-1, 0, 0}); - REQUIRE(face); - CHECK(face->styles[0] == 255); - CHECK(face->styles[1] == 255); - CHECK(face->styles[2] == 255); - CHECK(face->styles[3] == 255); - CHECK(face->lightofs == -1); + ASSERT_TRUE(face); + EXPECT_EQ(face->styles[0], 255); + EXPECT_EQ(face->styles[1], 255); + EXPECT_EQ(face->styles[2], 255); + EXPECT_EQ(face->styles[3], 255); + EXPECT_EQ(face->lightofs, -1); // confirmed that this renders as expected (black lightmaps) in the Dec 2023 HL build } } -TEST_CASE("q1 hdr") +TEST(ltfaceQ1, hdr) { // center of the room on the floor. // in the non-HDR lightmap this is pure black (0, 0, 0), but in the HDR one it's still receiving a bit of light @@ -1111,12 +1113,13 @@ TEST_CASE("q1 hdr") const qvec3f testnormal = {0, 0, 1}; const qvec3f expected_hdr_color = {0.00215912, 0.0018692, 0.00126648}; - SUBCASE("lit") { + SCOPED_TRACE("lit"); + auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_hdrtest.map", {"-hdr"}); - CHECK(bspx.empty()); - CHECK(std::holds_alternative(lit)); + EXPECT_TRUE(bspx.empty()); + EXPECT_TRUE(std::holds_alternative(lit)); // check hdr .lit file CheckFaceLuxelAtPoint_HDR(&bsp, &bsp.dmodels[0], expected_hdr_color, {1e-5, 1e-5, 1e-5}, testpoint, testnormal, @@ -1126,13 +1129,14 @@ TEST_CASE("q1 hdr") CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, testpoint, testnormal); } - SUBCASE("bspx") { + SCOPED_TRACE("bspx"); + auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_hdrtest.map", {"-bspxhdr"}); - CHECK(bspx.size() == 1); - CHECK(bspx.find("LIGHTING_E5BGR9") != bspx.end()); - CHECK(std::holds_alternative(lit)); + EXPECT_EQ(bspx.size(), 1); + EXPECT_NE(bspx.find("LIGHTING_E5BGR9"), bspx.end()); + EXPECT_TRUE(std::holds_alternative(lit)); // check hdr BSPX lump CheckFaceLuxelAtPoint_HDR(&bsp, &bsp.dmodels[0], expected_hdr_color, {1e-5, 1e-5, 1e-5}, testpoint, testnormal, diff --git a/tests/test_main.cc b/tests/test_main.cc index 72b31c46..b6bdbbd0 100644 --- a/tests/test_main.cc +++ b/tests/test_main.cc @@ -1,7 +1,6 @@ #include "test_main.hh" -#define DOCTEST_CONFIG_IMPLEMENT -#include +#include #include #include @@ -32,14 +31,6 @@ int main(int argc, char **argv) } } - doctest::Context context; - - context.applyCommandLine(argc, argv); - int res = context.run(); - - if (context.shouldExit()) { - return res; - } - - return res; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/tests/test_qbsp.cc b/tests/test_qbsp.cc index 1b4ede03..75764e9b 100644 --- a/tests/test_qbsp.cc +++ b/tests/test_qbsp.cc @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include "testutils.hh" #include "test_main.hh" @@ -46,7 +46,7 @@ void CheckFaceNormal(const mbsp_t *bsp, const mface_t *face) auto winding_plane = winding.plane(); - CHECK(qv::dot(face_normal_from_plane, winding_plane.normal) > 0.0); + EXPECT_GT(qv::dot(face_normal_from_plane, winding_plane.normal), 0.0); } void CheckBsp(const mbsp_t *bsp) @@ -64,7 +64,9 @@ mapentity_t &LoadMap(const char *map, size_t length) qbsp_options.target_version = &bspver_q1; qbsp_options.target_game = qbsp_options.target_version->game; - parser_source_location base_location {doctest::getContextOptions()->currentTest->m_name}; + parser_source_location base_location { + testing::UnitTest::GetInstance()->current_test_info()->name() + }; mapfile::map_file_t m = mapfile::parse(std::string_view(map, length), base_location); // FIXME: adds the brush to the global map... @@ -211,9 +213,9 @@ void CheckFilled(const mbsp_t &bsp, hull_index_t hullnum) int32_t contents = BSP_FindContentsAtPoint(&bsp, hullnum, &bsp.dmodels[0], qvec3d{8192, 8192, 8192}); if (bsp.loadversion->game->id == GAME_QUAKE_II) { - CHECK(contents == Q2_CONTENTS_SOLID); + EXPECT_EQ(contents, Q2_CONTENTS_SOLID); } else { - CHECK(contents == CONTENTS_SOLID); + EXPECT_EQ(contents, CONTENTS_SOLID); } } @@ -286,7 +288,7 @@ std::vector FacesWithTextureName(const mbsp_t &bsp, const std:: } // https://github.com/ericwa/ericw-tools/issues/158 -TEST_CASE("testTextureIssue" * doctest::test_suite("qbsp")) +TEST(qbsp, testTextureIssue) { const char *bufActual = R"( { @@ -325,13 +327,13 @@ TEST_CASE("testTextureIssue" * doctest::test_suite("qbsp")) #if 0 for (int i=0; i<2; i++) { for (int j=0; j<4; j++) { - CHECK(doctest::Approx(texvecsExpected[i][j]) == texvecsActual[i][j]); + EXPECT_EQ(doctest::Approx(texvecsExpected[i][j]), texvecsActual[i][j]); } } #endif } -TEST_CASE("duplicatePlanes" * doctest::test_suite("qbsp")) +TEST(qbsp, duplicatePlanes) { // a brush from e1m4.map with 7 planes, only 6 unique. const char *mapWithDuplicatePlanes = R"( @@ -350,18 +352,18 @@ TEST_CASE("duplicatePlanes" * doctest::test_suite("qbsp")) )"; mapentity_t &worldspawn = LoadMap(mapWithDuplicatePlanes); - REQUIRE(1 == worldspawn.mapbrushes.size()); - CHECK(6 == worldspawn.mapbrushes.front().faces.size()); + ASSERT_EQ(1, worldspawn.mapbrushes.size()); + EXPECT_EQ(6, worldspawn.mapbrushes.front().faces.size()); auto *game = bspver_q1.game; auto brush = LoadBrush(worldspawn, worldspawn.mapbrushes.front(), game->create_contents_from_native(CONTENTS_SOLID), 0, std::nullopt); - CHECK(6 == brush->sides.size()); + EXPECT_EQ(6, brush->sides.size()); } -TEST_CASE("empty brush" * doctest::test_suite("qbsp")) +TEST(qbsp, emptyBrush) { - INFO("the empty brush should be discarded"); + SCOPED_TRACE("the empty brush should be discarded"); const char *map_with_empty_brush = R"( // entity 0 { @@ -391,15 +393,15 @@ TEST_CASE("empty brush" * doctest::test_suite("qbsp")) )"; mapentity_t &worldspawn = LoadMap(map_with_empty_brush); - REQUIRE(2 == worldspawn.mapbrushes.size()); - REQUIRE(6 == worldspawn.mapbrushes[0].faces.size()); - REQUIRE(6 == worldspawn.mapbrushes[1].faces.size()); + ASSERT_EQ(2, worldspawn.mapbrushes.size()); + ASSERT_EQ(6, worldspawn.mapbrushes[0].faces.size()); + ASSERT_EQ(6, worldspawn.mapbrushes[1].faces.size()); } /** * Test that this skip face gets auto-corrected. */ -TEST_CASE("InvalidTextureProjection" * doctest::test_suite("qbsp")) +TEST(qbsp, InvalidTextureProjection) { const char *map = R"( // entity 0 @@ -421,18 +423,18 @@ TEST_CASE("InvalidTextureProjection" * doctest::test_suite("qbsp")) parser_t p(map, parser_source_location()); m.parse(p); - REQUIRE(1 == m.entities[0].brushes.size()); + ASSERT_EQ(1, m.entities[0].brushes.size()); const auto *face = &m.entities[0].brushes.front().faces[5]; - REQUIRE("skip" == face->texture); + ASSERT_EQ("skip", face->texture); - CHECK(face->is_valid_texture_projection()); + EXPECT_TRUE(face->is_valid_texture_projection()); } /** * Same as above but the texture scales are 0 */ -TEST_CASE("InvalidTextureProjection2" * doctest::test_suite("qbsp")) +TEST(qbsp, InvalidTextureProjection2) { const char *map = R"( // entity 0 @@ -454,18 +456,18 @@ TEST_CASE("InvalidTextureProjection2" * doctest::test_suite("qbsp")) parser_t p(map, parser_source_location()); m.parse(p); - REQUIRE(1 == m.entities[0].brushes.size()); + ASSERT_EQ(1, m.entities[0].brushes.size()); const auto *face = &m.entities[0].brushes.front().faces[5]; - REQUIRE("skip" == face->texture); + ASSERT_EQ("skip", face->texture); - CHECK(face->is_valid_texture_projection()); + EXPECT_TRUE(face->is_valid_texture_projection()); } /** * More realistic: *lava1 has tex vecs perpendicular to face */ -TEST_CASE("InvalidTextureProjection3" * doctest::test_suite("qbsp")) +TEST(qbsp, InvalidTextureProjection3) { const char *map = R"( // entity 0 @@ -488,106 +490,101 @@ TEST_CASE("InvalidTextureProjection3" * doctest::test_suite("qbsp")) parser_t p(map, parser_source_location()); m.parse(p); - REQUIRE(1 == m.entities[0].brushes.size()); + ASSERT_EQ(1, m.entities[0].brushes.size()); const auto *face = &m.entities[0].brushes.front().faces[3]; - REQUIRE("*lava1" == face->texture); + ASSERT_EQ("*lava1", face->texture); - CHECK(face->is_valid_texture_projection()); + EXPECT_TRUE(face->is_valid_texture_projection()); } -TEST_SUITE("mathlib") +TEST(winding, WindingArea) { - TEST_CASE("WindingArea") - { - winding_t w(5); + winding_t w(5); - // poor test.. but at least checks that the colinear point is treated correctly - w[0] = {0, 0, 0}; - w[1] = {0, 32, 0}; // colinear - w[2] = {0, 64, 0}; - w[3] = {64, 64, 0}; - w[4] = {64, 0, 0}; + // poor test.. but at least checks that the colinear point is treated correctly + w[0] = {0, 0, 0}; + w[1] = {0, 32, 0}; // colinear + w[2] = {0, 64, 0}; + w[3] = {64, 64, 0}; + w[4] = {64, 0, 0}; - CHECK(64.0f * 64.0f == w.area()); - } + EXPECT_EQ(64.0f * 64.0f, w.area()); } /** * checks that options are reset across tests. * set two random options and check that they don't carry over. */ -TEST_CASE("options_reset1" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, optionsReset1) { LoadTestmap("qbsp_simple_sealed.map", {"-noskip"}); - CHECK_FALSE(qbsp_options.forcegoodtree.value()); - CHECK(qbsp_options.noskip.value()); + EXPECT_FALSE(qbsp_options.forcegoodtree.value()); + EXPECT_TRUE(qbsp_options.noskip.value()); } -TEST_CASE("options_reset2" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, optionsReset2) { LoadTestmap("qbsp_simple_sealed.map", {"-forcegoodtree"}); - CHECK(qbsp_options.forcegoodtree.value()); - CHECK_FALSE(qbsp_options.noskip.value()); + EXPECT_TRUE(qbsp_options.forcegoodtree.value()); + EXPECT_FALSE(qbsp_options.noskip.value()); } /** * The brushes are touching but not intersecting, so ChopBrushes shouldn't change anything. */ -TEST_CASE("chop_no_change" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, chopNoChange) { LoadTestmapQ1("qbsp_chop_no_change.map"); // TODO: ideally we should check we get back the same brush pointers from ChopBrushes } -TEST_CASE("simple_sealed" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, simpleSealed) { const std::vector quake_maps{"qbsp_simple_sealed.map", "qbsp_simple_sealed_rotated.map"}; for (const auto &mapname : quake_maps) { - SUBCASE(fmt::format("testing {}", mapname).c_str()) - { + SCOPED_TRACE(fmt::format("testing {}", mapname)); - const auto [bsp, bspx, prt] = LoadTestmapQ1(mapname); + const auto [bsp, bspx, prt] = LoadTestmapQ1(mapname); - REQUIRE(bsp.dleafs.size() == 2); + ASSERT_EQ(bsp.dleafs.size(), 2); - REQUIRE(bsp.dleafs[0].contents == CONTENTS_SOLID); - REQUIRE(bsp.dleafs[1].contents == CONTENTS_EMPTY); + ASSERT_EQ(bsp.dleafs[0].contents, CONTENTS_SOLID); + ASSERT_EQ(bsp.dleafs[1].contents, CONTENTS_EMPTY); - // just a hollow box - REQUIRE(bsp.dfaces.size() == 6); + // just a hollow box + ASSERT_EQ(bsp.dfaces.size(), 6); - // no bspx lumps - CHECK(bspx.empty()); + // no bspx lumps + EXPECT_TRUE(bspx.empty()); - // check markfaces - CHECK(bsp.dleafs[0].nummarksurfaces == 0); - CHECK(bsp.dleafs[0].firstmarksurface == 0); + // check markfaces + EXPECT_EQ(bsp.dleafs[0].nummarksurfaces, 0); + EXPECT_EQ(bsp.dleafs[0].firstmarksurface, 0); - CHECK(bsp.dleafs[1].nummarksurfaces == 6); - CHECK(bsp.dleafs[1].firstmarksurface == 0); - CHECK_VECTORS_UNOREDERED_EQUAL(bsp.dleaffaces, std::vector{0, 1, 2, 3, 4, 5}); - } + EXPECT_EQ(bsp.dleafs[1].nummarksurfaces, 6); + EXPECT_EQ(bsp.dleafs[1].firstmarksurface, 0); + EXPECT_VECTORS_UNOREDERED_EQUAL(bsp.dleaffaces, std::vector{0, 1, 2, 3, 4, 5}); } } -TEST_CASE("simple_sealed2" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, simpleSealed2) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_simple_sealed2.map"); - CHECK(bsp.dleafs.size() == 3); + EXPECT_EQ(bsp.dleafs.size(), 3); - CHECK(bsp.dleafs[0].contents == CONTENTS_SOLID); - CHECK(bsp.dleafs[1].contents == CONTENTS_EMPTY); - CHECK(bsp.dleafs[2].contents == CONTENTS_EMPTY); + EXPECT_EQ(bsp.dleafs[0].contents, CONTENTS_SOLID); + EXPECT_EQ(bsp.dleafs[1].contents, CONTENTS_EMPTY); + EXPECT_EQ(bsp.dleafs[2].contents, CONTENTS_EMPTY); // L-shaped room // 2 ceiling + 2 floor + 6 wall faces - CHECK(bsp.dfaces.size() == 10); + EXPECT_EQ(bsp.dfaces.size(), 10); // get markfaces const qvec3d player_pos{-56, -96, 120}; @@ -608,21 +605,21 @@ TEST_CASE("simple_sealed2" * doctest::test_suite("testmaps_q1")) auto *other_plus_y = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-64, -368, 128), qvec3d(0, 1, 0)); // back wall +Y normal - CHECK_VECTORS_UNOREDERED_EQUAL(other_markfaces, + EXPECT_VECTORS_UNOREDERED_EQUAL(other_markfaces, std::vector{other_floor, other_ceil, other_minus_x, other_plus_x, other_plus_y}); } -TEST_CASE("simple_worldspawn_worldspawn" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, simpleWorldspawnWorldspawn) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_simple_worldspawn_worldspawn.map", {"-tjunc", "rotate"}); // 1 solid leaf // 5 empty leafs around the button - REQUIRE(bsp.dleafs.size() == 6); + ASSERT_EQ(bsp.dleafs.size(), 6); // 5 faces for the "button" // 9 faces for the room (6 + 3 extra for the floor splits) - REQUIRE(bsp.dfaces.size() == 14); + ASSERT_EQ(bsp.dfaces.size(), 14); int fan_faces = 0; int room_faces = 0; @@ -633,80 +630,80 @@ TEST_CASE("simple_worldspawn_worldspawn" * doctest::test_suite("testmaps_q1")) } else if (!strcmp(texname, "+0fan")) { ++fan_faces; } else { - FAIL(""); + FAIL(); } } - REQUIRE(fan_faces == 5); - REQUIRE(room_faces == 9); + ASSERT_EQ(fan_faces, 5); + ASSERT_EQ(room_faces, 9); } -TEST_CASE("simple_worldspawn_detail_wall" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, simpleWorldspawnDetailWall) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_simple_worldspawn_detail_wall.map"); - CHECK(prt.has_value()); + EXPECT_TRUE(prt.has_value()); // 5 faces for the "button" // 6 faces for the room - CHECK(bsp.dfaces.size() == 11); + EXPECT_EQ(bsp.dfaces.size(), 11); const qvec3d button_pos = {16, -48, 104}; auto *button_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], button_pos); - CHECK(button_leaf->contents == CONTENTS_SOLID); - CHECK(button_leaf == &bsp.dleafs[0]); // should be using shared solid leaf because it's func_detail_wall + EXPECT_EQ(button_leaf->contents, CONTENTS_SOLID); + EXPECT_EQ(button_leaf, &bsp.dleafs[0]); // should be using shared solid leaf because it's func_detail_wall } -TEST_CASE("simple_worldspawn_detail" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, simpleWorldspawnDetail) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_simple_worldspawn_detail.map", {"-tjunc", "rotate"}); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); // 5 faces for the "button" // 9 faces for the room - REQUIRE(bsp.dfaces.size() == 14); + ASSERT_EQ(bsp.dfaces.size(), 14); // 6 for the box room // 5 for the "button" - CHECK(bsp.dnodes.size() == 11); + EXPECT_EQ(bsp.dnodes.size(), 11); // this is how many we get with ericw-tools-v0.18.1-32-g6660c5f-win64 - CHECK(bsp.dclipnodes.size() <= 22); + EXPECT_LE(bsp.dclipnodes.size(), 22); } -TEST_CASE("simple_worldspawn_detail_illusionary" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, simpleWorldspawnDetailIllusionary) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_simple_worldspawn_detail_illusionary.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); // 5 faces for the "button" // 6 faces for the room - CHECK(bsp.dfaces.size() == 11); + EXPECT_EQ(bsp.dfaces.size(), 11); // leaf/node counts - CHECK(11 == bsp.dnodes.size()); // one node per face - CHECK(7 == bsp.dleafs.size()); // shared solid leaf + 6 empty leafs inside the room + EXPECT_EQ(11, bsp.dnodes.size()); // one node per face + EXPECT_EQ(7, bsp.dleafs.size()); // shared solid leaf + 6 empty leafs inside the room // where the func_detail_illusionary sticks into the void const qvec3d illusionary_in_void{8, -40, 72}; - CHECK(CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], illusionary_in_void)->contents); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], illusionary_in_void)->contents); - CHECK(prt->portals.size() == 0); - CHECK(prt->portalleafs == 1); + EXPECT_EQ(prt->portals.size(), 0); + EXPECT_EQ(prt->portalleafs, 1); } -TEST_CASE("simple_worldspawn_sky" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, simpleWorldspawnSky) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_simple_worldspawn_sky.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); // just a box with sky on the ceiling const auto textureToFace = MakeTextureToFaceMap(bsp); - CHECK(1 == textureToFace.at("sky3").size()); - CHECK(5 == textureToFace.at("orangestuff8").size()); + EXPECT_EQ(1, textureToFace.at("sky3").size()); + EXPECT_EQ(5, textureToFace.at("orangestuff8").size()); // leaf/node counts // - we'd get 7 nodes if it's cut like a cube (solid outside), with 1 additional cut inside to divide sky / empty @@ -714,148 +711,147 @@ TEST_CASE("simple_worldspawn_sky" * doctest::test_suite("testmaps_q1")) // - can get in between values if it does some vertical cuts, then the sky plane, then other vertical cuts // // the 7 solution is better but the BSP heuristics won't help reach that one in this trivial test map - CHECK(bsp.dnodes.size() >= 7); - CHECK(bsp.dnodes.size() <= 11); - CHECK(3 == bsp.dleafs.size()); // shared solid leaf + empty + sky + EXPECT_GE(bsp.dnodes.size(), 7); + EXPECT_LE(bsp.dnodes.size(), 11); + EXPECT_EQ(3, bsp.dleafs.size()); // shared solid leaf + empty + sky // check contents const qvec3d player_pos{-88, -64, 120}; const double inside_sky_z = 232; - CHECK(CONTENTS_EMPTY == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos)->contents); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos)->contents); // way above map is solid - sky should not fill outwards // (otherwise, if you had sky with a floor further up above it, it's not clear where the leafs would be divided, or // if the floor contents would turn to sky, etc.) - CHECK(CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0, 0, 500))->contents); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0, 0, 500))->contents); - CHECK(CONTENTS_SKY == + EXPECT_EQ(CONTENTS_SKY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], qvec3d(player_pos[0], player_pos[1], inside_sky_z))->contents); - CHECK(CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(500, 0, 0))->contents); - CHECK(CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(-500, 0, 0))->contents); - CHECK(CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0, 500, 0))->contents); - CHECK(CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0, -500, 0))->contents); - CHECK(CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0, 0, -500))->contents); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(500, 0, 0))->contents); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(-500, 0, 0))->contents); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0, 500, 0))->contents); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0, -500, 0))->contents); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0, 0, -500))->contents); - CHECK(prt->portals.size() == 0); + EXPECT_EQ(prt->portals.size(), 0); // FIXME: unsure what the expected number of visclusters is, does sky get one? - CHECK(12 == bsp.dclipnodes.size()); + EXPECT_EQ(12, bsp.dclipnodes.size()); } -TEST_CASE("water_detail_illusionary" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, waterDetailIllusionary) { static const std::string basic_mapname = "qbsp_water_detail_illusionary.map"; static const std::string mirrorinside_mapname = "qbsp_water_detail_illusionary_mirrorinside.map"; for (const auto &mapname : {basic_mapname, mirrorinside_mapname}) { - SUBCASE(fmt::format("testing {}", mapname).c_str()) - { - const auto [bsp, bspx, prt] = LoadTestmapQ1(mapname); + SCOPED_TRACE(fmt::format("testing {}", mapname)); - REQUIRE(prt.has_value()); + const auto [bsp, bspx, prt] = LoadTestmapQ1(mapname); - const qvec3d inside_water_and_fence{-20, -52, 124}; - const qvec3d inside_fence{-20, -52, 172}; + ASSERT_TRUE(prt.has_value()); - CHECK(BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], inside_water_and_fence)->contents == CONTENTS_WATER); - CHECK(BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], inside_fence)->contents == CONTENTS_EMPTY); + const qvec3d inside_water_and_fence{-20, -52, 124}; + const qvec3d inside_fence{-20, -52, 172}; - const qvec3d underwater_face_pos{-40, -52, 124}; - const qvec3d above_face_pos{-40, -52, 172}; + EXPECT_EQ(BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], inside_water_and_fence)->contents, CONTENTS_WATER); + EXPECT_EQ(BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], inside_fence)->contents, CONTENTS_EMPTY); - // make sure the detail_illusionary face underwater isn't clipped away - auto *underwater_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], underwater_face_pos, {-1, 0, 0}); - auto *underwater_face_inner = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], underwater_face_pos, {1, 0, 0}); + const qvec3d underwater_face_pos{-40, -52, 124}; + const qvec3d above_face_pos{-40, -52, 172}; - auto *above_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], above_face_pos, {-1, 0, 0}); - auto *above_face_inner = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], above_face_pos, {1, 0, 0}); + // make sure the detail_illusionary face underwater isn't clipped away + auto *underwater_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], underwater_face_pos, {-1, 0, 0}); + auto *underwater_face_inner = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], underwater_face_pos, {1, 0, 0}); - REQUIRE(nullptr != underwater_face); - REQUIRE(nullptr != above_face); + auto *above_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], above_face_pos, {-1, 0, 0}); + auto *above_face_inner = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], above_face_pos, {1, 0, 0}); - CHECK(std::string("{trigger") == Face_TextureName(&bsp, underwater_face)); - CHECK(std::string("{trigger") == Face_TextureName(&bsp, above_face)); + ASSERT_NE(nullptr, underwater_face); + ASSERT_NE(nullptr, above_face); - if (mapname == mirrorinside_mapname) { - REQUIRE(underwater_face_inner != nullptr); - REQUIRE(above_face_inner != nullptr); + EXPECT_EQ(std::string("{trigger"), Face_TextureName(&bsp, underwater_face)); + EXPECT_EQ(std::string("{trigger"), Face_TextureName(&bsp, above_face)); - CHECK(std::string("{trigger") == Face_TextureName(&bsp, underwater_face_inner)); - CHECK(std::string("{trigger") == Face_TextureName(&bsp, above_face_inner)); - } else { - CHECK(underwater_face_inner == nullptr); - CHECK(above_face_inner == nullptr); - } + if (mapname == mirrorinside_mapname) { + ASSERT_NE(underwater_face_inner, nullptr); + ASSERT_NE(above_face_inner, nullptr); + + EXPECT_EQ(std::string("{trigger"), Face_TextureName(&bsp, underwater_face_inner)); + EXPECT_EQ(std::string("{trigger"), Face_TextureName(&bsp, above_face_inner)); + } else { + EXPECT_EQ(underwater_face_inner, nullptr); + EXPECT_EQ(above_face_inner, nullptr); } } } -TEST_CASE("qbsp_bmodel_mirrorinside_with_liquid" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, bmodelMirrorinsideWithLiquid) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_bmodel_mirrorinside_with_liquid.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(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()); + EXPECT_EQ(2, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[1], model1_fenceface).size()); + EXPECT_EQ(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)); + SCOPED_TRACE(fmt::format("model: {} hull: {}", model_idx, hull)); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, {hull}, &model, (model.mins + model.maxs) / 2)); } } } -TEST_CASE("q1_bmodel_liquid" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, bmodelLiquid) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_bmodel_liquid.map", {"-bmodelcontents"}); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); // nonsolid brushes don't show up in clipping hulls. so 6 for the box room in hull1, and 6 for hull2. - REQUIRE(12 == bsp.dclipnodes.size()); + ASSERT_EQ(12, bsp.dclipnodes.size()); const auto inside_water = qvec3d{8, -120, 184}; - CHECK(CONTENTS_WATER == BSP_FindContentsAtPoint(&bsp, {0}, &bsp.dmodels[1], inside_water)); + EXPECT_EQ(CONTENTS_WATER, BSP_FindContentsAtPoint(&bsp, {0}, &bsp.dmodels[1], inside_water)); - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, {1}, &bsp.dmodels[1], inside_water)); - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, {2}, &bsp.dmodels[1], inside_water)); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, {1}, &bsp.dmodels[1], inside_water)); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, {2}, &bsp.dmodels[1], inside_water)); } -TEST_CASE("q1_liquid_mirrorinside_off" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, liquidMirrorinsideOff) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_liquid_mirrorinside_off.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); // normally there would be 2 faces, but with _mirrorinside 0 we should get only the upwards-pointing one - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels.at(0), {-52, -56, 8}, {0, 0, 1})); - CHECK(!BSP_FindFaceAtPoint(&bsp, &bsp.dmodels.at(0), {-52, -56, 8}, {0, 0, -1})); + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels.at(0), {-52, -56, 8}, {0, 0, 1})); + EXPECT_FALSE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels.at(0), {-52, -56, 8}, {0, 0, -1})); } -TEST_CASE("noclipfaces" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, noclipfaces) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_noclipfaces.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); - REQUIRE(bsp.dfaces.size() == 2); + ASSERT_EQ(bsp.dfaces.size(), 2); // TODO: contents should be empty in hull0 because it's func_detail_illusionary for (auto &face : bsp.dfaces) { - REQUIRE(std::string("{trigger") == Face_TextureName(&bsp, &face)); + ASSERT_EQ(std::string("{trigger"), Face_TextureName(&bsp, &face)); } - CHECK(prt->portals.size() == 0); - CHECK(prt->portalleafs == 1); + EXPECT_EQ(prt->portals.size(), 0); + EXPECT_EQ(prt->portalleafs, 1); } /** @@ -863,34 +859,33 @@ TEST_CASE("noclipfaces" * doctest::test_suite("testmaps_q1")) * * Currently, to simplify the implementation, we're treating that the same as if both had _noclipfaces 1 */ -TEST_CASE("noclipfaces_junction" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, noclipfacesJunction) { const std::vector maps{"qbsp_noclipfaces_junction.map", "q2_noclipfaces_junction.map"}; for (const auto &map : maps) { const bool q2 = (map.find("q2") == 0); - SUBCASE(map.c_str()) - { - const auto [bsp, bspx, prt] = q2 ? LoadTestmapQ2(map) : LoadTestmapQ1(map); + SCOPED_TRACE(map); - CHECK(bsp.dfaces.size() == 12); + const auto [bsp, bspx, prt] = q2 ? LoadTestmapQ2(map) : LoadTestmapQ1(map); - const qvec3d portal_pos{96, 56, 32}; + EXPECT_EQ(bsp.dfaces.size(), 12); - auto *pos_x = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], portal_pos, {1, 0, 0}); - auto *neg_x = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], portal_pos, {-1, 0, 0}); + const qvec3d portal_pos{96, 56, 32}; - REQUIRE(pos_x != nullptr); - REQUIRE(neg_x != nullptr); + auto *pos_x = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], portal_pos, {1, 0, 0}); + auto *neg_x = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], portal_pos, {-1, 0, 0}); - if (q2) { - CHECK(std::string("e1u1/wndow1_2") == Face_TextureName(&bsp, pos_x)); - CHECK(std::string("e1u1/window1") == Face_TextureName(&bsp, neg_x)); - } else { - CHECK(std::string("{trigger") == Face_TextureName(&bsp, pos_x)); - CHECK(std::string("blood1") == Face_TextureName(&bsp, neg_x)); - } + ASSERT_NE(pos_x, nullptr); + ASSERT_NE(neg_x, nullptr); + + if (q2) { + EXPECT_EQ(std::string("e1u1/wndow1_2"), Face_TextureName(&bsp, pos_x)); + EXPECT_EQ(std::string("e1u1/window1"), Face_TextureName(&bsp, neg_x)); + } else { + EXPECT_EQ(std::string("{trigger"), Face_TextureName(&bsp, pos_x)); + EXPECT_EQ(std::string("blood1"), Face_TextureName(&bsp, neg_x)); } } } @@ -898,213 +893,213 @@ TEST_CASE("noclipfaces_junction" * doctest::test_suite("testmaps_q1")) /** * Same as previous test, but the T shaped brush entity has _mirrorinside */ -TEST_CASE("noclipfaces_mirrorinside" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, noclipfacesMirrorinside) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_noclipfaces_mirrorinside.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); - REQUIRE(bsp.dfaces.size() == 4); + ASSERT_EQ(bsp.dfaces.size(), 4); // TODO: contents should be empty in hull0 because it's func_detail_illusionary for (auto &face : bsp.dfaces) { - REQUIRE(std::string("{trigger") == Face_TextureName(&bsp, &face)); + ASSERT_EQ(std::string("{trigger"), Face_TextureName(&bsp, &face)); } - CHECK(prt->portals.size() == 0); - CHECK(prt->portalleafs == 1); + EXPECT_EQ(prt->portals.size(), 0); + EXPECT_EQ(prt->portalleafs, 1); } -TEST_CASE("detail_illusionary_intersecting" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, detailIllusionaryIntersecting) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_detail_illusionary_intersecting.map", {"-tjunc", "rotate"}); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); // sides: 3*4 = 12 // top: 3 (4 with new tjunc code that prefers more faces over 0-area tris) // bottom: 3 (4 with new tjunc code that prefers more faces over 0-area tris) - CHECK(bsp.dfaces.size() >= 18); - CHECK(bsp.dfaces.size() <= 20); + EXPECT_GE(bsp.dfaces.size(), 18); + EXPECT_LE(bsp.dfaces.size(), 20); for (auto &face : bsp.dfaces) { - CHECK(std::string("{trigger") == Face_TextureName(&bsp, &face)); + EXPECT_EQ(std::string("{trigger"), Face_TextureName(&bsp, &face)); } // top of cross - CHECK(1 == BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -50, 120), qvec3d(0, 0, 1)).size()); + EXPECT_EQ(1, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -50, 120), qvec3d(0, 0, 1)).size()); // interior face that should be clipped away - CHECK(0 == BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -52, 116), qvec3d(0, -1, 0)).size()); + EXPECT_EQ(0, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -52, 116), qvec3d(0, -1, 0)).size()); - CHECK(prt->portals.size() == 0); - CHECK(prt->portalleafs == 1); + EXPECT_EQ(prt->portals.size(), 0); + EXPECT_EQ(prt->portalleafs, 1); } -TEST_CASE("detail_illusionary_noclipfaces_intersecting" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, detailIllusionaryNoclipfacesIntersecting) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_detail_illusionary_noclipfaces_intersecting.map", {"-tjunc", "rotate"}); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); for (auto &face : bsp.dfaces) { - CHECK(std::string("{trigger") == Face_TextureName(&bsp, &face)); + EXPECT_EQ(std::string("{trigger"), Face_TextureName(&bsp, &face)); } // top of cross has 2 faces Z-fighting, because we disabled clipping // (with qbsp3 method, there won't ever be z-fighting since we only ever generate 1 face per portal) size_t faces_at_top = BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -50, 120), qvec3d(0, 0, 1)).size(); - CHECK(faces_at_top >= 1); - CHECK(faces_at_top <= 2); + EXPECT_GE(faces_at_top, 1); + EXPECT_LE(faces_at_top, 2); // interior face not clipped away - CHECK(1 == BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -52, 116), qvec3d(0, -1, 0)).size()); + EXPECT_EQ(1, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -52, 116), qvec3d(0, -1, 0)).size()); - CHECK(prt->portals.size() == 0); - CHECK(prt->portalleafs == 1); + EXPECT_EQ(prt->portals.size(), 0); + EXPECT_EQ(prt->portalleafs, 1); } -TEST_CASE("q1_detail_non_sealing" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, detailNonSealing) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_detail_non_sealing.map"); - CHECK(!prt.has_value()); + EXPECT_FALSE(prt.has_value()); } -TEST_CASE("q1_sealing_contents" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, sealingContents) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_sealing_contents.map"); - CHECK(prt.has_value()); + EXPECT_TRUE(prt.has_value()); } -TEST_CASE("q1_detail_touching_water" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, detailTouchingWater) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_detail_touching_water.map"); - CHECK(prt.has_value()); + EXPECT_TRUE(prt.has_value()); } -TEST_CASE("detail_doesnt_remove_world_nodes" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, detailDoesntRemoveWorldNodes) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_detail_doesnt_remove_world_nodes.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); { // check for a face under the start pos const qvec3d floor_under_start{-56, -72, 64}; auto *floor_under_start_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], floor_under_start, {0, 0, 1}); - CHECK(nullptr != floor_under_start_face); + EXPECT_NE(nullptr, floor_under_start_face); } { // floor face should be clipped away by detail const qvec3d floor_inside_detail{64, -72, 64}; auto *floor_inside_detail_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], floor_inside_detail, {0, 0, 1}); - CHECK(nullptr == floor_inside_detail_face); + EXPECT_EQ(nullptr, floor_inside_detail_face); } // make sure the detail face exists - CHECK(nullptr != BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {32, -72, 136}, {-1, 0, 0})); + EXPECT_NE(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {32, -72, 136}, {-1, 0, 0})); { // but the sturctural nodes/leafs should not be clipped away by detail const qvec3d covered_by_detail{48, -88, 128}; auto *covered_by_detail_node = BSP_FindNodeAtPoint(&bsp, &bsp.dmodels[0], covered_by_detail, {-1, 0, 0}); - CHECK(nullptr != covered_by_detail_node); + EXPECT_NE(nullptr, covered_by_detail_node); } } -TEST_CASE("merge" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, merge) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_merge.map"); - REQUIRE_FALSE(prt.has_value()); - REQUIRE(bsp.dfaces.size() >= 6); + ASSERT_FALSE(prt.has_value()); + ASSERT_GE(bsp.dfaces.size(), 6); // BrushBSP does a split through the middle first to keep the BSP balanced, which prevents // two of the side face from being merged - REQUIRE(bsp.dfaces.size() <= 8); + ASSERT_LE(bsp.dfaces.size(), 8); const auto exp_bounds = aabb3d{{48, 0, 96}, {224, 96, 96}}; auto *top_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {48, 0, 96}, {0, 0, 1}); const auto top_winding = Face_Winding(&bsp, top_face); - CHECK(top_winding.bounds().mins() == exp_bounds.mins()); - CHECK(top_winding.bounds().maxs() == exp_bounds.maxs()); + EXPECT_EQ(top_winding.bounds().mins(), exp_bounds.mins()); + EXPECT_EQ(top_winding.bounds().maxs(), exp_bounds.maxs()); } -TEST_CASE("tjunc_many_sided_face" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, tjuncManySidedFace) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_tjunc_many_sided_face.map", {"-tjunc", "rotate"}); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); std::map> faces_by_normal; for (auto &face : bsp.dfaces) { faces_by_normal[Face_Normal(&bsp, &face)].push_back(&face); } - REQUIRE(6 == faces_by_normal.size()); + ASSERT_EQ(6, faces_by_normal.size()); // the floor has a 0.1 texture scale, so it gets subdivided into many small faces - CHECK(15 * 15 == (faces_by_normal.at({0, 0, 1}).size())); + EXPECT_EQ(15 * 15, (faces_by_normal.at({0, 0, 1}).size())); // the ceiling gets split into 2 faces because fixing T-Junctions with all of the // wall sections exceeds the max vertices per face limit - CHECK(2 == (faces_by_normal.at({0, 0, -1}).size())); + EXPECT_EQ(2, (faces_by_normal.at({0, 0, -1}).size())); } -TEST_CASE("tjunc_angled_face" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, tjuncAngledFace) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_tjunc_angled_face.map"); CheckFilled(bsp); auto faces = FacesWithTextureName(bsp, "bolt6"); - REQUIRE(faces.size() == 1); + ASSERT_EQ(faces.size(), 1); auto *bolt6_face = faces.at(0); - CHECK(bolt6_face->numedges == 5); + EXPECT_EQ(bolt6_face->numedges, 5); } /** * Because it comes second, the sbutt2 brush should "win" in clipping against the floor, * in both a worldspawn test case, as well as a func_wall. */ -TEST_CASE("brush_clipping_order" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, brushClippingOrder) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_brush_clipping_order.map", {"-tjunc", "rotate"}); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); const qvec3d world_button{-8, -8, 16}; const qvec3d func_wall_button{152, -8, 16}; // 0 = world, 1 = func_wall - REQUIRE(2 == bsp.dmodels.size()); + ASSERT_EQ(2, bsp.dmodels.size()); - REQUIRE(20 == bsp.dfaces.size()); + ASSERT_EQ(20, bsp.dfaces.size()); - REQUIRE(10 == bsp.dmodels[0].numfaces); // 5 faces for the sides + bottom, 5 faces for the top - REQUIRE(10 == bsp.dmodels[1].numfaces); // (same on worldspawn and func_wall) + ASSERT_EQ(10, bsp.dmodels[0].numfaces); // 5 faces for the sides + bottom, 5 faces for the top + ASSERT_EQ(10, bsp.dmodels[1].numfaces); // (same on worldspawn and func_wall) auto *world_button_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], world_button, {0, 0, 1}); - REQUIRE(nullptr != world_button_face); - REQUIRE(std::string("sbutt2") == Face_TextureName(&bsp, world_button_face)); + ASSERT_NE(nullptr, world_button_face); + ASSERT_EQ(std::string("sbutt2"), Face_TextureName(&bsp, world_button_face)); auto *func_wall_button_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[1], func_wall_button, {0, 0, 1}); - REQUIRE(nullptr != func_wall_button_face); - REQUIRE(std::string("sbutt2") == Face_TextureName(&bsp, func_wall_button_face)); + ASSERT_NE(nullptr, func_wall_button_face); + ASSERT_EQ(std::string("sbutt2"), Face_TextureName(&bsp, func_wall_button_face)); } /** * Box room with a rotating fan (just a cube). Works in a mod with hiprotate - AD, Quoth, etc. */ -TEST_CASE("origin" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, origin) { const std::vector maps{ "qbsp_origin.map", @@ -1112,163 +1107,163 @@ TEST_CASE("origin" * doctest::test_suite("testmaps_q1")) }; for (const auto &map : maps) { - SUBCASE(map.c_str()) - { - const auto [bsp, bspx, prt] = LoadTestmapQ1(map); + SCOPED_TRACE(map); - REQUIRE(prt.has_value()); + const auto [bsp, bspx, prt] = LoadTestmapQ1(map); - // 0 = world, 1 = rotate_object - REQUIRE(2 == bsp.dmodels.size()); + ASSERT_TRUE(prt.has_value()); - // check that the origin brush didn't clip away any solid faces, or generate faces - REQUIRE(6 == bsp.dmodels[1].numfaces); + // 0 = world, 1 = rotate_object + ASSERT_EQ(2, bsp.dmodels.size()); - // FIXME: should the origin brush update the dmodel's origin too? - REQUIRE(qvec3f(0, 0, 0) == bsp.dmodels[1].origin); + // check that the origin brush didn't clip away any solid faces, or generate faces + ASSERT_EQ(6, bsp.dmodels[1].numfaces); - // check that the origin brush updated the entity lump - auto ents = EntData_Parse(bsp); - auto it = std::find_if(ents.begin(), ents.end(), - [](const entdict_t &dict) -> bool { return dict.get("classname") == "rotate_object"; }); + // FIXME: should the origin brush update the dmodel's origin too? + ASSERT_EQ(qvec3f(0, 0, 0), bsp.dmodels[1].origin); - REQUIRE(it != ents.end()); - CHECK(it->get("origin") == "216 -216 340"); - } + // check that the origin brush updated the entity lump + auto ents = EntData_Parse(bsp); + auto it = std::find_if(ents.begin(), ents.end(), + [](const entdict_t &dict) -> bool { return dict.get("classname") == "rotate_object"; }); + + ASSERT_NE(it, ents.end()); + EXPECT_EQ(it->get("origin"), "216 -216 340"); } } -TEST_CASE("simple" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, simple) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_simple.map"); - REQUIRE_FALSE(prt.has_value()); + ASSERT_FALSE(prt.has_value()); } /** * Just a solid cuboid */ -TEST_CASE("q1_cube") +TEST(testmapsQ1, cube) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_cube.map"); - REQUIRE_FALSE(prt.has_value()); + ASSERT_FALSE(prt.has_value()); const aabb3f cube_bounds{{32, -240, 80}, {80, -144, 112}}; - CHECK(bsp.dedges.size() == 13); // index 0 is reserved, and the cube has 12 edges + EXPECT_EQ(bsp.dedges.size(), 13); // index 0 is reserved, and the cube has 12 edges - REQUIRE(7 == bsp.dleafs.size()); + ASSERT_EQ(7, bsp.dleafs.size()); // check the solid leaf auto &solid_leaf = bsp.dleafs[0]; - CHECK(solid_leaf.mins == qvec3f(0, 0, 0)); - CHECK(solid_leaf.maxs == qvec3f(0, 0, 0)); + EXPECT_EQ(solid_leaf.mins, qvec3f(0, 0, 0)); + EXPECT_EQ(solid_leaf.maxs, qvec3f(0, 0, 0)); // check the empty leafs for (int i = 1; i < 7; ++i) { - SUBCASE(fmt::format("leaf {}", i).c_str()) - { - auto &leaf = bsp.dleafs[i]; - CHECK(CONTENTS_EMPTY == leaf.contents); + SCOPED_TRACE(fmt::format("leaf {}", i)); - CHECK(1 == leaf.nummarksurfaces); - } + auto &leaf = bsp.dleafs[i]; + EXPECT_EQ(CONTENTS_EMPTY, leaf.contents); + + EXPECT_EQ(1, leaf.nummarksurfaces); } - REQUIRE(6 == bsp.dfaces.size()); + ASSERT_EQ(6, bsp.dfaces.size()); // node bounds auto cube_bounds_grown = cube_bounds.grow(24); auto &headnode = bsp.dnodes[bsp.dmodels[0].headnode[0]]; - CHECK(cube_bounds_grown.mins() == headnode.mins); - CHECK(cube_bounds_grown.maxs() == headnode.maxs); + EXPECT_EQ(cube_bounds_grown.mins(), headnode.mins); + EXPECT_EQ(cube_bounds_grown.maxs(), headnode.maxs); // model bounds are shrunk by 1 unit on each side for some reason - CHECK(cube_bounds.grow(-1).mins() == bsp.dmodels[0].mins); - CHECK(cube_bounds.grow(-1).maxs() == bsp.dmodels[0].maxs); + EXPECT_EQ(cube_bounds.grow(-1).mins(), bsp.dmodels[0].mins); + EXPECT_EQ(cube_bounds.grow(-1).maxs(), bsp.dmodels[0].maxs); - CHECK(6 == bsp.dnodes.size()); + EXPECT_EQ(6, bsp.dnodes.size()); - CHECK(12 == bsp.dclipnodes.size()); + EXPECT_EQ(12, bsp.dclipnodes.size()); } /** * Two solid cuboids touching along one edge */ -TEST_CASE("q1_cubes" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, cubes) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_cubes.map"); - CHECK(bsp.dedges.size() == 25); + EXPECT_EQ(bsp.dedges.size(), 25); } /** * Ensure submodels that are all "clip" get bounds set correctly */ -TEST_CASE("q1_clip_func_wall" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, clipFuncWall) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_clip_func_wall.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); const aabb3f cube_bounds{{64, 64, 48}, {128, 128, 80}}; - REQUIRE(2 == bsp.dmodels.size()); + ASSERT_EQ(2, bsp.dmodels.size()); // node bounds auto &headnode = bsp.dnodes[bsp.dmodels[1].headnode[0]]; - CHECK(cube_bounds.grow(24).mins() == headnode.mins); - CHECK(cube_bounds.grow(24).maxs() == headnode.maxs); + EXPECT_EQ(cube_bounds.grow(24).mins(), headnode.mins); + EXPECT_EQ(cube_bounds.grow(24).maxs(), headnode.maxs); // model bounds are shrunk by 1 unit on each side for some reason - CHECK(cube_bounds.grow(-1).mins() == bsp.dmodels[1].mins); - CHECK(cube_bounds.grow(-1).maxs() == bsp.dmodels[1].maxs); + EXPECT_EQ(cube_bounds.grow(-1).mins(), bsp.dmodels[1].mins); + EXPECT_EQ(cube_bounds.grow(-1).maxs(), bsp.dmodels[1].maxs); } /** * Lots of features in one map, more for testing in game than automated testing */ -TEST_CASE("features" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, features) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbspfeatures.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); - CHECK(bsp.loadversion == &bspver_q1); + EXPECT_EQ(bsp.loadversion, &bspver_q1); } -TEST_CASE("q1_detail_wall tjuncs" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, detailWallTjuncs) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_detail_wall.map"); - REQUIRE(prt.has_value()); - CHECK(bsp.loadversion == &bspver_q1); + ASSERT_TRUE(prt.has_value()); + EXPECT_EQ(bsp.loadversion, &bspver_q1); const auto behind_pillar = qvec3d(-160, -140, 120); auto *face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], behind_pillar, qvec3d(1, 0, 0)); - REQUIRE(face); + ASSERT_TRUE(face); - INFO("func_detail_wall should not generate extra tjunctions on structural faces"); + SCOPED_TRACE("func_detail_wall should not generate extra tjunctions on structural faces"); auto w = Face_Winding(&bsp, face); - CHECK(w.size() == 5); + EXPECT_EQ(w.size(), 5); } -TEST_CASE("q1_detail_wall_intersecting_detail" * doctest::test_suite("testmaps_q1") * doctest::may_fail()) +TEST(testmapsQ1, detailWallIntersectingDetail) { + GTEST_SKIP(); + const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_detail_wall_intersecting_detail.map"); const auto *left_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-152, -192, 160}, {1, 0, 0}); const auto *under_detail_wall_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-152, -176, 160}, {1, 0, 0}); const auto *right_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-152, -152, 160}, {1, 0, 0}); - CHECK(left_face != nullptr); - CHECK(under_detail_wall_face != nullptr); - CHECK(right_face != nullptr); + EXPECT_NE(left_face, nullptr); + EXPECT_NE(under_detail_wall_face, nullptr); + EXPECT_NE(right_face, nullptr); - CHECK(left_face == under_detail_wall_face); - CHECK(left_face == right_face); + EXPECT_EQ(left_face, under_detail_wall_face); + EXPECT_EQ(left_face, right_face); } bool PortalMatcher(const prtfile_winding_t &a, const prtfile_winding_t &b) @@ -1276,14 +1271,14 @@ bool PortalMatcher(const prtfile_winding_t &a, const prtfile_winding_t &b) return a.undirectional_equal(b); } -TEST_CASE("qbsp_func_detail various types" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, qbspFuncDetailVariousTypes) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_func_detail.map"); - REQUIRE(prt.has_value()); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + ASSERT_TRUE(prt.has_value()); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); - CHECK(1 == bsp.dmodels.size()); + EXPECT_EQ(1, bsp.dmodels.size()); const qvec3d in_func_detail{56, -56, 120}; const qvec3d in_func_detail_wall{56, -136, 120}; @@ -1293,11 +1288,11 @@ TEST_CASE("qbsp_func_detail various types" * doctest::test_suite("testmaps_q1")) // const double floor_z = 96; // detail clips away world faces, others don't - CHECK(nullptr == BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], in_func_detail - qvec3d(0, 0, 24), {0, 0, 1})); - CHECK(nullptr != BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], in_func_detail_wall - qvec3d(0, 0, 24), {0, 0, 1})); - CHECK(nullptr != + EXPECT_EQ(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], in_func_detail - qvec3d(0, 0, 24), {0, 0, 1})); + EXPECT_NE(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], in_func_detail_wall - qvec3d(0, 0, 24), {0, 0, 1})); + EXPECT_NE(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], in_func_detail_illusionary - qvec3d(0, 0, 24), {0, 0, 1})); - CHECK(nullptr != BSP_FindFaceAtPoint( + EXPECT_NE(nullptr, BSP_FindFaceAtPoint( &bsp, &bsp.dmodels[0], in_func_detail_illusionary_mirrorinside - qvec3d(0, 0, 24), {0, 0, 1})); // check for correct contents @@ -1307,77 +1302,81 @@ TEST_CASE("qbsp_func_detail various types" * doctest::test_suite("testmaps_q1")) auto *detail_illusionary_mirrorinside_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_func_detail_illusionary_mirrorinside); - CHECK(CONTENTS_SOLID == detail_leaf->contents); - CHECK(CONTENTS_SOLID == detail_wall_leaf->contents); - CHECK(CONTENTS_EMPTY == detail_illusionary_leaf->contents); - CHECK(CONTENTS_EMPTY == detail_illusionary_mirrorinside_leaf->contents); + EXPECT_EQ(CONTENTS_SOLID, detail_leaf->contents); + EXPECT_EQ(CONTENTS_SOLID, detail_wall_leaf->contents); + EXPECT_EQ(CONTENTS_EMPTY, detail_illusionary_leaf->contents); + EXPECT_EQ(CONTENTS_EMPTY, detail_illusionary_mirrorinside_leaf->contents); // portals - REQUIRE(2 == prt->portals.size()); + ASSERT_EQ(2, prt->portals.size()); const auto p0 = prtfile_winding_t{{-160, -8, 352}, {56, -8, 352}, {56, -8, 96}, {-160, -8, 96}}; const auto p1 = p0.translate({232, 0, 0}); - CHECK(((PortalMatcher(prt->portals[0].winding, p0) && PortalMatcher(prt->portals[1].winding, p1)) || + EXPECT_TRUE(((PortalMatcher(prt->portals[0].winding, p0) && PortalMatcher(prt->portals[1].winding, p1)) || (PortalMatcher(prt->portals[0].winding, p1) && PortalMatcher(prt->portals[1].winding, p0)))); - CHECK(prt->portalleafs == 3); - CHECK(prt->portalleafs_real > 3); + EXPECT_EQ(prt->portalleafs, 3); + EXPECT_GT(prt->portalleafs_real, 3); } -TEST_CASE("qbsp_angled_brush" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, angledBrush) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_angled_brush.map"); - REQUIRE(prt.has_value()); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + ASSERT_TRUE(prt.has_value()); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); - CHECK(1 == bsp.dmodels.size()); + EXPECT_EQ(1, bsp.dmodels.size()); // tilted cuboid floating in a box room, so shared solid leaf + 6 empty leafs around the cube - CHECK(6 + 1 == bsp.dleafs.size()); + EXPECT_EQ(6 + 1, bsp.dleafs.size()); } -TEST_CASE("qbsp_sealing_point_entity_on_outside" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, sealingPointEntityOnOutside) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_sealing_point_entity_on_outside.map"); - REQUIRE(prt.has_value()); + ASSERT_TRUE(prt.has_value()); } -TEST_CASE("q1_sealing_hull1_onnode" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, sealingHull1Onnode) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_sealing_hull1_onnode.map"); const auto player_start_pos = qvec3d(-192, 132, 56); - INFO("hull0 is empty at the player start"); - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], player_start_pos)); + SCOPED_TRACE("hull0 is empty at the player start"); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], player_start_pos)); - INFO("hull1/2 are empty just above the player start"); - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1))); - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1))); + SCOPED_TRACE("hull1/2 are empty just above the player start"); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1))); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1))); - INFO("hull0/1/2 are solid in the void"); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000))); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000))); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000))); + SCOPED_TRACE("hull0/1/2 are solid in the void"); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000))); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000))); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], player_start_pos + qvec3d(0, 0, 1000))); } -TEST_CASE("q1_0125unit_faces" * doctest::test_suite("testmaps_q1") * doctest::may_fail()) +TEST(testmapsQ1, 0125UnitFaces) { + GTEST_SKIP(); + const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_0125unit_faces.map"); - CHECK(bsp.loadversion == &bspver_q1); - CHECK(2 == bsp.dfaces.size()); + EXPECT_EQ(bsp.loadversion, &bspver_q1); + EXPECT_EQ(2, bsp.dfaces.size()); } -TEST_CASE("mountain" * doctest::test_suite("testmaps_q1") * doctest::skip() * doctest::may_fail()) +TEST(testmapsQ1, mountain) { + GTEST_SKIP(); + const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_mountain.map"); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); - CHECK(prt); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); + EXPECT_TRUE(prt); CheckFilled(bsp); } @@ -1387,11 +1386,11 @@ TEST_CASE("mountain" * doctest::test_suite("testmaps_q1") * doctest::skip() * do * - hull1+ can't, because it would cause areas containing no entities but connected by a thin gap to the * rest of the world to get sealed off as solid. **/ -TEST_CASE("q1_sealing" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, sealing) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_sealing.map"); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); const qvec3d in_start_room{-192, 144, 104}; const qvec3d in_emptyroom{-168, 544, 104}; @@ -1399,197 +1398,199 @@ TEST_CASE("q1_sealing" * doctest::test_suite("testmaps_q1")) const qvec3d connected_by_thin_gap{72, 136, 104}; // check leaf contents in hull 0 - CHECK(CONTENTS_EMPTY == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_start_room)->contents); - CHECK(CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_emptyroom) + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_start_room)->contents); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_emptyroom) ->contents); // can get sealed, since there are no entities - CHECK(CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_void)->contents); - CHECK(CONTENTS_EMPTY == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], connected_by_thin_gap)->contents); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_void)->contents); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], connected_by_thin_gap)->contents); // check leaf contents in hull 1 - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], in_start_room)); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], in_emptyroom)); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], in_void)); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], in_start_room)); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], in_emptyroom)); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], in_void)); // ideally this wouldn't get sealed, but we need to do the "inside filling" for compatibility with complex // maps using e.g. obj2map geometry, otherwise the clipnodes count explodes - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], connected_by_thin_gap)); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], connected_by_thin_gap)); // check leaf contents in hull 2 - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], in_start_room)); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], in_emptyroom)); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], in_void)); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], connected_by_thin_gap)); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], in_start_room)); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], in_emptyroom)); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], in_void)); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], connected_by_thin_gap)); - CHECK(prt->portals.size() == 2); - CHECK(prt->portalleafs == 3); // 2 connected rooms + gap (other room is filled in with solid) - CHECK(prt->portalleafs_real == 3); // no detail, so same as above + EXPECT_EQ(prt->portals.size(), 2); + EXPECT_EQ(prt->portalleafs, 3); // 2 connected rooms + gap (other room is filled in with solid) + EXPECT_EQ(prt->portalleafs_real, 3); // no detail, so same as above } -TEST_CASE("q1_csg" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, csg) { auto *game = bspver_q1.game; auto &entity = LoadMapPath("q1_csg.map"); - REQUIRE(entity.mapbrushes.size() == 2); + ASSERT_EQ(entity.mapbrushes.size(), 2); bspbrush_t::container bspbrushes; for (int i = 0; i < 2; ++i) { auto b = LoadBrush(entity, entity.mapbrushes[i], game->create_contents_from_native(CONTENTS_SOLID), 0, std::nullopt); - CHECK(6 == b->sides.size()); + EXPECT_EQ(6, b->sides.size()); bspbrushes.push_back(bspbrush_t::make_ptr(std::move(*b))); } auto csged = CSGFaces(bspbrushes); - CHECK(2 == csged.size()); + EXPECT_EQ(2, csged.size()); for (int i = 0; i < 2; ++i) { - CHECK(5 == csged[i]->sides.size()); + EXPECT_EQ(5, csged[i]->sides.size()); } } /** * Test for WAD internal textures **/ -TEST_CASE("q1_wad_internal" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, wadInternal) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_simple.map"); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); - CHECK(bsp.dtex.textures.size() == 4); + EXPECT_EQ(bsp.dtex.textures.size(), 4); // skip is only here because of the water - CHECK(bsp.dtex.textures[0].name == "skip"); + EXPECT_EQ(bsp.dtex.textures[0].name, "skip"); - CHECK(bsp.dtex.textures[1].name == "orangestuff8"); - CHECK(bsp.dtex.textures[2].name == "*zwater1"); - CHECK(bsp.dtex.textures[3].name == "brown_brick"); + EXPECT_EQ(bsp.dtex.textures[1].name, "orangestuff8"); + EXPECT_EQ(bsp.dtex.textures[2].name, "*zwater1"); + EXPECT_EQ(bsp.dtex.textures[3].name, "brown_brick"); - CHECK(!bsp.dtex.textures[1].data.empty()); - CHECK(!bsp.dtex.textures[2].data.empty()); - CHECK(!bsp.dtex.textures[3].data.empty()); + EXPECT_FALSE(bsp.dtex.textures[1].data.empty()); + EXPECT_FALSE(bsp.dtex.textures[2].data.empty()); + EXPECT_FALSE(bsp.dtex.textures[3].data.empty()); - CHECK(img::load_mip("orangestuff8", bsp.dtex.textures[1].data, false, bsp.loadversion->game)); - CHECK(img::load_mip("*zwater1", bsp.dtex.textures[2].data, false, bsp.loadversion->game)); - CHECK(img::load_mip("brown_brick", bsp.dtex.textures[3].data, false, bsp.loadversion->game)); + EXPECT_TRUE(img::load_mip("orangestuff8", bsp.dtex.textures[1].data, false, bsp.loadversion->game)); + EXPECT_TRUE(img::load_mip("*zwater1", bsp.dtex.textures[2].data, false, bsp.loadversion->game)); + EXPECT_TRUE(img::load_mip("brown_brick", bsp.dtex.textures[3].data, false, bsp.loadversion->game)); } /** * Test for WAD internal textures **/ -TEST_CASE("q1_wad_external" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, wadExternal) { const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_simple.map", {"-xwadpath", std::string(testmaps_dir)}); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); - CHECK(bsp.dtex.textures.size() == 4); + EXPECT_EQ(bsp.dtex.textures.size(), 4); // skip is only here because of the water - CHECK(bsp.dtex.textures[0].name == "skip"); + EXPECT_EQ(bsp.dtex.textures[0].name, "skip"); - CHECK(bsp.dtex.textures[1].name == "orangestuff8"); - CHECK(bsp.dtex.textures[2].name == "*zwater1"); - CHECK(bsp.dtex.textures[3].name == "brown_brick"); + EXPECT_EQ(bsp.dtex.textures[1].name, "orangestuff8"); + EXPECT_EQ(bsp.dtex.textures[2].name, "*zwater1"); + EXPECT_EQ(bsp.dtex.textures[3].name, "brown_brick"); - CHECK(bsp.dtex.textures[1].data.size() == sizeof(dmiptex_t)); - CHECK(bsp.dtex.textures[2].data.size() == sizeof(dmiptex_t)); - CHECK(bsp.dtex.textures[3].data.size() == sizeof(dmiptex_t)); + EXPECT_EQ(bsp.dtex.textures[1].data.size(), sizeof(dmiptex_t)); + EXPECT_EQ(bsp.dtex.textures[2].data.size(), sizeof(dmiptex_t)); + EXPECT_EQ(bsp.dtex.textures[3].data.size(), sizeof(dmiptex_t)); } -TEST_CASE("q1_loose_textures_ignored" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, looseTexturesIgnored) { - INFO("q1 should only load textures from .wad's. loose textures should not be included."); + SCOPED_TRACE("q1 should only load textures from .wad's. loose textures should not be included."); const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_loose_textures_ignored/q1_loose_textures_ignored.map"); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); - REQUIRE(bsp.dtex.textures.size() == 4); + ASSERT_EQ(bsp.dtex.textures.size(), 4); // FIXME: we shouldn't really write out skip const miptex_t &skip = bsp.dtex.textures[0]; - CHECK(skip.name == "skip"); - CHECK(!skip.null_texture); - CHECK(skip.width == 64); - CHECK(skip.height == 64); - CHECK(skip.data.size() > sizeof(dmiptex_t)); + EXPECT_EQ(skip.name, "skip"); + EXPECT_FALSE(skip.null_texture); + EXPECT_EQ(skip.width, 64); + EXPECT_EQ(skip.height, 64); + EXPECT_GT(skip.data.size(), sizeof(dmiptex_t)); // the .map directory contains a "orangestuff8.png" which is 16x16. // make sure it's not picked up (https://github.com/ericwa/ericw-tools/issues/404). const miptex_t &orangestuff8 = bsp.dtex.textures[1]; - CHECK(orangestuff8.name == "orangestuff8"); - CHECK(!orangestuff8.null_texture); - CHECK(orangestuff8.width == 64); - CHECK(orangestuff8.height == 64); - CHECK(orangestuff8.data.size() > sizeof(dmiptex_t)); + EXPECT_EQ(orangestuff8.name, "orangestuff8"); + EXPECT_FALSE(orangestuff8.null_texture); + EXPECT_EQ(orangestuff8.width, 64); + EXPECT_EQ(orangestuff8.height, 64); + EXPECT_GT(orangestuff8.data.size(), sizeof(dmiptex_t)); const miptex_t &zwater1 = bsp.dtex.textures[2]; - CHECK(zwater1.name == "*zwater1"); - CHECK(!zwater1.null_texture); - CHECK(zwater1.width == 64); - CHECK(zwater1.height == 64); - CHECK(zwater1.data.size() > sizeof(dmiptex_t)); + EXPECT_EQ(zwater1.name, "*zwater1"); + EXPECT_FALSE(zwater1.null_texture); + EXPECT_EQ(zwater1.width, 64); + EXPECT_EQ(zwater1.height, 64); + EXPECT_GT(zwater1.data.size(), sizeof(dmiptex_t)); const miptex_t &brown_brick = bsp.dtex.textures[3]; - CHECK(brown_brick.name == "brown_brick"); - CHECK(!brown_brick.null_texture); - CHECK(brown_brick.width == 128); - CHECK(brown_brick.height == 128); - CHECK(brown_brick.data.size() > sizeof(dmiptex_t)); + EXPECT_EQ(brown_brick.name, "brown_brick"); + EXPECT_FALSE(brown_brick.null_texture); + EXPECT_EQ(brown_brick.width, 128); + EXPECT_EQ(brown_brick.height, 128); + EXPECT_GT(brown_brick.data.size(), sizeof(dmiptex_t)); } /** * Test that we automatically try to load X.wad when compiling X.map **/ -TEST_CASE("q1_wad_mapname" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, wadMapname) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_wad_mapname.map"); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + EXPECT_EQ(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() == 0); // no texture data - CHECK(bsp.dtex.textures[0].null_texture); // no texture data + EXPECT_EQ(bsp.dtex.textures.size(), 2); + EXPECT_EQ(bsp.dtex.textures[0].name, ""); // skip + EXPECT_EQ(bsp.dtex.textures[0].data.size(), 0); // no texture data + EXPECT_TRUE(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)); + EXPECT_EQ(bsp.dtex.textures[1].name, "{trigger"); + EXPECT_GT(bsp.dtex.textures[1].data.size(), sizeof(dmiptex_t)); } -TEST_CASE("q1_merge_maps" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, mergeMaps) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_merge_maps_base.map", {"-add", "q1_merge_maps_addition.map"}); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); // check brushwork from the two maps is merged - REQUIRE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {5, 0, 16}, {0, 0, 1})); - REQUIRE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-5, 0, 16}, {0, 0, 1})); + ASSERT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {5, 0, 16}, {0, 0, 1})); + ASSERT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-5, 0, 16}, {0, 0, 1})); // check that the worldspawn keys from the base map are used auto ents = EntData_Parse(bsp); - REQUIRE(ents.size() == 3); // worldspawn, info_player_start, func_wall + ASSERT_EQ(ents.size(), 3); // worldspawn, info_player_start, func_wall - REQUIRE(ents[0].get("classname") == "worldspawn"); - CHECK(ents[0].get("message") == "merge maps base"); + ASSERT_EQ(ents[0].get("classname"), "worldspawn"); + EXPECT_EQ(ents[0].get("message"), "merge maps base"); // check info_player_start auto it = std::find_if(ents.begin(), ents.end(), [](const entdict_t &dict) -> bool { return dict.get("classname") == "info_player_start"; }); - REQUIRE(it != ents.end()); + ASSERT_NE(it, ents.end()); // check func_wall entity from addition map is included it = std::find_if( ents.begin(), ents.end(), [](const entdict_t &dict) -> bool { return dict.get("classname") == "func_wall"; }); - REQUIRE(it != ents.end()); + ASSERT_NE(it, ents.end()); } /** * Tests that hollow obj2map style geometry (tetrahedrons) get filled in, in all hulls. */ -TEST_CASE("q1_rocks" * doctest::test_suite("testmaps_q1") * doctest::may_fail()) +TEST(testmapsQ1, rocks) { + GTEST_SKIP(); + constexpr auto *q1_rocks_structural_cube = "q1_rocks_structural_cube.map"; const auto mapnames = { @@ -1600,40 +1601,39 @@ TEST_CASE("q1_rocks" * doctest::test_suite("testmaps_q1") * doctest::may_fail()) q1_rocks_structural_cube // simpler version where the mountain is just a cube }; for (auto *mapname : mapnames) { - SUBCASE(mapname) - { - const auto [bsp, bspx, prt] = LoadTestmapQ1(mapname); + SCOPED_TRACE(mapname); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + const auto [bsp, bspx, prt] = LoadTestmapQ1(mapname); - const qvec3d point{48, 320, 88}; + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], point)); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], point)); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], point)); + const qvec3d point{48, 320, 88}; - for (int i = 1; i <= 2; ++i) { - INFO("hull " << i); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], point)); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], point)); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 2, &bsp.dmodels[0], point)); - const auto clipnodes = CountClipnodeLeafsByContentType(bsp, i); + for (int i = 1; i <= 2; ++i) { + SCOPED_TRACE(fmt::format("hull {}", i)); - REQUIRE(clipnodes.size() == 2); - REQUIRE(clipnodes.find(CONTENTS_SOLID) != clipnodes.end()); - REQUIRE(clipnodes.find(CONTENTS_EMPTY) != clipnodes.end()); + const auto clipnodes = CountClipnodeLeafsByContentType(bsp, i); - // 6 for the walls of the box, and 1 for the rock structure, which is convex - CHECK(clipnodes.at(CONTENTS_SOLID) == 7); + ASSERT_EQ(clipnodes.size(), 2); + ASSERT_NE(clipnodes.find(CONTENTS_SOLID), clipnodes.end()); + ASSERT_NE(clipnodes.find(CONTENTS_EMPTY), clipnodes.end()); - if (std::string(q1_rocks_structural_cube) == mapname) { - CHECK((5 + 6) == CountClipnodeNodes(bsp, i)); - } - } + // 6 for the walls of the box, and 1 for the rock structure, which is convex + EXPECT_EQ(clipnodes.at(CONTENTS_SOLID), 7); - // for completion's sake, check the nodes if (std::string(q1_rocks_structural_cube) == mapname) { - CHECK((5 + 6) == bsp.dnodes.size()); + EXPECT_EQ((5 + 6), CountClipnodeNodes(bsp, i)); } } + + // for completion's sake, check the nodes + if (std::string(q1_rocks_structural_cube) == mapname) { + EXPECT_EQ((5 + 6), bsp.dnodes.size()); + } } } @@ -1686,23 +1686,25 @@ int CountClipnodeNodes(const mbsp_t &bsp, int hullnum) /** * Tests a bad hull expansion */ -TEST_CASE("q1_hull_expansion_lip" * doctest::test_suite("testmaps_q1") * doctest::may_fail()) +TEST(testmapsQ1, hullExpansionLip) { + GTEST_SKIP(); + const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_hull_expansion_lip.map"); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); const qvec3d point{174, 308, 42}; - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], point)); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], point)); for (int i = 1; i <= 2; ++i) { - INFO("hull " << i); + SCOPED_TRACE(fmt::format("hull {}", i)); const auto clipnodes = CountClipnodeLeafsByContentType(bsp, i); - REQUIRE(clipnodes.size() == 2); - REQUIRE(clipnodes.find(CONTENTS_SOLID) != clipnodes.end()); - REQUIRE(clipnodes.find(CONTENTS_EMPTY) != clipnodes.end()); + ASSERT_EQ(clipnodes.size(), 2); + ASSERT_NE(clipnodes.find(CONTENTS_SOLID), clipnodes.end()); + ASSERT_NE(clipnodes.find(CONTENTS_EMPTY), clipnodes.end()); // room shaped like: // @@ -1711,19 +1713,19 @@ TEST_CASE("q1_hull_expansion_lip" * doctest::test_suite("testmaps_q1") * doctest // |______| // // 6 solid leafs for the walls/floor, 3 for the empty regions inside - CHECK(clipnodes.at(CONTENTS_SOLID) == 6); - CHECK(clipnodes.at(CONTENTS_EMPTY) == 3); + EXPECT_EQ(clipnodes.at(CONTENTS_SOLID), 6); + EXPECT_EQ(clipnodes.at(CONTENTS_EMPTY), 3); // 6 walls + 2 floors - CHECK(CountClipnodeNodes(bsp, i) == 8); + EXPECT_EQ(CountClipnodeNodes(bsp, i), 8); } } -TEST_CASE("q1_hull1_content_types" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, hull1ContentTypes) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_hull1_content_types.map"); - CHECK(GAME_QUAKE == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE, bsp.loadversion->game->id); enum leaf { @@ -1760,26 +1762,26 @@ TEST_CASE("q1_hull1_content_types" * doctest::test_suite("testmaps_q1")) for (const auto &[point, expected_types] : expected) { std::string message = qv::to_string(point); - CAPTURE(message); + SCOPED_TRACE(message); // hull 0 auto *hull0_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], point); - CHECK(expected_types.hull0_contenttype == hull0_leaf->contents); + EXPECT_EQ(expected_types.hull0_contenttype, hull0_leaf->contents); ptrdiff_t hull0_leaf_index = hull0_leaf - &bsp.dleafs[0]; if (expected_types.hull0_leaf == shared_leaf_0) { - CHECK(hull0_leaf_index == 0); + EXPECT_EQ(hull0_leaf_index, 0); } else { - CHECK(hull0_leaf_index != 0); + EXPECT_NE(hull0_leaf_index, 0); } // hull 1 - CHECK(expected_types.hull1_contenttype == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], point)); + EXPECT_EQ(expected_types.hull1_contenttype, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], point)); } } -TEST_CASE("BrushFromBounds") +TEST(qbsp, BrushFromBounds) { map.reset(); qbsp_options.reset(); @@ -1787,7 +1789,7 @@ TEST_CASE("BrushFromBounds") auto brush = BrushFromBounds({{2, 2, 2}, {32, 32, 32}}); - CHECK(brush->sides.size() == 6); + EXPECT_EQ(brush->sides.size(), 6); const auto top_winding = winding_t{{2, 2, 32}, {2, 32, 32}, {32, 32, 32}, {32, 2, 32}}; const auto bottom_winding = winding_t{{32, 2, 2}, {32, 32, 2}, {2, 32, 2}, {2, 2, 2}}; @@ -1795,55 +1797,57 @@ TEST_CASE("BrushFromBounds") int found = 0; for (auto &side : brush->sides) { - CHECK(side.w); + EXPECT_TRUE(side.w); if (side.w.directional_equal(top_winding)) { found++; auto &plane = side.get_plane(); - CHECK(plane.get_normal() == qvec3d{0, 0, 1}); - CHECK(plane.get_dist() == 32); + EXPECT_EQ(plane.get_normal(), qvec3d(0, 0, 1)); + EXPECT_EQ(plane.get_dist(), 32); } if (side.w.directional_equal(bottom_winding)) { found++; auto plane = side.get_plane(); - CHECK(plane.get_normal() == qvec3d{0, 0, -1}); - CHECK(plane.get_dist() == -2); + EXPECT_EQ(plane.get_normal(), qvec3d(0, 0, -1)); + EXPECT_EQ(plane.get_dist(), -2); } } - CHECK(found == 2); + EXPECT_EQ(found, 2); } // FIXME: failing because water tjuncs with walls -TEST_CASE("q1_water_subdivision with lit water off" * doctest::may_fail()) +TEST(qbspQ1, waterSubdivisionWithLitWaterOff) { - INFO("-litwater 0 should suppress water subdivision"); + GTEST_SKIP(); + + SCOPED_TRACE("-litwater 0 should suppress water subdivision"); const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_water_subdivision.map", {"-litwater", "0"}); auto faces = FacesWithTextureName(bsp, "*swater5"); - CHECK(2 == faces.size()); + EXPECT_EQ(2, faces.size()); for (auto *face : faces) { auto *texinfo = BSP_GetTexinfo(&bsp, face->texinfo); - CHECK(texinfo->flags.native == TEX_SPECIAL); + EXPECT_EQ(texinfo->flags.native, TEX_SPECIAL); } } -TEST_CASE("q1_water_subdivision with defaults") +TEST(qbspQ1, waterSubdivisionWithDefaults) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_water_subdivision.map"); auto faces = FacesWithTextureName(bsp, "*swater5"); - CHECK(faces.size() > 2); + EXPECT_GT(faces.size(), 2); for (auto *face : faces) { auto *texinfo = BSP_GetTexinfo(&bsp, face->texinfo); - CHECK(texinfo->flags.native == 0); + EXPECT_EQ(texinfo->flags.native, 0); } } -TEST_CASE("textures search relative to current directory") +TEST(qbspQ1, texturesSearchRelativeToCurrentDirectory) { // QuArK runs the compilers like this: // @@ -1863,74 +1867,76 @@ 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()); + ASSERT_EQ(2, bsp.dtex.textures.size()); // FIXME: we shouldn't really be writing skip - CHECK("" == bsp.dtex.textures[0].name); + EXPECT_EQ("", bsp.dtex.textures[0].name); // make sure the texture was written - CHECK("orangestuff8" == bsp.dtex.textures[1].name); - CHECK(64 == bsp.dtex.textures[1].width); - CHECK(64 == bsp.dtex.textures[1].height); - CHECK(bsp.dtex.textures[1].data.size() > 0); + EXPECT_EQ("orangestuff8", bsp.dtex.textures[1].name); + EXPECT_EQ(64, bsp.dtex.textures[1].width); + EXPECT_EQ(64, bsp.dtex.textures[1].height); + EXPECT_GT(bsp.dtex.textures[1].data.size(), 0); } // specifically designed to break the old isHexen2() // (has 0 faces, and model lump size is divisible by both Q1 and H2 model struct size) -TEST_CASE("q1_skip_only") +TEST(qbspQ1, skipOnly) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_skip_only.map"); - CHECK(bsp.loadversion == &bspver_q1); - CHECK(0 == bsp.dfaces.size()); + EXPECT_EQ(bsp.loadversion, &bspver_q1); + EXPECT_EQ(0, bsp.dfaces.size()); } // specifically designed to break the old isHexen2() // (has 0 faces, and model lump size is divisible by both Q1 and H2 model struct size) -TEST_CASE("h2_skip_only") +TEST(qbspH2, skipOnly) { const auto [bsp, bspx, prt] = LoadTestmap("h2_skip_only.map", {"-hexen2"}); - CHECK(bsp.loadversion == &bspver_h2); - CHECK(0 == bsp.dfaces.size()); + EXPECT_EQ(bsp.loadversion, &bspver_h2); + EXPECT_EQ(0, bsp.dfaces.size()); } -TEST_CASE("q1_hull1_fail" * doctest::may_fail()) +TEST(qbspQ1, hull1Fail) { - INFO("weird example of a phantom clip brush in hull1"); + GTEST_SKIP(); + + SCOPED_TRACE("weird example of a phantom clip brush in hull1"); const auto [bsp, bspx, prt] = LoadTestmap("q1_hull1_fail.map"); { - INFO("contents at info_player_start"); - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], qvec3d{-2256, -64, 264})); + SCOPED_TRACE("contents at info_player_start"); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], qvec3d{-2256, -64, 264})); } { - INFO("contents at air_bubbles"); - CHECK(CONTENTS_EMPTY == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], qvec3d{-2164, 126, 260})); + SCOPED_TRACE("contents at air_bubbles"); + EXPECT_EQ(CONTENTS_EMPTY, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], qvec3d{-2164, 126, 260})); } { - INFO("contents in void"); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], qvec3d{0, 0, 0})); - CHECK(CONTENTS_SOLID == BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], qvec3d{0, 0, 0})); + SCOPED_TRACE("contents in void"); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 0, &bsp.dmodels[0], qvec3d{0, 0, 0})); + EXPECT_EQ(CONTENTS_SOLID, BSP_FindContentsAtPoint(&bsp, 1, &bsp.dmodels[0], qvec3d{0, 0, 0})); } } -TEST_CASE("q1_sky_window") +TEST(qbspQ1, skyWindow) { - INFO("faces partially covered by sky were getting wrongly merged and deleted"); + SCOPED_TRACE("faces partially covered by sky were getting wrongly merged and deleted"); const auto [bsp, bspx, prt] = LoadTestmap("q1_sky_window.map"); { - INFO("faces around window"); - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-184, -252, -32))); // bottom - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-184, -252, 160))); // top - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-184, -288, 60))); // left - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-184, -224, 60))); // right + SCOPED_TRACE("faces around window"); + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-184, -252, -32))); // bottom + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-184, -252, 160))); // top + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-184, -288, 60))); // left + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-184, -224, 60))); // right } } -TEST_CASE("q1_liquid_software") +TEST(qbspQ1, liquidSoftware) { - INFO("map with just 1 liquid brush + a 'skip' platform, has render corruption on tyrquake"); + SCOPED_TRACE("map with just 1 liquid brush + a 'skip' platform, has render corruption on tyrquake"); const auto [bsp, bspx, prt] = LoadTestmap("q1_liquid_software.map"); const qvec3d top_face_point{-56, -56, 8}; @@ -1942,10 +1948,10 @@ TEST_CASE("q1_liquid_software") auto *side = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], side_face_point, {0, -1, 0}); auto *side_inwater = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], side_face_point, {0, 1, 0}); - REQUIRE(top); - REQUIRE(top_inwater); - REQUIRE(side); - REQUIRE(side_inwater); + ASSERT_TRUE(top); + ASSERT_TRUE(top_inwater); + ASSERT_TRUE(side); + ASSERT_TRUE(side_inwater); // gather edge set used in and out of water. // recall that if edge 5 is from vert 12 to vert 13, @@ -1976,131 +1982,131 @@ TEST_CASE("q1_liquid_software") add_face_edges_to_set(bsp, *top_inwater, inwater_undirected_edges); add_face_edges_to_set(bsp, *side_inwater, inwater_undirected_edges); - CHECK(7 == outwater_undirected_edges.size()); - CHECK(7 == inwater_undirected_edges.size()); + EXPECT_EQ(7, outwater_undirected_edges.size()); + EXPECT_EQ(7, inwater_undirected_edges.size()); // make sure there's no reuse between out-of-water and in-water for (int e : outwater_undirected_edges) { - CHECK(inwater_undirected_edges.find(e) == inwater_undirected_edges.end()); + EXPECT_EQ(inwater_undirected_edges.find(e), inwater_undirected_edges.end()); } } -TEST_CASE("q1_missing_texture") +TEST(qbspQ1, missingTexture) { const auto [bsp, bspx, prt] = LoadTestmap("q1_missing_texture.map"); - REQUIRE(2 == bsp.dtex.textures.size()); + ASSERT_EQ(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); + EXPECT_EQ("skip", bsp.dtex.textures[0].name); + EXPECT_FALSE(bsp.dtex.textures[0].null_texture); + EXPECT_EQ(64, bsp.dtex.textures[0].width); + EXPECT_EQ(64, bsp.dtex.textures[0].height); - CHECK("" == bsp.dtex.textures[1].name); - CHECK(bsp.dtex.textures[1].null_texture); + EXPECT_EQ("", bsp.dtex.textures[1].name); + EXPECT_TRUE(bsp.dtex.textures[1].null_texture); - CHECK(6 == bsp.dfaces.size()); + EXPECT_EQ(6, bsp.dfaces.size()); } -TEST_CASE("q1_missing_texture, -missing_textures_as_zero_size") +TEST(qbspQ1, missingTextureAndMissingTexturesAsZeroSize) { const auto [bsp, bspx, prt] = LoadTestmap("q1_missing_texture.map", {"-missing_textures_as_zero_size"}); - REQUIRE(2 == bsp.dtex.textures.size()); + ASSERT_EQ(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); + EXPECT_EQ("skip", bsp.dtex.textures[0].name); + EXPECT_FALSE(bsp.dtex.textures[0].null_texture); + EXPECT_EQ(64, bsp.dtex.textures[0].width); + EXPECT_EQ(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); + EXPECT_EQ("somemissingtext", bsp.dtex.textures[1].name); + EXPECT_FALSE(bsp.dtex.textures[1].null_texture); + EXPECT_EQ(0, bsp.dtex.textures[1].width); + EXPECT_EQ(0, bsp.dtex.textures[1].height); - CHECK(6 == bsp.dfaces.size()); + EXPECT_EQ(6, bsp.dfaces.size()); } -TEST_CASE("q1 notex") +TEST(qbspQ1, notex) { const auto [bsp, bspx, prt] = LoadTestmap("q1_cube.map", {"-notex"}); - REQUIRE(2 == bsp.dtex.textures.size()); + ASSERT_EQ(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.) auto &t0 = bsp.dtex.textures[0]; - CHECK("skip" == t0.name); - CHECK(!t0.null_texture); - CHECK(64 == t0.width); - CHECK(64 == t0.height); - CHECK(t0.data.size() == sizeof(dmiptex_t)); + EXPECT_EQ("skip", t0.name); + EXPECT_FALSE(t0.null_texture); + EXPECT_EQ(64, t0.width); + EXPECT_EQ(64, t0.height); + EXPECT_EQ(t0.data.size(), sizeof(dmiptex_t)); for (int i = 0; i < 4; ++i) - CHECK(t0.offsets[i] == 0); + EXPECT_EQ(t0.offsets[i], 0); } { auto &t1 = bsp.dtex.textures[1]; - CHECK("orangestuff8" == t1.name); - CHECK(!t1.null_texture); - CHECK(64 == t1.width); - CHECK(64 == t1.height); - CHECK(t1.data.size() == sizeof(dmiptex_t)); + EXPECT_EQ("orangestuff8", t1.name); + EXPECT_FALSE(t1.null_texture); + EXPECT_EQ(64, t1.width); + EXPECT_EQ(64, t1.height); + EXPECT_EQ(t1.data.size(), sizeof(dmiptex_t)); for (int i = 0; i < 4; ++i) - CHECK(t1.offsets[i] == 0); + EXPECT_EQ(t1.offsets[i], 0); } } -TEST_CASE("hl_basic") +TEST(qbspHL, basic) { const auto [bsp, bspx, prt] = LoadTestmap("hl_basic.map", {"-hlbsp"}); - CHECK(prt); + EXPECT_TRUE(prt); - REQUIRE(2 == bsp.dtex.textures.size()); + ASSERT_EQ(2, bsp.dtex.textures.size()); // FIXME: we shouldn't really be writing skip - CHECK(bsp.dtex.textures[0].null_texture); + EXPECT_TRUE(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); + EXPECT_EQ("hltest", bsp.dtex.textures[1].name); + EXPECT_FALSE(bsp.dtex.textures[1].null_texture); + EXPECT_EQ(64, bsp.dtex.textures[1].width); + EXPECT_EQ(64, bsp.dtex.textures[1].height); } -TEST_CASE("wrbrushes + misc_external_map") +TEST(qbspQ1, wrbrushesAndMiscExternalMap) { const auto [bsp, bspx, prt] = LoadTestmap("q1_external_map_base.map", {"-wrbrushes"}); bspxbrushes lump = deserialize(bspx.at("BRUSHLIST")); - REQUIRE(lump.models.size() == 1); + ASSERT_EQ(lump.models.size(), 1); auto &model = lump.models.at(0); - REQUIRE(model.brushes.size() == 1); + ASSERT_EQ(model.brushes.size(), 1); auto &brush = model.brushes.at(0); - REQUIRE(brush.bounds.maxs() == qvec3f{64,64,16}); - REQUIRE(brush.bounds.mins() == qvec3f{-64,-64,-16}); + ASSERT_EQ(brush.bounds.maxs(), qvec3f(64,64,16)); + ASSERT_EQ(brush.bounds.mins(), qvec3f(-64,-64,-16)); } -TEST_CASE("wrbrushes content types") +TEST(qbspQ1, wrbrushesContentTypes) { const auto [bsp, bspx, prt] = LoadTestmap("q1_hull1_content_types.map", {"-wrbrushes"}); const bspxbrushes lump = deserialize(bspx.at("BRUSHLIST")); - REQUIRE(lump.models.size() == 1); + ASSERT_EQ(lump.models.size(), 1); auto &model = lump.models.at(0); - REQUIRE(model.numfaces == 0); // all faces are axial - REQUIRE(model.modelnum == 0); + ASSERT_EQ(model.numfaces, 0); // all faces are axial + ASSERT_EQ(model.modelnum, 0); const std::vector expected { CONTENTS_SOLID, @@ -2122,15 +2128,15 @@ TEST_CASE("wrbrushes content types") // detail illusionary brush should be omitted CONTENTS_SOLID // detail wall in source map }; - REQUIRE(model.brushes.size() == expected.size()); + ASSERT_EQ(model.brushes.size(), expected.size()); for (size_t i = 0; i < expected.size(); ++i) { - INFO("brush ", i); - CHECK(expected[i] == model.brushes[i].contents); + SCOPED_TRACE(fmt::format("brush {}", i)); + EXPECT_EQ(expected[i], model.brushes[i].contents); } } -TEST_CASE("read bspx brushes") +TEST(qbsp, readBspxBrushes) { auto bsp_path = std::filesystem::path(testmaps_dir) / "compiled" / "q1_cube.bsp"; @@ -2140,33 +2146,35 @@ TEST_CASE("read bspx brushes") ConvertBSPFormat(&bspdata, &bspver_generic); const bspxbrushes lump = deserialize(bspdata.bspx.entries.at("BRUSHLIST")); - REQUIRE(lump.models.size() == 1); + ASSERT_EQ(lump.models.size(), 1); - CHECK(lump.models[0].modelnum == 0); - CHECK(lump.models[0].numfaces == 0); - CHECK(lump.models[0].ver == 1); - REQUIRE(lump.models[0].brushes.size() == 1); + EXPECT_EQ(lump.models[0].modelnum, 0); + EXPECT_EQ(lump.models[0].numfaces, 0); + EXPECT_EQ(lump.models[0].ver, 1); + ASSERT_EQ(lump.models[0].brushes.size(), 1); auto &brush = lump.models[0].brushes[0]; - CHECK(brush.bounds == aabb3f{qvec3f{32, -240, 80}, qvec3f{80, -144, 112}}); - CHECK(brush.contents == CONTENTS_SOLID); - CHECK(brush.faces.size() == 0); + EXPECT_EQ(brush.bounds, aabb3f(qvec3f{32, -240, 80}, qvec3f{80, -144, 112})); + EXPECT_EQ(brush.contents, CONTENTS_SOLID); + EXPECT_EQ(brush.faces.size(), 0); } -TEST_CASE("lq e3m4.map" * doctest::may_fail()) +TEST(qbspQ1, lqE3m4map) { + GTEST_SKIP(); + const auto [bsp, bspx, prt] = LoadTestmap("LibreQuake/lq1/maps/src/e3/e3m4.map"); - CHECK(prt); + EXPECT_TRUE(prt); } -TEST_CASE("q1_tjunc_matrix") +TEST(qbspQ1, tjuncMatrix) { // TODO: test opaque water in q1 mode const auto [b, bspx, prt] = LoadTestmap("q1_tjunc_matrix.map"); const mbsp_t &bsp = b; // workaround clang not allowing capturing bindings in lambdas auto *game = bsp.loadversion->game; - CHECK(GAME_QUAKE == game->id); + EXPECT_EQ(GAME_QUAKE, game->id); const qvec3d face_midpoint_origin {-24, 0, 24}; const qvec3d face_midpoint_to_tjunc {8, 0, 8}; @@ -2203,192 +2211,196 @@ TEST_CASE("q1_tjunc_matrix") }; { - INFO("INDEX_SOLID horizontal - welds with anything opaque except detail_wall"); - CHECK( has_tjunc(INDEX_SOLID, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_SOLID, INDEX_SOLID_DETAIL)); - CHECK(!has_tjunc(INDEX_SOLID, INDEX_DETAIL_WALL)); - CHECK(!has_tjunc(INDEX_SOLID, INDEX_DETAIL_FENCE)); - CHECK(!has_tjunc(INDEX_SOLID, INDEX_DETAIL_FENCE_MIRRORINSIDE)); - CHECK(!has_tjunc(INDEX_SOLID, INDEX_DETAIL_ILLUSIONARY)); - CHECK(!has_tjunc(INDEX_SOLID, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); - CHECK(!has_tjunc(INDEX_SOLID, INDEX_WATER)); - CHECK( has_tjunc(INDEX_SOLID, INDEX_SKY)); + SCOPED_TRACE("INDEX_SOLID horizontal - welds with anything opaque except detail_wall"); + EXPECT_TRUE( has_tjunc(INDEX_SOLID, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID, INDEX_SOLID_DETAIL)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID, INDEX_DETAIL_WALL)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID, INDEX_DETAIL_FENCE)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID, INDEX_DETAIL_FENCE_MIRRORINSIDE)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID, INDEX_DETAIL_ILLUSIONARY)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID, INDEX_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID, INDEX_SKY)); } { - INFO("INDEX_SOLID_DETAIL horizontal - welds with anything opaque except detail_wall"); - CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SOLID_DETAIL)); - CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_WALL)); - CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_FENCE)); - CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_FENCE_MIRRORINSIDE)); - CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_ILLUSIONARY)); - CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); + SCOPED_TRACE("INDEX_SOLID_DETAIL horizontal - welds with anything opaque except detail_wall"); + EXPECT_TRUE( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SOLID_DETAIL)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_WALL)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_FENCE)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_FENCE_MIRRORINSIDE)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_ILLUSIONARY)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); // see INDEX_SOLID, INDEX_WATER explanation - CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_WATER)); - CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SKY)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID_DETAIL, INDEX_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SKY)); } { - INFO("INDEX_DETAIL_WALL horizontal"); + SCOPED_TRACE("INDEX_DETAIL_WALL horizontal"); // solid cuts a hole in detail_wall - CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID)); // solid detail cuts a hole in detail_wall - CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID_DETAIL)); - CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_WALL)); - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_FENCE)); - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_FENCE_MIRRORINSIDE)); - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_ILLUSIONARY)); - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_WALL)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_FENCE)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_FENCE_MIRRORINSIDE)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_ILLUSIONARY)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); // see INDEX_SOLID, INDEX_WATER explanation - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_WATER)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_WATER)); // sky cuts a hole in detail_wall - CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_WALL, INDEX_SKY)); } { - INFO("INDEX_DETAIL_FENCE horizontal"); + SCOPED_TRACE("INDEX_DETAIL_FENCE horizontal"); // solid cuts a hole in fence - CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE, INDEX_SOLID)); // solid detail cuts a hole in fence - CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE, INDEX_SOLID_DETAIL)); // detail wall cuts a hole in fence - CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_FENCE)); - CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_FENCE_MIRRORINSIDE)); - CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_ILLUSIONARY)); - CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_FENCE)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_FENCE_MIRRORINSIDE)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_ILLUSIONARY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); // weld because both are translucent - CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE, INDEX_WATER)); // sky cuts a hole in fence - CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE, INDEX_SKY)); } { - INFO("INDEX_DETAIL_FENCE_MIRRORINSIDE horizontal"); + SCOPED_TRACE("INDEX_DETAIL_FENCE_MIRRORINSIDE horizontal"); // solid cuts a hole in fence - CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_SOLID)); // solid detail cuts a hole in fence - CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_SOLID_DETAIL)); // detail wall cuts a hole in fence - CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_FENCE)); - CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_FENCE_MIRRORINSIDE)); - CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_ILLUSIONARY)); - CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_FENCE)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_FENCE_MIRRORINSIDE)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_ILLUSIONARY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); // weld because both are translucent - CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_WATER)); // sky cuts a hole in fence - CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_SKY)); } { - INFO("INDEX_DETAIL_ILLUSIONARY horizontal"); + SCOPED_TRACE("INDEX_DETAIL_ILLUSIONARY horizontal"); // solid cuts a hole in illusionary - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_SOLID)); // solid detail cuts a hole in illusionary - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_SOLID_DETAIL)); // detail wall cuts a hole in illusionary - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_WALL)); // fence and illusionary are both translucent, so weld - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_FENCE)); - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_FENCE_MIRRORINSIDE)); - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_ILLUSIONARY)); - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_FENCE)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_FENCE_MIRRORINSIDE)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_ILLUSIONARY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); // weld because both are translucent - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_WATER)); // sky cuts a hole in illusionary - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_SKY)); } { - INFO("INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES horizontal"); + SCOPED_TRACE("INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES horizontal"); // solid cuts a hole in illusionary - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_SOLID)); // solid detail cuts a hole in illusionary - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_SOLID_DETAIL)); // detail wall cuts a hole in illusionary - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_WALL)); // fence and illusionary are both translucent, so weld - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_FENCE)); - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_FENCE_MIRRORINSIDE)); - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_ILLUSIONARY)); - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_FENCE)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_FENCE_MIRRORINSIDE)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_ILLUSIONARY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); // weld because both are translucent - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_WATER)); // sky cuts a hole in illusionary - CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_SKY)); } { - INFO("INDEX_WATER horizontal"); + SCOPED_TRACE("INDEX_WATER horizontal"); // solid cuts a hole in water - CHECK( has_tjunc(INDEX_WATER, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_WATER, INDEX_SOLID)); // solid detail cuts a hole in illusionary - CHECK( has_tjunc(INDEX_WATER, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_WATER, INDEX_SOLID_DETAIL)); // detail wall cuts a hole in water - CHECK( has_tjunc(INDEX_WATER, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_WATER, INDEX_DETAIL_FENCE)); - CHECK( has_tjunc(INDEX_WATER, INDEX_DETAIL_FENCE_MIRRORINSIDE)); - CHECK( has_tjunc(INDEX_WATER, INDEX_DETAIL_ILLUSIONARY)); - CHECK( has_tjunc(INDEX_WATER, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); - CHECK( has_tjunc(INDEX_WATER, INDEX_WATER)); - CHECK( has_tjunc(INDEX_WATER, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_WATER, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_WATER, INDEX_DETAIL_FENCE)); + EXPECT_TRUE( has_tjunc(INDEX_WATER, INDEX_DETAIL_FENCE_MIRRORINSIDE)); + EXPECT_TRUE( has_tjunc(INDEX_WATER, INDEX_DETAIL_ILLUSIONARY)); + EXPECT_TRUE( has_tjunc(INDEX_WATER, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); + EXPECT_TRUE( has_tjunc(INDEX_WATER, INDEX_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_WATER, INDEX_SKY)); } { - INFO("INDEX_SKY horizontal"); - CHECK( has_tjunc(INDEX_SKY, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_SKY, INDEX_SOLID_DETAIL)); - CHECK(!has_tjunc(INDEX_SKY, INDEX_DETAIL_WALL)); - CHECK(!has_tjunc(INDEX_SKY, INDEX_DETAIL_FENCE)); - CHECK(!has_tjunc(INDEX_SKY, INDEX_DETAIL_FENCE_MIRRORINSIDE)); - CHECK(!has_tjunc(INDEX_SKY, INDEX_DETAIL_ILLUSIONARY)); - CHECK(!has_tjunc(INDEX_SKY, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); - CHECK(!has_tjunc(INDEX_SKY, INDEX_WATER)); - CHECK( has_tjunc(INDEX_SKY, INDEX_SKY)); + SCOPED_TRACE("INDEX_SKY horizontal"); + EXPECT_TRUE( has_tjunc(INDEX_SKY, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_SKY, INDEX_SOLID_DETAIL)); + EXPECT_FALSE(has_tjunc(INDEX_SKY, INDEX_DETAIL_WALL)); + EXPECT_FALSE(has_tjunc(INDEX_SKY, INDEX_DETAIL_FENCE)); + EXPECT_FALSE(has_tjunc(INDEX_SKY, INDEX_DETAIL_FENCE_MIRRORINSIDE)); + EXPECT_FALSE(has_tjunc(INDEX_SKY, INDEX_DETAIL_ILLUSIONARY)); + EXPECT_FALSE(has_tjunc(INDEX_SKY, INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES)); + EXPECT_FALSE(has_tjunc(INDEX_SKY, INDEX_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_SKY, INDEX_SKY)); } } -TEST_CASE("q1_liquid_is_detail" * doctest::test_suite("testmaps_q1")) +TEST(testmapsQ1, liquidIsDetail) { const auto portal_underwater = prtfile_winding_t{{-168, -384, 32}, {-168, -320, 32}, {-168, -320, -32}, {-168, -384, -32}}; const auto portal_above = portal_underwater.translate({0, 320, 128}); - SUBCASE("transparent water") { + { + SCOPED_TRACE("transparent water"); + // by default, we're compiling with transparent water // this implies water is detail const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_liquid_is_detail.map"); - REQUIRE(prt.has_value()); - REQUIRE(2 == prt->portals.size()); + ASSERT_TRUE(prt.has_value()); + ASSERT_EQ(2, prt->portals.size()); - CHECK(((PortalMatcher(prt->portals[0].winding, portal_underwater) && PortalMatcher(prt->portals[1].winding, portal_above)) || + EXPECT_TRUE(((PortalMatcher(prt->portals[0].winding, portal_underwater) && PortalMatcher(prt->portals[1].winding, portal_above)) || (PortalMatcher(prt->portals[0].winding, portal_above) && PortalMatcher(prt->portals[1].winding, portal_underwater)))); // only 3 clusters: room with water, side corridors - CHECK(prt->portalleafs == 3); + EXPECT_EQ(prt->portalleafs, 3); // above water, in water, plus 2 side rooms. // note - CHECK(prt->portalleafs_real == 4); + EXPECT_EQ(prt->portalleafs_real, 4); } - SUBCASE("opaque water") { + { + SCOPED_TRACE("opaque water"); + const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_liquid_is_detail.map", {"-notranswater"}); - REQUIRE(prt.has_value()); - REQUIRE(2 == prt->portals.size()); + ASSERT_TRUE(prt.has_value()); + ASSERT_EQ(2, prt->portals.size()); // same portals as transparent water case // (since the water is opqaue, it doesn't get a portal) - CHECK(((PortalMatcher(prt->portals[0].winding, portal_underwater) && PortalMatcher(prt->portals[1].winding, portal_above)) || + EXPECT_TRUE(((PortalMatcher(prt->portals[0].winding, portal_underwater) && PortalMatcher(prt->portals[1].winding, portal_above)) || (PortalMatcher(prt->portals[0].winding, portal_above) && PortalMatcher(prt->portals[1].winding, portal_underwater)))); // 4 clusters this time: // above water, in water, plus 2 side rooms. - CHECK(prt->portalleafs == 4); - CHECK(prt->portalleafs_real == 4); + EXPECT_EQ(prt->portalleafs, 4); + EXPECT_EQ(prt->portalleafs_real, 4); } } diff --git a/tests/test_qbsp_q2.cc b/tests/test_qbsp_q2.cc index f19192f8..13543702 100644 --- a/tests/test_qbsp_q2.cc +++ b/tests/test_qbsp_q2.cc @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -11,31 +13,31 @@ #include "test_qbsp.hh" #include "testutils.hh" -TEST_CASE("detail" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, detail) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_detail.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); // stats - CHECK(1 == bsp.dmodels.size()); + EXPECT_EQ(1, bsp.dmodels.size()); // Q2 reserves leaf 0 as an invalid leaf const auto &leaf0 = bsp.dleafs[0]; - CHECK(Q2_CONTENTS_SOLID == leaf0.contents); - CHECK(-1 == leaf0.visofs); - CHECK(qvec3f{} == leaf0.mins); - CHECK(qvec3f{} == leaf0.maxs); - CHECK(0 == leaf0.firstmarksurface); - CHECK(0 == leaf0.nummarksurfaces); - CHECK(leaf0.ambient_level == std::array{0, 0, 0, 0}); - CHECK(CLUSTER_INVALID == leaf0.cluster); - CHECK(AREA_INVALID == leaf0.area); - CHECK(0 == leaf0.firstleafbrush); - CHECK(0 == leaf0.numleafbrushes); + EXPECT_EQ(Q2_CONTENTS_SOLID, leaf0.contents); + EXPECT_EQ(-1, leaf0.visofs); + EXPECT_EQ(qvec3f{}, leaf0.mins); + EXPECT_EQ(qvec3f{}, leaf0.maxs); + EXPECT_EQ(0, leaf0.firstmarksurface); + EXPECT_EQ(0, leaf0.nummarksurfaces); + EXPECT_EQ(leaf0.ambient_level, (std::array{0, 0, 0, 0})); + EXPECT_EQ(CLUSTER_INVALID, leaf0.cluster); + EXPECT_EQ(AREA_INVALID, leaf0.area); + EXPECT_EQ(0, leaf0.firstleafbrush); + EXPECT_EQ(0, leaf0.numleafbrushes); // no areaportals except the placeholder - CHECK(1 == bsp.dareaportals.size()); - CHECK(2 == bsp.dareas.size()); + EXPECT_EQ(1, bsp.dareaportals.size()); + EXPECT_EQ(2, bsp.dareas.size()); // leafs: // 6 solid leafs outside the room (* can be more depending on when the "divider" is cut) @@ -48,12 +50,12 @@ TEST_CASE("detail" * doctest::test_suite("testmaps_q2")) for (size_t i = 1; i < bsp.dleafs.size(); ++i) { ++counts_by_contents[bsp.dleafs[i].contents]; } - CHECK(3 == counts_by_contents.size()); // number of types + EXPECT_EQ(3, counts_by_contents.size()); // number of types - CHECK(1 == counts_by_contents.at(Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL)); // detail leafs - CHECK(8 == counts_by_contents.at(0)); // empty leafs - CHECK(counts_by_contents.at(Q2_CONTENTS_SOLID) >= 6); - CHECK(counts_by_contents.at(Q2_CONTENTS_SOLID) <= 12); + EXPECT_EQ(1, counts_by_contents.at(Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL)); // detail leafs + EXPECT_EQ(8, counts_by_contents.at(0)); // empty leafs + EXPECT_GE(counts_by_contents.at(Q2_CONTENTS_SOLID), 6); + EXPECT_LE(counts_by_contents.at(Q2_CONTENTS_SOLID), 12); // clusters: // 1 empty cluster filling the room above the divider @@ -67,7 +69,7 @@ TEST_CASE("detail" * doctest::test_suite("testmaps_q2")) clusters.insert(bsp.dleafs[i].cluster); } } - CHECK(4 == clusters.size()); + EXPECT_EQ(4, clusters.size()); // various points in the main room cluster const qvec3d under_button{246, 436, 96}; // directly on the main floor plane @@ -79,116 +81,116 @@ TEST_CASE("detail" * doctest::test_suite("testmaps_q2")) const qvec3d side_room{138, 576, 140}; // detail clips away world faces - CHECK(nullptr == BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], under_button, {0, 0, 1})); + EXPECT_EQ(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], under_button, {0, 0, 1})); // check for correct contents auto *detail_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], inside_button); - CHECK((Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL) == detail_leaf->contents); - CHECK(-1 == detail_leaf->cluster); - CHECK(0 == detail_leaf->area); // solid leafs get the invalid area 0 + EXPECT_EQ((Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL), detail_leaf->contents); + EXPECT_EQ(-1, detail_leaf->cluster); + EXPECT_EQ(0, detail_leaf->area); // solid leafs get the invalid area 0 // check for button (detail) brush - CHECK(1 == Leaf_Brushes(&bsp, detail_leaf).size()); - CHECK((Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL) == Leaf_Brushes(&bsp, detail_leaf).at(0)->contents); + EXPECT_EQ(1, Leaf_Brushes(&bsp, detail_leaf).size()); + EXPECT_EQ((Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL), Leaf_Brushes(&bsp, detail_leaf).at(0)->contents); // get more leafs auto *empty_leaf_above_button = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], above_button); - CHECK(0 == empty_leaf_above_button->contents); - CHECK(0 == Leaf_Brushes(&bsp, empty_leaf_above_button).size()); - CHECK(1 == empty_leaf_above_button->area); + EXPECT_EQ(0, empty_leaf_above_button->contents); + EXPECT_EQ(0, Leaf_Brushes(&bsp, empty_leaf_above_button).size()); + EXPECT_EQ(1, empty_leaf_above_button->area); auto *empty_leaf_side_room = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], side_room); - CHECK(0 == empty_leaf_side_room->contents); - CHECK(0 == Leaf_Brushes(&bsp, empty_leaf_side_room).size()); - CHECK(empty_leaf_side_room->cluster != empty_leaf_above_button->cluster); - CHECK(1 == empty_leaf_side_room->area); + EXPECT_EQ(0, empty_leaf_side_room->contents); + EXPECT_EQ(0, Leaf_Brushes(&bsp, empty_leaf_side_room).size()); + EXPECT_NE(empty_leaf_side_room->cluster, empty_leaf_above_button->cluster); + EXPECT_EQ(1, empty_leaf_side_room->area); auto *empty_leaf_beside_button = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], beside_button); - CHECK(0 == empty_leaf_beside_button->contents); - CHECK(-1 != empty_leaf_beside_button->cluster); - CHECK(empty_leaf_above_button->cluster == empty_leaf_beside_button->cluster); - CHECK(empty_leaf_above_button != empty_leaf_beside_button); - CHECK(1 == empty_leaf_beside_button->area); - - CHECK(prt->portals.size() == 5); - CHECK(prt->portalleafs_real == 0); // not used by Q2 - CHECK(prt->portalleafs == 4); + EXPECT_EQ(0, empty_leaf_beside_button->contents); + EXPECT_NE(-1, empty_leaf_beside_button->cluster); + EXPECT_EQ(empty_leaf_above_button->cluster, empty_leaf_beside_button->cluster); + EXPECT_NE(empty_leaf_above_button, empty_leaf_beside_button); + EXPECT_EQ(1, empty_leaf_beside_button->area); + + EXPECT_EQ(prt->portals.size(), 5); + EXPECT_EQ(prt->portalleafs_real, 0); // not used by Q2 + EXPECT_EQ(prt->portalleafs, 4); } -TEST_CASE("q2 detail with -nodetail" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, Q2DetailWithNodetail) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_detail.map", {"-nodetail"}); const qvec3d inside_button{246, 436, 98}; auto *inside_button_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], inside_button); - CHECK(Q2_CONTENTS_SOLID == inside_button_leaf->contents); + EXPECT_EQ(Q2_CONTENTS_SOLID, inside_button_leaf->contents); - CHECK(prt->portals.size() > 5); - CHECK(prt->portalleafs == 8); + EXPECT_GT(prt->portals.size(), 5); + EXPECT_EQ(prt->portalleafs, 8); } -TEST_CASE("q2 detail with -omitdetail" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, Q2DetailWithOmitdetail) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_detail.map", {"-omitdetail"}); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); const qvec3d inside_button{246, 436, 98}; const qvec3d above_button{246, 436, 120}; auto *inside_button_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], inside_button); - CHECK(Q2_CONTENTS_EMPTY == inside_button_leaf->contents); + EXPECT_EQ(Q2_CONTENTS_EMPTY, inside_button_leaf->contents); auto *above_button_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], above_button); - CHECK(inside_button_leaf == above_button_leaf); + EXPECT_EQ(inside_button_leaf, above_button_leaf); } -TEST_CASE("-omitdetail removing all brushes in a func" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, omitdetailRemovingAllBrushesInAFunc) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_omitdetail_in_func.map", {"-omitdetail"}); } -TEST_CASE("playerclip" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, playerclip) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_playerclip.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); const qvec3d in_playerclip{32, -136, 144}; auto *playerclip_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_playerclip); - CHECK((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_DETAIL) == playerclip_leaf->contents); + EXPECT_EQ((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_DETAIL), playerclip_leaf->contents); // make sure faces at these locations aren't clipped away const qvec3d floor_under_clip{32, -136, 96}; const qvec3d pillar_side_in_clip1{32, -48, 144}; const qvec3d pillar_side_in_clip2{32, -208, 144}; - CHECK(nullptr != BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], floor_under_clip, {0, 0, 1})); - CHECK(nullptr != BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], pillar_side_in_clip1, {0, -1, 0})); - CHECK(nullptr != BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], pillar_side_in_clip2, {0, 1, 0})); + EXPECT_NE(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], floor_under_clip, {0, 0, 1})); + EXPECT_NE(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], pillar_side_in_clip1, {0, -1, 0})); + EXPECT_NE(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], pillar_side_in_clip2, {0, 1, 0})); // make sure no face is generated for the playerclip brush const qvec3d playerclip_front_face{16, -152, 144}; - CHECK(nullptr == BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], playerclip_front_face, {-1, 0, 0})); + EXPECT_EQ(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], playerclip_front_face, {-1, 0, 0})); // check for brush - CHECK(1 == Leaf_Brushes(&bsp, playerclip_leaf).size()); - CHECK((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_DETAIL) == Leaf_Brushes(&bsp, playerclip_leaf).at(0)->contents); + EXPECT_EQ(1, Leaf_Brushes(&bsp, playerclip_leaf).size()); + EXPECT_EQ((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_DETAIL), Leaf_Brushes(&bsp, playerclip_leaf).at(0)->contents); } -TEST_CASE("areaportal" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, areaportal) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_areaportal.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); // area 0 is a placeholder // areaportal 0 is a placeholder // // the conceptual area portal has portalnum 1, and consists of two dareaportals entries with connections to area 1 // and 2 - CHECK_VECTORS_UNOREDERED_EQUAL(bsp.dareaportals, std::vector{{0, 0}, {1, 1}, {1, 2}}); - CHECK_VECTORS_UNOREDERED_EQUAL(bsp.dareas, std::vector{{0, 0}, {1, 1}, {1, 2}}); + EXPECT_VECTORS_UNOREDERED_EQUAL(bsp.dareaportals, std::vector{{0, 0}, {1, 1}, {1, 2}}); + EXPECT_VECTORS_UNOREDERED_EQUAL(bsp.dareas, std::vector{{0, 0}, {1, 1}, {1, 2}}); // look up the leafs const qvec3d player_start{-88, -112, 120}; @@ -202,108 +204,95 @@ TEST_CASE("areaportal" * doctest::test_suite("testmaps_q2")) auto *void_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], void_pos); // check leaf contents - CHECK(0 == player_start_leaf->contents); - CHECK(0 == other_room_leaf->contents); - CHECK(Q2_CONTENTS_AREAPORTAL == areaportal_leaf->contents); - CHECK(Q2_CONTENTS_SOLID == void_leaf->contents); + EXPECT_EQ(0, player_start_leaf->contents); + EXPECT_EQ(0, other_room_leaf->contents); + EXPECT_EQ(Q2_CONTENTS_AREAPORTAL, areaportal_leaf->contents); + EXPECT_EQ(Q2_CONTENTS_SOLID, void_leaf->contents); // make sure faces at these locations aren't clipped away const qvec3d floor_under_areaportal{32, -136, 96}; - CHECK(nullptr != BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], floor_under_areaportal, {0, 0, 1})); + EXPECT_NE(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], floor_under_areaportal, {0, 0, 1})); // check for brushes - CHECK(1 == Leaf_Brushes(&bsp, areaportal_leaf).size()); - CHECK(Q2_CONTENTS_AREAPORTAL == Leaf_Brushes(&bsp, areaportal_leaf).at(0)->contents); + EXPECT_EQ(1, Leaf_Brushes(&bsp, areaportal_leaf).size()); + EXPECT_EQ(Q2_CONTENTS_AREAPORTAL, Leaf_Brushes(&bsp, areaportal_leaf).at(0)->contents); - CHECK(1 == Leaf_Brushes(&bsp, void_leaf).size()); - CHECK(Q2_CONTENTS_SOLID == Leaf_Brushes(&bsp, void_leaf).at(0)->contents); + EXPECT_EQ(1, Leaf_Brushes(&bsp, void_leaf).size()); + EXPECT_EQ(Q2_CONTENTS_SOLID, Leaf_Brushes(&bsp, void_leaf).at(0)->contents); // check leaf areas - CHECK_VECTORS_UNOREDERED_EQUAL( + EXPECT_VECTORS_UNOREDERED_EQUAL( (std::vector{1, 2}), std::vector{player_start_leaf->area, other_room_leaf->area}); // the areaportal leaf itself actually gets assigned to one of the two sides' areas - CHECK((areaportal_leaf->area == 1 || areaportal_leaf->area == 2)); - CHECK(0 == void_leaf->area); // a solid leaf gets the invalid area + EXPECT_TRUE(areaportal_leaf->area == 1 || areaportal_leaf->area == 2); + EXPECT_EQ(0, void_leaf->area); // a solid leaf gets the invalid area // check the func_areaportal entity had its "style" set auto ents = EntData_Parse(bsp); auto it = std::find_if( ents.begin(), ents.end(), [](const entdict_t &dict) { return dict.get("classname") == "func_areaportal"; }); - REQUIRE(it != ents.end()); - REQUIRE("1" == it->get("style")); + ASSERT_NE(it, ents.end()); + ASSERT_EQ("1", it->get("style")); } /** * Similar to above test, but there's a detail brush sticking into the area portal */ -TEST_CASE("areaportal_with_detail" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, areaportalWithDetail) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_areaportal_with_detail.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); // area 0 is a placeholder // areaportal 0 is a placeholder // // the conceptual area portal has portalnum 1, and consists of two dareaportals entries with connections to area 1 // and 2 - CHECK_VECTORS_UNOREDERED_EQUAL(bsp.dareaportals, std::vector{{0, 0}, {1, 1}, {1, 2}}); - CHECK_VECTORS_UNOREDERED_EQUAL(bsp.dareas, std::vector{{0, 0}, {1, 1}, {1, 2}}); + EXPECT_VECTORS_UNOREDERED_EQUAL(bsp.dareaportals, std::vector{{0, 0}, {1, 1}, {1, 2}}); + EXPECT_VECTORS_UNOREDERED_EQUAL(bsp.dareas, std::vector{{0, 0}, {1, 1}, {1, 2}}); } -TEST_CASE("nodraw_light" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, nodrawLight) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_nodraw_light.map", {"-includeskip"}); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); const qvec3d topface_center{160, -148, 208}; auto *topface = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], topface_center, {0, 0, 1}); - REQUIRE(nullptr != topface); + ASSERT_NE(nullptr, topface); auto *texinfo = Face_Texinfo(&bsp, topface); - CHECK(std::string(texinfo->texture.data()) == "e1u1/trigger"); - CHECK(texinfo->flags.native == (Q2_SURF_LIGHT | Q2_SURF_NODRAW)); + EXPECT_EQ(std::string(texinfo->texture.data()), "e1u1/trigger"); + EXPECT_EQ(texinfo->flags.native, (Q2_SURF_LIGHT | Q2_SURF_NODRAW)); } -TEST_CASE("q2_long_texture_name" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, longTextureName) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_long_texture_name.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); auto *topface = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 16}, {0, 0, 1}); - REQUIRE(nullptr != topface); + ASSERT_NE(nullptr, topface); // this won't work in game, but we're mostly checking for lack of memory corruption // (a warning is issued) auto *texinfo = Face_Texinfo(&bsp, topface); - CHECK(std::string(texinfo->texture.data()) == "long_folder_name_test/long_text"); - CHECK(texinfo->nexttexinfo == -1); + EXPECT_EQ(std::string(texinfo->texture.data()), "long_folder_name_test/long_text"); + EXPECT_EQ(texinfo->nexttexinfo, -1); } -TEST_CASE("nodraw_light" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, base1) { - const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_nodraw_light.map", {"-includeskip"}); - - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); - - const qvec3d topface_center{160, -148, 208}; - auto *topface = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], topface_center, {0, 0, 1}); - REQUIRE(nullptr != topface); - - auto *texinfo = Face_Texinfo(&bsp, topface); - CHECK(std::string(texinfo->texture.data()) == "e1u1/trigger"); - CHECK(texinfo->flags.native == (Q2_SURF_LIGHT | Q2_SURF_NODRAW)); -} + GTEST_SKIP(); -TEST_CASE("base1" * doctest::test_suite("testmaps_q2") * doctest::skip() * doctest::may_fail()) -{ const auto [bsp, bspx, prt] = LoadTestmapQ2("base1-test.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); - CHECK(prt); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); + EXPECT_TRUE(prt); CheckFilled(bsp); // bspinfo output from a compile done with @@ -329,8 +318,8 @@ TEST_CASE("base1" * doctest::test_suite("testmaps_q2") * doctest::skip() * docte // visdata 0 // entdata 53623 - CHECK(3 == bsp.dareaportals.size()); - CHECK(3 == bsp.dareas.size()); + EXPECT_EQ(3, bsp.dareaportals.size()); + EXPECT_EQ(3, bsp.dareas.size()); // check for a sliver face which we had issues with being missing { @@ -340,42 +329,42 @@ TEST_CASE("base1" * doctest::test_suite("testmaps_q2") * doctest::skip() * docte const qvec3d normal = qv::normalize(normal_point - face_point); auto *sliver_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], face_point, normal); - REQUIRE(nullptr != sliver_face); + ASSERT_NE(nullptr, sliver_face); - CHECK(std::string_view("e1u1/metal3_5") == Face_TextureName(&bsp, sliver_face)); - CHECK(Face_Winding(&bsp, sliver_face).area() < 5.0); + EXPECT_EQ(std::string_view("e1u1/metal3_5"), Face_TextureName(&bsp, sliver_face)); + EXPECT_LT(Face_Winding(&bsp, sliver_face).area(), 5.0); } } -TEST_CASE("base1leak" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, base1leak) { const auto [bsp, bspx, prt] = LoadTestmapQ2("base1leak.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); - CHECK(8 == bsp.dbrushes.size()); + EXPECT_EQ(8, bsp.dbrushes.size()); - CHECK(bsp.dleafs.size() >= 8); // 1 placeholder + 1 empty (room interior) + 6 solid (sides of room) - CHECK(bsp.dleafs.size() <= 12); // q2tools-220 generates 12 + EXPECT_GE(bsp.dleafs.size(), 8); // 1 placeholder + 1 empty (room interior) + 6 solid (sides of room) + EXPECT_LE(bsp.dleafs.size(), 12); // q2tools-220 generates 12 const qvec3d in_plus_y_wall{-776, 976, -24}; auto *plus_y_wall_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_plus_y_wall); - CHECK(Q2_CONTENTS_SOLID == plus_y_wall_leaf->contents); + EXPECT_EQ(Q2_CONTENTS_SOLID, plus_y_wall_leaf->contents); - CHECK(3 == plus_y_wall_leaf->numleafbrushes); + EXPECT_EQ(3, plus_y_wall_leaf->numleafbrushes); - CHECK(prt->portals.size() == 0); - CHECK(prt->portalleafs == 1); + EXPECT_EQ(prt->portals.size(), 0); + EXPECT_EQ(prt->portalleafs, 1); } /** * e1u1/brlava brush intersecting e1u1/clip **/ -TEST_CASE("lavaclip" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, lavaclip) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_lavaclip.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); // not touching the lava, but inside the clip const qvec3d playerclip_outside1{-88, -32, 8}; @@ -390,67 +379,67 @@ TEST_CASE("lavaclip" * doctest::test_suite("testmaps_q2")) const qvec3d lava_top_face_in_playerclip{0, -32, 16}; // check leaf contents - CHECK((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP | Q2_CONTENTS_DETAIL) == + EXPECT_EQ((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP | Q2_CONTENTS_DETAIL), BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], playerclip_outside1)->contents); - CHECK((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP | Q2_CONTENTS_DETAIL) == + EXPECT_EQ((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP | Q2_CONTENTS_DETAIL), BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], playerclip_outside2)->contents); - CHECK((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP | Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA) == + EXPECT_EQ((Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP | Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA), BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], playerclip_inside_lava)->contents); - CHECK(Q2_CONTENTS_LAVA == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_lava_only)->contents); + EXPECT_EQ(Q2_CONTENTS_LAVA, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_lava_only)->contents); // search for face auto *topface = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], lava_top_face_in_playerclip, {0, 0, 1}); - REQUIRE(nullptr != topface); + ASSERT_NE(nullptr, topface); auto *texinfo = Face_Texinfo(&bsp, topface); - CHECK(std::string(texinfo->texture.data()) == "e1u1/brlava"); - CHECK(texinfo->flags.native == (Q2_SURF_LIGHT | Q2_SURF_WARP)); + EXPECT_EQ(std::string(texinfo->texture.data()), "e1u1/brlava"); + EXPECT_EQ(texinfo->flags.native, (Q2_SURF_LIGHT | Q2_SURF_WARP)); } /** * check that e1u1/clip intersecting mist doesn't split up the mist faces **/ -TEST_CASE("mist_clip" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, mistClip) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_mist_clip.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); // mist is two sided, so 12 faces for a cube - CHECK(12 == bsp.dfaces.size()); + EXPECT_EQ(12, bsp.dfaces.size()); } /** * e1u1/brlava brush intersecting e1u1/brwater **/ -TEST_CASE("lavawater" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, lavawater) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_lavawater.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); const qvec3d inside_both{0, 32, 8}; // check leaf contents - CHECK((Q2_CONTENTS_LAVA | Q2_CONTENTS_WATER) == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], inside_both)->contents); + EXPECT_EQ((Q2_CONTENTS_LAVA | Q2_CONTENTS_WATER), BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], inside_both)->contents); } /** * Weird mystery issue with a func_wall with broken collision * (ended up being a PLANE_X/Y/Z plane with negative facing normal, which is illegal - engine assumes they are positive) */ -TEST_CASE("q2_bmodel_collision" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, bmodelCollision) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_bmodel_collision.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); const qvec3d in_bmodel{-544, -312, -258}; - REQUIRE(2 == bsp.dmodels.size()); - CHECK(Q2_CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[1], in_bmodel)->contents); + ASSERT_EQ(2, bsp.dmodels.size()); + EXPECT_EQ(Q2_CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[1], in_bmodel)->contents); } -TEST_CASE("q2_liquids" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, liquids) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_liquids.map"); @@ -461,11 +450,11 @@ TEST_CASE("q2_liquids" * doctest::test_suite("testmaps_q2")) const qvec3d wateropaque_trans33 = watertrans33_trans66 - qvec3d(0, 0, 48); const qvec3d floor_wateropaque = wateropaque_trans33 - qvec3d(0, 0, 48); - CHECK_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], watertrans66_air)), + EXPECT_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], watertrans66_air)), std::vector({"e1u1/bluwter", "e1u1/bluwter"})); - CHECK(0 == BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], watertrans33_trans66).size()); - CHECK(0 == BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], wateropaque_trans33).size()); - CHECK_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], floor_wateropaque)), + EXPECT_EQ(0, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], watertrans33_trans66).size()); + EXPECT_EQ(0, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], wateropaque_trans33).size()); + EXPECT_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], floor_wateropaque)), std::vector({"e1u1/c_met11_2"})); } @@ -473,11 +462,11 @@ TEST_CASE("q2_liquids" * doctest::test_suite("testmaps_q2")) // water trans66 / slime trans66 { - CHECK_VECTORS_UNOREDERED_EQUAL( + EXPECT_VECTORS_UNOREDERED_EQUAL( TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], watertrans66_slimetrans66, qvec3d(0, -1, 0))), std::vector({"e1u1/sewer1"})); - CHECK_VECTORS_UNOREDERED_EQUAL( + EXPECT_VECTORS_UNOREDERED_EQUAL( TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], watertrans66_slimetrans66, qvec3d(0, 1, 0))), std::vector({"e1u1/sewer1"})); } @@ -485,11 +474,11 @@ TEST_CASE("q2_liquids" * doctest::test_suite("testmaps_q2")) // slime trans66 / lava trans66 const qvec3d slimetrans66_lavatrans66 = watertrans66_slimetrans66 + qvec3d(0, 48, 0); { - CHECK_VECTORS_UNOREDERED_EQUAL( + EXPECT_VECTORS_UNOREDERED_EQUAL( TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], slimetrans66_lavatrans66, qvec3d(0, -1, 0))), std::vector({"e1u1/brlava"})); - CHECK_VECTORS_UNOREDERED_EQUAL( + EXPECT_VECTORS_UNOREDERED_EQUAL( TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], slimetrans66_lavatrans66, qvec3d(0, 1, 0))), std::vector({"e1u1/brlava"})); } @@ -498,49 +487,49 @@ TEST_CASE("q2_liquids" * doctest::test_suite("testmaps_q2")) /** * Empty rooms are sealed to solid in Q2 **/ -TEST_CASE("q2_seal_empty_rooms" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, sealEmptyRooms) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_seal_empty_rooms.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); const qvec3d in_start_room{-240, 80, 56}; const qvec3d in_empty_room{-244, 476, 68}; // check leaf contents - CHECK(Q2_CONTENTS_EMPTY == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_start_room)->contents); - CHECK(Q2_CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_empty_room)->contents); + EXPECT_EQ(Q2_CONTENTS_EMPTY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_start_room)->contents); + EXPECT_EQ(Q2_CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_empty_room)->contents); - CHECK(prt->portals.size() == 0); - CHECK(prt->portalleafs == 1); + EXPECT_EQ(prt->portals.size(), 0); + EXPECT_EQ(prt->portalleafs, 1); } -TEST_CASE("q2_detail_non_sealing" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, detailNonSealing) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_detail_non_sealing.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); const qvec3d in_start_room{-240, 80, 56}; const qvec3d in_void{-336, 80, 56}; // check leaf contents - CHECK(Q2_CONTENTS_EMPTY == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_start_room)->contents); - CHECK(Q2_CONTENTS_EMPTY == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_void)->contents); + EXPECT_EQ(Q2_CONTENTS_EMPTY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_start_room)->contents); + EXPECT_EQ(Q2_CONTENTS_EMPTY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_void)->contents); } -TEST_CASE("q2_detail_overlapping_solid_sealing" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, detailOverlappingSolidSealing) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_detail_overlapping_solid_sealing.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); const qvec3d in_start_room{-240, 80, 56}; const qvec3d in_void{-336, 80, 56}; // check leaf contents - CHECK(Q2_CONTENTS_EMPTY == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_start_room)->contents); - CHECK((Q2_CONTENTS_SOLID & BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_void)->contents) == Q2_CONTENTS_SOLID); + EXPECT_EQ(Q2_CONTENTS_EMPTY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_start_room)->contents); + EXPECT_EQ((Q2_CONTENTS_SOLID & BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_void)->contents), Q2_CONTENTS_SOLID); } /** @@ -549,153 +538,155 @@ TEST_CASE("q2_detail_overlapping_solid_sealing" * doctest::test_suite("testmaps_ * Also, the faces on the ceiling/floor cross the areaportal * (due to our aggressive face merging). */ -TEST_CASE("q2_double_areaportal" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, doubleAreaportal) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_double_areaportal.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); CheckFilled(bsp); - CHECK(4 == bsp.dareas.size()); - CHECK(5 == bsp.dareaportals.size()); + EXPECT_EQ(4, bsp.dareas.size()); + EXPECT_EQ(5, bsp.dareaportals.size()); } -TEST_CASE("q2_areaportal_split" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, areaportalSplit) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_areaportal_split.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); CheckFilled(bsp); - CHECK(3 == bsp.dareas.size()); // 1 invalid index zero reserved + 2 areas - CHECK(3 == bsp.dareaportals + EXPECT_EQ(3, bsp.dareas.size()); // 1 invalid index zero reserved + 2 areas + EXPECT_EQ(3, bsp.dareaportals .size()); // 1 invalid index zero reserved + 2 dareaportals to store the two directions of the portal } /** * Test for q2 bmodel bounds **/ -TEST_CASE("q2_door" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, door) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_door.map"); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); 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); + EXPECT_EQ(world_tight_bounds.mins(), bsp.dmodels[0].mins); + EXPECT_EQ(world_tight_bounds.maxs(), bsp.dmodels[0].maxs); - CHECK(bmodel_tight_bounds.mins() == bsp.dmodels[1].mins); - CHECK(bmodel_tight_bounds.maxs() == bsp.dmodels[1].maxs); + EXPECT_EQ(bmodel_tight_bounds.mins(), bsp.dmodels[1].mins); + EXPECT_EQ(bmodel_tight_bounds.maxs(), bsp.dmodels[1].maxs); } -TEST_CASE("q2_mirrorinside" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, mirrorinside) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_mirrorinside.map"); { - INFO("window is not two sided by default"); + SCOPED_TRACE("window is not two sided by default"); const qvec3d window_pos{192, 96, 156}; - CHECK_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], window_pos)), + EXPECT_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], window_pos)), std::vector({"e2u2/wndow1_1"})); } { - INFO("aux is not two sided by default"); + SCOPED_TRACE("aux is not two sided by default"); const qvec3d aux_pos{32, 96, 156}; - CHECK_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], aux_pos)), + EXPECT_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], aux_pos)), std::vector({"e1u1/brwater"})); } { - INFO("mist is two sided by default"); + SCOPED_TRACE("mist is two sided by default"); const qvec3d mist_pos{32, -28, 156}; - CHECK_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], mist_pos)), + EXPECT_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], mist_pos)), std::vector({"e1u1/brwater", "e1u1/brwater"})); } { - INFO("_mirrorinside 0 disables the inside faces on mist"); + SCOPED_TRACE("_mirrorinside 0 disables the inside faces on mist"); const qvec3d mist_mirrorinside0_pos{32, -224, 156}; - CHECK_VECTORS_UNOREDERED_EQUAL( + EXPECT_VECTORS_UNOREDERED_EQUAL( TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], mist_mirrorinside0_pos)), std::vector({"e1u1/brwater"})); } { - INFO("_mirrorinside 1 works on func_detail_fence"); - CHECK_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], {32, -348, 156})), + SCOPED_TRACE("_mirrorinside 1 works on func_detail_fence"); + EXPECT_VECTORS_UNOREDERED_EQUAL(TexNames(bsp, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], {32, -348, 156})), std::vector({"e1u1/alphamask", "e1u1/alphamask"})); } } -TEST_CASE("q2_alphatest_window" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, alphatestWindow) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_alphatest_window.map"); - INFO("alphatest + window implies detail and translucent"); + SCOPED_TRACE("alphatest + window implies detail and translucent"); auto *leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}); - CHECK(leaf->contents == (Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW | Q2_CONTENTS_TRANSLUCENT)); + EXPECT_EQ(leaf->contents, (Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW | Q2_CONTENTS_TRANSLUCENT)); } -TEST_CASE("q2_alphatest_solid" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, alphatestSolid) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_alphatest_solid.map"); - INFO("alphatest + solid implies window, detail and translucent"); + SCOPED_TRACE("alphatest + solid implies window, detail and translucent"); auto *leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}); - CHECK(leaf->contents == (Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW | Q2_CONTENTS_TRANSLUCENT)); + EXPECT_EQ(leaf->contents, (Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW | Q2_CONTENTS_TRANSLUCENT)); } -TEST_CASE("q2_trans33_window" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, trans33Window) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_trans33_window.map"); - INFO("trans33 + window implies detail and translucent"); + SCOPED_TRACE("trans33 + window implies detail and translucent"); auto *leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}); - CHECK(leaf->contents == (Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW | Q2_CONTENTS_TRANSLUCENT)); + EXPECT_EQ(leaf->contents, (Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW | Q2_CONTENTS_TRANSLUCENT)); } -TEST_CASE("q2_trans33_solid" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, trans33Solid) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_trans33_solid.map"); - INFO("trans33 + solid implies window, detail and translucent"); + SCOPED_TRACE("trans33 + solid implies window, detail and translucent"); auto *leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}); - CHECK(leaf->contents == (Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW | Q2_CONTENTS_TRANSLUCENT)); + EXPECT_EQ(leaf->contents, (Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW | Q2_CONTENTS_TRANSLUCENT)); } /** * Ensure that leaked maps still get areas assigned properly * (empty leafs should get area 1, solid leafs area 0) */ -TEST_CASE("q2_leaked" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, leaked) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_leaked.map"); - CHECK(!prt); - CHECK(GAME_QUAKE_II == bsp.loadversion->game->id); + EXPECT_FALSE(prt); + EXPECT_EQ(GAME_QUAKE_II, bsp.loadversion->game->id); - CHECK(bsp.dareaportals.size() == 1); - CHECK(bsp.dareas.size() == 2); - CHECK(bsp.dleafs.size() == 8); + EXPECT_EQ(bsp.dareaportals.size(), 1); + EXPECT_EQ(bsp.dareas.size(), 2); + EXPECT_EQ(bsp.dleafs.size(), 8); for (auto &leaf : bsp.dleafs) { if (leaf.contents == Q2_CONTENTS_SOLID) { - CHECK(0 == leaf.area); + EXPECT_EQ(0, leaf.area); } else { - CHECK(1 == leaf.area); + EXPECT_EQ(1, leaf.area); } } } -TEST_CASE("q2_missing_faces" * doctest::test_suite("testmaps_q2") * doctest::may_fail()) +TEST(testmapsQ2, missingFaces) { + GTEST_SKIP(); + const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_missing_faces.map"); const qvec3d point_on_missing_face{-137, 125, -76.1593}; @@ -703,12 +694,12 @@ TEST_CASE("q2_missing_faces" * doctest::test_suite("testmaps_q2") * doctest::may const qvec3d point_on_present_face{-137, 133, -76.6997}; CheckFilled(bsp); - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point_on_missing_face)); - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point_on_missing_face2)); - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point_on_present_face)); + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point_on_missing_face)); + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point_on_missing_face2)); + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], point_on_present_face)); } -TEST_CASE("q2_ladder" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, ladder) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_ladder.map"); @@ -721,132 +712,132 @@ TEST_CASE("q2_ladder" * doctest::test_suite("testmaps_q2")) // the brush lacked a visible contents, so it became solid. // ladder and detail flags are preseved now. // (previously we were wiping them out and just writing out leafs as Q2_CONTENTS_SOLID). - CHECK(leaf->contents == (Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER | Q2_CONTENTS_DETAIL)); + EXPECT_EQ(leaf->contents, (Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER | Q2_CONTENTS_DETAIL)); - CHECK(1 == Leaf_Brushes(&bsp, leaf).size()); - CHECK((Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER | Q2_CONTENTS_DETAIL) == Leaf_Brushes(&bsp, leaf).at(0)->contents); + EXPECT_EQ(1, Leaf_Brushes(&bsp, leaf).size()); + EXPECT_EQ((Q2_CONTENTS_SOLID | Q2_CONTENTS_LADDER | Q2_CONTENTS_DETAIL), Leaf_Brushes(&bsp, leaf).at(0)->contents); } -TEST_CASE("q2_hint_missing_faces" * doctest::test_suite("testmaps_q2") * doctest::may_fail()) +TEST(testmapsQ2, hintMissingFaces) { + GTEST_SKIP(); + const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_hint_missing_faces.map"); - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {36, 144, 30})); + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {36, 144, 30})); } -TEST_CASE("q2_tb_cleanup" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, tbCleanup) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_tb_cleanup.map"); { - INFO("check that __TB_empty was converted to skip"); - CHECK(nullptr == BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0})); + SCOPED_TRACE("check that __TB_empty was converted to skip"); + EXPECT_EQ(nullptr, BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0})); } { auto ents = EntData_Parse(bsp); - REQUIRE(ents.size() == 2); - INFO("check that _tb_textures was stripped out"); - CHECK(entdict_t{{"classname", "worldspawn"}} == ents[0]); + ASSERT_EQ(ents.size(), 2); + SCOPED_TRACE("check that _tb_textures was stripped out"); + EXPECT_EQ((entdict_t{{"classname", "worldspawn"}}), ents[0]); } } -TEST_CASE("q2_detail_wall" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, detailWall) { // q2_detail_wall_with_detail_bit.map has the DETAIL content flag set on the // brushes inside the func_detail_wall. the func_detail_wall should take priority. const std::vector maps{"q2_detail_wall.map", "q2_detail_wall_with_detail_bit.map"}; for (const auto &mapname : maps) { - SUBCASE(mapname.c_str()) - { - const auto [bsp, bspx, prt] = LoadTestmapQ2(mapname); - auto *game = bsp.loadversion->game; + SCOPED_TRACE(mapname); - CHECK(GAME_QUAKE_II == game->id); + const auto [bsp, bspx, prt] = LoadTestmapQ2(mapname); + auto *game = bsp.loadversion->game; - const auto deleted_face_pos = qvec3d{320, 384, 96}; - const auto in_detail_wall = qvec3d{320, 384, 100}; + EXPECT_EQ(GAME_QUAKE_II, game->id); - auto *detail_wall_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_detail_wall); + const auto deleted_face_pos = qvec3d{320, 384, 96}; + const auto in_detail_wall = qvec3d{320, 384, 100}; - { - INFO("check leaf / brush contents"); + auto *detail_wall_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], in_detail_wall); - CAPTURE(game->create_contents_from_native(detail_wall_leaf->contents).to_string(game)); - CHECK((Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL) == detail_wall_leaf->contents); + { + SCOPED_TRACE("check leaf / brush contents"); - REQUIRE(1 == Leaf_Brushes(&bsp, detail_wall_leaf).size()); - auto *brush = Leaf_Brushes(&bsp, detail_wall_leaf).at(0); + SCOPED_TRACE(game->create_contents_from_native(detail_wall_leaf->contents).to_string(game)); + EXPECT_EQ((Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL), detail_wall_leaf->contents); - CAPTURE(game->create_contents_from_native(brush->contents).to_string(game)); - CHECK((Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL) == brush->contents); - } + ASSERT_EQ(1, Leaf_Brushes(&bsp, detail_wall_leaf).size()); + auto *brush = Leaf_Brushes(&bsp, detail_wall_leaf).at(0); - { - INFO("check fully covered face is deleted"); - CHECK(!BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], deleted_face_pos)); - } + SCOPED_TRACE(game->create_contents_from_native(brush->contents).to_string(game)); + EXPECT_EQ((Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL), brush->contents); + } - { - INFO("check floor under detail fence is not deleted, and not split"); + { + SCOPED_TRACE("check fully covered face is deleted"); + EXPECT_FALSE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], deleted_face_pos)); + } - auto *face_under_fence = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 348, 96}); - auto *face_outside_fence = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 312, 96}); + { + SCOPED_TRACE("check floor under detail fence is not deleted, and not split"); - CHECK(face_under_fence); - CHECK(face_under_fence == face_outside_fence); - } + auto *face_under_fence = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 348, 96}); + auto *face_outside_fence = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 312, 96}); + + EXPECT_TRUE(face_under_fence); + EXPECT_EQ(face_under_fence, face_outside_fence); } } } -TEST_CASE("q2_detail_fence" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, detailFence) { const std::vector maps{"q2_detail_fence.map", "q2_detail_fence_with_detail_bit.map"}; for (const auto &mapname : maps) { - SUBCASE(mapname.c_str()) - { - const auto [bsp, bspx, prt] = LoadTestmapQ2(mapname); - auto *game = bsp.loadversion->game; + SCOPED_TRACE(mapname); - CHECK(GAME_QUAKE_II == game->id); + const auto [bsp, bspx, prt] = LoadTestmapQ2(mapname); + auto *game = bsp.loadversion->game; - auto *detail_wall_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 384, 100}); + EXPECT_EQ(GAME_QUAKE_II, game->id); - { - INFO("check leaf / brush contents"); - CAPTURE(game->create_contents_from_native(detail_wall_leaf->contents).to_string(game)); + auto *detail_wall_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 384, 100}); - CHECK( - (Q2_CONTENTS_WINDOW | Q2_CONTENTS_DETAIL | Q2_CONTENTS_TRANSLUCENT) == detail_wall_leaf->contents); + { + SCOPED_TRACE("check leaf / brush contents"); + SCOPED_TRACE(game->create_contents_from_native(detail_wall_leaf->contents).to_string(game)); - REQUIRE(1 == Leaf_Brushes(&bsp, detail_wall_leaf).size()); - CHECK((Q2_CONTENTS_WINDOW | Q2_CONTENTS_DETAIL | Q2_CONTENTS_TRANSLUCENT) == - Leaf_Brushes(&bsp, detail_wall_leaf).at(0)->contents); - } + EXPECT_EQ( + (Q2_CONTENTS_WINDOW | Q2_CONTENTS_DETAIL | Q2_CONTENTS_TRANSLUCENT), detail_wall_leaf->contents); - { - INFO("check fully covered face is not deleted"); - CHECK(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 384, 96})); - } + ASSERT_EQ(1, Leaf_Brushes(&bsp, detail_wall_leaf).size()); + EXPECT_EQ((Q2_CONTENTS_WINDOW | Q2_CONTENTS_DETAIL | Q2_CONTENTS_TRANSLUCENT), + Leaf_Brushes(&bsp, detail_wall_leaf).at(0)->contents); + } + + { + SCOPED_TRACE("check fully covered face is not deleted"); + EXPECT_TRUE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 384, 96})); + } - { - INFO("check floor under detail fence is not deleted, and not split"); + { + SCOPED_TRACE("check floor under detail fence is not deleted, and not split"); - auto *face_under_fence = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 348, 96}); - auto *face_outside_fence = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 312, 96}); + auto *face_under_fence = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 348, 96}); + auto *face_outside_fence = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], qvec3d{320, 312, 96}); - CHECK(face_under_fence); - CHECK(face_under_fence == face_outside_fence); - } + EXPECT_TRUE(face_under_fence); + EXPECT_EQ(face_under_fence, face_outside_fence); } } } -TEST_CASE("q2_mist_transwater" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, mistTranswater) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_mist_transwater.map", {"-tjunc", "none"}); @@ -855,27 +846,27 @@ TEST_CASE("q2_mist_transwater" * doctest::test_suite("testmaps_q2")) auto up_faces = BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], top_of_water, {0, 0, 1}); auto down_faces = BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], top_of_water, {0, 0, -1}); - REQUIRE(1 == up_faces.size()); - REQUIRE(1 == down_faces.size()); + ASSERT_EQ(1, up_faces.size()); + ASSERT_EQ(1, down_faces.size()); // water has a higher priority (lower content bits are stronger), so it should cut a hole in the mist - CHECK(Face_TextureNameView(&bsp, up_faces[0]) == "e1u1/water6"); - CHECK(Face_TextureNameView(&bsp, down_faces[0]) == "e1u1/water6"); + EXPECT_EQ(Face_TextureNameView(&bsp, up_faces[0]), "e1u1/water6"); + EXPECT_EQ(Face_TextureNameView(&bsp, down_faces[0]), "e1u1/water6"); const auto top_of_water_up = winding_t{{-232,-32,352}, {-232,0, 352}, {-200,0, 352}, {-200,-32,352}}; const auto top_of_water_dn = top_of_water_up.flip(); - CHECK(Face_Winding(&bsp, up_faces[0]).directional_equal(top_of_water_up)); - CHECK(Face_Winding(&bsp, down_faces[0]).directional_equal(top_of_water_dn)); + EXPECT_TRUE(Face_Winding(&bsp, up_faces[0]).directional_equal(top_of_water_up)); + EXPECT_TRUE(Face_Winding(&bsp, down_faces[0]).directional_equal(top_of_water_dn)); } -TEST_CASE("q2_tjunc_matrix" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, tjuncMatrix) { const auto [b, bspx, prt] = LoadTestmapQ2("q2_tjunc_matrix.map"); const mbsp_t &bsp = b; // workaround clang not allowing capturing bindings in lambdas auto *game = bsp.loadversion->game; - CHECK(GAME_QUAKE_II == game->id); + EXPECT_EQ(GAME_QUAKE_II, game->id); const qvec3d face_midpoint_origin {-24, 0, 24}; const qvec3d face_midpoint_to_tjunc {8, 0, 8}; @@ -912,192 +903,194 @@ TEST_CASE("q2_tjunc_matrix" * doctest::test_suite("testmaps_q2")) }; { - INFO("INDEX_DETAIL_WALL horizontal"); - CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_WALL)); + SCOPED_TRACE("INDEX_DETAIL_WALL horizontal"); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_WALL)); // this one is tricky - the solid cuts a hole in the top // that hole (the detail_wall faces) are what weld with the side - CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID)); // same as INDEX_DETAIL_WALL, INDEX_SOLID - CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID_DETAIL)); - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_TRANSPARENT_WATER)); - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_OPAQUE_WATER)); - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_OPAQUE_MIST)); - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_TRANSPARENT_WINDOW)); - CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_OPAQUE_AUX)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID_DETAIL)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_TRANSPARENT_WATER)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_OPAQUE_WATER)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_OPAQUE_MIST)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_TRANSPARENT_WINDOW)); + EXPECT_FALSE(has_tjunc(INDEX_DETAIL_WALL, INDEX_OPAQUE_AUX)); // same as INDEX_DETAIL_WALL, INDEX_SOLID - CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_DETAIL_WALL, INDEX_SKY)); } { - INFO("INDEX_SOLID horizontal - welds with anything opaque except detail_wall"); - CHECK(!has_tjunc(INDEX_SOLID, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_SOLID, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_SOLID, INDEX_SOLID_DETAIL)); - CHECK(!has_tjunc(INDEX_SOLID, INDEX_TRANSPARENT_WATER)); - CHECK( has_tjunc(INDEX_SOLID, INDEX_OPAQUE_WATER)); - CHECK( has_tjunc(INDEX_SOLID, INDEX_OPAQUE_MIST)); - CHECK(!has_tjunc(INDEX_SOLID, INDEX_TRANSPARENT_WINDOW)); - CHECK( has_tjunc(INDEX_SOLID, INDEX_OPAQUE_AUX)); - CHECK( has_tjunc(INDEX_SOLID, INDEX_SKY)); + SCOPED_TRACE("INDEX_SOLID horizontal - welds with anything opaque except detail_wall"); + EXPECT_FALSE(has_tjunc(INDEX_SOLID, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID, INDEX_SOLID_DETAIL)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID, INDEX_TRANSPARENT_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID, INDEX_OPAQUE_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID, INDEX_OPAQUE_MIST)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID, INDEX_TRANSPARENT_WINDOW)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID, INDEX_OPAQUE_AUX)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID, INDEX_SKY)); } { - INFO("INDEX_SOLID_DETAIL horizontal - same as INDEX_SOLID"); - CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SOLID_DETAIL)); - CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_TRANSPARENT_WATER)); - CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_OPAQUE_WATER)); - CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_OPAQUE_MIST)); - CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_TRANSPARENT_WINDOW)); - CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_OPAQUE_AUX)); - CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SKY)); + SCOPED_TRACE("INDEX_SOLID_DETAIL horizontal - same as INDEX_SOLID"); + EXPECT_FALSE(has_tjunc(INDEX_SOLID_DETAIL, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SOLID_DETAIL)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID_DETAIL, INDEX_TRANSPARENT_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID_DETAIL, INDEX_OPAQUE_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID_DETAIL, INDEX_OPAQUE_MIST)); + EXPECT_FALSE(has_tjunc(INDEX_SOLID_DETAIL, INDEX_TRANSPARENT_WINDOW)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID_DETAIL, INDEX_OPAQUE_AUX)); + EXPECT_TRUE( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SKY)); } { - INFO("INDEX_TRANSPARENT_WATER horizontal"); - CHECK( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_SOLID_DETAIL)); - CHECK( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_TRANSPARENT_WATER)); - CHECK( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_OPAQUE_WATER)); + SCOPED_TRACE("INDEX_TRANSPARENT_WATER horizontal"); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_TRANSPARENT_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_OPAQUE_WATER)); // water is stronger than mist, so cuts away the bottom face of the mist // the top face of the water then doesn't need to weld because - CHECK(!has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_OPAQUE_MIST)); - CHECK( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_TRANSPARENT_WINDOW)); - CHECK( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_OPAQUE_AUX)); - CHECK( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_SKY)); + EXPECT_FALSE(has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_OPAQUE_MIST)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_TRANSPARENT_WINDOW)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_OPAQUE_AUX)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_SKY)); } { - INFO("INDEX_OPAQUE_WATER horizontal"); + SCOPED_TRACE("INDEX_OPAQUE_WATER horizontal"); // detail wall is stronger than water, so cuts a hole and the water then welds with itself - CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_WATER, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_WATER, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_WATER, INDEX_SOLID_DETAIL)); // welds because opaque water and translucent don't get a face between them - CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_TRANSPARENT_WATER)); - CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_OPAQUE_WATER)); - CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_OPAQUE_MIST)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_WATER, INDEX_TRANSPARENT_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_WATER, INDEX_OPAQUE_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_WATER, INDEX_OPAQUE_MIST)); // window is stronger and cuts a hole in the water - CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_TRANSPARENT_WINDOW)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_WATER, INDEX_TRANSPARENT_WINDOW)); // same with aux - CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_OPAQUE_AUX)); - CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_WATER, INDEX_OPAQUE_AUX)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_WATER, INDEX_SKY)); } { - INFO("INDEX_OPAQUE_MIST horizontal"); + SCOPED_TRACE("INDEX_OPAQUE_MIST horizontal"); // detail wall is stronger, cuts mist - CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_MIST, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_MIST, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_MIST, INDEX_SOLID_DETAIL)); // water is stronger, cuts mist - CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_TRANSPARENT_WATER)); - CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_OPAQUE_WATER)); - CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_OPAQUE_MIST)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_MIST, INDEX_TRANSPARENT_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_MIST, INDEX_OPAQUE_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_MIST, INDEX_OPAQUE_MIST)); // window is stronger, cuts mist - CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_TRANSPARENT_WINDOW)); - CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_OPAQUE_AUX)); - CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_MIST, INDEX_TRANSPARENT_WINDOW)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_MIST, INDEX_OPAQUE_AUX)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_MIST, INDEX_SKY)); } { - INFO("INDEX_TRANSPARENT_WINDOW horizontal"); + SCOPED_TRACE("INDEX_TRANSPARENT_WINDOW horizontal"); // detail wall is stronger than window, cuts a hole in the window, so window // tjuncs with itself - CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_DETAIL_WALL)); // solid cuts a hole in the window - CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SOLID_DETAIL)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SOLID_DETAIL)); // translucent window and translucent water weld - CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_TRANSPARENT_WATER)); - CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_OPAQUE_WATER)); - CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_OPAQUE_MIST)); - CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_TRANSPARENT_WINDOW)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_TRANSPARENT_WATER)); + EXPECT_FALSE(has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_OPAQUE_WATER)); + EXPECT_FALSE(has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_OPAQUE_MIST)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_TRANSPARENT_WINDOW)); // note, aux is lower priority than window, so bottom face of aux gets cut away - CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_OPAQUE_AUX)); + EXPECT_FALSE(has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_OPAQUE_AUX)); // sky cuts hole in window - CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SKY)); } { - INFO("INDEX_OPAQUE_AUX horizontal"); + SCOPED_TRACE("INDEX_OPAQUE_AUX horizontal"); // detail_wall is higher priority, cuts a hole in aux, which welds with itself - CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_SOLID_DETAIL)); - CHECK(!has_tjunc(INDEX_OPAQUE_AUX, INDEX_TRANSPARENT_WATER)); - CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_OPAQUE_WATER)); - CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_OPAQUE_MIST)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_AUX, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_AUX, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_AUX, INDEX_SOLID_DETAIL)); + EXPECT_FALSE(has_tjunc(INDEX_OPAQUE_AUX, INDEX_TRANSPARENT_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_AUX, INDEX_OPAQUE_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_AUX, INDEX_OPAQUE_MIST)); // window is stronger, cuts a hole which causes aux to weld - CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_TRANSPARENT_WINDOW)); - CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_OPAQUE_AUX)); - CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_SKY)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_AUX, INDEX_TRANSPARENT_WINDOW)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_AUX, INDEX_OPAQUE_AUX)); + EXPECT_TRUE( has_tjunc(INDEX_OPAQUE_AUX, INDEX_SKY)); } { - INFO("INDEX_SKY horizontal - same as INDEX_SOLID"); - CHECK(!has_tjunc(INDEX_SKY, INDEX_DETAIL_WALL)); - CHECK( has_tjunc(INDEX_SKY, INDEX_SOLID)); - CHECK( has_tjunc(INDEX_SKY, INDEX_SOLID_DETAIL)); - CHECK(!has_tjunc(INDEX_SKY, INDEX_TRANSPARENT_WATER)); - CHECK( has_tjunc(INDEX_SKY, INDEX_OPAQUE_WATER)); - CHECK( has_tjunc(INDEX_SKY, INDEX_OPAQUE_MIST)); - CHECK(!has_tjunc(INDEX_SKY, INDEX_TRANSPARENT_WINDOW)); - CHECK( has_tjunc(INDEX_SKY, INDEX_OPAQUE_AUX)); - CHECK( has_tjunc(INDEX_SKY, INDEX_SKY)); + SCOPED_TRACE("INDEX_SKY horizontal - same as INDEX_SOLID"); + EXPECT_FALSE(has_tjunc(INDEX_SKY, INDEX_DETAIL_WALL)); + EXPECT_TRUE( has_tjunc(INDEX_SKY, INDEX_SOLID)); + EXPECT_TRUE( has_tjunc(INDEX_SKY, INDEX_SOLID_DETAIL)); + EXPECT_FALSE(has_tjunc(INDEX_SKY, INDEX_TRANSPARENT_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_SKY, INDEX_OPAQUE_WATER)); + EXPECT_TRUE( has_tjunc(INDEX_SKY, INDEX_OPAQUE_MIST)); + EXPECT_FALSE(has_tjunc(INDEX_SKY, INDEX_TRANSPARENT_WINDOW)); + EXPECT_TRUE( has_tjunc(INDEX_SKY, INDEX_OPAQUE_AUX)); + EXPECT_TRUE( has_tjunc(INDEX_SKY, INDEX_SKY)); } } -TEST_CASE("q2_unknown_contents" * doctest::test_suite("testmaps_q2")) +TEST(testmapsQ2, unknownContents) { const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_unknown_contents.map"); { - INFO("leaf with contents 1<<10 which is not a valid contents"); + SCOPED_TRACE("leaf with contents 1<<10 which is not a valid contents"); auto *leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}); // FIXME: should the unknown contents get converted to SOLID in the leaf? - CHECK(leaf->contents == (Q2_CONTENTS_SOLID | 1024)); + EXPECT_EQ(leaf->contents, (Q2_CONTENTS_SOLID | 1024)); - CHECK(1 == Leaf_Brushes(&bsp, leaf).size()); + EXPECT_EQ(1, Leaf_Brushes(&bsp, leaf).size()); // FIXME: should the unknown contents have SOLID added in the brush? - CHECK((Q2_CONTENTS_SOLID | 1024)== + EXPECT_EQ((Q2_CONTENTS_SOLID | 1024), Leaf_Brushes(&bsp, leaf).at(0)->contents); } { - INFO("leaf with contents 1<<30 which is not a valid contents"); + SCOPED_TRACE("leaf with contents 1<<30 which is not a valid contents"); auto *leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], {64, 0, 0}); // FIXME: should the unknown contents get converted to SOLID in the leaf? - CHECK(leaf->contents == (Q2_CONTENTS_SOLID | nth_bit(30))); + EXPECT_EQ(leaf->contents, (Q2_CONTENTS_SOLID | nth_bit(30))); - CHECK(1 == Leaf_Brushes(&bsp, leaf).size()); + EXPECT_EQ(1, Leaf_Brushes(&bsp, leaf).size()); // FIXME: should the unknown contents have SOLID added in the brush? - CHECK((Q2_CONTENTS_SOLID | nth_bit(30))== + EXPECT_EQ((Q2_CONTENTS_SOLID | nth_bit(30)), Leaf_Brushes(&bsp, leaf).at(0)->contents); } { - INFO("face with contents 1<<10 which is not a valid surrflags"); + SCOPED_TRACE("face with contents 1<<10 which is not a valid surrflags"); auto *top_face = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {128, 0, 16}, {0, 0, 1}); - REQUIRE(top_face); + ASSERT_TRUE(top_face); auto *texinfo = BSP_GetTexinfo(&bsp, top_face->texinfo); - REQUIRE(texinfo); + ASSERT_TRUE(texinfo); - CHECK(texinfo->flags.native == 1024); + EXPECT_EQ(texinfo->flags.native, 1024); } } -TEST_CASE("q2_noclipfaces_nodraw" * doctest::test_suite("testmaps_q2") * doctest::may_fail()) +TEST(ltfaceQ2, noclipfacesNodraw) { - INFO("when _noclipfaces has a choice of faces, don't use the nodraw one"); + GTEST_SKIP(); + + SCOPED_TRACE("when _noclipfaces has a choice of faces, don't use the nodraw one"); const auto [bsp, bspx, prt] = LoadTestmapQ2("q2_noclipfaces_nodraw.map"); @@ -1106,9 +1099,9 @@ TEST_CASE("q2_noclipfaces_nodraw" * doctest::test_suite("testmaps_q2") * doctest auto up_faces = BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], top_of_water, {0, 0, 1}); auto down_faces = BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], top_of_water, {0, 0, -1}); - REQUIRE(1 == up_faces.size()); - REQUIRE(1 == down_faces.size()); + ASSERT_EQ(1, up_faces.size()); + ASSERT_EQ(1, down_faces.size()); - CHECK(Face_TextureNameView(&bsp, up_faces[0]) == "e1u1/water1_8"); - CHECK(Face_TextureNameView(&bsp, down_faces[0]) == "e1u1/water1_8"); -} \ No newline at end of file + EXPECT_EQ(Face_TextureNameView(&bsp, up_faces[0]), "e1u1/water1_8"); + EXPECT_EQ(Face_TextureNameView(&bsp, down_faces[0]), "e1u1/water1_8"); +} diff --git a/tests/test_vis.cc b/tests/test_vis.cc index ee74df8a..622807c6 100644 --- a/tests/test_vis.cc +++ b/tests/test_vis.cc @@ -7,7 +7,7 @@ #include "test_qbsp.hh" #include "testutils.hh" -TEST_CASE("q2_detail_leak_test.map") +TEST(vis, detailLeakTest) { auto [bsp, bspx] = QbspVisLight_Q2("q2_detail_leak_test.map", {}, runvis_t::yes); const auto vis = DecompressAllVis(&bsp); @@ -28,27 +28,27 @@ TEST_CASE("q2_detail_leak_test.map") auto *player_start_curve_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_start_curve); auto *player_start_leaf = BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_start); - CHECK(item_enviro_leaf->contents == 0); - CHECK(item_enviro_curve_leaf->contents == 0); - CHECK(player_start_curve_leaf->contents == 0); - CHECK(player_start_leaf->contents == 0); + EXPECT_EQ(item_enviro_leaf->contents, 0); + EXPECT_EQ(item_enviro_curve_leaf->contents, 0); + EXPECT_EQ(player_start_curve_leaf->contents, 0); + EXPECT_EQ(player_start_leaf->contents, 0); { - INFO("check item_enviro_leaf"); - CHECK(leaf_sees(item_enviro_leaf, item_enviro_curve_leaf)); - CHECK(!leaf_sees(item_enviro_leaf, player_start_curve_leaf)); - CHECK(!leaf_sees(item_enviro_leaf, player_start_leaf)); + SCOPED_TRACE("check item_enviro_leaf"); + EXPECT_TRUE(leaf_sees(item_enviro_leaf, item_enviro_curve_leaf)); + EXPECT_FALSE(leaf_sees(item_enviro_leaf, player_start_curve_leaf)); + EXPECT_FALSE(leaf_sees(item_enviro_leaf, player_start_leaf)); } { - INFO("check player_start_leaf"); - CHECK(leaf_sees(player_start_leaf, player_start_curve_leaf)); - CHECK(!leaf_sees(player_start_leaf, item_enviro_curve_leaf)); - CHECK(!leaf_sees(player_start_leaf, item_enviro_leaf)); + SCOPED_TRACE("check player_start_leaf"); + EXPECT_TRUE(leaf_sees(player_start_leaf, player_start_curve_leaf)); + EXPECT_FALSE(leaf_sees(player_start_leaf, item_enviro_curve_leaf)); + EXPECT_FALSE(leaf_sees(player_start_leaf, item_enviro_leaf)); } } -TEST_CASE("ClipStackWinding") { +TEST(vis, ClipStackWinding) { pstack_t stack{}; visstats_t stats{}; @@ -61,11 +61,11 @@ TEST_CASE("ClipStackWinding") { w1->set_winding_sphere(); w1 = ClipStackWinding(stats, w1, stack, qplane3d({-1, 0, 0}, -16)); - CHECK(w1->size() == 4); - CHECK((*w1)[0] == qvec3d(0, 0, 0)); - CHECK((*w1)[1] == qvec3d(16, 0, 0)); - CHECK((*w1)[2] == qvec3d(16, 0, -32)); - CHECK((*w1)[3] == qvec3d(0, 0, -32)); + EXPECT_EQ(w1->size(), 4); + EXPECT_EQ((*w1)[0], qvec3d(0, 0, 0)); + EXPECT_EQ((*w1)[1], qvec3d(16, 0, 0)); + EXPECT_EQ((*w1)[2], qvec3d(16, 0, -32)); + EXPECT_EQ((*w1)[3], qvec3d(0, 0, -32)); FreeStackWinding(w1, stack); } diff --git a/tests/testutils.hh b/tests/testutils.hh index 398e9ba3..e546cd08 100644 --- a/tests/testutils.hh +++ b/tests/testutils.hh @@ -1,19 +1,19 @@ #pragma once -#include +#include #include template -void CHECK_VECTORS_UNOREDERED_EQUAL(const A &a, const A &b) +void EXPECT_VECTORS_UNOREDERED_EQUAL(const A &a, const A &b) { if (a.size() != b.size()) { - FAIL_CHECK("Expected vectors to be equal (ignoring order)"); + ADD_FAILURE() << "Expected vectors to be equal (ignoring order)"; return; } for (auto &a_elem : a) { if (std::find(b.begin(), b.end(), a_elem) == b.end()) { - FAIL_CHECK("Expected vectors to be equal (ignoring order)"); + ADD_FAILURE() << "Expected vectors to be equal (ignoring order)"; return; } } From 263dd5faa1de4edefa0e35800adb15d75c2d777b Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Fri, 5 Jul 2024 21:54:33 -0600 Subject: [PATCH 03/13] tests: check that we're not outputting >64 sided faces in Q1 mode --- testmaps/qbsp_many_sided_face.map | 373 ++++++++++++ testmaps/qbsp_tjunc_many_sided_sky.map | 751 +++++++++++++++++++++++++ tests/test_qbsp.cc | 73 ++- 3 files changed, 1195 insertions(+), 2 deletions(-) create mode 100644 testmaps/qbsp_many_sided_face.map create mode 100644 testmaps/qbsp_tjunc_many_sided_sky.map diff --git a/testmaps/qbsp_many_sided_face.map b/testmaps/qbsp_many_sided_face.map new file mode 100644 index 00000000..a02c1992 --- /dev/null +++ b/testmaps/qbsp_many_sided_face.map @@ -0,0 +1,373 @@ +// Game: Quake +// Format: Valve +// entity 0 +{ +"mapversion" "220" +"classname" "worldspawn" +"wad" "deprecated/free_wad.wad" +// brush 0 +{ +( -64 256 -16 ) ( -63.98050498001612 253.76609197602374 0 ) ( -63.98050498001954 253.76609197602772 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.98050498001248 258.23390802397444 0 ) ( -64 256 -16 ) ( -63.98050498001248 258.23390802397444 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.92202585844643 260.467135577921 0 ) ( -63.98050498001248 258.23390802397444 -16 ) ( -63.92202585844643 260.467135577921 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.92202585844607 251.53286442207985 -16 ) ( -63.98050498001612 253.76609197602374 0 ) ( -63.92202585844643 251.532864422079 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.82458044858629 249.30099760090314 -16 ) ( -63.92202585844643 251.532864422079 0 ) ( -63.82458044858868 249.30099760090525 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.82458044858686 262.6990023990984 0 ) ( -63.92202585844643 260.467135577921 -16 ) ( -63.82458044858686 262.6990023990984 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.68819843325764 264.92882863924933 0 ) ( -63.82458044858686 262.6990023990984 -16 ) ( -63.68819843325764 264.92882863924933 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.68819843325764 247.0711713607525 0 ) ( -63.82458044858629 249.30099760090314 -16 ) ( -63.82458044858868 249.30099760090525 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.512921355746585 267.1559350716998 0 ) ( -63.68819843325764 264.92882863924933 -16 ) ( -63.512921355746585 267.1559350716998 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.51292135574113 244.8440649283075 0 ) ( -63.68819843325944 247.07117136075192 -16 ) ( -63.68819843325764 247.0711713607525 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.29880260713617 242.62035670174373 0 ) ( -63.512921355743046 244.84406492829987 -16 ) ( -63.51292135574113 244.8440649283075 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.512921355746585 267.1559350716998 -16 ) ( -63.298802607139805 269.3796432982599 0 ) ( -63.512921355746585 267.1559350716998 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.298802607139336 242.6203567017404 -16 ) ( -63.04590741009088 240.4007240441424 0 ) ( -63.045907410090884 240.40072404414101 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -63.04590741009088 271.5992759558576 0 ) ( -63.298802607139805 269.3796432982599 -16 ) ( -63.04590741009088 271.5992759558576 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -62.75431279891759 238.18584307711217 -16 ) ( -63.04590741009088 240.4007240441424 0 ) ( -62.75431279891927 238.1858430771099 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -62.754312798915635 273.8141569228901 0 ) ( -63.04590741009088 271.5992759558576 -16 ) ( -62.754312798915635 273.8141569228901 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -62.424107596181784 276.0236115251482 0 ) ( -62.754312798915635 273.8141569228901 -16 ) ( -62.424107596181784 276.0236115251482 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -62.75431279891927 238.1858430771099 0 ) ( -62.42410759617687 235.97638847485067 -16 ) ( -62.75431279891759 238.18584307711217 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -62.42410759617687 235.97638847485067 -16 ) ( -62.0553923855623 233.77303325863613 0 ) ( -62.055392385564495 233.7730332586327 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -62.0553923855623 278.2269667413675 0 ) ( -62.424107596181784 276.0236115251482 -16 ) ( -62.0553923855623 278.2269667413675 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -61.64827948130369 280.42355140819564 0 ) ( -62.0553923855623 278.2269667413675 -16 ) ( -61.64827948130369 280.42355140819564 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -61.64827948130187 231.57644859180436 0 ) ( -62.055392385564495 233.7730332586327 -16 ) ( -62.0553923855623 233.77303325863613 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -61.202892893927086 229.3873035753269 -16 ) ( -61.64827948130187 231.57644859180436 0 ) ( -61.20289289392895 229.38730357532768 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -61.20289289392531 282.6126964246723 0 ) ( -61.64827948130369 280.42355140819564 -16 ) ( -61.20289289392531 282.6126964246723 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -60.719368292513536 284.7937349560161 0 ) ( -61.20289289392531 282.6126964246723 -16 ) ( -60.719368292513536 284.7937349560161 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -61.202892893927086 229.3873035753269 -16 ) ( -60.7193682925099 227.20626504399115 0 ) ( -60.71936829251093 227.2062650439852 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -60.19785296332681 225.03399736324283 -16 ) ( -60.7193682925099 227.20626504399115 0 ) ( -60.19785296332702 225.03399736324354 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -60.19785296333248 286.96600263675464 0 ) ( -60.719368292513536 284.7937349560161 -16 ) ( -60.19785296333248 286.96600263675464 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -59.63850576499996 222.87116222687766 -16 ) ( -60.19785296332702 225.03399736324354 0 ) ( -59.638505765000446 222.871162226882 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -59.638505765002265 289.12883777312345 0 ) ( -60.19785296333248 286.96600263675464 -16 ) ( -59.638505765002265 289.12883777312345 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -59.04149708010322 220.71841845542465 -16 ) ( -59.638505765000446 222.871162226882 0 ) ( -59.04149708010664 220.71841845542804 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -59.04149708010118 291.2815815445774 0 ) ( -59.638505765002265 289.12883777312345 -16 ) ( -59.04149708010118 291.2815815445774 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -58.407008763269914 293.4235782045089 0 ) ( -59.04149708010118 291.2815815445774 -16 ) ( -58.407008763269914 293.4235782045089 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -59.04149708010322 220.71841845542465 -16 ) ( -58.40700876326446 218.57642179549293 0 ) ( -58.40700876326742 218.57642179549015 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -57.735234085779666 216.4458247200091 0 ) ( -58.40700876326742 218.57642179549015 -16 ) ( -58.40700876326446 218.57642179549293 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -57.735234085781485 295.5541752799927 0 ) ( -58.407008763269914 293.4235782045089 -16 ) ( -57.735234085781485 295.5541752799927 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -57.02637767671695 297.6727237705154 0 ) ( -57.735234085781485 295.5541752799927 -16 ) ( -57.02637767671695 297.6727237705154 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -57.02637767671331 214.32727622948732 0 ) ( -57.73523408578045 216.44582472000658 -16 ) ( -57.735234085779666 216.4458247200091 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -56.28065546060134 299.77857834568476 0 ) ( -57.02637767671695 297.6727237705154 -16 ) ( -56.28065546060134 299.77857834568476 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -56.28065546059224 212.22142165431433 0 ) ( -57.026377676714375 214.32727622948343 -16 ) ( -57.02637767671331 214.32727622948732 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -55.49829459164281 210.12890245820128 -16 ) ( -56.28065546059224 212.22142165431433 0 ) ( -55.49829459164357 210.12890245820017 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -55.49829459164175 301.8710975417998 0 ) ( -56.28065546060134 299.77857834568476 -16 ) ( -55.49829459164175 301.8710975417998 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -54.679533384553 303.9496439572358 0 ) ( -55.49829459164175 301.8710975417998 -16 ) ( -54.679533384553 303.9496439572358 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -54.67953338454754 208.05035604276782 0 ) ( -55.49829459164281 210.12890245820128 -16 ) ( -55.49829459164357 210.12890245820017 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -53.82462124191079 205.98641555337963 0 ) ( -54.67953338454842 208.0503560427635 -16 ) ( -54.67953338454754 208.05035604276782 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -53.82462124191443 306.0135844466258 0 ) ( -54.679533384553 303.9496439572358 -16 ) ( -53.82462124191443 306.0135844466258 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -52.93381857825254 203.9377096862978 -16 ) ( -53.82462124191079 205.98641555337963 0 ) ( -52.93381857825261 203.9377096863027 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -52.93381857825443 308.06229031370367 0 ) ( -53.82462124191443 306.0135844466258 -16 ) ( -52.93381857825443 308.06229031370367 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -52.007396740693366 310.09513750280985 0 ) ( -52.93381857825443 308.06229031370367 -16 ) ( -52.007396740693366 310.09513750280985 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -52.93381857825254 203.9377096862978 -16 ) ( -52.00739674068609 201.90486249719834 0 ) ( -52.0073967406874 201.9048624971923 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -51.04563792629415 199.88849321100133 0 ) ( -52.0073967406874 201.9048624971923 -16 ) ( -52.00739674068609 201.90486249719834 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -51.04563792629233 312.1115067890014 0 ) ( -52.007396740693366 310.09513750280985 -16 ) ( -51.04563792629233 312.1115067890014 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -50.04883509611136 314.1107839666638 0 ) ( -51.04563792629233 312.1115067890014 -16 ) ( -50.04883509611136 314.1107839666638 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -50.0488350961105 197.88921603333836 -16 ) ( -51.04563792629415 199.88849321100133 0 ) ( -50.04883509611136 197.88921603334165 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -50.0488350961105 197.88921603333836 -16 ) ( -49.01729188594254 195.90763996340684 0 ) ( -49.01729188594372 195.90763996340547 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -49.01729188594254 316.09236003659316 0 ) ( -50.04883509611136 314.1107839666638 -16 ) ( -49.01729188594254 316.09236003659316 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -47.95132251384345 193.94436860846855 -16 ) ( -49.01729188594254 195.90763996340684 0 ) ( -47.9513225138453 193.94436860846326 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -47.95132251384348 318.0556313915331 0 ) ( -49.01729188594254 316.09236003659316 -16 ) ( -47.95132251384348 318.0556313915331 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -46.851251684405725 320 0 ) ( -47.95132251384348 318.0556313915331 -16 ) ( -46.851251684405725 320 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -46.85125168440754 192 0 ) ( -47.95132251384345 193.94436860846855 -16 ) ( -47.9513225138453 193.94436860846326 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -45.71741448986904 321.9248735884885 0 ) ( -46.851251684405725 320 -16 ) ( -45.71741448986904 321.9248735884885 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -45.71741448987061 190.075126411513 -16 ) ( -46.85125168440754 192 0 ) ( -45.71741448987086 190.07512641151698 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -44.55015630802154 323.82966582185145 0 ) ( -45.71741448986904 321.9248735884885 -16 ) ( -44.55015630802154 323.82966582185145 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -45.71741448987061 190.075126411513 -16 ) ( -44.55015630802245 188.170334178154 0 ) ( -44.550156308022856 188.1703341781497 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -43.34983269701479 186.2862035180733 0 ) ( -44.550156308022856 188.1703341781497 -16 ) ( -44.55015630802245 188.170334178154 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -43.34983269701479 325.71379648192305 0 ) ( -44.55015630802154 323.82966582185145 -16 ) ( -43.34983269701479 325.71379648192305 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -42.116809287048454 327.57669164425715 0 ) ( -43.34983269701479 325.71379648192305 -16 ) ( -42.116809287048454 327.57669164425715 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -42.11680928704361 184.4233083557457 -16 ) ( -43.34983269701479 186.2862035180733 0 ) ( -42.11680928704391 184.42330835574103 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -40.85146166898891 182.58221614706764 -16 ) ( -42.11680928704391 184.42330835574103 0 ) ( -40.85146166898994 182.58221614706417 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -40.85146166898994 329.417783852934 0 ) ( -42.116809287048454 327.57669164425715 -16 ) ( -40.85146166898994 329.417783852934 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -39.55417527999397 180.76348770656304 -16 ) ( -40.85146166898994 182.58221614706417 0 ) ( -39.554175279994524 180.7634877065575 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -39.554175279992705 331.23651229343704 0 ) ( -40.85146166898994 329.417783852934 -16 ) ( -39.554175279992705 331.23651229343704 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -38.225345286056836 333.03232296346505 0 ) ( -39.554175279992705 331.23651229343704 -16 ) ( -38.225345286056836 333.03232296346505 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -38.22534528605047 178.9676770365404 0 ) ( -39.55417527999397 180.76348770656304 -16 ) ( -39.554175279994524 180.7634877065575 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -36.86537646166285 177.19533115831393 -16 ) ( -38.22534528605047 178.9676770365404 0 ) ( -36.865376461663345 177.1953311583129 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -36.86537646165925 334.80466884168527 0 ) ( -38.225345286056836 333.03232296346505 -16 ) ( -36.86537646165925 334.80466884168527 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -36.86537646165925 334.80466884168527 -16 ) ( -35.47468306649171 336.5530100543783 0 ) ( -36.86537646165925 334.80466884168527 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -35.474683066491565 175.4469899456215 -16 ) ( -36.865376461663345 177.1953311583129 0 ) ( -35.47468306649171 175.4469899456235 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -34.05368871922725 173.72318596012465 -16 ) ( -35.47468306649171 175.4469899456235 0 ) ( -34.053688719227466 173.7231859601161 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -34.05368871922656 338.2768140398766 0 ) ( -35.47468306649171 336.5530100543783 -16 ) ( -34.05368871922656 338.2768140398766 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -34.05368871922725 173.72318596012465 -16 ) ( -32.60282626851631 172.02444428921444 0 ) ( -32.60282626851649 172.02444428921373 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -32.602826268515855 339.97555571078374 0 ) ( -34.05368871922656 338.2768140398766 -16 ) ( -32.602826268515855 339.97555571078374 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -31.122537661107344 341.64871761393624 0 ) ( -32.602826268515855 339.97555571078374 -16 ) ( -31.122537661107344 341.64871761393624 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -31.122537661104595 170.35128238606794 -16 ) ( -32.60282626851631 172.02444428921444 0 ) ( -31.122537661104843 170.35128238606558 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -29.613273807253957 168.70420991200015 -16 ) ( -31.122537661104843 170.35128238606558 0 ) ( -29.613273807254018 168.7042099120008 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -29.61327380725379 343.295790088001 0 ) ( -31.122537661107344 341.64871761393624 -16 ) ( -29.61327380725379 343.295790088001 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -28.075494443346543 344.91627141875324 0 ) ( -29.61327380725379 343.295790088001 -16 ) ( -28.075494443346543 344.91627141875324 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -29.613273807253957 168.70420991200015 -16 ) ( -28.075494443346997 167.08372858125222 0 ) ( -28.07549444334702 167.08372858124875 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -28.075494443346997 167.08372858125222 0 ) ( -26.509667991878015 165.49033200812207 -16 ) ( -28.07549444334702 167.08372858124875 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -26.509667991875176 346.5096679918788 -16 ) ( -28.075494443346543 344.91627141875324 0 ) ( -28.075494443346543 344.91627141875324 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -24.916271418755084 163.92450555664922 -16 ) ( -26.509667991878015 165.4903320081212 0 ) ( -24.916271418755173 163.92450555664982 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -24.916271418747783 348.07549444334654 -16 ) ( -26.509667991878814 346.5096679918752 0 ) ( -26.509667991875176 346.5096679918788 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -23.295790087999194 349.6132738072556 0 ) ( -24.916271418747783 348.07549444334654 -16 ) ( -23.295790087999194 349.61327380725197 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -23.295790088002377 162.3867261927444 0 ) ( -24.916271418755084 163.92450555664922 -16 ) ( -24.916271418755173 163.92450555664982 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -21.648717613934423 351.1225376611037 0 ) ( -23.295790087999194 349.61327380725197 -16 ) ( -21.64871761393806 351.12253766110734 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -23.29579008800241 162.3867261927435 -16 ) ( -21.648717613935105 160.87746233888902 0 ) ( -21.64871761393522 160.87746233889212 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -21.64871761393522 160.87746233889212 -16 ) ( -19.975555710785102 159.39717373148596 0 ) ( -19.975555710785244 159.39717373148497 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -19.975555710785557 352.60282626851404 0 ) ( -21.64871761393806 351.12253766110734 -16 ) ( -19.975555710785557 352.60282626851404 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -18.276814039878484 157.94631128076924 -16 ) ( -19.975555710785102 159.39717373148596 0 ) ( -18.2768140398789 157.94631128077162 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -18.276814039876626 354.0536887192284 0 ) ( -19.975555710785557 352.60282626851404 -16 ) ( -18.276814039876626 354.0536887192284 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -16.553010054381048 156.52531693350466 0 ) ( -18.276814039878484 157.94631128076924 -16 ) ( -18.2768140398789 157.94631128077162 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -16.5530100543765 355.4746830664917 -16 ) ( -18.276814039876626 354.0536887192284 0 ) ( -18.276814039876626 354.0536887192284 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -14.804668841683451 356.86537646166107 -16 ) ( -16.5530100543765 355.47468306648807 0 ) ( -16.5530100543765 355.4746830664917 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -14.804668841682997 155.13462353833893 0 ) ( -16.553010054381556 156.525316933505 -16 ) ( -16.553010054381048 156.52531693350466 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -13.03232296346323 358.2253452860532 0 ) ( -14.804668841683451 356.86537646166107 -16 ) ( -13.03232296346323 358.2253452860532 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -13.0323229634622 153.7746547139467 -16 ) ( -14.804668841682997 155.13462353833893 0 ) ( -13.03232296346232 153.7746547139468 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -11.236512293435226 359.5541752799909 0 ) ( -13.03232296346323 358.2253452860532 -16 ) ( -11.236512293438864 359.55417527999816 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -11.236512293438135 152.44582472000477 -16 ) ( -13.03232296346232 153.7746547139468 0 ) ( -11.236512293438864 152.4458247200091 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -9.417783852935475 151.14853833100702 -16 ) ( -11.236512293438864 152.4458247200091 0 ) ( -9.417783852935827 151.14853833100642 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -11.236512293435226 359.5541752799909 0 ) ( -9.417783852932189 360.85146166898994 -16 ) ( -9.417783852932189 360.8514616689863 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -7.576691644251696 362.116809287043 -16 ) ( -9.417783852932189 360.8514616689863 0 ) ( -9.417783852932189 360.85146166898994 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -7.576691644253725 149.88319071295763 -16 ) ( -9.417783852935827 151.14853833100642 0 ) ( -7.576691644254424 149.883190712957 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -5.713796481926594 148.65016730298115 -16 ) ( -7.576691644254424 149.883190712957 0 ) ( -5.713796481926693 148.6501673029852 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -7.576691644251696 362.116809287043 -16 ) ( -5.713796481923055 363.3498326970148 0 ) ( -7.576691644255334 362.1168092870503 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -3.8296658218496304 364.55015630802154 -16 ) ( -5.713796481923055 363.3498326970148 0 ) ( -5.713796481926693 363.3498326970148 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -3.829665821848721 147.4498436919821 0 ) ( -5.713796481926594 148.65016730298115 -16 ) ( -5.713796481926693 148.6501673029852 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -1.924873588486662 146.2825855101255 0 ) ( -3.829665821849007 147.44984369197965 -16 ) ( -3.829665821848721 147.4498436919821 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -1.924873588483024 365.71741448987086 -16 ) ( -3.8296658218532684 364.5501563080252 0 ) ( -3.8296658218496304 364.55015630802154 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 0 366.851251684413 0 ) ( -1.924873588483024 365.71741448987086 -16 ) ( 0 366.85125168440936 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 0 145.1487483155888 -16 ) ( -1.924873588486662 146.2825855101255 0 ) ( 0 145.148748315587 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 1.9443686084705405 367.9513225138362 0 ) ( 0 366.85125168440936 -16 ) ( 1.9443686084669025 367.9513225138435 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 1.9443686084687215 144.04867748615652 0 ) ( 0 145.1487483155888 -16 ) ( 0 145.148748315587 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 3.9076399634050176 142.98270811405382 0 ) ( 1.9443686084686924 144.0486774861571 -16 ) ( 1.9443686084687215 144.04867748615652 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 3.9076399634104746 369.0172918859389 0 ) ( 1.9443686084669025 367.9513225138435 -16 ) ( 3.9076399634031986 369.0172918859462 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 5.889216033338016 141.95116490389046 0 ) ( 3.9076399634049253 142.9827081140554 -16 ) ( 3.9076399634050176 142.98270811405382 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 5.889216033341654 370.04883509610954 -16 ) ( 3.9076399634104746 369.0172918859389 0 ) ( 3.9076399634031986 369.0172918859462 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 7.888493211001332 371.0456379262905 -16 ) ( 5.889216033338016 370.04883509610954 0 ) ( 5.889216033341654 370.04883509610954 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 7.888493210999976 140.9543620737106 -16 ) ( 5.889216033338016 141.95116490389046 0 ) ( 7.888493210997694 140.95436207371677 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 7.888493210997694 140.95436207371677 0 ) ( 9.904862497190013 139.9926032593079 -16 ) ( 7.888493210999976 140.9543620737106 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 9.9048624971947 372.0073967406861 0 ) ( 7.888493211001332 371.0456379262905 -16 ) ( 9.904862497191061 372.0073967406897 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 11.937709686300877 372.93381857825443 0 ) ( 9.904862497191061 372.0073967406897 -16 ) ( 11.937709686299058 372.93381857825443 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 11.93770968629724 139.06618142174557 0 ) ( 9.904862497190013 139.9926032593079 -16 ) ( 9.904862497189242 139.99260325930663 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 13.98641555337301 138.1753787580879 -16 ) ( 11.93770968629724 139.06618142174557 0 ) ( 13.986415553372353 138.1753787580892 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 13.986415553375991 373.8246212419108 0 ) ( 11.937709686299058 372.93381857825443 -16 ) ( 13.986415553374172 373.8246212419108 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 16.05035604276418 374.67953338454754 -16 ) ( 13.986415553375991 373.8246212419108 0 ) ( 13.986415553374172 373.8246212419108 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 16.050356042764772 137.3204666154551 -16 ) ( 13.986415553372353 138.1753787580892 0 ) ( 16.05035604276418 137.3204666154561 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 18.128902458201992 136.50170540835097 0 ) ( 16.050356042764772 137.3204666154551 -16 ) ( 16.05035604276418 137.3204666154561 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 16.05035604276418 374.67953338454754 -16 ) ( 18.128902458201992 375.49829459164175 0 ) ( 16.05035604276236 374.6795333845512 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 20.221421654312508 376.2806554605959 -16 ) ( 18.128902458201992 375.49829459164175 0 ) ( 18.128902458200173 375.4982945916454 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 20.221421654314327 135.71934453940776 0 ) ( 18.128902458201402 136.5017054083579 -16 ) ( 18.128902458201992 136.50170540835097 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 22.32727622948005 377.02637767671695 -16 ) ( 20.22142165431069 376.2806554605995 0 ) ( 20.221421654312508 376.2806554605959 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 22.32727622948424 134.97362232328848 -16 ) ( 20.221421654314327 135.71934453940776 0 ) ( 22.327276229481868 134.97362232329033 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 24.445824720009114 134.26476591422397 0 ) ( 22.32727622948424 134.97362232328848 -16 ) ( 22.327276229481868 134.97362232329033 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 24.445824720010933 377.735234085776 -16 ) ( 22.32727622947823 377.02637767671695 0 ) ( 22.32727622948005 377.02637767671695 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 26.576421795492934 378.40700876326446 0 ) ( 24.445824720010933 377.735234085776 -16 ) ( 26.576421795491115 378.40700876327173 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 26.576421795489477 133.59299123673094 -16 ) ( 24.445824720009114 134.26476591422397 0 ) ( 26.576421795489296 133.59299123673554 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 28.7184184554244 132.95850291989336 0 ) ( 26.576421795489477 133.59299123673094 -16 ) ( 26.576421795489296 133.59299123673554 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 28.7184184554244 379.04149708010664 -16 ) ( 26.576421795492934 378.40700876326446 0 ) ( 26.576421795491115 378.40700876327173 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 30.87116222687655 379.6385057650041 -16 ) ( 28.71841845542258 379.04149708010664 0 ) ( 28.7184184554244 379.04149708010664 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 30.87116222687734 132.36149423499947 -16 ) ( 28.7184184554244 132.95850291989336 0 ) ( 30.87116222687473 132.3614942350032 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 30.87116222687473 132.3614942350032 0 ) ( 33.03399736324273 131.8021470366735 -16 ) ( 30.87116222687734 132.36149423499947 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 30.87116222687473 379.6385057650041 0 ) ( 33.03399736324627 380.1978529633234 -16 ) ( 33.03399736324536 380.197852963327 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 35.206265043986605 380.7193682925099 -16 ) ( 33.03399736324536 380.197852963327 0 ) ( 33.03399736324627 380.1978529633234 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 35.206265043985326 131.28063170749036 -16 ) ( 33.033997363239905 131.80214703667298 0 ) ( 35.206265043983876 131.28063170748646 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 37.38730357532495 381.20289289392895 -16 ) ( 35.206265043986605 380.71936829250626 0 ) ( 35.206265043986605 380.7193682925099 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 37.38730357532768 130.79710710608197 0 ) ( 35.206265043985326 131.28063170749036 -16 ) ( 35.206265043983876 131.28063170748646 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 39.576448591801515 130.35172051869546 -16 ) ( 37.38730357532768 130.79710710608197 0 ) ( 39.57644859180073 130.35172051870177 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 39.576448591801636 381.64827948129823 0 ) ( 37.38730357532495 381.20289289392895 -16 ) ( 39.57644859179982 381.6482794813055 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 41.773033258632495 129.9446076144377 0 ) ( 39.576448591801515 130.35172051869546 -16 ) ( 39.57644859180073 130.35172051870177 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 41.773033258634314 382.0553923855623 0 ) ( 39.57644859179982 381.6482794813055 -16 ) ( 41.773033258634314 382.0553923855623 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 41.773033258634314 382.0553923855623 -16 ) ( 43.97638847484723 382.4241075961836 0 ) ( 41.773033258634314 382.0553923855623 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 43.976388474850154 129.57589240382077 -16 ) ( 41.773033258632495 129.9446076144377 0 ) ( 43.976388474849955 129.57589240382367 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 46.18584307711145 129.24568720107808 -16 ) ( 43.976388474849955 129.57589240382367 0 ) ( 46.185843077109894 129.24568720108073 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 46.18584307711444 382.7543127989193 0 ) ( 43.97638847484723 382.4241075961836 -16 ) ( 46.18584307711444 382.7543127989193 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 48.40072404414102 128.9540925899102 -16 ) ( 46.185843077109894 129.24568720108073 0 ) ( 48.40072404413877 128.95409258991276 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 48.400724044144226 383.0459074100836 -16 ) ( 46.18584307711444 382.7543127989193 0 ) ( 46.18584307711444 382.7543127989193 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 48.400724044144226 383.0459074100836 -16 ) ( 50.62035670174146 383.2988026071398 0 ) ( 48.40072404414377 383.04590741008724 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 50.62035670174064 128.70119739286412 -16 ) ( 48.40072404413877 128.95409258991276 0 ) ( 50.62035670174009 128.70119739286747 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 52.84406492830385 128.48707864425523 0 ) ( 50.62035670174064 128.70119739286412 -16 ) ( 50.62035670174009 128.70119739286747 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 52.84406492830203 383.5129213557411 -16 ) ( 50.62035670174146 383.2988026071398 0 ) ( 50.62035670174146 383.2988026071398 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 55.07117136075021 383.68819843325764 -16 ) ( 52.84406492830203 383.5129213557375 0 ) ( 52.84406492830203 383.5129213557411 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 55.07117136075191 128.31180156674233 -16 ) ( 52.84406492830385 128.48707864425523 0 ) ( 55.07117136074885 128.31180156674236 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 57.300997600902065 383.82458044858504 0 ) ( 55.07117136075021 383.68819843325764 -16 ) ( 57.30099760090184 383.8245804485887 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 57.30099760090525 128.17541955141496 0 ) ( 55.07117136075191 128.31180156674233 -16 ) ( 55.07117136074885 128.31180156674236 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 59.53286442207992 128.07797414155803 -16 ) ( 57.30099760090525 128.17541955141496 0 ) ( 59.532864422079 128.07797414155357 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 59.53286442208014 383.9220258584428 -16 ) ( 57.300997600902065 383.82458044858504 0 ) ( 57.30099760090184 383.8245804485887 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 59.53286442208014 383.9220258584428 -16 ) ( 61.766091976026814 383.98050498001976 0 ) ( 59.53286442207991 383.92202585844643 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 61.76609197602771 128.01949501998467 -16 ) ( 59.532864422079 128.07797414155357 0 ) ( 61.76609197602738 128.01949501998752 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 64 128 0 ) ( 61.76609197602771 128.01949501998467 -16 ) ( 61.76609197602738 128.01949501998752 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 61.766091976026814 383.9805049800161 -16 ) ( 64 384 0 ) ( 61.766091976026814 383.98050498001976 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 192 256 -16 ) ( 191.92202585844444 251.53286442208 -16 ) ( 191.98050498001825 253.76609197602778 -16 ) bolt16 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 192 256 0 ) ( 191.92202585844421 260.46713557792003 0 ) ( 191.98050498002019 258.2339080239744 0 ) bolt16 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 66.23390802397387 383.98050498001976 0 ) ( 64 384 -16 ) ( 66.23390802397387 383.98050498001976 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 66.23390802397262 128.01949501998024 0 ) ( 64 128 -16 ) ( 64 128 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 68.46713557792464 128.07797414154993 0 ) ( 66.23390802397228 128.01949501998317 -16 ) ( 66.23390802397262 128.01949501998024 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 68.46713557791986 383.92202585844643 -16 ) ( 66.23390802397387 383.98050498001976 0 ) ( 66.23390802397387 383.98050498001976 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 70.69900239909771 383.82458044858504 0 ) ( 68.46713557791986 383.92202585844643 -16 ) ( 70.69900239909771 383.82458044858504 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 70.69900239909663 128.17541955141755 -16 ) ( 68.46713557792464 128.07797414154993 0 ) ( 70.69900239909475 128.1754195514186 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 70.69900239909663 128.17541955141755 -16 ) ( 72.92882863925115 128.31180156674236 0 ) ( 72.92882863924787 128.3118015667447 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 72.92882863924979 383.68819843325764 -16 ) ( 70.69900239909771 383.82458044858504 0 ) ( 70.69900239909771 383.82458044858504 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 72.92882863924933 383.68819843325036 0 ) ( 75.15593507170115 383.5129213557484 -16 ) ( 75.1559350717007 383.51292135574477 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 72.92882863925115 128.31180156674236 0 ) ( 75.1559350717002 128.48707864425702 -16 ) ( 72.92882863924787 128.3118015667447 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 77.37964329825991 128.70119739286383 0 ) ( 75.1559350717002 128.48707864425702 -16 ) ( 75.15593507169979 128.48707864425887 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 77.37964329825991 383.2988026071398 0 ) ( 75.15593507170115 383.5129213557484 -16 ) ( 77.37964329825945 383.29880260713617 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 77.37964329825945 383.29880260713617 -16 ) ( 79.59927595585941 383.0459074100945 0 ) ( 77.37964329825991 383.2988026071398 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 79.59927595585907 128.95409258990912 -16 ) ( 77.37964329825991 128.70119739286383 0 ) ( 79.59927595585759 128.95409258990912 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 81.814156922888 129.2456872010817 -16 ) ( 79.59927595585759 128.95409258990912 0 ) ( 81.81415692288647 129.24568720108437 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 79.5992759558585 383.04590741008724 -16 ) ( 81.81415692288829 382.7543127989193 0 ) ( 79.59927595585941 383.0459074100945 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 84.02361152515005 129.57589240382367 0 ) ( 81.814156922888 129.2456872010817 -16 ) ( 81.81415692288647 129.24568720108437 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 84.02361152515095 382.42410759617997 -16 ) ( 81.81415692288829 382.7543127989193 0 ) ( 81.81415692288829 382.7543127989193 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 86.2269667413675 129.94460761443406 0 ) ( 84.02361152514938 129.57589240382362 -16 ) ( 84.02361152515005 129.57589240382367 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 86.2269667413675 382.0553923855623 0 ) ( 84.02361152515095 382.42410759617997 -16 ) ( 86.2269667413675 382.0553923855623 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 88.42355140819564 381.6482794813055 0 ) ( 86.2269667413675 382.0553923855623 -16 ) ( 88.42355140819564 381.6482794813055 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 88.42355140819846 130.35172051869543 -16 ) ( 86.2269667413675 129.94460761443406 0 ) ( 88.42355140819564 130.3517205186945 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 90.61269642467393 130.79710710606955 -16 ) ( 88.42355140819564 130.3517205186945 0 ) ( 90.61269642466868 130.7971071060747 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 90.61269642467505 381.2028928939253 0 ) ( 88.42355140819564 381.6482794813055 -16 ) ( 90.61269642467414 381.20289289392167 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 92.79373495601612 131.28063170748646 0 ) ( 90.61269642467393 130.79710710606955 -16 ) ( 90.61269642466868 130.7971071060747 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 90.61269642467505 381.2028928939253 0 ) ( 92.7937349560143 380.7193682925172 -16 ) ( 92.7937349560134 380.71936829251354 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 92.79373495601612 131.28063170748646 0 ) ( 94.96600263675747 131.80214703667252 -16 ) ( 92.7937349560155 131.2806317074866 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 94.96600263675828 380.197852963327 0 ) ( 92.7937349560143 380.7193682925172 -16 ) ( 94.96600263675828 380.197852963327 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 97.12883777312527 379.63850576500045 -16 ) ( 94.96600263675828 380.197852963327 0 ) ( 94.96600263675828 380.197852963327 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 97.12883777312163 132.3614942350032 0 ) ( 94.96600263675747 131.80214703667252 -16 ) ( 94.96600263675646 131.80214703666934 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 99.28158154457924 132.95850291989336 0 ) ( 97.1288377731211 132.3614942350051 -16 ) ( 97.12883777312163 132.3614942350032 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 99.28158154457924 379.0414970801103 -16 ) ( 97.12883777312163 379.6385057649968 0 ) ( 97.12883777312527 379.63850576500045 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 101.42357820451099 133.59299123672932 -16 ) ( 99.28158154457924 132.95850291989336 0 ) ( 101.42357820450707 133.59299123672827 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 99.28158154457924 379.0414970801103 -16 ) ( 101.42357820451252 378.40700876326446 0 ) ( 99.2815815445756 379.04149708010664 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 103.55417527999452 377.7352340857833 0 ) ( 101.42357820451252 378.40700876326446 -16 ) ( 103.5541752799927 377.73523408577967 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 103.55417527999238 134.26476591422332 -16 ) ( 101.42357820450707 133.59299123672827 0 ) ( 103.55417527999089 134.26476591422397 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 105.6727237705175 134.97362232328337 -16 ) ( 103.55417527999089 134.26476591422397 0 ) ( 105.67272377051631 134.97362232328305 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 105.67272377051813 377.02637767671695 -16 ) ( 103.55417527999452 377.7352340857833 0 ) ( 103.5541752799927 377.73523408577967 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 105.67272377051813 377.02637767671695 -16 ) ( 107.77857834568567 376.2806554605995 0 ) ( 105.6727237705145 377.02637767670967 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 107.77857834568579 135.7193445394034 -16 ) ( 105.67272377051631 134.97362232328305 0 ) ( 107.77857834568204 135.71934453940412 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 109.87109754179983 136.50170540835643 0 ) ( 107.77857834568579 135.7193445394034 -16 ) ( 107.77857834568204 135.71934453940412 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 109.87109754179801 375.49829459164175 0 ) ( 107.77857834568567 376.2806554605959 -16 ) ( 109.87109754179801 375.49829459164175 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 111.9496439572373 137.32046661545004 -16 ) ( 109.87109754179983 136.50170540835643 0 ) ( 111.94964395723582 137.32046661545064 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 111.94964395723764 374.6795333845439 -16 ) ( 109.87109754179801 375.49829459164175 0 ) ( 109.87109754179801 375.49829459164175 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 114.01358444662765 138.1753787580892 0 ) ( 111.9496439572373 137.32046661545004 -16 ) ( 111.94964395723582 137.32046661545064 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 111.94964395723764 374.6795333845439 -16 ) ( 114.01358444662836 373.82462124190937 0 ) ( 111.94964395723673 374.67953338454913 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 116.06229031370177 139.06618142174887 -16 ) ( 114.01358444662765 138.1753787580892 0 ) ( 116.06229031370094 139.0661814217492 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 116.06229031370276 372.93381857824716 -16 ) ( 114.01358444662836 373.82462124190937 0 ) ( 114.01358444662765 373.8246212419108 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 118.09513750281258 139.99260325930663 0 ) ( 116.06229031370177 139.06618142174887 -16 ) ( 116.06229031370094 139.0661814217492 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 118.09513750281076 372.0073967406897 -16 ) ( 116.0622903137026 372.93381857825284 0 ) ( 116.06229031370276 372.93381857824716 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 120.11150678900167 371.0456379262944 0 ) ( 118.09513750281076 372.0073967406897 -16 ) ( 120.11150678900049 371.0456379262978 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 120.11150678900594 140.95436207370403 0 ) ( 118.09513750281104 139.99260325930575 -16 ) ( 118.09513750281258 139.99260325930663 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 122.11078396666338 370.0488350961072 0 ) ( 120.11150678900049 371.0456379262978 -16 ) ( 122.11078396666198 370.0488350961132 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 122.11078396666237 141.95116490388838 -16 ) ( 120.11150678900594 140.95436207370403 0 ) ( 122.11078396666198 141.95116490388864 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 122.11078396666198 370.0488350961132 -16 ) ( 124.09236003659467 369.01729188594084 0 ) ( 122.11078396666338 370.0488350961072 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 124.09236003659336 142.9827081140588 -16 ) ( 122.11078396666198 141.95116490388864 0 ) ( 124.09236003659316 142.98270811405928 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 126.0556313915331 367.9513225138435 -16 ) ( 124.09236003659467 369.01729188594084 0 ) ( 124.09236003659316 369.01729188594254 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 124.09236003659316 142.98270811405928 0 ) ( 126.05563139153041 144.0486774861589 -16 ) ( 124.09236003659336 142.9827081140588 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 128 145.1487483155929 -16 ) ( 126.05563139152946 144.04867748615834 0 ) ( 128 145.14874831559246 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 128 366.8512516844061 0 ) ( 126.0556313915331 367.9513225138435 -16 ) ( 128 366.8512516844057 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 129.92487358848666 365.7174144898745 -16 ) ( 128 366.8512516844061 0 ) ( 128 366.8512516844057 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 128 145.1487483155929 -16 ) ( 129.92487358848666 146.28258551013096 0 ) ( 129.92487358848626 146.28258551013093 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 131.82966582184963 147.44984369197937 0 ) ( 129.92487358848626 146.28258551013093 -16 ) ( 129.92487358848666 146.28258551013096 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 131.82966582185327 364.55015630802154 -16 ) ( 129.92487358848638 365.717414489874 0 ) ( 129.92487358848666 365.7174144898745 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 133.7137964819267 148.6501673029852 0 ) ( 131.82966582184923 147.44984369197923 -16 ) ( 131.82966582184963 147.44984369197937 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 133.7137964819235 363.3498326970158 0 ) ( 131.82966582185327 364.55015630802154 -16 ) ( 133.71379648192305 363.34983269701115 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 133.71379648192305 363.34983269701115 -16 ) ( 135.57669164425533 362.11680928704857 0 ) ( 133.7137964819235 363.3498326970158 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 135.57669164425897 149.88319071295518 0 ) ( 133.7137964819238 148.6501673029853 -16 ) ( 133.7137964819267 148.6501673029852 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 135.57669164425533 362.116809287043 -16 ) ( 137.41778385293378 360.8514616689937 0 ) ( 135.57669164425533 362.11680928704857 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 137.4177838529339 151.14853833100918 -16 ) ( 135.57669164425897 149.88319071295518 0 ) ( 137.4177838529322 151.14853833100915 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 139.2365122934371 152.44582472000607 -16 ) ( 137.4177838529322 151.14853833100915 0 ) ( 139.23651229343523 152.44582472000639 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 137.4177838529322 360.85146166898994 -16 ) ( 139.23651229343682 359.55417527999384 0 ) ( 137.41778385293378 360.8514616689937 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 141.03232296346323 358.2253452860532 -16 ) ( 139.23651229343682 359.55417527999384 0 ) ( 139.23651229343523 359.5541752799909 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 141.03232296346323 153.7746547139468 0 ) ( 139.2365122934371 152.44582472000607 -16 ) ( 139.23651229343523 152.44582472000639 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 142.8046688416871 155.13462353833756 0 ) ( 141.03232296346195 153.7746547139469 -16 ) ( 141.03232296346323 153.7746547139468 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 142.8046688416847 356.8653764616601 0 ) ( 141.03232296346323 358.2253452860532 -16 ) ( 142.80466884168345 356.86537646166107 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 144.55301005438014 355.4746830664917 -16 ) ( 142.8046688416847 356.8653764616601 0 ) ( 142.80466884168345 356.86537646166107 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 144.55301005438014 156.5253169335092 0 ) ( 142.8046688416857 155.13462353833788 -16 ) ( 142.8046688416871 155.13462353833756 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 144.5530100543781 156.5253169335092 -16 ) ( 146.27681403988026 157.94631128077071 0 ) ( 146.27681403987722 157.9463112807707 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 146.27681403987714 354.053688719232 0 ) ( 144.55301005438014 355.4746830664917 -16 ) ( 146.27681403987663 354.05368871922474 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 147.9755557107892 352.60282626851404 -16 ) ( 146.27681403987714 354.053688719232 0 ) ( 146.27681403987663 354.05368871922474 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 146.27681403987722 157.9463112807707 -16 ) ( 147.97555571078556 159.39717373148642 0 ) ( 147.97555571078405 159.39717373148636 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 147.97555571078405 159.39717373148636 -16 ) ( 149.64871761393442 160.87746233889425 0 ) ( 149.64871761393346 160.8774623388941 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 149.64871761393442 351.12253766110734 -16 ) ( 147.9755557107857 352.60282626850886 0 ) ( 147.9755557107892 352.60282626851404 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 149.64871761393442 160.87746233889425 0 ) ( 151.29579008800127 162.38672619274476 -16 ) ( 149.64871761393346 160.8774623388941 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 151.29579008800033 349.61327380725015 0 ) ( 149.64871761393442 351.12253766110734 -16 ) ( 151.2957900879992 349.61327380725197 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 152.91627141875202 348.07549444334745 0 ) ( 151.2957900879992 349.61327380725197 -16 ) ( 152.91627141875142 348.07549444334654 -16 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 152.91627141875142 163.9245055566538 0 ) ( 151.29579008800127 162.38672619274476 -16 ) ( 151.2957900879992 162.38672619274485 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 154.50966799188245 165.4903320081201 0 ) ( 152.91627141875068 163.92450555665383 -16 ) ( 152.91627141875142 163.9245055566538 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 152.91627141875202 348.07549444334745 0 ) ( 154.50966799188245 346.5096679918788 -16 ) ( 154.50966799187842 346.50966799187813 0 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 156.07549444334708 167.08372858124875 0 ) ( 154.50966799188004 165.4903320081201 -16 ) ( 154.50966799188245 165.4903320081201 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 156.07549444334765 344.9162714187503 0 ) ( 154.50966799188245 346.5096679918788 -16 ) ( 156.07549444334654 344.9162714187478 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 157.6132738072556 343.29579008800283 -16 ) ( 156.07549444334765 344.9162714187503 0 ) ( 156.07549444334654 344.9162714187478 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 157.61327380725925 168.70420991199785 -16 ) ( 156.07549444334708 167.08372858124875 0 ) ( 157.6132738072565 168.70420991199782 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 159.12253766111127 170.35128238606188 0 ) ( 157.61327380725925 168.70420991199785 -16 ) ( 157.6132738072565 168.70420991199782 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 157.61327380725416 343.2957900880004 0 ) ( 159.12253766110734 341.64871761393806 -16 ) ( 159.12253766110678 341.6487176139347 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 160.60282626851767 339.9755557107819 -16 ) ( 159.12253766110678 341.6487176139347 0 ) ( 159.12253766110734 341.64871761393806 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 160.60282626851404 172.02444428921535 -16 ) ( 159.12253766111127 170.35128238606188 0 ) ( 160.6028262685108 172.02444428921868 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 162.0536887192299 173.72318596012252 0 ) ( 160.60282626851404 172.02444428921535 -16 ) ( 160.6028262685108 172.02444428921868 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 162.053688719232 338.2768140398766 -16 ) ( 160.60282626851492 339.97555571078226 0 ) ( 160.60282626851767 339.9755557107819 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 163.474683066492 175.44698994562117 0 ) ( 162.05368871922838 173.7231859601261 -16 ) ( 162.0536887192299 173.72318596012252 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 163.47468306649228 336.55301005437633 0 ) ( 162.053688719232 338.2768140398766 -16 ) ( 163.4746830664917 336.5530100543765 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 164.86537646166107 334.80466884168345 -16 ) ( 163.47468306649228 336.55301005437633 0 ) ( 163.4746830664917 336.5530100543765 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 163.47468306648807 175.4469899456244 -16 ) ( 164.86537646166323 177.19533115831368 0 ) ( 164.86537646165743 177.19533115832064 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 164.86537646166323 177.19533115831368 0 ) ( 166.22534528605684 178.96767703653586 -16 ) ( 164.86537646165743 177.19533115832064 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 166.22534528605374 333.0323229634623 0 ) ( 164.86537646166107 334.80466884168345 -16 ) ( 166.2253452860532 333.03232296346323 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 167.55417527999452 331.2365122934425 -16 ) ( 166.22534528605374 333.0323229634623 0 ) ( 166.2253452860532 333.03232296346323 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 167.55417527999376 180.76348770656324 0 ) ( 166.22534528605684 178.96767703653586 -16 ) ( 166.22534528605297 178.96767703653836 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 168.85146166899574 182.5822161470629 0 ) ( 167.5541752799909 180.7634877065666 -16 ) ( 167.55417527999376 180.76348770656324 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 168.85146166899102 329.417783852933 0 ) ( 167.55417527999452 331.2365122934425 -16 ) ( 168.85146166898994 329.4177838529322 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 168.85146166899574 182.5822161470629 0 ) ( 170.11680928705027 184.42330835574103 -16 ) ( 168.85146166898994 182.58221614706963 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 170.11680928704664 327.57669164425533 -16 ) ( 168.85146166899102 329.417783852933 0 ) ( 168.85146166898994 329.4177838529322 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 171.3498326970148 325.71379648192305 -16 ) ( 170.11680928704533 327.57669164425425 0 ) ( 170.11680928704664 327.57669164425533 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 170.11680928704155 184.4233083557471 0 ) ( 171.3498326970148 186.28620351807513 -16 ) ( 170.11680928705027 184.42330835574103 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 172.55015630802518 188.17033417814582 -16 ) ( 171.349832697012 186.28620351807817 0 ) ( 172.55015630802396 188.17033417814906 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 172.55015630802518 323.82966582185327 -16 ) ( 171.34983269701422 325.71379648192215 0 ) ( 171.3498326970148 325.71379648192305 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 173.71741448987086 321.92487358848666 -16 ) ( 172.55015630802222 323.8296658218481 0 ) ( 172.55015630802518 323.82966582185327 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 173.71741448986788 190.0751264115147 0 ) ( 172.55015630802518 188.17033417814582 -16 ) ( 172.55015630802396 188.17033417814906 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 173.7174144898636 190.07512641151334 -16 ) ( 174.8512516844073 192 0 ) ( 174.85125168440572 192 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 174.85125168440936 320 -16 ) ( 173.71741448987012 321.92487358848524 0 ) ( 173.71741448987086 321.92487358848666 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 175.95132251384283 193.94436860846878 0 ) ( 174.85125168440572 192 -16 ) ( 174.8512516844073 192 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 175.9513225138445 318.05563139153696 0 ) ( 174.85125168440936 320 -16 ) ( 175.95132251383984 318.05563139153674 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 177.01729188594504 195.90763996340476 0 ) ( 175.95132251383984 193.94436860846872 -16 ) ( 175.95132251384283 193.94436860846878 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 177.01729188594334 316.09236003659584 0 ) ( 175.95132251383984 318.05563139153674 -16 ) ( 177.01729188594254 316.0923600365968 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 177.01729188594504 195.90763996340476 0 ) ( 178.04883509610954 197.88921603333984 -16 ) ( 177.0172918859389 195.90763996340502 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 178.04883509611284 314.1107839666671 0 ) ( 177.01729188594254 316.0923600365968 -16 ) ( 178.04883509610954 314.1107839666656 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 179.0456379262978 312.1115067890041 -16 ) ( 178.04883509611284 314.1107839666671 0 ) ( 178.04883509610954 314.1107839666656 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 179.04563792629415 199.8884932109995 -16 ) ( 178.04883509610949 197.88921603333887 0 ) ( 179.04563792628628 199.8884932110016 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 179.04563792628628 199.8884932110016 0 ) ( 180.00739674068973 201.90486249718924 -16 ) ( 179.04563792629415 199.8884932109995 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 180.00739674069337 310.0951375028071 -16 ) ( 179.04563792629347 312.11150678900196 0 ) ( 179.0456379262978 312.1115067890041 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 180.93381857825466 203.9377096862969 0 ) ( 180.00739674068973 201.90486249718924 -16 ) ( 180.0073967406873 201.9048624971924 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 180.00739674069337 310.0951375028071 -16 ) ( 180.93381857825227 308.06229031370054 0 ) ( 180.00739674068976 310.09513750280547 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 181.82462124191443 306.01358444662765 -16 ) ( 180.93381857825227 308.06229031370054 0 ) ( 180.9338185782508 308.06229031370094 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 181.8246212419158 205.98641555337161 0 ) ( 180.9338185782508 203.93770968629724 -16 ) ( 180.93381857825466 203.9377096862969 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 182.6795333845492 303.9496439572375 0 ) ( 181.82462124191443 306.01358444662765 -16 ) ( 182.67953338454754 303.94964395723946 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 182.6795333845496 208.05035604276358 -16 ) ( 181.8246212419158 205.98641555337161 0 ) ( 182.67953338454754 208.05035604276418 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 183.49829459164175 301.8710975417998 -16 ) ( 182.6795333845492 303.9496439572375 0 ) ( 182.67953338454754 303.94964395723946 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 183.4982945916414 210.1289024582013 -16 ) ( 182.67953338454754 208.05035604276418 0 ) ( 183.4982945916381 210.128902458202 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 183.4982945916381 210.128902458202 0 ) ( 184.28065546059844 212.22142165431538 -16 ) ( 183.4982945916414 210.1289024582013 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 184.28065546059727 299.77857834568766 0 ) ( 183.49829459164175 301.8710975417998 -16 ) ( 184.28065546059588 299.7785783456875 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 185.0263776767133 214.3272762294855 0 ) ( 184.28065546059844 212.22142165431538 -16 ) ( 184.28065546059224 212.22142165431433 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 185.02637767671223 297.6727237705153 0 ) ( 184.28065546059588 299.7785783456875 -16 ) ( 185.02637767670967 297.6727237705127 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 185.7352340857803 295.5541752799944 0 ) ( 185.02637767670967 297.6727237705127 -16 ) ( 185.73523408577603 295.5541752799945 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 185.73523408578046 216.4458247200071 -16 ) ( 185.0263776767133 214.3272762294855 0 ) ( 185.73523408577603 216.44582472000548 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 186.4070087632685 293.4235782045101 0 ) ( 185.73523408577603 295.5541752799945 -16 ) ( 186.4070087632681 293.4235782045107 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 186.40700876326994 218.57642179549035 -16 ) ( 185.73523408577603 216.44582472000548 0 ) ( 186.40700876326446 218.5764217954893 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 187.04149708010664 220.7184184554244 0 ) ( 186.40700876326994 218.57642179549035 -16 ) ( 186.40700876326446 218.5764217954893 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 187.04149708010493 291.2815815445759 0 ) ( 186.4070087632681 293.4235782045107 -16 ) ( 187.041497080103 291.2815815445738 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 187.63850576500067 289.1288377731225 0 ) ( 187.041497080103 291.2815815445738 -16 ) ( 187.63850576500045 289.12883777312345 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 187.04149708010442 220.71841845542383 -16 ) ( 187.63850576500408 222.87116222687655 0 ) ( 187.63850576499937 222.8711622268765 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 188.19785296332884 286.9660026367596 0 ) ( 187.63850576500045 289.12883777312345 -16 ) ( 188.19785296332702 286.9660026367592 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 188.19785296332984 225.03399736324386 -16 ) ( 187.63850576500408 222.87116222687655 0 ) ( 188.19785296332338 225.03399736324263 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 188.71936829251007 284.7937349560146 0 ) ( 188.19785296332702 286.9660026367592 -16 ) ( 188.7193682925099 284.7937349560152 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 188.19785296332338 225.03399736324263 0 ) ( 188.7193682925116 227.20626504398612 -16 ) ( 188.19785296332984 225.03399736324386 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 188.7193682925099 227.20626504398479 0 ) ( 189.20289289392773 229.3873035753271 -16 ) ( 188.7193682925116 227.20626504398612 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 189.20289289392792 282.6126964246744 0 ) ( 188.7193682925099 284.7937349560152 -16 ) ( 189.2028928939253 282.61269642467323 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 189.64827948130187 280.42355140819836 -16 ) ( 189.20289289392792 282.6126964246744 0 ) ( 189.2028928939253 282.61269642467323 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 189.6482794813013 231.5764485918024 -16 ) ( 189.2028928939253 229.38730357532768 0 ) ( 189.64827948129823 231.57644859180255 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 190.05539238556264 233.77303325863284 -16 ) ( 189.64827948129823 231.57644859180255 0 ) ( 190.0553923855623 233.7730332586334 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 190.05539238556327 278.2269667413679 0 ) ( 189.64827948130187 280.42355140819836 -16 ) ( 190.0553923855623 278.2269667413675 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 190.42410759617997 235.97638847485086 0 ) ( 190.05539238556264 233.77303325863284 -16 ) ( 190.0553923855623 233.7730332586334 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 190.4241075961765 276.0236115251478 0 ) ( 190.0553923855623 278.2269667413675 -16 ) ( 190.42410759617633 276.0236115251482 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 190.75431279892655 273.814156922891 -16 ) ( 190.4241075961765 276.0236115251478 0 ) ( 190.42410759617633 276.0236115251482 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 190.7543127989229 238.1858430771117 0 ) ( 190.42410759617806 235.97638847485072 -16 ) ( 190.42410759617997 235.97638847485086 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.04590741009088 240.40072404414104 0 ) ( 190.75431279892163 238.18584307711197 -16 ) ( 190.7543127989229 238.1858430771117 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.04590741008772 271.59927595585697 0 ) ( 190.75431279892655 273.814156922891 -16 ) ( 191.04590741008724 271.59927595585714 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.04590741008724 271.59927595585714 -16 ) ( 191.29880260713844 269.37964329825894 0 ) ( 191.04590741008772 271.59927595585697 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.29880260713938 242.62035670174055 -16 ) ( 191.04590741009088 240.40072404414104 0 ) ( 191.29880260713617 242.62035670174055 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.29880260713617 269.379643298259 -16 ) ( 191.51292135574553 267.1559350717027 0 ) ( 191.29880260713844 269.37964329825894 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.5129213557437 244.84406492829984 -16 ) ( 191.29880260713617 242.62035670174055 0 ) ( 191.51292135574113 244.84406492829976 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.68819843325792 247.07117136075226 -16 ) ( 191.51292135574113 244.84406492829976 0 ) ( 191.68819843325764 247.07117136075203 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.68819843325764 264.9288286392466 -16 ) ( 191.51292135574553 267.1559350717027 0 ) ( 191.51292135574477 267.15593507170297 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.68819843325764 264.9288286392466 -16 ) ( 191.82458044858532 262.6990023990966 0 ) ( 191.68819843325633 264.9288286392467 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.68819843325764 247.07117136075203 0 ) ( 191.8245804485857 249.30099760090332 -16 ) ( 191.68819843325792 247.07117136075226 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.82458044858504 262.69900239909657 -16 ) ( 191.92202585844421 260.46713557792003 0 ) ( 191.82458044858532 262.6990023990966 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.92202585844444 251.53286442208 -16 ) ( 191.82458044858504 249.30099760090343 0 ) ( 191.9220258584428 251.5328644220799 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.98050498002019 258.2339080239744 0 ) ( 191.92202585843916 260.46713557791986 -16 ) ( 191.98050498001612 258.2339080239743 -16 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.98050498001825 253.76609197602778 -16 ) ( 191.9220258584428 251.5328644220799 0 ) ( 191.98050498001612 253.76609197602795 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 192 256 0 ) ( 191.98050498001825 253.76609197602778 -16 ) ( 191.98050498001612 253.76609197602795 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 191.98050498001612 258.2339080239743 -16 ) ( 192 256 0 ) ( 191.98050498002019 258.2339080239744 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +} diff --git a/testmaps/qbsp_tjunc_many_sided_sky.map b/testmaps/qbsp_tjunc_many_sided_sky.map new file mode 100644 index 00000000..13e3448a --- /dev/null +++ b/testmaps/qbsp_tjunc_many_sided_sky.map @@ -0,0 +1,751 @@ +// Game: Quake +// Format: Valve +// entity 0 +{ +"mapversion" "220" +"classname" "worldspawn" +"wad" "deprecated/free_wad.wad" +// brush 0 +{ +( 0 0 -288 ) ( 0 1 -288 ) ( 0 0 -287 ) bolt16 [ 0 -1 0 0 ] [ 0 0 -1 -16 ] 0 4 4 +( 0 0 -288 ) ( 0 0 -287 ) ( 1 0 -288 ) bolt16 [ 1 0 0 0 ] [ 0 0 -1 -16 ] 0 4 4 +( 0 0 -16 ) ( 1 0 -16 ) ( 0 1 -16 ) bolt16 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 4 4 +( 256 256 0 ) ( 256 257 0 ) ( 257 256 0 ) bolt16 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 0.1 0.1 +( 256 320 0 ) ( 257 320 0 ) ( 256 320 1 ) bolt16 [ -1 0 0 0 ] [ 0 0 -1 -16 ] 0 4 4 +( 320 256 0 ) ( 320 256 1 ) ( 320 257 0 ) bolt16 [ 0 1 0 0 ] [ 0 0 -1 -16 ] 0 4 4 +} +// brush 1 +{ +( -32 0 0 ) ( -32 1 0 ) ( -32 0 1 ) bolt1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -32 0 0 ) ( -32 0 1 ) ( -31 0 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 0 0 ) ( -31 0 0 ) ( -32 1 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 16 272 ) ( 0 17 272 ) ( 1 16 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 16 16 ) ( 1 16 16 ) ( 0 16 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 16 16 ) ( 0 16 17 ) ( 0 17 16 ) bolt1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 2 +{ +( -32 32 0 ) ( -32 33 0 ) ( -32 32 1 ) bolt1 [ 0 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( -32 32 0 ) ( -32 32 1 ) ( -31 32 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 32 0 ) ( -31 32 0 ) ( -32 33 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 48 272 ) ( 0 49 272 ) ( 1 48 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 48 16 ) ( 1 48 16 ) ( 0 48 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 48 16 ) ( 0 48 17 ) ( 0 49 16 ) bolt1 [ 0 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 3 +{ +( -32 64 0 ) ( -32 65 0 ) ( -32 64 1 ) bolt1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -32 64 0 ) ( -32 64 1 ) ( -31 64 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 64 0 ) ( -31 64 0 ) ( -32 65 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 80 272 ) ( 0 81 272 ) ( 1 80 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 80 16 ) ( 1 80 16 ) ( 0 80 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 80 16 ) ( 0 80 17 ) ( 0 81 16 ) bolt1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 4 +{ +( -32 96 0 ) ( -32 97 0 ) ( -32 96 1 ) bolt1 [ 0 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( -32 96 0 ) ( -32 96 1 ) ( -31 96 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 96 0 ) ( -31 96 0 ) ( -32 97 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 112 272 ) ( 0 113 272 ) ( 1 112 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 112 16 ) ( 1 112 16 ) ( 0 112 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 112 16 ) ( 0 112 17 ) ( 0 113 16 ) bolt1 [ 0 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 5 +{ +( -32 128 0 ) ( -32 129 0 ) ( -32 128 1 ) bolt1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -32 128 0 ) ( -32 128 1 ) ( -31 128 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 128 0 ) ( -31 128 0 ) ( -32 129 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 144 272 ) ( 0 145 272 ) ( 1 144 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 144 16 ) ( 1 144 16 ) ( 0 144 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 144 16 ) ( 0 144 17 ) ( 0 145 16 ) bolt1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 6 +{ +( -32 160 0 ) ( -32 161 0 ) ( -32 160 1 ) bolt1 [ 0 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( -32 160 0 ) ( -32 160 1 ) ( -31 160 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 160 0 ) ( -31 160 0 ) ( -32 161 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 176 272 ) ( 0 177 272 ) ( 1 176 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 176 16 ) ( 1 176 16 ) ( 0 176 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 176 16 ) ( 0 176 17 ) ( 0 177 16 ) bolt1 [ 0 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 7 +{ +( -32 192 0 ) ( -32 193 0 ) ( -32 192 1 ) bolt1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -32 192 0 ) ( -32 192 1 ) ( -31 192 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 192 0 ) ( -31 192 0 ) ( -32 193 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 208 272 ) ( 0 209 272 ) ( 1 208 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 208 16 ) ( 1 208 16 ) ( 0 208 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 208 16 ) ( 0 208 17 ) ( 0 209 16 ) bolt1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 8 +{ +( -32 224 0 ) ( -32 225 0 ) ( -32 224 1 ) bolt1 [ 0 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( -32 224 0 ) ( -32 224 1 ) ( -31 224 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 224 0 ) ( -31 224 0 ) ( -32 225 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 240 272 ) ( 0 241 272 ) ( 1 240 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 240 16 ) ( 1 240 16 ) ( 0 240 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 240 16 ) ( 0 240 17 ) ( 0 241 16 ) bolt1 [ 0 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 9 +{ +( -32 256 0 ) ( -32 257 0 ) ( -32 256 1 ) bolt1 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -32 256 0 ) ( -32 256 1 ) ( -31 256 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 256 0 ) ( -31 256 0 ) ( -32 257 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 272 272 ) ( 0 273 272 ) ( 1 272 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 0 ] 0 1 1 +( 0 272 16 ) ( 1 272 16 ) ( 0 272 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 272 16 ) ( 0 272 17 ) ( 0 273 16 ) bolt1 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 10 +{ +( -32 288 0 ) ( -32 289 0 ) ( -32 288 1 ) bolt1 [ 0 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( -32 288 0 ) ( -32 288 1 ) ( -31 288 0 ) bolt1 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 288 0 ) ( -31 288 0 ) ( -32 289 0 ) bolt1 [ -1 0 0 -16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 304 272 ) ( 0 305 272 ) ( 1 304 272 ) bolt1 [ 1 0 0 16 ] [ 0 -1 0 32 ] 0 1 1 +( 0 304 16 ) ( 1 304 16 ) ( 0 304 17 ) bolt1 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 304 16 ) ( 0 304 17 ) ( 0 305 16 ) bolt1 [ 0 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 11 +{ +( -32 16 0 ) ( -32 17 0 ) ( -32 16 1 ) bolt10 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 16 0 ) ( -32 16 1 ) ( -31 16 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 16 0 ) ( -31 16 0 ) ( -32 17 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 32 272 ) ( 0 33 272 ) ( 1 32 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 32 16 ) ( 1 32 16 ) ( 0 32 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 32 16 ) ( 0 32 17 ) ( 0 33 16 ) bolt10 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 12 +{ +( -32 48 0 ) ( -32 49 0 ) ( -32 48 1 ) bolt10 [ 0 -1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( -32 48 0 ) ( -32 48 1 ) ( -31 48 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 48 0 ) ( -31 48 0 ) ( -32 49 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 64 272 ) ( 0 65 272 ) ( 1 64 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 64 16 ) ( 1 64 16 ) ( 0 64 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 64 16 ) ( 0 64 17 ) ( 0 65 16 ) bolt10 [ 0 1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 13 +{ +( -32 80 0 ) ( -32 81 0 ) ( -32 80 1 ) bolt10 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 80 0 ) ( -32 80 1 ) ( -31 80 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 80 0 ) ( -31 80 0 ) ( -32 81 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 96 272 ) ( 0 97 272 ) ( 1 96 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 96 16 ) ( 1 96 16 ) ( 0 96 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 96 16 ) ( 0 96 17 ) ( 0 97 16 ) bolt10 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 14 +{ +( -32 112 0 ) ( -32 113 0 ) ( -32 112 1 ) bolt10 [ 0 -1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( -32 112 0 ) ( -32 112 1 ) ( -31 112 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 112 0 ) ( -31 112 0 ) ( -32 113 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 128 272 ) ( 0 129 272 ) ( 1 128 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 128 16 ) ( 1 128 16 ) ( 0 128 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 128 16 ) ( 0 128 17 ) ( 0 129 16 ) bolt10 [ 0 1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 15 +{ +( -32 144 0 ) ( -32 145 0 ) ( -32 144 1 ) bolt10 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 144 0 ) ( -32 144 1 ) ( -31 144 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 144 0 ) ( -31 144 0 ) ( -32 145 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 160 272 ) ( 0 161 272 ) ( 1 160 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 160 16 ) ( 1 160 16 ) ( 0 160 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 160 16 ) ( 0 160 17 ) ( 0 161 16 ) bolt10 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 16 +{ +( -32 176 0 ) ( -32 177 0 ) ( -32 176 1 ) bolt10 [ 0 -1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( -32 176 0 ) ( -32 176 1 ) ( -31 176 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 176 0 ) ( -31 176 0 ) ( -32 177 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 192 272 ) ( 0 193 272 ) ( 1 192 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 192 16 ) ( 1 192 16 ) ( 0 192 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 192 16 ) ( 0 192 17 ) ( 0 193 16 ) bolt10 [ 0 1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 17 +{ +( -32 304 0 ) ( -32 305 0 ) ( -32 304 1 ) bolt10 [ 0 -1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( -32 304 0 ) ( -32 304 1 ) ( -31 304 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 304 0 ) ( -31 304 0 ) ( -32 305 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 320 272 ) ( 0 321 272 ) ( 1 320 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 320 16 ) ( 1 320 16 ) ( 0 320 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 320 16 ) ( 0 320 17 ) ( 0 321 16 ) bolt10 [ 0 1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 18 +{ +( -32 272 0 ) ( -32 273 0 ) ( -32 272 1 ) bolt10 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 272 0 ) ( -32 272 1 ) ( -31 272 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 272 0 ) ( -31 272 0 ) ( -32 273 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 288 272 ) ( 0 289 272 ) ( 1 288 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 288 16 ) ( 1 288 16 ) ( 0 288 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 288 16 ) ( 0 288 17 ) ( 0 289 16 ) bolt10 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 19 +{ +( -32 240 0 ) ( -32 241 0 ) ( -32 240 1 ) bolt10 [ 0 -1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( -32 240 0 ) ( -32 240 1 ) ( -31 240 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 240 0 ) ( -31 240 0 ) ( -32 241 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 256 272 ) ( 0 257 272 ) ( 1 256 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 48 ] 0 1 1 +( 0 256 16 ) ( 1 256 16 ) ( 0 256 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 256 16 ) ( 0 256 17 ) ( 0 257 16 ) bolt10 [ 0 1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 20 +{ +( -32 208 0 ) ( -32 209 0 ) ( -32 208 1 ) bolt10 [ 0 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 208 0 ) ( -32 208 1 ) ( -31 208 0 ) bolt10 [ 1 0 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( -32 208 0 ) ( -31 208 0 ) ( -32 209 0 ) bolt10 [ -1 0 0 -16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 224 272 ) ( 0 225 272 ) ( 1 224 272 ) bolt10 [ 1 0 0 16 ] [ 0 -1 0 16 ] 0 1 1 +( 0 224 16 ) ( 1 224 16 ) ( 0 224 17 ) bolt10 [ -1 0 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 224 16 ) ( 0 224 17 ) ( 0 225 16 ) bolt10 [ 0 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 21 +{ +( 0 320 0 ) ( 0 321 0 ) ( 0 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 0 320 0 ) ( 0 320 1 ) ( 1 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 0 320 0 ) ( 1 320 0 ) ( 0 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 0 ] 90 1 1 +( 16 352 272 ) ( 16 353 272 ) ( 17 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 0 ] 270 1 1 +( 16 352 16 ) ( 17 352 16 ) ( 16 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 16 352 16 ) ( 16 352 17 ) ( 16 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 22 +{ +( 32 320 0 ) ( 32 321 0 ) ( 32 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 32 320 0 ) ( 32 320 1 ) ( 33 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 32 320 0 ) ( 33 320 0 ) ( 32 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 32 ] 90 1 1 +( 48 352 272 ) ( 48 353 272 ) ( 49 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 32 ] 270 1 1 +( 48 352 16 ) ( 49 352 16 ) ( 48 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 48 352 16 ) ( 48 352 17 ) ( 48 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 23 +{ +( 64 320 0 ) ( 64 321 0 ) ( 64 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 64 320 0 ) ( 64 320 1 ) ( 65 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 64 320 0 ) ( 65 320 0 ) ( 64 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 0 ] 90 1 1 +( 80 352 272 ) ( 80 353 272 ) ( 81 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 0 ] 270 1 1 +( 80 352 16 ) ( 81 352 16 ) ( 80 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 80 352 16 ) ( 80 352 17 ) ( 80 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 24 +{ +( 96 320 0 ) ( 96 321 0 ) ( 96 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 96 320 0 ) ( 96 320 1 ) ( 97 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 96 320 0 ) ( 97 320 0 ) ( 96 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 32 ] 90 1 1 +( 112 352 272 ) ( 112 353 272 ) ( 113 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 32 ] 270 1 1 +( 112 352 16 ) ( 113 352 16 ) ( 112 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 112 352 16 ) ( 112 352 17 ) ( 112 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 25 +{ +( 128 320 0 ) ( 128 321 0 ) ( 128 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 128 320 0 ) ( 128 320 1 ) ( 129 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 128 320 0 ) ( 129 320 0 ) ( 128 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 0 ] 90 1 1 +( 144 352 272 ) ( 144 353 272 ) ( 145 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 0 ] 270 1 1 +( 144 352 16 ) ( 145 352 16 ) ( 144 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 144 352 16 ) ( 144 352 17 ) ( 144 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 26 +{ +( 160 320 0 ) ( 160 321 0 ) ( 160 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 160 320 0 ) ( 160 320 1 ) ( 161 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 160 320 0 ) ( 161 320 0 ) ( 160 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 32 ] 90 1 1 +( 176 352 272 ) ( 176 353 272 ) ( 177 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 32 ] 270 1 1 +( 176 352 16 ) ( 177 352 16 ) ( 176 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 176 352 16 ) ( 176 352 17 ) ( 176 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 27 +{ +( 192 320 0 ) ( 192 321 0 ) ( 192 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 192 320 0 ) ( 192 320 1 ) ( 193 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 192 320 0 ) ( 193 320 0 ) ( 192 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 0 ] 90 1 1 +( 208 352 272 ) ( 208 353 272 ) ( 209 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 0 ] 270 1 1 +( 208 352 16 ) ( 209 352 16 ) ( 208 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 208 352 16 ) ( 208 352 17 ) ( 208 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 28 +{ +( 224 320 0 ) ( 224 321 0 ) ( 224 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 224 320 0 ) ( 224 320 1 ) ( 225 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 224 320 0 ) ( 225 320 0 ) ( 224 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 32 ] 90 1 1 +( 240 352 272 ) ( 240 353 272 ) ( 241 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 32 ] 270 1 1 +( 240 352 16 ) ( 241 352 16 ) ( 240 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 240 352 16 ) ( 240 352 17 ) ( 240 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 29 +{ +( 256 320 0 ) ( 256 321 0 ) ( 256 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 256 320 0 ) ( 256 320 1 ) ( 257 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 256 320 0 ) ( 257 320 0 ) ( 256 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 0 ] 90 1 1 +( 272 352 272 ) ( 272 353 272 ) ( 273 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 0 ] 270 1 1 +( 272 352 16 ) ( 273 352 16 ) ( 272 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 272 352 16 ) ( 272 352 17 ) ( 272 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 30 +{ +( 288 320 0 ) ( 288 321 0 ) ( 288 320 1 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 288 320 0 ) ( 288 320 1 ) ( 289 320 0 ) bolt1 [ -1 -6.123233995736766e-17 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 288 320 0 ) ( 289 320 0 ) ( 288 321 0 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 32 ] 90 1 1 +( 304 352 272 ) ( 304 353 272 ) ( 305 352 272 ) bolt1 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 32 ] 270 1 1 +( 304 352 16 ) ( 305 352 16 ) ( 304 352 17 ) bolt1 [ 1 6.123233995736766e-17 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 304 352 16 ) ( 304 352 17 ) ( 304 353 16 ) bolt1 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 31 +{ +( 16 320 0 ) ( 16 321 0 ) ( 16 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 16 320 0 ) ( 16 320 1 ) ( 17 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 16 320 0 ) ( 17 320 0 ) ( 16 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 16 ] 90 1 1 +( 32 352 272 ) ( 32 353 272 ) ( 33 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 16 ] 270 1 1 +( 32 352 16 ) ( 33 352 16 ) ( 32 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 32 352 16 ) ( 32 352 17 ) ( 32 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 32 +{ +( 48 320 0 ) ( 48 321 0 ) ( 48 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 48 320 0 ) ( 48 320 1 ) ( 49 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 48 320 0 ) ( 49 320 0 ) ( 48 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 48 ] 90 1 1 +( 64 352 272 ) ( 64 353 272 ) ( 65 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 48 ] 270 1 1 +( 64 352 16 ) ( 65 352 16 ) ( 64 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 64 352 16 ) ( 64 352 17 ) ( 64 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 33 +{ +( 80 320 0 ) ( 80 321 0 ) ( 80 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 80 320 0 ) ( 80 320 1 ) ( 81 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 80 320 0 ) ( 81 320 0 ) ( 80 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 16 ] 90 1 1 +( 96 352 272 ) ( 96 353 272 ) ( 97 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 16 ] 270 1 1 +( 96 352 16 ) ( 97 352 16 ) ( 96 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 96 352 16 ) ( 96 352 17 ) ( 96 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 34 +{ +( 112 320 0 ) ( 112 321 0 ) ( 112 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 112 320 0 ) ( 112 320 1 ) ( 113 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 112 320 0 ) ( 113 320 0 ) ( 112 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 48 ] 90 1 1 +( 128 352 272 ) ( 128 353 272 ) ( 129 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 48 ] 270 1 1 +( 128 352 16 ) ( 129 352 16 ) ( 128 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 128 352 16 ) ( 128 352 17 ) ( 128 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 35 +{ +( 144 320 0 ) ( 144 321 0 ) ( 144 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 144 320 0 ) ( 144 320 1 ) ( 145 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 144 320 0 ) ( 145 320 0 ) ( 144 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 16 ] 90 1 1 +( 160 352 272 ) ( 160 353 272 ) ( 161 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 16 ] 270 1 1 +( 160 352 16 ) ( 161 352 16 ) ( 160 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 160 352 16 ) ( 160 352 17 ) ( 160 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 36 +{ +( 176 320 0 ) ( 176 321 0 ) ( 176 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 176 320 0 ) ( 176 320 1 ) ( 177 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 176 320 0 ) ( 177 320 0 ) ( 176 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 48 ] 90 1 1 +( 192 352 272 ) ( 192 353 272 ) ( 193 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 48 ] 270 1 1 +( 192 352 16 ) ( 193 352 16 ) ( 192 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 192 352 16 ) ( 192 352 17 ) ( 192 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 37 +{ +( 304 320 0 ) ( 304 321 0 ) ( 304 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 304 320 0 ) ( 304 320 1 ) ( 305 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 304 320 0 ) ( 305 320 0 ) ( 304 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 48 ] 90 1 1 +( 320 352 272 ) ( 320 353 272 ) ( 321 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 48 ] 270 1 1 +( 320 352 16 ) ( 321 352 16 ) ( 320 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 320 352 16 ) ( 320 352 17 ) ( 320 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 38 +{ +( 272 320 0 ) ( 272 321 0 ) ( 272 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 272 320 0 ) ( 272 320 1 ) ( 273 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 272 320 0 ) ( 273 320 0 ) ( 272 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 16 ] 90 1 1 +( 288 352 272 ) ( 288 353 272 ) ( 289 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 16 ] 270 1 1 +( 288 352 16 ) ( 289 352 16 ) ( 288 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 288 352 16 ) ( 288 352 17 ) ( 288 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 39 +{ +( 240 320 0 ) ( 240 321 0 ) ( 240 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 240 320 0 ) ( 240 320 1 ) ( 241 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 240 320 0 ) ( 241 320 0 ) ( 240 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 48 ] 90 1 1 +( 256 352 272 ) ( 256 353 272 ) ( 257 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 48 ] 270 1 1 +( 256 352 16 ) ( 257 352 16 ) ( 256 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 256 352 16 ) ( 256 352 17 ) ( 256 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 40 +{ +( 208 320 0 ) ( 208 321 0 ) ( 208 320 1 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ 0 0 -1 0 ] 0 1 1 +( 208 320 0 ) ( 208 320 1 ) ( 209 320 0 ) bolt10 [ -1 -6.123233995736766e-17 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 208 320 0 ) ( 209 320 0 ) ( 208 321 0 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ -1 -6.123233995736766e-17 0 16 ] 90 1 1 +( 224 352 272 ) ( 224 353 272 ) ( 225 352 272 ) bolt10 [ -6.123233995736766e-17 1 0 48 ] [ -1 -6.123233995736766e-17 0 16 ] 270 1 1 +( 224 352 16 ) ( 225 352 16 ) ( 224 352 17 ) bolt10 [ 1 6.123233995736766e-17 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 224 352 16 ) ( 224 352 17 ) ( 224 353 16 ) bolt10 [ 6.123233995736766e-17 -1 0 -48 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 41 +{ +( 320 304 16 ) ( 320 304 17 ) ( 320 303 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 320 304 16 ) ( 319 304 16 ) ( 320 304 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 320 0 ) ( 351 320 0 ) ( 352 319 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 320 304 272 ) ( 320 303 272 ) ( 319 304 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 352 320 0 ) ( 352 320 1 ) ( 351 320 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 320 0 ) ( 352 319 0 ) ( 352 320 1 ) bolt1 [ 1.2246467991473532e-16 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 42 +{ +( 320 272 16 ) ( 320 272 17 ) ( 320 271 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( 320 272 16 ) ( 319 272 16 ) ( 320 272 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 288 0 ) ( 351 288 0 ) ( 352 287 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 320 272 272 ) ( 320 271 272 ) ( 319 272 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 352 288 0 ) ( 352 288 1 ) ( 351 288 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 288 0 ) ( 352 287 0 ) ( 352 288 1 ) bolt1 [ 1.2246467991473532e-16 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 43 +{ +( 320 240 16 ) ( 320 240 17 ) ( 320 239 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 320 240 16 ) ( 319 240 16 ) ( 320 240 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 256 0 ) ( 351 256 0 ) ( 352 255 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 320 240 272 ) ( 320 239 272 ) ( 319 240 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 352 256 0 ) ( 352 256 1 ) ( 351 256 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 256 0 ) ( 352 255 0 ) ( 352 256 1 ) bolt1 [ 1.2246467991473532e-16 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 44 +{ +( 320 208 16 ) ( 320 208 17 ) ( 320 207 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( 320 208 16 ) ( 319 208 16 ) ( 320 208 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 224 0 ) ( 351 224 0 ) ( 352 223 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 320 208 272 ) ( 320 207 272 ) ( 319 208 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 352 224 0 ) ( 352 224 1 ) ( 351 224 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 224 0 ) ( 352 223 0 ) ( 352 224 1 ) bolt1 [ 1.2246467991473532e-16 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 45 +{ +( 320 176 16 ) ( 320 176 17 ) ( 320 175 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 320 176 16 ) ( 319 176 16 ) ( 320 176 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 192 0 ) ( 351 192 0 ) ( 352 191 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 320 176 272 ) ( 320 175 272 ) ( 319 176 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 352 192 0 ) ( 352 192 1 ) ( 351 192 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 192 0 ) ( 352 191 0 ) ( 352 192 1 ) bolt1 [ 1.2246467991473532e-16 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 46 +{ +( 320 144 16 ) ( 320 144 17 ) ( 320 143 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( 320 144 16 ) ( 319 144 16 ) ( 320 144 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 160 0 ) ( 351 160 0 ) ( 352 159 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 320 144 272 ) ( 320 143 272 ) ( 319 144 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 352 160 0 ) ( 352 160 1 ) ( 351 160 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 160 0 ) ( 352 159 0 ) ( 352 160 1 ) bolt1 [ 1.2246467991473532e-16 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 47 +{ +( 320 112 16 ) ( 320 112 17 ) ( 320 111 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 320 112 16 ) ( 319 112 16 ) ( 320 112 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 128 0 ) ( 351 128 0 ) ( 352 127 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 320 112 272 ) ( 320 111 272 ) ( 319 112 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 352 128 0 ) ( 352 128 1 ) ( 351 128 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 128 0 ) ( 352 127 0 ) ( 352 128 1 ) bolt1 [ 1.2246467991473532e-16 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 48 +{ +( 320 80 16 ) ( 320 80 17 ) ( 320 79 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( 320 80 16 ) ( 319 80 16 ) ( 320 80 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 96 0 ) ( 351 96 0 ) ( 352 95 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 320 80 272 ) ( 320 79 272 ) ( 319 80 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 352 96 0 ) ( 352 96 1 ) ( 351 96 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 96 0 ) ( 352 95 0 ) ( 352 96 1 ) bolt1 [ 1.2246467991473532e-16 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 49 +{ +( 320 48 16 ) ( 320 48 17 ) ( 320 47 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 320 48 16 ) ( 319 48 16 ) ( 320 48 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 64 0 ) ( 351 64 0 ) ( 352 63 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 320 48 272 ) ( 320 47 272 ) ( 319 48 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 0 ] 180 1 1 +( 352 64 0 ) ( 352 64 1 ) ( 351 64 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 64 0 ) ( 352 63 0 ) ( 352 64 1 ) bolt1 [ 1.2246467991473532e-16 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 50 +{ +( 320 16 16 ) ( 320 16 17 ) ( 320 15 16 ) bolt1 [ -1.2246467991473532e-16 -1 0 32 ] [ 0 0 -1 0 ] 0 1 1 +( 320 16 16 ) ( 319 16 16 ) ( 320 16 17 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 32 0 ) ( 351 32 0 ) ( 352 31 0 ) bolt1 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 320 16 272 ) ( 320 15 272 ) ( 319 16 272 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -32 ] 180 1 1 +( 352 32 0 ) ( 352 32 1 ) ( 351 32 0 ) bolt1 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 32 0 ) ( 352 31 0 ) ( 352 32 1 ) bolt1 [ 1.2246467991473532e-16 1 0 -32 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 51 +{ +( 320 288 16 ) ( 320 288 17 ) ( 320 287 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 288 16 ) ( 319 288 16 ) ( 320 288 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 304 0 ) ( 351 304 0 ) ( 352 303 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 320 288 272 ) ( 320 287 272 ) ( 319 288 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 352 304 0 ) ( 352 304 1 ) ( 351 304 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 304 0 ) ( 352 303 0 ) ( 352 304 1 ) bolt10 [ 1.2246467991473532e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 52 +{ +( 320 256 16 ) ( 320 256 17 ) ( 320 255 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 256 16 ) ( 319 256 16 ) ( 320 256 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 272 0 ) ( 351 272 0 ) ( 352 271 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 320 256 272 ) ( 320 255 272 ) ( 319 256 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 352 272 0 ) ( 352 272 1 ) ( 351 272 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 272 0 ) ( 352 271 0 ) ( 352 272 1 ) bolt10 [ 1.2246467991473532e-16 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 53 +{ +( 320 224 16 ) ( 320 224 17 ) ( 320 223 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 224 16 ) ( 319 224 16 ) ( 320 224 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 240 0 ) ( 351 240 0 ) ( 352 239 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 320 224 272 ) ( 320 223 272 ) ( 319 224 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 352 240 0 ) ( 352 240 1 ) ( 351 240 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 240 0 ) ( 352 239 0 ) ( 352 240 1 ) bolt10 [ 1.2246467991473532e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 54 +{ +( 320 192 16 ) ( 320 192 17 ) ( 320 191 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 192 16 ) ( 319 192 16 ) ( 320 192 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 208 0 ) ( 351 208 0 ) ( 352 207 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 320 192 272 ) ( 320 191 272 ) ( 319 192 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 352 208 0 ) ( 352 208 1 ) ( 351 208 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 208 0 ) ( 352 207 0 ) ( 352 208 1 ) bolt10 [ 1.2246467991473532e-16 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 55 +{ +( 320 160 16 ) ( 320 160 17 ) ( 320 159 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 160 16 ) ( 319 160 16 ) ( 320 160 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 176 0 ) ( 351 176 0 ) ( 352 175 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 320 160 272 ) ( 320 159 272 ) ( 319 160 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 352 176 0 ) ( 352 176 1 ) ( 351 176 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 176 0 ) ( 352 175 0 ) ( 352 176 1 ) bolt10 [ 1.2246467991473532e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 56 +{ +( 320 128 16 ) ( 320 128 17 ) ( 320 127 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 128 16 ) ( 319 128 16 ) ( 320 128 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 144 0 ) ( 351 144 0 ) ( 352 143 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 320 128 272 ) ( 320 127 272 ) ( 319 128 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 352 144 0 ) ( 352 144 1 ) ( 351 144 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 144 0 ) ( 352 143 0 ) ( 352 144 1 ) bolt10 [ 1.2246467991473532e-16 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 57 +{ +( 320 0 16 ) ( 320 0 17 ) ( 320 -1 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 0 16 ) ( 319 0 16 ) ( 320 0 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 16 0 ) ( 351 16 0 ) ( 352 15 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 320 0 272 ) ( 320 -1 272 ) ( 319 0 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 352 16 0 ) ( 352 16 1 ) ( 351 16 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 16 0 ) ( 352 15 0 ) ( 352 16 1 ) bolt10 [ 1.2246467991473532e-16 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 58 +{ +( 320 32 16 ) ( 320 32 17 ) ( 320 31 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 32 16 ) ( 319 32 16 ) ( 320 32 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 48 0 ) ( 351 48 0 ) ( 352 47 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 320 32 272 ) ( 320 31 272 ) ( 319 32 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 352 48 0 ) ( 352 48 1 ) ( 351 48 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 48 0 ) ( 352 47 0 ) ( 352 48 1 ) bolt10 [ 1.2246467991473532e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 59 +{ +( 320 64 16 ) ( 320 64 17 ) ( 320 63 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 64 16 ) ( 319 64 16 ) ( 320 64 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 80 0 ) ( 351 80 0 ) ( 352 79 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 320 64 272 ) ( 320 63 272 ) ( 319 64 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 -16 ] 180 1 1 +( 352 80 0 ) ( 352 80 1 ) ( 351 80 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 80 0 ) ( 352 79 0 ) ( 352 80 1 ) bolt10 [ 1.2246467991473532e-16 1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 60 +{ +( 320 96 16 ) ( 320 96 17 ) ( 320 95 16 ) bolt10 [ -1.2246467991473532e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 320 96 16 ) ( 319 96 16 ) ( 320 96 17 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 112 0 ) ( 351 112 0 ) ( 352 111 0 ) bolt10 [ 1 -1.2246467991473532e-16 0 -16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 320 96 272 ) ( 320 95 272 ) ( 319 96 272 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 1.2246467991473532e-16 1 0 16 ] 180 1 1 +( 352 112 0 ) ( 352 112 1 ) ( 351 112 0 ) bolt10 [ -1 1.2246467991473532e-16 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 352 112 0 ) ( 352 111 0 ) ( 352 112 1 ) bolt10 [ 1.2246467991473532e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 61 +{ +( 304 -32 16 ) ( 304 -32 17 ) ( 304 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 304 -32 16 ) ( 303 -32 16 ) ( 304 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 320 0 0 ) ( 319 0 0 ) ( 320 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 0 ] 270 1 1 +( 304 -32 272 ) ( 304 -33 272 ) ( 303 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 0 ] 90 1 1 +( 320 0 0 ) ( 320 0 1 ) ( 319 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 320 0 0 ) ( 320 -1 0 ) ( 320 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 62 +{ +( 272 -32 16 ) ( 272 -32 17 ) ( 272 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 272 -32 16 ) ( 271 -32 16 ) ( 272 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 288 0 0 ) ( 287 0 0 ) ( 288 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -32 ] 270 1 1 +( 272 -32 272 ) ( 272 -33 272 ) ( 271 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -32 ] 90 1 1 +( 288 0 0 ) ( 288 0 1 ) ( 287 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 288 0 0 ) ( 288 -1 0 ) ( 288 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 63 +{ +( 240 -32 16 ) ( 240 -32 17 ) ( 240 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 240 -32 16 ) ( 239 -32 16 ) ( 240 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 256 0 0 ) ( 255 0 0 ) ( 256 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 0 ] 270 1 1 +( 240 -32 272 ) ( 240 -33 272 ) ( 239 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 0 ] 90 1 1 +( 256 0 0 ) ( 256 0 1 ) ( 255 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 256 0 0 ) ( 256 -1 0 ) ( 256 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 64 +{ +( 208 -32 16 ) ( 208 -32 17 ) ( 208 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 208 -32 16 ) ( 207 -32 16 ) ( 208 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 224 0 0 ) ( 223 0 0 ) ( 224 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -32 ] 270 1 1 +( 208 -32 272 ) ( 208 -33 272 ) ( 207 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -32 ] 90 1 1 +( 224 0 0 ) ( 224 0 1 ) ( 223 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 224 0 0 ) ( 224 -1 0 ) ( 224 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 65 +{ +( 176 -32 16 ) ( 176 -32 17 ) ( 176 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 176 -32 16 ) ( 175 -32 16 ) ( 176 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 192 0 0 ) ( 191 0 0 ) ( 192 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 0 ] 270 1 1 +( 176 -32 272 ) ( 176 -33 272 ) ( 175 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 0 ] 90 1 1 +( 192 0 0 ) ( 192 0 1 ) ( 191 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 192 0 0 ) ( 192 -1 0 ) ( 192 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 66 +{ +( 144 -32 16 ) ( 144 -32 17 ) ( 144 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 144 -32 16 ) ( 143 -32 16 ) ( 144 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 160 0 0 ) ( 159 0 0 ) ( 160 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -32 ] 270 1 1 +( 144 -32 272 ) ( 144 -33 272 ) ( 143 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -32 ] 90 1 1 +( 160 0 0 ) ( 160 0 1 ) ( 159 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 160 0 0 ) ( 160 -1 0 ) ( 160 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 67 +{ +( 112 -32 16 ) ( 112 -32 17 ) ( 112 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 112 -32 16 ) ( 111 -32 16 ) ( 112 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 128 0 0 ) ( 127 0 0 ) ( 128 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 0 ] 270 1 1 +( 112 -32 272 ) ( 112 -33 272 ) ( 111 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 0 ] 90 1 1 +( 128 0 0 ) ( 128 0 1 ) ( 127 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 128 0 0 ) ( 128 -1 0 ) ( 128 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 68 +{ +( 80 -32 16 ) ( 80 -32 17 ) ( 80 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 80 -32 16 ) ( 79 -32 16 ) ( 80 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 96 0 0 ) ( 95 0 0 ) ( 96 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -32 ] 270 1 1 +( 80 -32 272 ) ( 80 -33 272 ) ( 79 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -32 ] 90 1 1 +( 96 0 0 ) ( 96 0 1 ) ( 95 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 96 0 0 ) ( 96 -1 0 ) ( 96 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 69 +{ +( 48 -32 16 ) ( 48 -32 17 ) ( 48 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 48 -32 16 ) ( 47 -32 16 ) ( 48 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 64 0 0 ) ( 63 0 0 ) ( 64 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 0 ] 270 1 1 +( 48 -32 272 ) ( 48 -33 272 ) ( 47 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 0 ] 90 1 1 +( 64 0 0 ) ( 64 0 1 ) ( 63 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 0 ] [ 0 0 -1 0 ] 180 1 1 +( 64 0 0 ) ( 64 -1 0 ) ( 64 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 70 +{ +( 16 -32 16 ) ( 16 -32 17 ) ( 16 -33 16 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 16 -32 16 ) ( 15 -32 16 ) ( 16 -32 17 ) bolt1 [ -1 -1.8369701987210297e-16 0 32 ] [ 0 0 -1 0 ] 180 1 1 +( 32 0 0 ) ( 31 0 0 ) ( 32 -1 0 ) bolt1 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -32 ] 270 1 1 +( 16 -32 272 ) ( 16 -33 272 ) ( 15 -32 272 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -32 ] 90 1 1 +( 32 0 0 ) ( 32 0 1 ) ( 31 0 0 ) bolt1 [ 1 1.8369701987210297e-16 0 -32 ] [ 0 0 -1 0 ] 180 1 1 +( 32 0 0 ) ( 32 -1 0 ) ( 32 0 1 ) bolt1 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 71 +{ +( 288 -32 16 ) ( 288 -32 17 ) ( 288 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 288 -32 16 ) ( 287 -32 16 ) ( 288 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 304 0 0 ) ( 303 0 0 ) ( 304 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -48 ] 270 1 1 +( 288 -32 272 ) ( 288 -33 272 ) ( 287 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -48 ] 90 1 1 +( 304 0 0 ) ( 304 0 1 ) ( 303 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 304 0 0 ) ( 304 -1 0 ) ( 304 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 72 +{ +( 256 -32 16 ) ( 256 -32 17 ) ( 256 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 256 -32 16 ) ( 255 -32 16 ) ( 256 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 272 0 0 ) ( 271 0 0 ) ( 272 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -16 ] 270 1 1 +( 256 -32 272 ) ( 256 -33 272 ) ( 255 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -16 ] 90 1 1 +( 272 0 0 ) ( 272 0 1 ) ( 271 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 272 0 0 ) ( 272 -1 0 ) ( 272 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 73 +{ +( 224 -32 16 ) ( 224 -32 17 ) ( 224 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 224 -32 16 ) ( 223 -32 16 ) ( 224 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 240 0 0 ) ( 239 0 0 ) ( 240 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -48 ] 270 1 1 +( 224 -32 272 ) ( 224 -33 272 ) ( 223 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -48 ] 90 1 1 +( 240 0 0 ) ( 240 0 1 ) ( 239 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 240 0 0 ) ( 240 -1 0 ) ( 240 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 74 +{ +( 192 -32 16 ) ( 192 -32 17 ) ( 192 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 192 -32 16 ) ( 191 -32 16 ) ( 192 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 208 0 0 ) ( 207 0 0 ) ( 208 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -16 ] 270 1 1 +( 192 -32 272 ) ( 192 -33 272 ) ( 191 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -16 ] 90 1 1 +( 208 0 0 ) ( 208 0 1 ) ( 207 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 208 0 0 ) ( 208 -1 0 ) ( 208 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 75 +{ +( 160 -32 16 ) ( 160 -32 17 ) ( 160 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 160 -32 16 ) ( 159 -32 16 ) ( 160 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 176 0 0 ) ( 175 0 0 ) ( 176 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -48 ] 270 1 1 +( 160 -32 272 ) ( 160 -33 272 ) ( 159 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -48 ] 90 1 1 +( 176 0 0 ) ( 176 0 1 ) ( 175 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 176 0 0 ) ( 176 -1 0 ) ( 176 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 76 +{ +( 128 -32 16 ) ( 128 -32 17 ) ( 128 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 128 -32 16 ) ( 127 -32 16 ) ( 128 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 144 0 0 ) ( 143 0 0 ) ( 144 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -16 ] 270 1 1 +( 128 -32 272 ) ( 128 -33 272 ) ( 127 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -16 ] 90 1 1 +( 144 0 0 ) ( 144 0 1 ) ( 143 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 144 0 0 ) ( 144 -1 0 ) ( 144 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 77 +{ +( 0 -32 16 ) ( 0 -32 17 ) ( 0 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 0 -32 16 ) ( -1 -32 16 ) ( 0 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 16 0 0 ) ( 15 0 0 ) ( 16 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -16 ] 270 1 1 +( 0 -32 272 ) ( 0 -33 272 ) ( -1 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -16 ] 90 1 1 +( 16 0 0 ) ( 16 0 1 ) ( 15 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 16 0 0 ) ( 16 -1 0 ) ( 16 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 78 +{ +( 32 -32 16 ) ( 32 -32 17 ) ( 32 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 32 -32 16 ) ( 31 -32 16 ) ( 32 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 48 0 0 ) ( 47 0 0 ) ( 48 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -48 ] 270 1 1 +( 32 -32 272 ) ( 32 -33 272 ) ( 31 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -48 ] 90 1 1 +( 48 0 0 ) ( 48 0 1 ) ( 47 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 48 0 0 ) ( 48 -1 0 ) ( 48 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 79 +{ +( 64 -32 16 ) ( 64 -32 17 ) ( 64 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 64 -32 16 ) ( 63 -32 16 ) ( 64 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 16 ] [ 0 0 -1 0 ] 180 1 1 +( 80 0 0 ) ( 79 0 0 ) ( 80 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -16 ] 270 1 1 +( 64 -32 272 ) ( 64 -33 272 ) ( 63 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -16 ] 90 1 1 +( 80 0 0 ) ( 80 0 1 ) ( 79 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -16 ] [ 0 0 -1 0 ] 180 1 1 +( 80 0 0 ) ( 80 -1 0 ) ( 80 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 80 +{ +( 96 -32 16 ) ( 96 -32 17 ) ( 96 -33 16 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 0 0 -1 0 ] 0 1 1 +( 96 -32 16 ) ( 95 -32 16 ) ( 96 -32 17 ) bolt10 [ -1 -1.8369701987210297e-16 0 48 ] [ 0 0 -1 0 ] 180 1 1 +( 112 0 0 ) ( 111 0 0 ) ( 112 -1 0 ) bolt10 [ -1.8369701987210297e-16 1 0 16 ] [ 1 1.8369701987210297e-16 0 -48 ] 270 1 1 +( 96 -32 272 ) ( 96 -33 272 ) ( 95 -32 272 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 1 1.8369701987210297e-16 0 -48 ] 90 1 1 +( 112 0 0 ) ( 112 0 1 ) ( 111 0 0 ) bolt10 [ 1 1.8369701987210297e-16 0 -48 ] [ 0 0 -1 0 ] 180 1 1 +( 112 0 0 ) ( 112 -1 0 ) ( 112 0 1 ) bolt10 [ 1.8369701987210297e-16 -1 0 -16 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 81 +{ +( 0 0 560 ) ( 0 0 559 ) ( 0 1 560 ) sky3 [ 0 -1 0 0 ] [ 0 0 1 -20 ] 0 4 4 +( 0 0 560 ) ( 1 0 560 ) ( 0 0 559 ) sky3 [ 1 0 0 0 ] [ 0 0 1 -20 ] 0 4 4 +( 256 256 272 ) ( 257 256 272 ) ( 256 257 272 ) sky3 [ 1 0 0 0 ] [ 0 -1 0 0 ] 180 5 5 +( 0 0 288 ) ( 0 1 288 ) ( 1 0 288 ) sky3 [ -1 0 0 0 ] [ 0 -1 0 0 ] 180 4 4 +( 256 320 272 ) ( 256 320 271 ) ( 257 320 272 ) sky3 [ -1 0 0 0 ] [ 0 0 1 -20 ] 0 4 4 +( 320 256 272 ) ( 320 257 272 ) ( 320 256 271 ) sky3 [ 0 1 0 0 ] [ 0 0 1 -20 ] 0 4 4 +} +} +// entity 1 +{ +"classname" "info_player_start" +"origin" "176 192 24" +} diff --git a/tests/test_qbsp.cc b/tests/test_qbsp.cc index 75764e9b..8e28e748 100644 --- a/tests/test_qbsp.cc +++ b/tests/test_qbsp.cc @@ -1046,12 +1046,81 @@ TEST(testmapsQ1, tjuncManySidedFace) ASSERT_EQ(6, faces_by_normal.size()); + const std::vector &floor_faces = faces_by_normal.at({0, 0, 1}); + // the floor has a 0.1 texture scale, so it gets subdivided into many small faces - EXPECT_EQ(15 * 15, (faces_by_normal.at({0, 0, 1}).size())); + EXPECT_EQ(15 * 15, floor_faces.size()); + for (auto *face : floor_faces) { + // these should all be <= 6 sided + EXPECT_LE(face->numedges, 6); + } // the ceiling gets split into 2 faces because fixing T-Junctions with all of the // wall sections exceeds the max vertices per face limit - EXPECT_EQ(2, (faces_by_normal.at({0, 0, -1}).size())); + const std::vector &ceiling_faces = faces_by_normal.at({0, 0, -1}); + ASSERT_EQ(2, ceiling_faces.size()); + + for (auto *face : ceiling_faces) { + // these should all be <= 64 sided + EXPECT_LE(face->numedges, 64); + } + + // ceiling faces: one is 0 area (it's just repairing a bunch of tjuncs) + auto ceiling_winding0 = Face_Winding(&bsp, ceiling_faces[0]); + auto ceiling_winding1 = Face_Winding(&bsp, ceiling_faces[1]); + + float w0_area = ceiling_winding0.area(); + float w1_area = ceiling_winding1.area(); + + if (w0_area > w1_area) { + EXPECT_EQ(320 * 320, w0_area); + EXPECT_EQ(0, w1_area); + } else { + EXPECT_EQ(0, w0_area); + EXPECT_EQ(320 * 320, w1_area); + } +} + +TEST(testmapsQ1, tjuncManySidedFaceMaxedges0) +{ + // same as above, but -maxedges 0 allows the ceiling to be >64 sides so it can be just 1 face + const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_tjunc_many_sided_face.map", {"-tjunc", "rotate", "-maxedges", "0"}); + + std::map> faces_by_normal; + for (auto &face : bsp.dfaces) { + faces_by_normal[Face_Normal(&bsp, &face)].push_back(&face); + } + + const std::vector &ceiling_faces = faces_by_normal.at({0, 0, -1}); + ASSERT_EQ(1, ceiling_faces.size()); + EXPECT_GT(ceiling_faces[0]->numedges, 64); +} + +TEST(testmapsQ1, tjuncManySidedFaceSky) { + const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_tjunc_many_sided_sky.map", {"-tjunc", "rotate"}); + + for (auto &face : bsp.dfaces) { + EXPECT_LE(face.numedges, 64); + } +} + +TEST(testmapsQ1, tjuncManySidedFaceSkyWithDefaultTjuncMode) { + const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_tjunc_many_sided_sky.map", {}); + + for (auto &face : bsp.dfaces) { + EXPECT_LE(face.numedges, 64); + } +} + +TEST(testmapsQ1, manySidedFace) { + // FIXME: 360 sided cylinder is really slow to compile + GTEST_SKIP(); + + const auto [bsp, bspx, prt] = LoadTestmapQ1("qbsp_many_sided_face.map", {}); + + for (auto &face : bsp.dfaces) { + EXPECT_LE(face.numedges, 64); + } } TEST(testmapsQ1, tjuncAngledFace) From 51421162941f806d252d344b7358404b6e0e00c5 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 6 Jul 2024 13:53:55 -0600 Subject: [PATCH 04/13] docs: update -maxedges docs --- docs/qbsp.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/qbsp.rst b/docs/qbsp.rst index 6420059d..13cae0ae 100644 --- a/docs/qbsp.rst +++ b/docs/qbsp.rst @@ -522,7 +522,9 @@ Options .. option:: -maxedges n - The max number of edges/vertices on a single face before it is split into another face. + The max number of edges/vertices on a single face before it is split into another face. 0 means unlimited. + + Default is 0 for Q2, and 64 for Q1. .. option:: -worldextent n From 8ac82b7c79dafc9e409dbb4ada947cd0a3d85613 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 20 Jul 2024 19:51:19 -0600 Subject: [PATCH 05/13] Add Ubuntu 24.04 CI, Fix Embree 4 (#434) * Add Ubuntu 24.04 to CI matrix. * Fix embree4 bitrot. * For Linux, only upload artifact from Ubuntu 22.04 for now Should fix this error reported on ubuntu-22.04's CI Error: Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run * Use system TBB and Embree 4 on ubuntu-24.04 * skip packaging of embree/TBB if using system provided packages * For Linux, only upload artifact from Ubuntu 22.04 for now (fix) * common: fix ASan error on non-null-terminated (16 char long) texture names --------- Co-authored-by: Daniel Svensson --- .github/workflows/continuous-building.yml | 10 ++++++-- build-linux-64.sh | 31 +++++++++++++++-------- common/fs.cc | 22 +++++++++++++++- include/light/trace_embree.hh | 8 +++--- 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/.github/workflows/continuous-building.yml b/.github/workflows/continuous-building.yml index c9be5573..26566f4a 100644 --- a/.github/workflows/continuous-building.yml +++ b/.github/workflows/continuous-building.yml @@ -13,6 +13,7 @@ jobs: matrix: os: - ubuntu-22.04 + - ubuntu-24.04 - macos-12 - windows-2022 use-asan: @@ -33,6 +34,11 @@ jobs: run: | sudo apt update sudo apt install qtbase5-dev libqt5svg5-dev + - name: 'Linux: Install TBB and Embree, if using distro packages' + if: ${{ matrix.os == 'ubuntu-24.04' }} + run: | + sudo apt install libtbb-dev libembree-dev + echo "USE_SYSTEM_TBB_AND_EMBREE=1" >> $GITHUB_ENV - name: 'Linux: Build the artifact' if: startsWith(matrix.os, 'ubuntu-') run: ./build-linux-64.sh @@ -42,7 +48,7 @@ jobs: mkdir ericw-tools-linux unzip build-linux/*-Linux.zip -d ericw-tools-linux - name: 'Linux: Upload the artifact' - if: ${{ startsWith(matrix.os, 'ubuntu-') && matrix.use-asan == 'NO' }} + if: ${{ matrix.os == 'ubuntu-22.04' && matrix.use-asan == 'NO' }} uses: actions/upload-artifact@v4 with: path: ericw-tools-linux/ @@ -50,7 +56,7 @@ jobs: if-no-files-found: error - name: 'Linux: Create GitHub Release and upload build' uses: softprops/action-gh-release@v1 - if: ${{ startsWith(matrix.os, 'ubuntu-') && matrix.use-asan == 'NO' && startsWith(github.ref, 'refs/tags/') }} + if: ${{ matrix.os == 'ubuntu-22.04' && matrix.use-asan == 'NO' && startsWith(github.ref, 'refs/tags/') }} with: draft: true files: | diff --git a/build-linux-64.sh b/build-linux-64.sh index fd3c5598..47f9dd4c 100755 --- a/build-linux-64.sh +++ b/build-linux-64.sh @@ -14,20 +14,29 @@ cmake --version mkdir "$BUILD_DIR" cd "$BUILD_DIR" -wget -q https://github.com/embree/embree/releases/download/v3.13.1/embree-3.13.1.x86_64.linux.tar.gz -O embree.tgz -wget -q https://github.com/oneapi-src/oneTBB/releases/download/v2021.3.0/oneapi-tbb-2021.3.0-lin.tgz -O tbb.tgz -tar xf embree.tgz -tar xf tbb.tgz +if [ "$USE_SYSTEM_TBB_AND_EMBREE" == "1" ]; then + if [ "$USE_ASAN" == "YES" ]; then + cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DERICWTOOLS_ASAN=YES -DSKIP_EMBREE_INSTALL=YES -DSKIP_TBB_INSTALL=YES + else + cmake .. -DCMAKE_BUILD_TYPE=Release -DSKIP_EMBREE_INSTALL=YES -DSKIP_TBB_INSTALL=YES + fi +else + wget -q https://github.com/embree/embree/releases/download/v3.13.1/embree-3.13.1.x86_64.linux.tar.gz -O embree.tgz + wget -q https://github.com/oneapi-src/oneTBB/releases/download/v2021.3.0/oneapi-tbb-2021.3.0-lin.tgz -O tbb.tgz -EMBREE_CMAKE_DIR="$(pwd)/embree-3.13.1.x86_64.linux/lib/cmake/embree-3.13.1" -TBB_CMAKE_DIR="$(pwd)/oneapi-tbb-2021.3.0/lib/cmake" + tar xf embree.tgz + tar xf tbb.tgz -# check USE_ASAN environment variable (see cmake.yml) -if [ "$USE_ASAN" == "YES" ]; then - cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_PREFIX_PATH="$EMBREE_CMAKE_DIR;$TBB_CMAKE_DIR" -DENABLE_LIGHTPREVIEW=YES -DERICWTOOLS_ASAN=YES -else - cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$EMBREE_CMAKE_DIR;$TBB_CMAKE_DIR" + EMBREE_CMAKE_DIR="$(pwd)/embree-3.13.1.x86_64.linux/lib/cmake/embree-3.13.1" + TBB_CMAKE_DIR="$(pwd)/oneapi-tbb-2021.3.0/lib/cmake" + + # check USE_ASAN environment variable (see cmake.yml) + if [ "$USE_ASAN" == "YES" ]; then + cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_PREFIX_PATH="$EMBREE_CMAKE_DIR;$TBB_CMAKE_DIR" -DENABLE_LIGHTPREVIEW=YES -DERICWTOOLS_ASAN=YES + else + cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$EMBREE_CMAKE_DIR;$TBB_CMAKE_DIR" + fi fi # not yet free of memory leaks, so don't abort on leak detection diff --git a/common/fs.cc b/common/fs.cc index 2f98216e..cfc37618 100644 --- a/common/fs.cc +++ b/common/fs.cc @@ -159,8 +159,24 @@ struct wad_archive : archive_like uint8_t compression; padding<2> pad; std::array name; // must be null terminated + // NOTE: textures using all 16 exist in the wild, e.g. openquartzmirror + // in free_wad.wad auto stream_data() { return std::tie(filepos, disksize, size, type, compression, pad, name); } + + std::string name_as_string() const { + size_t length = 0; + + // count the number of leading non-null characters + for (int i = 0; i < 16; ++i) { + if (name[i] != 0) + ++length; + else + break; + } + + return std::string(name.data(), length); + } }; std::unordered_map, case_insensitive_hash, case_insensitive_equal> @@ -189,7 +205,11 @@ struct wad_archive : archive_like wadstream >= file; - files[file.name.data()] = std::make_tuple(file.filepos, file.disksize); + std::string tex_name = file.name_as_string(); + if (tex_name.size() == 16) { + logging::print("WARNING: texture name {} ({}) is not null-terminated\n", tex_name, pathname); + } + files[tex_name] = std::make_tuple(file.filepos, file.disksize); } } diff --git a/include/light/trace_embree.hh b/include/light/trace_embree.hh index e5c3c65e..bfe761dc 100644 --- a/include/light/trace_embree.hh +++ b/include/light/trace_embree.hh @@ -243,8 +243,8 @@ public: #ifdef HAVE_EMBREE4 RTCIntersectArguments embree4_args = ctx2.setup_intersection_arguments(); - for (int i = 0; i < _numrays; ++i) - rtcIntersect1(scene, &_rays[i], &embree4_args); + for (auto &ray : _rays) + rtcIntersect1(scene, &ray.ray, &embree4_args); #else rtcIntersect1M(scene, &ctx2, &_rays.data()->ray, _rays.size(), sizeof(_rays[0])); #endif @@ -310,8 +310,8 @@ public: ray_source_info ctx2(this, self, shadowmask); #ifdef HAVE_EMBREE4 RTCOccludedArguments embree4_args = ctx2.setup_occluded_arguments(); - for (int i = 0; i < _numrays; ++i) - rtcOccluded1(scene, &_rays[i], &embree4_args); + for (auto &ray : _rays) + rtcOccluded1(scene, &ray.ray.ray, &embree4_args); #else rtcOccluded1M(scene, &ctx2, &_rays.data()->ray.ray, _rays.size(), sizeof(_rays[0])); #endif From e5f01ce0e3920da10ff60fbb1d81962a0a69fef5 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 20 Jul 2024 23:11:26 -0600 Subject: [PATCH 06/13] light: add _switchableshadow_target Switchable shadows setup which works in id1 Fixes #432 --- common/bsputils.cc | 6 +- docs/light.rst | 20 ++ include/common/bsputils.hh | 2 +- include/light/entities.hh | 5 + light/entities.cc | 23 ++- light/light.cc | 7 + testmaps/q1_light_switchableshadow_target.map | 184 ++++++++++++++++++ tests/test_ltface.cc | 35 +++- 8 files changed, 275 insertions(+), 7 deletions(-) create mode 100644 testmaps/q1_light_switchableshadow_target.map diff --git a/common/bsputils.cc b/common/bsputils.cc index 28f6db7a..d3d3046d 100644 --- a/common/bsputils.cc +++ b/common/bsputils.cc @@ -1099,10 +1099,10 @@ static int StyleOffset(int style, const mface_t *face, const faceextents_t &face } /** - * Samples the lightmap at an integer coordinate in style 0 + * Samples the lightmap at an integer coordinate in the given style */ qvec3b LM_Sample(const mbsp_t *bsp, const mface_t *face, const lit_variant_t *lit, const faceextents_t &faceextents, - int byte_offset_of_face, qvec2i coord) + int byte_offset_of_face, qvec2i coord, int style) { if (byte_offset_of_face == -1) { return {0, 0, 0}; @@ -1113,7 +1113,7 @@ qvec3b LM_Sample(const mbsp_t *bsp, const mface_t *face, const lit_variant_t *li Q_assert(coord[0] < faceextents.width()); Q_assert(coord[1] < faceextents.height()); - int style_offset = StyleOffset(0, face, faceextents); + int style_offset = StyleOffset(style, face, faceextents); if (style_offset == -1) { return {0, 0, 0}; } diff --git a/docs/light.rst b/docs/light.rst index 88f861de..c26896e0 100644 --- a/docs/light.rst +++ b/docs/light.rst @@ -665,6 +665,11 @@ If used on func_detail* or func_group, a full qbsp pass need to be run. through the switchable shadow casters, regardless of whether the shadow is off or on. + .. seealso:: + + The light entity key :light-key:`_switchableshadow_target` allows using switchable + shadows in ID1 Quake, without custom QC, although the setup is more awkward. + .. bmodel-key:: "_dirt" "n" For brush models, -1 prevents dirtmapping on the brush model. Useful @@ -918,6 +923,21 @@ Point Lights Set to 1 to make the light compiler ignore this entity (prevents it from casting any light). e.g. could be useful with rtlights. +.. light-key:: "_switchableshadow_target" "name" + + Calculate lighting with and without bmodels with a "targetname" equal to "name", + and stores the resulting switchable shadow data in a light style which is stored in this light + entity's "style" key. + + You should give this light a :light-key:`targetname` and typically set "spawnflags" "1" (start off). + + Implies :light-key:`_nostaticlight` (this entity itself does not cast any light). + + .. hint:: + + If your mod supports it, you should prefer to use bmodel key :bmodel-key:`_switchableshadow` + to enable switchable shadows. + Spotlights ---------- diff --git a/include/common/bsputils.hh b/include/common/bsputils.hh index aeb9eafb..5f33c0bb 100644 --- a/include/common/bsputils.hh +++ b/include/common/bsputils.hh @@ -176,7 +176,7 @@ public: }; qvec3b LM_Sample(const mbsp_t *bsp, const mface_t *face, const lit_variant_t *lit, const faceextents_t &faceextents, - int byte_offset_of_face, qvec2i coord); + int byte_offset_of_face, qvec2i coord, int style = 0); qvec3f LM_Sample_HDR(const mbsp_t *bsp, const mface_t *face, diff --git a/include/light/entities.hh b/include/light/entities.hh index 568dd8e5..707c7917 100644 --- a/include/light/entities.hh +++ b/include/light/entities.hh @@ -113,6 +113,7 @@ public: settings::setting_int32 light_channel_mask; settings::setting_int32 shadow_channel_mask; settings::setting_bool nonudge; + settings::setting_string switchableshadow_target; light_t(); @@ -143,6 +144,10 @@ std::vector> &GetLights(); const std::vector &GetEntdicts(); std::vector &GetSuns(); std::vector &GetRadLights(); +/** + * Returns the light entity that has "_switchableshadow_target" set to the given value, or nullptr. + */ +light_t *LightWithSwitchableShadowTargetValue(const std::string &target); const std::vector> &GetSurfaceLightTemplates(); diff --git a/light/entities.cc b/light/entities.cc index 572edb13..516cea00 100644 --- a/light/entities.cc +++ b/light/entities.cc @@ -42,6 +42,7 @@ static std::vector> lightstyleForTargetname; static std::vector> surfacelight_templates; static std::ofstream surflights_dump_file; static fs::path surflights_dump_filename; +static std::map lights_by_switchableshadow_target; /** * Resets global data in this file @@ -58,6 +59,7 @@ void ResetLightEntities() surfacelight_templates.clear(); surflights_dump_file = {}; surflights_dump_filename.clear(); + lights_by_switchableshadow_target.clear(); } std::vector> &GetLights() @@ -80,6 +82,16 @@ std::vector &GetRadLights() return radlights; } +light_t *LightWithSwitchableShadowTargetValue(const std::string &target) +{ + auto it = lights_by_switchableshadow_target.find(target); + + if (it == lights_by_switchableshadow_target.end()) + return nullptr; + + return it->second; +} + /* surface lights */ static void MakeSurfaceLights(const mbsp_t *bsp); @@ -121,7 +133,8 @@ light_t::light_t() surflight_atten{this, "surflight_atten", 1.f}, light_channel_mask{this, "light_channel_mask", CHANNEL_MASK_DEFAULT}, shadow_channel_mask{this, "shadow_channel_mask", CHANNEL_MASK_DEFAULT}, - nonudge{this, "nonudge", false} + nonudge{this, "nonudge", false}, + switchableshadow_target{this, "switchableshadow_target", ""} { } @@ -1062,6 +1075,14 @@ void LoadEntities(const settings::worldspawn_keys &cfg, const mbsp_t *bsp) entity->projfov.value(), entity->projectionmatrix); } + // vanilla-compatible switchable shadows + const std::string &switchableshadow_target = entity->switchableshadow_target.value(); + if (!switchableshadow_target.empty()) { + entity->nostaticlight.set_value(true, settings::source::DEFAULT); + + lights_by_switchableshadow_target[switchableshadow_target] = entity.get(); + } + CheckEntityFields(bsp, cfg, entity.get()); } } diff --git a/light/light.cc b/light/light.cc index ff7e3575..4d06d0b0 100644 --- a/light/light.cc +++ b/light/light.cc @@ -725,6 +725,13 @@ static void FindModelInfo(const mbsp_t *bsp) // apply settings info->set_settings(*entdict, settings::source::MAP); + // vanilla-compatible switchable shadows + if (auto *light = LightWithSwitchableShadowTargetValue(entdict->get("targetname"))) { + // take the "style" key from this light entity and enable switchable shadows on ourself + info->switchableshadow.set_value(true, settings::source::DEFAULT); + info->switchshadstyle.set_value(light->style.value(), settings::source::DEFAULT); + } + /* Check if this model will cast shadows (shadow => shadowself) */ if (info->switchableshadow.boolValue()) { Q_assert(info->switchshadstyle.value() != 0); diff --git a/testmaps/q1_light_switchableshadow_target.map b/testmaps/q1_light_switchableshadow_target.map new file mode 100644 index 00000000..86a18fc8 --- /dev/null +++ b/testmaps/q1_light_switchableshadow_target.map @@ -0,0 +1,184 @@ +// Game: Quake +// Format: Standard +// entity 0 +{ +"classname" "worldspawn" +"_tb_textures" "textures/e1u1" +"wad" "deprecated/free_wad.wad" +// brush 0 +{ +( 480 1088 928 ) ( 480 1089 928 ) ( 480 1088 929 ) bolt14 0 32 0 1 1 +( 704 1088 928 ) ( 704 1088 929 ) ( 705 1088 928 ) bolt14 0 32 0 1 1 +( 704 1088 928 ) ( 705 1088 928 ) ( 704 1089 928 ) bolt14 0 0 0 1 1 +( 944 1472 944 ) ( 944 1473 944 ) ( 945 1472 944 ) bolt14 0 0 0 1 1 +( 944 1488 944 ) ( 945 1488 944 ) ( 944 1488 945 ) bolt14 0 32 0 1 1 +( 1056 1472 944 ) ( 1056 1472 945 ) ( 1056 1473 944 ) bolt14 0 32 0 1 1 +} +// brush 1 +{ +( 480 1088 1248 ) ( 480 1089 1248 ) ( 480 1088 1249 ) bolt14 0 96 0 1 1 +( 704 1072 1248 ) ( 704 1072 1249 ) ( 705 1072 1248 ) bolt14 0 96 0 1 1 +( 704 1088 1248 ) ( 705 1088 1248 ) ( 704 1089 1248 ) bolt14 0 0 0 1 1 +( 944 1472 1264 ) ( 944 1473 1264 ) ( 945 1472 1264 ) bolt14 0 0 0 1 1 +( 944 1488 1264 ) ( 945 1488 1264 ) ( 944 1488 1265 ) bolt14 0 96 0 1 1 +( 1056 1472 1264 ) ( 1056 1472 1265 ) ( 1056 1473 1264 ) bolt14 0 96 0 1 1 +} +// brush 2 +{ +( 480 1072 928 ) ( 480 1073 928 ) ( 480 1072 929 ) bolt14 16 32 0 1 1 +( 704 1072 928 ) ( 704 1072 929 ) ( 705 1072 928 ) bolt14 0 32 0 1 1 +( 704 1072 928 ) ( 705 1072 928 ) ( 704 1073 928 ) bolt14 0 -16 0 1 1 +( 944 1456 1248 ) ( 944 1457 1248 ) ( 945 1456 1248 ) bolt14 0 -16 0 1 1 +( 944 1088 944 ) ( 945 1088 944 ) ( 944 1088 945 ) bolt14 0 32 0 1 1 +( 1056 1456 944 ) ( 1056 1456 945 ) ( 1056 1457 944 ) bolt14 16 32 0 1 1 +} +// brush 3 +{ +( 480 1392 928 ) ( 480 1393 928 ) ( 480 1392 929 ) bolt14 -48 32 0 1 1 +( 832 1488 928 ) ( 832 1488 929 ) ( 833 1488 928 ) bolt14 -128 32 0 1 1 +( 832 1392 928 ) ( 833 1392 928 ) ( 832 1393 928 ) bolt14 -128 48 0 1 1 +( 1072 1776 1248 ) ( 1072 1777 1248 ) ( 1073 1776 1248 ) bolt14 -128 48 0 1 1 +( 1072 1504 944 ) ( 1073 1504 944 ) ( 1072 1504 945 ) bolt14 -128 32 0 1 1 +( 1056 1392 928 ) ( 1056 1392 929 ) ( 1056 1393 928 ) bolt14 -48 32 0 1 1 +} +// brush 4 +{ +( 1056 1088 1056 ) ( 1056 1089 1056 ) ( 1056 1088 1057 ) bolt14 0 32 0 1 1 +( 736 1088 1056 ) ( 736 1088 1057 ) ( 737 1088 1056 ) bolt14 -32 32 0 1 1 +( 736 1088 928 ) ( 737 1088 928 ) ( 736 1089 928 ) bolt14 -32 0 0 1 1 +( 976 1472 1248 ) ( 976 1473 1248 ) ( 977 1472 1248 ) bolt14 -32 0 0 1 1 +( 976 1488 1072 ) ( 977 1488 1072 ) ( 976 1488 1073 ) bolt14 -32 32 0 1 1 +( 1072 1472 1072 ) ( 1072 1472 1073 ) ( 1072 1473 1072 ) bolt14 0 32 0 1 1 +} +// brush 5 +{ +( 464 1088 1056 ) ( 464 1089 1056 ) ( 464 1088 1057 ) bolt14 0 32 0 1 1 +( 144 1072 1056 ) ( 144 1072 1057 ) ( 145 1072 1056 ) bolt14 48 32 0 1 1 +( 144 1088 928 ) ( 145 1088 928 ) ( 144 1089 928 ) bolt14 48 0 0 1 1 +( 384 1472 1248 ) ( 384 1473 1248 ) ( 385 1472 1248 ) bolt14 48 0 0 1 1 +( 384 1488 1072 ) ( 385 1488 1072 ) ( 384 1488 1073 ) bolt14 48 32 0 1 1 +( 480 1472 1072 ) ( 480 1472 1073 ) ( 480 1473 1072 ) bolt14 0 32 0 1 1 +} +// brush 6 +{ +( 704 1088 944 ) ( 704 1089 944 ) ( 704 1088 945 ) bolt10 0 0 0 1 1 +( 704 1088 944 ) ( 704 1088 945 ) ( 705 1088 944 ) bolt10 0 0 0 1 1 +( 704 1088 944 ) ( 705 1088 944 ) ( 704 1089 944 ) bolt10 0 0 0 1 1 +( 720 1216 1008 ) ( 720 1217 1008 ) ( 721 1216 1008 ) bolt10 0 0 0 1 1 +( 720 1216 960 ) ( 721 1216 960 ) ( 720 1216 961 ) bolt10 0 0 0 1 1 +( 720 1216 960 ) ( 720 1216 961 ) ( 720 1217 960 ) bolt10 0 0 0 1 1 +} +// brush 7 +{ +( 704 1088 944 ) ( 704 1089 944 ) ( 704 1088 945 ) bolt10 0 0 0 1 1 +( 720 1344 960 ) ( 720 1344 961 ) ( 721 1344 960 ) bolt10 0 0 0 1 1 +( 704 1088 944 ) ( 705 1088 944 ) ( 704 1089 944 ) bolt10 0 0 0 1 1 +( 720 1216 1008 ) ( 720 1217 1008 ) ( 721 1216 1008 ) bolt10 0 0 0 1 1 +( 720 1488 960 ) ( 721 1488 960 ) ( 720 1488 961 ) bolt10 0 0 0 1 1 +( 720 1216 960 ) ( 720 1216 961 ) ( 720 1217 960 ) bolt10 0 0 0 1 1 +} +// brush 8 +{ +( 704 1088 944 ) ( 704 1089 944 ) ( 704 1088 945 ) bolt10 0 0 0 1 1 +( 704 1088 944 ) ( 704 1088 945 ) ( 705 1088 944 ) bolt10 0 0 0 1 1 +( 720 1216 1008 ) ( 721 1216 1008 ) ( 720 1217 1008 ) bolt10 0 0 0 1 1 +( 720 1216 1016 ) ( 720 1217 1016 ) ( 721 1216 1016 ) bolt10 0 0 0 1 1 +( 720 1488 960 ) ( 721 1488 960 ) ( 720 1488 961 ) bolt10 0 0 0 1 1 +( 720 1216 960 ) ( 720 1216 961 ) ( 720 1217 960 ) bolt10 0 0 0 1 1 +} +} +// entity 1 +{ +"classname" "info_player_start" +"origin" "848 1280 968" +"angle" "180" +} +// entity 2 +{ +"classname" "light" +"origin" "760 1280 1048" +"light" "1000" +"targetname" "toggle_door1_shadow" +"spawnflags" "1" +"_switchableshadow_target" "door1" +} +// entity 3 +{ +"classname" "light" +"origin" "568 1264 952" +"_color" "1 0 0" +"light" "500" +} +// entity 4 +{ +"classname" "func_door" +"targetname" "door1" +"spawnflags" "32" +"angle" "-2" +"speed" "1000" +// brush 0 +{ +( 704 1320 944 ) ( 704 1321 944 ) ( 704 1320 945 ) bolt10 -8 0 0 1 1 +( 704 1320 944 ) ( 704 1320 945 ) ( 705 1320 944 ) bolt10 0 0 0 1 1 +( 704 1320 944 ) ( 705 1320 944 ) ( 704 1321 944 ) bolt10 0 8 0 1 1 +( 720 1448 1008 ) ( 720 1449 1008 ) ( 721 1448 1008 ) bolt10 0 8 0 1 1 +( 720 1336 960 ) ( 721 1336 960 ) ( 720 1336 961 ) bolt10 0 0 0 1 1 +( 720 1448 960 ) ( 720 1448 961 ) ( 720 1449 960 ) bolt10 -8 0 0 1 1 +} +// brush 1 +{ +( 704 1224 944 ) ( 704 1225 944 ) ( 704 1224 945 ) bolt10 -8 0 0 1 1 +( 704 1224 944 ) ( 704 1224 945 ) ( 705 1224 944 ) bolt10 0 0 0 1 1 +( 704 1224 944 ) ( 705 1224 944 ) ( 704 1225 944 ) bolt10 0 8 0 1 1 +( 720 1352 1008 ) ( 720 1353 1008 ) ( 721 1352 1008 ) bolt10 0 8 0 1 1 +( 720 1240 960 ) ( 721 1240 960 ) ( 720 1240 961 ) bolt10 0 0 0 1 1 +( 720 1352 960 ) ( 720 1352 961 ) ( 720 1353 960 ) bolt10 -8 0 0 1 1 +} +// brush 2 +{ +( 704 1256 944 ) ( 704 1257 944 ) ( 704 1256 945 ) bolt10 -8 0 0 1 1 +( 704 1256 944 ) ( 704 1256 945 ) ( 705 1256 944 ) bolt10 0 0 0 1 1 +( 704 1256 944 ) ( 705 1256 944 ) ( 704 1257 944 ) bolt10 0 8 0 1 1 +( 720 1384 1008 ) ( 720 1385 1008 ) ( 721 1384 1008 ) bolt10 0 8 0 1 1 +( 720 1272 960 ) ( 721 1272 960 ) ( 720 1272 961 ) bolt10 0 0 0 1 1 +( 720 1384 960 ) ( 720 1384 961 ) ( 720 1385 960 ) bolt10 -8 0 0 1 1 +} +// brush 3 +{ +( 704 1288 944 ) ( 704 1289 944 ) ( 704 1288 945 ) bolt10 -8 0 0 1 1 +( 704 1288 944 ) ( 704 1288 945 ) ( 705 1288 944 ) bolt10 0 0 0 1 1 +( 704 1288 944 ) ( 705 1288 944 ) ( 704 1289 944 ) bolt10 0 8 0 1 1 +( 720 1416 1008 ) ( 720 1417 1008 ) ( 721 1416 1008 ) bolt10 0 8 0 1 1 +( 720 1304 960 ) ( 721 1304 960 ) ( 720 1304 961 ) bolt10 0 0 0 1 1 +( 720 1416 960 ) ( 720 1416 961 ) ( 720 1417 960 ) bolt10 -8 0 0 1 1 +} +} +// entity 5 +{ +"classname" "func_button" +"target" "switch1" +"angle" "-2" +// brush 0 +{ +( 752 1288 952 ) ( 752 1289 952 ) ( 752 1288 953 ) swire2 -56 0 0 1 1 +( 792 1272 944 ) ( 791 1272 944 ) ( 792 1272 945 ) swire2 48 0 180 1 -1 +( 792 1248 944 ) ( 792 1249 944 ) ( 791 1248 944 ) swire2 -56 -48 90 1 1 +( 744 1288 952 ) ( 743 1288 952 ) ( 744 1289 952 ) +0switch -8 -16 90 1 1 +( 744 1288 952 ) ( 744 1288 953 ) ( 743 1288 952 ) swire2 48 0 180 1 -1 +( 784 1248 944 ) ( 784 1248 945 ) ( 784 1249 944 ) swire2 -56 0 0 1 1 +} +} +// entity 6 +{ +"classname" "trigger_relay" +"origin" "760 1256 1000" +"targetname" "switch1" +"target" "door1" +} +// entity 7 +{ +"classname" "trigger_relay" +"origin" "760 1320 1000" +"targetname" "switch1" +"target" "toggle_door1_shadow" +} diff --git a/tests/test_ltface.cc b/tests/test_ltface.cc index 8d8e3492..a191f2f9 100644 --- a/tests/test_ltface.cc +++ b/tests/test_ltface.cc @@ -319,7 +319,7 @@ static void CheckFaceLuxelsNonBlack(const mbsp_t &bsp, const mface_t &face) static void CheckFaceLuxelAtPoint(const mbsp_t *bsp, const dmodelh2_t *model, const qvec3b &expected_color, const qvec3d &point, const qvec3d &normal = {0, 0, 0}, const lit_variant_t *lit = nullptr, - const bspxentries_t *bspx = nullptr) + const bspxentries_t *bspx = nullptr, int style = 0) { auto *face = BSP_FindFaceAtPoint(bsp, model, point, normal); ASSERT_TRUE(face); @@ -340,7 +340,7 @@ static void CheckFaceLuxelAtPoint(const mbsp_t *bsp, const dmodelh2_t *model, co const auto coord = extents.worldToLMCoord(point); const auto int_coord = qvec2i(round(coord[0]), round(coord[1])); - const qvec3b sample = LM_Sample(bsp, face, lit, extents, offset, int_coord); + const qvec3b sample = LM_Sample(bsp, face, lit, extents, offset, int_coord, style); SCOPED_TRACE(fmt::format("world point: {}", point)); SCOPED_TRACE(fmt::format("lm coord: {}", coord)); SCOPED_TRACE(fmt::format("lm int_coord: {}", int_coord)); @@ -1145,4 +1145,35 @@ TEST(ltfaceQ1, hdr) // check internal lightmap - greyscale, since Q1 CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, testpoint, testnormal); } +} + +TEST(ltfaceQ1, switchableshadowTarget) +{ + SCOPED_TRACE("Vanilla-compatible switchable shadows"); + + auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_switchableshadow_target.map", {}); + + // find the light controlling the switchable shadow + auto entdicts = EntData_Parse(bsp); + + auto it = std::find_if(entdicts.begin(), entdicts.end(), [](const entdict_t& dict) -> bool { + return dict.get("_switchableshadow_target") == "door1"; + }); + ASSERT_NE(it, entdicts.end()); + + ASSERT_TRUE(it->has("style")); + int switchable_style = it->get_int("style"); + + ASSERT_EQ(32, switchable_style); + + const qvec3f not_in_shadow {792, 1240, 944}; + const qvec3f in_shadow {792, 1264, 944}; + + // not in shadow - should be lit up red in style 0, and black in style 32 + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {68, 0, 0}, not_in_shadow, {0, 0, 1}, &lit, &bspx, 0); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, not_in_shadow, {0, 0, 1}, &lit, &bspx, 32); + + // in (switchable) shadow - should be black in style 0, and red in style 32 + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, in_shadow, {0, 0, 1}, &lit, &bspx, 0); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {68, 0, 0}, in_shadow, {0, 0, 1}, &lit, &bspx, 32); } \ No newline at end of file From adeb3e81aa6c2092be9f67891371dd6d96eaef59 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Thu, 25 Jul 2024 22:45:18 -0600 Subject: [PATCH 07/13] light: fix _surflight_group --- light/entities.cc | 6 +- testmaps/q1_light_surflight_group.map | 114 ++++++++++++++++++++++++++ tests/test_ltface.cc | 17 +++- 3 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 testmaps/q1_light_surflight_group.map diff --git a/light/entities.cc b/light/entities.cc index 516cea00..19719375 100644 --- a/light/entities.cc +++ b/light/entities.cc @@ -1371,10 +1371,8 @@ bool FaceMatchesSurfaceLightTemplate( const surfflags_t &extended_flags = extended_texinfo_flags[face->texinfo]; - if (extended_flags.surflight_group) { - if (surflight.surflight_group.value() && surflight.surflight_group.value() != extended_flags.surflight_group) { - return false; - } + if (surflight.surflight_group.value() != extended_flags.surflight_group) { + return false; } return !Q_strcasecmp(texname, surflight.epairs->get("_surface")); diff --git a/testmaps/q1_light_surflight_group.map b/testmaps/q1_light_surflight_group.map new file mode 100644 index 00000000..dda16d5b --- /dev/null +++ b/testmaps/q1_light_surflight_group.map @@ -0,0 +1,114 @@ +// Game: Quake +// Format: Standard +// entity 0 +{ +"classname" "worldspawn" +"wad" "deprecated/free_wad.wad" +// brush 0 +{ +( 480 1088 928 ) ( 480 1089 928 ) ( 480 1088 929 ) bolt14 0 32 0 1 1 +( 704 1088 928 ) ( 704 1088 929 ) ( 705 1088 928 ) bolt14 0 32 0 1 1 +( 704 1088 928 ) ( 705 1088 928 ) ( 704 1089 928 ) bolt14 0 0 0 1 1 +( 944 1472 944 ) ( 944 1473 944 ) ( 945 1472 944 ) bolt14 0 0 0 1 1 +( 944 1488 944 ) ( 945 1488 944 ) ( 944 1488 945 ) bolt14 0 32 0 1 1 +( 1056 1472 944 ) ( 1056 1472 945 ) ( 1056 1473 944 ) bolt14 0 32 0 1 1 +} +// brush 1 +{ +( 480 1088 1248 ) ( 480 1089 1248 ) ( 480 1088 1249 ) bolt14 0 96 0 1 1 +( 704 1072 1248 ) ( 704 1072 1249 ) ( 705 1072 1248 ) bolt14 0 96 0 1 1 +( 704 1088 1248 ) ( 705 1088 1248 ) ( 704 1089 1248 ) bolt14 0 0 0 1 1 +( 944 1472 1264 ) ( 944 1473 1264 ) ( 945 1472 1264 ) bolt14 0 0 0 1 1 +( 944 1488 1264 ) ( 945 1488 1264 ) ( 944 1488 1265 ) bolt14 0 96 0 1 1 +( 1056 1472 1264 ) ( 1056 1472 1265 ) ( 1056 1473 1264 ) bolt14 0 96 0 1 1 +} +// brush 2 +{ +( 480 1072 928 ) ( 480 1073 928 ) ( 480 1072 929 ) bolt14 16 32 0 1 1 +( 704 1072 928 ) ( 704 1072 929 ) ( 705 1072 928 ) bolt14 0 32 0 1 1 +( 704 1072 928 ) ( 705 1072 928 ) ( 704 1073 928 ) bolt14 0 -16 0 1 1 +( 944 1456 1248 ) ( 944 1457 1248 ) ( 945 1456 1248 ) bolt14 0 -16 0 1 1 +( 944 1088 944 ) ( 945 1088 944 ) ( 944 1088 945 ) bolt14 0 32 0 1 1 +( 1056 1456 944 ) ( 1056 1456 945 ) ( 1056 1457 944 ) bolt14 16 32 0 1 1 +} +// brush 3 +{ +( 480 1392 928 ) ( 480 1393 928 ) ( 480 1392 929 ) bolt14 -48 32 0 1 1 +( 832 1488 928 ) ( 832 1488 929 ) ( 833 1488 928 ) bolt14 -128 32 0 1 1 +( 832 1392 928 ) ( 833 1392 928 ) ( 832 1393 928 ) bolt14 -128 48 0 1 1 +( 1072 1776 1248 ) ( 1072 1777 1248 ) ( 1073 1776 1248 ) bolt14 -128 48 0 1 1 +( 1072 1504 944 ) ( 1073 1504 944 ) ( 1072 1504 945 ) bolt14 -128 32 0 1 1 +( 1056 1392 928 ) ( 1056 1392 929 ) ( 1056 1393 928 ) bolt14 -48 32 0 1 1 +} +// brush 4 +{ +( 1056 1088 1056 ) ( 1056 1089 1056 ) ( 1056 1088 1057 ) bolt14 0 32 0 1 1 +( 736 1088 1056 ) ( 736 1088 1057 ) ( 737 1088 1056 ) bolt14 -32 32 0 1 1 +( 736 1088 928 ) ( 737 1088 928 ) ( 736 1089 928 ) bolt14 -32 0 0 1 1 +( 976 1472 1248 ) ( 976 1473 1248 ) ( 977 1472 1248 ) bolt14 -32 0 0 1 1 +( 976 1488 1072 ) ( 977 1488 1072 ) ( 976 1488 1073 ) bolt14 -32 32 0 1 1 +( 1072 1472 1072 ) ( 1072 1472 1073 ) ( 1072 1473 1072 ) bolt14 0 32 0 1 1 +} +// brush 5 +{ +( 464 1088 1056 ) ( 464 1089 1056 ) ( 464 1088 1057 ) bolt14 0 32 0 1 1 +( 144 1072 1056 ) ( 144 1072 1057 ) ( 145 1072 1056 ) bolt14 48 32 0 1 1 +( 144 1088 928 ) ( 145 1088 928 ) ( 144 1089 928 ) bolt14 48 0 0 1 1 +( 384 1472 1248 ) ( 384 1473 1248 ) ( 385 1472 1248 ) bolt14 48 0 0 1 1 +( 384 1488 1072 ) ( 385 1488 1072 ) ( 384 1488 1073 ) bolt14 48 32 0 1 1 +( 480 1472 1072 ) ( 480 1472 1073 ) ( 480 1473 1072 ) bolt14 0 32 0 1 1 +} +// brush 6 +{ +( 640 1152 944 ) ( 640 1153 944 ) ( 640 1152 945 ) bolt14 0 0 0 1 1 +( 640 1152 944 ) ( 640 1152 945 ) ( 641 1152 944 ) bolt14 0 0 0 1 1 +( 640 1152 944 ) ( 641 1152 944 ) ( 640 1153 944 ) bolt14 0 0 0 1 1 +( 768 1216 960 ) ( 768 1217 960 ) ( 769 1216 960 ) bolt16 0 0 0 1 1 +( 768 1216 960 ) ( 769 1216 960 ) ( 768 1216 961 ) bolt14 0 0 0 1 1 +( 768 1216 960 ) ( 768 1216 961 ) ( 768 1217 960 ) bolt14 0 0 0 1 1 +} +// brush 7 +{ +( 480 1280 944 ) ( 480 1281 944 ) ( 480 1280 945 ) bolt14 0 0 0 1 1 +( 480 1280 944 ) ( 480 1280 945 ) ( 481 1280 944 ) bolt14 0 0 0 1 1 +( 480 1280 944 ) ( 481 1280 944 ) ( 480 1281 944 ) bolt14 0 0 0 1 1 +( 768 1296 1056 ) ( 768 1297 1056 ) ( 769 1296 1056 ) bolt14 0 0 0 1 1 +( 768 1296 960 ) ( 769 1296 960 ) ( 768 1296 961 ) bolt14 0 0 0 1 1 +( 768 1296 960 ) ( 768 1296 961 ) ( 768 1297 960 ) bolt14 0 0 0 1 1 +} +} +// entity 1 +{ +"classname" "info_player_start" +"origin" "896 1232 968" +"angle" "180" +} +// entity 2 +{ +"classname" "light" +"origin" "808 1176 952" +"_surface" "bolt16" +"_color" "1 0 0" +} +// entity 3 +{ +"classname" "light" +"origin" "808 1368 952" +"_surface" "bolt16" +"_surflight_group" "1" +"_color" "0 0 1" +} +// entity 4 +{ +"classname" "func_detail" +"_surflight_group" "1" +// brush 0 +{ +( 640 1344 944 ) ( 640 1345 944 ) ( 640 1344 945 ) bolt14 0 0 0 1 1 +( 640 1344 944 ) ( 640 1344 945 ) ( 641 1344 944 ) bolt14 0 0 0 1 1 +( 640 1344 944 ) ( 641 1344 944 ) ( 640 1345 944 ) bolt14 0 0 0 1 1 +( 768 1408 960 ) ( 768 1409 960 ) ( 769 1408 960 ) bolt16 0 0 0 1 1 +( 768 1408 960 ) ( 769 1408 960 ) ( 768 1408 961 ) bolt14 0 0 0 1 1 +( 768 1408 960 ) ( 768 1408 961 ) ( 768 1409 960 ) bolt14 0 0 0 1 1 +} +} diff --git a/tests/test_ltface.cc b/tests/test_ltface.cc index a191f2f9..86028e36 100644 --- a/tests/test_ltface.cc +++ b/tests/test_ltface.cc @@ -1176,4 +1176,19 @@ TEST(ltfaceQ1, switchableshadowTarget) // in (switchable) shadow - should be black in style 0, and red in style 32 CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 0}, in_shadow, {0, 0, 1}, &lit, &bspx, 0); CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {68, 0, 0}, in_shadow, {0, 0, 1}, &lit, &bspx, 32); -} \ No newline at end of file +} + +TEST(ltfaceQ1, surflightGroup) +{ + auto [bsp, bspx, lit] = QbspVisLight_Q1("q1_light_surflight_group.map", {}); + + { + SCOPED_TRACE("left brush is just receiving red light"); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {75, 0, 0}, {720, 1184, 960}, {0, 0, 1}, &lit, &bspx); + } + + { + SCOPED_TRACE("right brush is just receiving blue light"); + CheckFaceLuxelAtPoint(&bsp, &bsp.dmodels[0], {0, 0, 75}, {720, 1376, 960}, {0, 0, 1}, &lit, &bspx); + } +} From a6ee0c6c4d79e6f4e586667a4812e87525d99681 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 4 Aug 2024 10:45:12 -0600 Subject: [PATCH 08/13] bsputil: move settings to header --- bsputil/bsputil.cc | 147 ++++++++++++------------------------- include/bsputil/bsputil.hh | 86 ++++++++++++++++++++++ 2 files changed, 134 insertions(+), 99 deletions(-) diff --git a/bsputil/bsputil.cc b/bsputil/bsputil.cc index 243ec6e9..ab4c2c94 100644 --- a/bsputil/bsputil.cc +++ b/bsputil/bsputil.cc @@ -17,6 +17,8 @@ See file, 'COPYING', for details. */ +#include + #include #ifndef _WIN32 @@ -42,93 +44,41 @@ #include #include -// special type of setting that combines multiple -// settings. just for the casting capability of bsputil_settings. -class setting_combined : public settings::setting_base -{ -protected: - std::vector> _settings; - -public: - setting_combined(settings::setting_container *dictionary, const settings::nameset &names, std::initializer_list> settings, - const settings::setting_group *group = nullptr, const char *description = "") - : setting_base(dictionary, names, group, description), - _settings(settings) - { - } - bool copy_from(const setting_base &other) override - { - throw std::runtime_error("not implemented"); - } - void reset() override - { - throw std::runtime_error("not implemented"); - } - bool parse(const std::string &setting_name, parser_base_t &parser, settings::source source) override - { - throw std::runtime_error("not implemented"); - } - std::string string_value() const override - { - throw std::runtime_error("not implemented"); - } - std::string format() const override - { - throw std::runtime_error("not implemented"); - } +// bsputil_settings - template - const TSetting *get(size_t index) const - { - return dynamic_cast(_settings[index].get()); - } -}; - -struct bsputil_settings : public settings::common_settings +bool bsputil_settings::load_setting(const std::string &name, settings::source src) { -private: - template - bool load_setting(const std::string &name, parser_base_t &parser, settings::source src, TArgs&& ...setting_arguments) - { - auto setting = std::make_unique(nullptr, name, std::forward(setting_arguments)...); - bool parsed = setting->parse(name, parser, src); - operations.push_back(std::move(setting)); - return parsed; - } - - bool load_setting(const std::string &name, settings::source src) - { - auto setting = std::make_unique(nullptr, name, nullptr); - operations.push_back(std::move(setting)); - return true; - } + auto setting = std::make_unique(nullptr, name, nullptr); + operations.push_back(std::move(setting)); + return true; +} -public: - settings::setting_func scale{this, "scale", [&](const std::string &name, parser_base_t &parser, settings::source src) { +bsputil_settings::bsputil_settings() : + scale{this, "scale", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, 0.f, 0.f, 0.f); - }, nullptr, "Scale the BSP by the given scalar vectors (can be negative, too)"}; - settings::setting_func replace_entities{this, "replace-entities", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Scale the BSP by the given scalar vectors (can be negative, too)"}, + replace_entities{this, "replace-entities", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, ""); - }, nullptr, "Replace BSP entities with the given files' contents"}; - settings::setting_func extract_entities{this, "extract-entities", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Replace BSP entities with the given files' contents"}, + extract_entities{this, "extract-entities", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, ""); - }, nullptr, "Extract BSP entities to the given file name"}; - settings::setting_func extract_textures{this, "extract-textures", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Extract BSP entities to the given file name"}, + extract_textures{this, "extract-textures", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, ""); - }, nullptr, "Extract BSP texutres to the given wad file"}; - settings::setting_func replace_textures{this, "replace-textures", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Extract BSP texutres to the given wad file"}, + replace_textures{this, "replace-textures", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, ""); - }, nullptr, "Replace BSP textures with the given wads' contents"}; - settings::setting_func convert{this, "convert", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Replace BSP textures with the given wads' contents"}, + convert{this, "convert", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, ""); - }, nullptr, "Convert the BSP file to a different BSP format"}; - settings::setting_func check{this, "check", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Convert the BSP file to a different BSP format"}, + check{this, "check", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, src); - }, nullptr, "Check/verify BSP data"}; - settings::setting_func modelinfo{this, "modelinfo", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Check/verify BSP data"}, + modelinfo{this, "modelinfo", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, src); - }, nullptr, "Print model info"}; - settings::setting_func findfaces{this, "findfaces", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Print model info"}, + findfaces{this, "findfaces", [&](const std::string &name, parser_base_t &parser, settings::source src) { auto pos = std::make_shared(nullptr, name, 0.f, 0.f, 0.f); if (bool parsed = pos->parse(name, parser, src); !parsed) return false; @@ -137,11 +87,11 @@ struct bsputil_settings : public settings::common_settings return false; operations.push_back(std::make_unique(nullptr, name, std::initializer_list> { pos, norm })); return true; - }, nullptr, "Find faces with specified pos/normal"}; - settings::setting_func findleaf{this, "findleaf", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Find faces with specified pos/normal"}, + findleaf{this, "findleaf", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, 0.f, 0.f, 0.f); - }, nullptr, "Find closest leaf"}; - settings::setting_func settexinfo{this, "settexinfo", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Find closest leaf"}, + settexinfo{this, "settexinfo", [&](const std::string &name, parser_base_t &parser, settings::source src) { auto faceNum = std::make_shared(nullptr, name, 0); if (bool parsed = faceNum->parse(name, parser, src); !parsed) return false; @@ -150,20 +100,20 @@ struct bsputil_settings : public settings::common_settings return false; operations.push_back(std::make_unique(nullptr, name, std::initializer_list> { faceNum, texInfoNum })); return true; - }, nullptr, "Set texinfo"}; - settings::setting_func decompile{this, "decompile", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Set texinfo"}, + decompile{this, "decompile", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, src); - }, nullptr, "Decompile to the given .map file"}; - settings::setting_func decompile_geomonly{this, "decompile-geomonly", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Decompile to the given .map file"}, + decompile_geomonly{this, "decompile-geomonly", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, src); - }, nullptr, "Decompile"}; - settings::setting_func decompile_ignore_brushes{this, "decompile-ignore-brushes", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Decompile"}, + decompile_ignore_brushes{this, "decompile-ignore-brushes", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, src); - }, nullptr, "Decompile entities only"}; - settings::setting_func decompile_hull{this, "decompile-hull", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Decompile entities only"}, + decompile_hull{this, "decompile-hull", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, 0); - }, nullptr, "Decompile specific hull"}; - settings::setting_func extract_bspx_lump{this, "extract-bspx-lump", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Decompile specific hull"}, + extract_bspx_lump{this, "extract-bspx-lump", [&](const std::string &name, parser_base_t &parser, settings::source src) { auto lump = std::make_shared(nullptr, name, ""); if (bool parsed = lump->parse(name, parser, src); !parsed) return false; @@ -172,8 +122,8 @@ struct bsputil_settings : public settings::common_settings return false; operations.push_back(std::make_unique(nullptr, name, std::initializer_list> { lump, output })); return true; - }, nullptr, "Extract a BSPX lump"}; - settings::setting_func insert_bspx_lump{this, "insert-bspx-lump", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Extract a BSPX lump"}, + insert_bspx_lump{this, "insert-bspx-lump", [&](const std::string &name, parser_base_t &parser, settings::source src) { auto lump = std::make_shared(nullptr, name, ""); if (bool parsed = lump->parse(name, parser, src); !parsed) return false; @@ -182,16 +132,15 @@ struct bsputil_settings : public settings::common_settings return false; operations.push_back(std::make_unique(nullptr, name, std::initializer_list> { lump, input })); return true; - }, nullptr, "Insert a BSPX lump"}; - settings::setting_func remove_bspx_lump{this, "remove-bspx-lump", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Insert a BSPX lump"}, + remove_bspx_lump{this, "remove-bspx-lump", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, ""); - }, nullptr, "Remove a BSPX lump"}; - settings::setting_func svg{this, "svg", [&](const std::string &name, parser_base_t &parser, settings::source src) { + }, nullptr, "Remove a BSPX lump"}, + svg{this, "svg", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, 0); - }, nullptr, "Create an SVG view of the input BSP"}; + }, nullptr, "Create an SVG view of the input BSP"} +{} - std::vector> operations; -}; bsputil_settings bsputil_options; diff --git a/include/bsputil/bsputil.hh b/include/bsputil/bsputil.hh index 75ae9901..aeb49e7b 100644 --- a/include/bsputil/bsputil.hh +++ b/include/bsputil/bsputil.hh @@ -21,6 +21,92 @@ #include +#include + +// special type of setting that combines multiple +// settings. just for the casting capability of bsputil_settings. +class setting_combined : public settings::setting_base +{ +protected: + std::vector> _settings; + +public: + setting_combined(settings::setting_container *dictionary, const settings::nameset &names, std::initializer_list> settings, + const settings::setting_group *group = nullptr, const char *description = "") + : setting_base(dictionary, names, group, description), + _settings(settings) + { + } + bool copy_from(const setting_base &other) override + { + throw std::runtime_error("not implemented"); + } + void reset() override + { + throw std::runtime_error("not implemented"); + } + bool parse(const std::string &setting_name, parser_base_t &parser, settings::source source) override + { + throw std::runtime_error("not implemented"); + } + std::string string_value() const override + { + throw std::runtime_error("not implemented"); + } + std::string format() const override + { + throw std::runtime_error("not implemented"); + } + + template + const TSetting *get(size_t index) const + { + return dynamic_cast(_settings[index].get()); + } +}; + + + +struct bsputil_settings : public settings::common_settings +{ +private: + template + bool load_setting(const std::string &name, parser_base_t &parser, settings::source src, TArgs&& ...setting_arguments) + { + auto setting = std::make_unique(nullptr, name, std::forward(setting_arguments)...); + bool parsed = setting->parse(name, parser, src); + operations.push_back(std::move(setting)); + return parsed; + } + + bool load_setting(const std::string &name, settings::source src); + +public: + settings::setting_func scale; + settings::setting_func replace_entities; + settings::setting_func extract_entities; + settings::setting_func extract_textures; + settings::setting_func replace_textures; + settings::setting_func convert; + settings::setting_func check; + settings::setting_func modelinfo; + settings::setting_func findfaces; + settings::setting_func findleaf; + settings::setting_func settexinfo; + settings::setting_func decompile; + settings::setting_func decompile_geomonly; + settings::setting_func decompile_ignore_brushes; + settings::setting_func decompile_hull; + settings::setting_func extract_bspx_lump; + settings::setting_func insert_bspx_lump; + settings::setting_func remove_bspx_lump; + settings::setting_func svg; + + std::vector> operations; + + bsputil_settings(); +}; + struct mbsp_t; void ExportWad(std::ofstream &wadfile, const mbsp_t *bsp); From 5b06cde21c4338b144a515fa3c55a25682a6d31f Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 4 Aug 2024 11:43:33 -0600 Subject: [PATCH 09/13] tests: add a few more settings tests (parse remainder) --- tests/test.cc | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/tests/test.cc b/tests/test.cc index c8b74073..e8e6fa03 100644 --- a/tests/test.cc +++ b/tests/test.cc @@ -11,8 +11,9 @@ TEST(settings, booleanFlagImplicit) settings::setting_bool boolSetting(&settings, "locked", false); const char *arguments[] = {"qbsp.exe", "-locked"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(boolSetting.value(), true); + ASSERT_TRUE(remainder.empty()); } TEST(settings, booleanFlagExplicit) @@ -21,8 +22,9 @@ TEST(settings, booleanFlagExplicit) settings::setting_bool boolSetting(&settings, "locked", false); const char *arguments[] = {"qbsp.exe", "-locked", "1"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(boolSetting.value(), true); + ASSERT_TRUE(remainder.empty()); } TEST(settings, booleanFlagStray) @@ -31,8 +33,9 @@ TEST(settings, booleanFlagStray) settings::setting_bool boolSetting(&settings, "locked", false); const char *arguments[] = {"qbsp.exe", "-locked", "stray"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(boolSetting.value(), true); + ASSERT_EQ(remainder, (std::vector{"stray"})); } // test scalars @@ -42,8 +45,9 @@ TEST(settings, scalarSimple) settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale", "1.25"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(scalarSetting.value(), 1.25f); + ASSERT_TRUE(remainder.empty()); } TEST(settings, scalarNegative) @@ -52,8 +56,9 @@ TEST(settings, scalarNegative) settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale", "-0.25"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(scalarSetting.value(), -0.25f); + ASSERT_TRUE(remainder.empty()); } TEST(settings, scalarInfinity) @@ -62,8 +67,9 @@ TEST(settings, scalarInfinity) settings::setting_scalar scalarSetting(&settings, "scale", 1.0, 0.0, std::numeric_limits::infinity()); const char *arguments[] = {"qbsp.exe", "-scale", "INFINITY"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(scalarSetting.value(), std::numeric_limits::infinity()); + ASSERT_TRUE(remainder.empty()); } TEST(settings, scalarNAN) @@ -72,8 +78,9 @@ TEST(settings, scalarNAN) settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale", "NAN"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_TRUE(std::isnan(scalarSetting.value())); + ASSERT_TRUE(remainder.empty()); } TEST(settings, scalarScientific) @@ -82,8 +89,9 @@ TEST(settings, scalarScientific) settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-scale", "1.54334E-34"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(scalarSetting.value(), 1.54334E-34f); + ASSERT_TRUE(remainder.empty()); } TEST(settings, scalarEOF) @@ -120,8 +128,9 @@ TEST(settings, int32CanOmitArgumentSimple) settings::can_omit_argument_tag(), 1); const char *arguments[] = {"qbsp.exe", "-bounce", "2"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(setting.value(), 2); + ASSERT_TRUE(remainder.empty()); } TEST(settings, int32CanOmitArgumentWithFollingSetting) @@ -132,9 +141,10 @@ TEST(settings, int32CanOmitArgumentWithFollingSetting) settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-bounce", "-scale", "0.25"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(setting.value(), 1); ASSERT_EQ(scalarSetting.value(), 0.25); + ASSERT_TRUE(remainder.empty()); } TEST(settings, int32CanOmitArgumentEOF) @@ -145,8 +155,9 @@ TEST(settings, int32CanOmitArgumentEOF) settings::setting_scalar scalarSetting(&settings, "scale", 1.0); const char *arguments[] = {"qbsp.exe", "-bounce"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(setting.value(), 1); + ASSERT_TRUE(remainder.empty()); } TEST(settings, int32CanOmitArgumentRemainder) @@ -158,7 +169,7 @@ TEST(settings, int32CanOmitArgumentRemainder) const char *arguments[] = {"qbsp.exe", "-bounce", "remainder"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; auto remainder = settings.parse(p); - ASSERT_EQ(remainder, std::vector{"remainder"});\ + ASSERT_EQ(remainder, std::vector{"remainder"}); } // test vec3 @@ -168,8 +179,9 @@ TEST(settings, vec3Simple) settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); const char *arguments[] = {"qbsp.exe", "-origin", "1", "2", "3"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(scalarSetting.value(), (qvec3f{1, 2, 3})); + ASSERT_TRUE(remainder.empty()); } TEST(settings, vec3Complex) @@ -178,10 +190,11 @@ TEST(settings, vec3Complex) settings::setting_vec3 scalarSetting(&settings, "origin", 0, 0, 0); const char *arguments[] = {"qbsp.exe", "-origin", "-12.5", "-INFINITY", "NAN"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(scalarSetting.value()[0], -12.5f); ASSERT_EQ(scalarSetting.value()[1], -std::numeric_limits::infinity()); ASSERT_TRUE(std::isnan(scalarSetting.value()[2])); + ASSERT_TRUE(remainder.empty()); } TEST(settings, vec3Incomplete) @@ -209,8 +222,9 @@ TEST(settings, stringSimple) settings::setting_string stringSetting(&settings, "name", ""); const char *arguments[] = {"qbsp.exe", "-name", "i am a string with spaces in it"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(stringSetting.value(), arguments[2]); + ASSERT_TRUE(remainder.empty()); } // test remainder @@ -234,9 +248,10 @@ TEST(settings, doubleHyphen) settings::setting_string stringSetting(&settings, "name", ""); const char *arguments[] = {"qbsp.exe", "--locked", "--name", "my name!"}; token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; - settings.parse(p); + auto remainder = settings.parse(p); ASSERT_EQ(boolSetting.value(), true); ASSERT_EQ(stringSetting.value(), "my name!"); + ASSERT_TRUE(remainder.empty()); } // test groups; ensure that performance is the first group From 2354209fead5c1d4d7756a77594d914bf6e68b66 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 4 Aug 2024 11:46:27 -0600 Subject: [PATCH 10/13] tests: rename test,cc > test_settings.cc --- tests/CMakeLists.txt | 2 +- tests/{test.cc => test_settings.cc} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/{test.cc => test_settings.cc} (100%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5f32ff2f..cca831e6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ add_executable(tests - test.cc + test_settings.cc test_main.cc test_common.cc test_entities.cc diff --git a/tests/test.cc b/tests/test_settings.cc similarity index 100% rename from tests/test.cc rename to tests/test_settings.cc From 0b2395dacd0cde490626cd19feab96570c6591a8 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 4 Aug 2024 12:09:12 -0600 Subject: [PATCH 11/13] bsputil: fix --extract-entities and --extract-textures command line parsing Fixes #435 --- bsputil/bsputil.cc | 4 ++-- tests/test_bsputil.cc | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/bsputil/bsputil.cc b/bsputil/bsputil.cc index ab4c2c94..bb47a020 100644 --- a/bsputil/bsputil.cc +++ b/bsputil/bsputil.cc @@ -61,10 +61,10 @@ bsputil_settings::bsputil_settings() : return this->load_setting(name, parser, src, ""); }, nullptr, "Replace BSP entities with the given files' contents"}, extract_entities{this, "extract-entities", [&](const std::string &name, parser_base_t &parser, settings::source src) { - return this->load_setting(name, parser, src, ""); + return this->load_setting(name, parser, src, ""); }, nullptr, "Extract BSP entities to the given file name"}, extract_textures{this, "extract-textures", [&](const std::string &name, parser_base_t &parser, settings::source src) { - return this->load_setting(name, parser, src, ""); + return this->load_setting(name, parser, src, ""); }, nullptr, "Extract BSP texutres to the given wad file"}, replace_textures{this, "replace-textures", [&](const std::string &name, parser_base_t &parser, settings::source src) { return this->load_setting(name, parser, src, ""); diff --git a/tests/test_bsputil.cc b/tests/test_bsputil.cc index d8dfdfa4..f36d7d3e 100644 --- a/tests/test_bsputil.cc +++ b/tests/test_bsputil.cc @@ -71,3 +71,27 @@ TEST(bsputil, extractTextures) EXPECT_TRUE(loaded_tex); } } + +TEST(bsputil, parseExtractTextures) +{ + bsputil_settings settings; + + const char *arguments[] = {"bsputil.exe", "--extract-textures", "test.bsp"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + auto remainder = settings.parse(p); + + ASSERT_EQ(1, remainder.size()); + ASSERT_EQ("test.bsp", remainder[0]); +} + +TEST(bsputil, parseExtractEntities) +{ + bsputil_settings settings; + + const char *arguments[] = {"bsputil.exe", "--extract-entities", "test.bsp"}; + token_parser_t p{std::size(arguments) - 1, arguments + 1, {}}; + auto remainder = settings.parse(p); + + ASSERT_EQ(1, remainder.size()); + ASSERT_EQ("test.bsp", remainder[0]); +} From 3f1659dd842d3e3f4430ac01b7b45ceff324a625 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 5 Aug 2024 15:45:55 -0600 Subject: [PATCH 12/13] qbsp: only reuse edges once for software renderer compat Issue reported by Izhido --- include/qbsp/map.hh | 6 ++ qbsp/faces.cc | 9 ++- qbsp/map.cc | 3 +- testmaps/q1_edge_sharing_software.map | 92 +++++++++++++++++++++++++++ tests/test_qbsp.cc | 20 ++++++ 5 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 testmaps/q1_edge_sharing_software.map diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index ab15a1f7..2c23e2d5 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -171,6 +171,12 @@ struct hashedge_t * the face that edge v1 -> v2 belongs to */ const face_t *face; + + /** + * Has v2 -> v1 been referenced by another face yet, by using -edge_index? + * This is only allowed to happen once (software renderer limitation). + */ + bool has_been_reused; }; struct mapdata_t diff --git a/qbsp/faces.cc b/qbsp/faces.cc index 54abf07a..3bfea7f0 100644 --- a/qbsp/faces.cc +++ b/qbsp/faces.cc @@ -150,11 +150,16 @@ inline int64_t GetEdge(const size_t &v1, const size_t &v2, const face_t *face, e if (!qbsp_options.noedgereuse.value()) { // search for existing edges if (auto it = map.hashedges.find(std::make_pair(v2, v1)); it != map.hashedges.end()) { - const hashedge_t &existing = it->second; + hashedge_t &existing = it->second; // this content check is required for software renderers // (see q1_liquid_software test case) if (existing.face->contents.front.equals(qbsp_options.target_game, face->contents.front)) { - return -existing.edge_index; + // only reusing an edge once is a separate limitation of software renderers + // (see q1_edge_sharing_software.map test case) + if (!existing.has_been_reused) { + existing.has_been_reused = true; + return -existing.edge_index; + } } } } diff --git a/qbsp/map.cc b/qbsp/map.cc index 4c90d7ab..974e7f13 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -168,7 +168,8 @@ void mapdata_t::add_hash_vector(const qvec3d &point, const size_t &num) void mapdata_t::add_hash_edge(size_t v1, size_t v2, int64_t edge_index, const face_t *face) { - hashedges.emplace(std::make_pair(v1, v2), hashedge_t{.v1 = v1, .v2 = v2, .edge_index = edge_index, .face = face}); + hashedges.emplace(std::make_pair(v1, v2), hashedge_t{.v1 = v1, .v2 = v2, .edge_index = edge_index, .face = face, + .has_been_reused = false}); } const std::optional &mapdata_t::load_image_meta(const std::string_view &name) diff --git a/testmaps/q1_edge_sharing_software.map b/testmaps/q1_edge_sharing_software.map new file mode 100644 index 00000000..7ffb7113 --- /dev/null +++ b/testmaps/q1_edge_sharing_software.map @@ -0,0 +1,92 @@ +// Game: Quake +// Format: Valve +// entity 0 +{ +"mapversion" "220" +"classname" "worldspawn" +"wad" "deprecated/free_wad.wad;deprecated/hintskip.wad" +// brush 0 +{ +( 184 112 152 ) ( 184 -432 152 ) ( 184 112 -80 ) skip [ 0 1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 192 -432 -80 ) ( 184 -432 -80 ) ( 192 -432 152 ) skip [ 1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 192 112 -80 ) ( 184 112 -80 ) ( 192 -432 -80 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 192 -432 152 ) ( 184 -432 152 ) ( 192 112 152 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 192 112 152 ) ( 184 112 152 ) ( 192 112 -80 ) skip [ -1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 192 -432 152 ) ( 192 112 152 ) ( 192 -432 -80 ) skip [ 0 1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +} +// brush 1 +{ +( -176 -432 -80 ) ( -176 112 -80 ) ( -176 -432 152 ) skip [ 0 -1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( -176 -432 152 ) ( -168 -432 152 ) ( -176 -432 -80 ) skip [ 1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( -176 -432 -80 ) ( -168 -432 -80 ) ( -176 112 -80 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -176 112 152 ) ( -168 112 152 ) ( -176 -432 152 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -176 112 -80 ) ( -168 112 -80 ) ( -176 112 152 ) skip [ -1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( -168 -432 -80 ) ( -168 -432 152 ) ( -168 112 -80 ) skip [ 0 -1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +} +// brush 2 +{ +( -168 112 152 ) ( -168 104 152 ) ( -168 112 -80 ) skip [ 0 -1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 184 104 152 ) ( 184 104 -80 ) ( -168 104 152 ) skip [ -1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( -168 112 -80 ) ( -168 104 -80 ) ( 184 112 -80 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 184 112 152 ) ( 184 104 152 ) ( -168 112 152 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 184 112 152 ) ( -168 112 152 ) ( 184 112 -80 ) skip [ -1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 184 112 -80 ) ( 184 104 -80 ) ( 184 112 152 ) skip [ 0 1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +} +// brush 3 +{ +( -168 -432 -80 ) ( -168 -424 -80 ) ( -168 -432 152 ) skip [ 0 -1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 184 -432 -80 ) ( -168 -432 -80 ) ( 184 -432 152 ) skip [ 1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 184 -432 -80 ) ( 184 -424 -80 ) ( -168 -432 -80 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -168 -432 152 ) ( -168 -424 152 ) ( 184 -432 152 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -168 -424 -80 ) ( 184 -424 -80 ) ( -168 -424 152 ) skip [ 1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 184 -432 152 ) ( 184 -424 152 ) ( 184 -432 -80 ) skip [ 0 1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +} +// brush 4 +{ +( -168 -424 152 ) ( -168 -424 144 ) ( -168 104 152 ) skip [ 0 -1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 184 -424 152 ) ( 184 -424 144 ) ( -168 -424 152 ) skip [ 1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 184 104 144 ) ( -168 104 144 ) ( 184 -424 144 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 184 104 152 ) ( 184 -424 152 ) ( -168 104 152 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -168 104 152 ) ( -168 104 144 ) ( 184 104 152 ) skip [ -1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 184 104 152 ) ( 184 104 144 ) ( 184 -424 152 ) skip [ 0 1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +} +// brush 5 +{ +( -168 104 -80 ) ( -168 104 -72 ) ( -168 -424 -80 ) skip [ 0 -1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( -168 -424 -80 ) ( -168 -424 -72 ) ( 184 -424 -80 ) skip [ 1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( -168 104 -80 ) ( -168 -424 -80 ) ( 184 104 -80 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -168 -424 -72 ) ( -168 104 -72 ) ( 184 -424 -72 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 184 104 -80 ) ( 184 104 -72 ) ( -168 104 -80 ) skip [ -1 0 0 0 ] [ 0 0 -1 32 ] 0 1 1 +( 184 -424 -80 ) ( 184 -424 -72 ) ( 184 104 -80 ) skip [ 0 1 0 0 ] [ 0 0 -1 32 ] 0 1 1 +} +// brush 6 +{ +( -80 16 -16 ) ( -112 -64 -16 ) ( -112 -64 -64 ) bolt12 [ 0 1.0000000000000002 0 20 ] [ 0 0 -1.0000000000000002 -8 ] 0 1 1 +( -112 -64 -16 ) ( -64 -64 -48 ) ( -64 -64 -64 ) bolt12 [ -1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 -8 ] 0 1 1 +( -112 -64 -64 ) ( -64 -64 -64 ) ( -80 16 -64 ) bolt12 [ -1.0000000000000002 0 0 0 ] [ 0 1.0000000000000002 0 42.13333 ] 0 1 1 +( -80 16 -16 ) ( -64 -64 -48 ) ( -112 -64 -16 ) bolt16 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -64 -64 -64 ) ( -64 -64 -48 ) ( -80 16 -16 ) bolt12 [ 0 0 1.0000000000000002 32 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +} +// brush 7 +{ +( -64 -64 -48 ) ( 0 -48 0 ) ( 0 -96 0 ) bolt16 [ -3.061616997868383e-16 -1 0 -16 ] [ 1 -3.061616997868383e-16 0 -15.999998 ] 321.38824 1 1 +( 0 -96 -64 ) ( -64 -64 -64 ) ( -64 -64 -48 ) bolt12 [ -1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 -8 ] 0 1 1 +( 0 -48 -64 ) ( 0 -48 0 ) ( -64 -64 -48 ) bolt12 [ 0 0 1.0000000000000002 32 ] [ 1.0000000000000002 -3.0616169978683836e-16 0 -16 ] 0 1 1 +( 0 -96 -64 ) ( 0 -48 -64 ) ( -64 -64 -64 ) bolt12 [ -1.0000000000000002 0 0 0 ] [ 0 1.0000000000000002 0 10.133331 ] 0 1 1 +( 0 -96 0 ) ( 0 -48 0 ) ( 0 -48 -64 ) bolt12 [ -3.0616169978683836e-16 -1.0000000000000002 0 -16 ] [ 0 0 -1.0000000000000002 -8 ] 0 1 1 +} +// brush 8 +{ +( -64 -16 -16 ) ( -64 -64 -48 ) ( -64 -64 -64 ) bolt12 [ 1.8369701987210302e-16 1.0000000000000002 0 0 ] [ 0 0 -1.0000000000000002 -8 ] 0 1 1 +( -64 -16 -16 ) ( 16 -16 -16 ) ( -64 -64 -48 ) bolt16 [ 1.8369701987210297e-16 1 0 0 ] [ -1 1.8369701987210297e-16 0 0 ] 90 1 1 +( -64 -64 -64 ) ( 16 -16 -64 ) ( -64 -16 -64 ) bolt12 [ -1.0000000000000002 0 0 0 ] [ 0 1.0000000000000002 0 60.266663 ] 0 1 1 +( 16 -16 -64 ) ( 16 -16 -16 ) ( -64 -16 -16 ) bolt12 [ -1.0000000000000002 0 0 0 ] [ 0 0 -1.0000000000000002 -8 ] 0 1 1 +( 16 -16 -64 ) ( -64 -64 -64 ) ( -64 -64 -48 ) bolt12 [ 0 0 1.0000000000000002 32 ] [ -1.0000000000000002 1.8369701987210302e-16 0 0 ] 0 1 1 +} +} +// entity 1 +{ +"classname" "info_player_start" +"origin" "-96 -304 -40" +"angle" "90" +} diff --git a/tests/test_qbsp.cc b/tests/test_qbsp.cc index 8e28e748..164a979b 100644 --- a/tests/test_qbsp.cc +++ b/tests/test_qbsp.cc @@ -2060,6 +2060,26 @@ TEST(qbspQ1, liquidSoftware) } } +TEST(qbspQ1, edgeSharingSoftware) +{ + SCOPED_TRACE("the software renderer only allows a given edge to be reused at most once, as the backwards version (negative index)"); + const auto [bsp, bspx, prt] = LoadTestmap("q1_edge_sharing_software.map"); + + std::map> signed_edge_faces; + for (auto &face : bsp.dfaces) { + for (int i = face.firstedge; i < (face.firstedge + face.numedges); ++i) { + // may be negative + const int edge = bsp.dsurfedges.at(i); + + signed_edge_faces[edge].push_back(&face); + } + } + + for (auto &[edge, faces] : signed_edge_faces) { + EXPECT_EQ(1, faces.size()); + } +} + TEST(qbspQ1, missingTexture) { const auto [bsp, bspx, prt] = LoadTestmap("q1_missing_texture.map"); From 7389adfa38ce817f2e49e0fbb7a039f93a3ced3c Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 5 Aug 2024 17:12:05 -0600 Subject: [PATCH 13/13] tests: update test expectation for last commit --- tests/test_qbsp.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_qbsp.cc b/tests/test_qbsp.cc index 164a979b..dec3332f 100644 --- a/tests/test_qbsp.cc +++ b/tests/test_qbsp.cc @@ -1263,7 +1263,10 @@ TEST(testmapsQ1, cubes) { const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_cubes.map"); - EXPECT_EQ(bsp.dedges.size(), 25); + // 1 + 12 for cube A + 13 for cube B. + // for the "four way" vertical edge, two of the faces can share an edge on cube A, but this blocks any further + // sharing on that edge in cube B. + EXPECT_EQ(bsp.dedges.size(), 26); } /**