qbsp: adjust tjunc logic

This commit is contained in:
Eric Wasylishen 2024-01-24 00:22:27 -07:00
parent a8443ef0cf
commit 0aeb0acd94
10 changed files with 2040 additions and 45 deletions

View File

@ -628,6 +628,22 @@ public:
return contents_are_solid(contents) || contents_are_sky(contents);
}
bool contents_are_opaque(const contentflags_t &contents, bool transwater) const override
{
auto bits = contentflags_to_bits(contents).visible_contents();
if (bits.solid) return true;
else if (bits.sky) return true;
else if (bits.wall) return true;
else if (bits.fence) return false;
else if (bits.lava) return !transwater;
else if (bits.slime) return !transwater;
else if (bits.water) return !transwater;
else if (bits.mist) return false;
return false;
}
contentflags_t contents_remap_for_export(const contentflags_t &contents, remap_type_t type) const override
{
/*
@ -1310,6 +1326,22 @@ struct gamedef_q2_t : public gamedef_t
return contents_are_solid(contents) || contents_are_sky(contents);
}
bool contents_are_opaque(const contentflags_t &contents, bool transwater) const override
{
int32_t c = contents.native;
if (!this->visible_contents(c))
return false;
// it's visible..
if (c & Q2_CONTENTS_TRANSLUCENT) {
return false;
}
return true;
}
contentflags_t contents_remap_for_export(const contentflags_t &contents, remap_type_t type) const override
{
// HACK: borrowing Q2_CONTENTS_MONSTER for func_detail_wall

View File

@ -7,7 +7,7 @@ maputil - utiltiy for working with Quake MAP files
Synopsis
========
**bsputil** [OPTION]... MAPFILE
**maputil** MAPFILE [OPTION]...
Options
=======

View File

@ -298,6 +298,7 @@ struct gamedef_t
virtual bool portal_can_see_through(
const contentflags_t &contents0, const contentflags_t &contents1, bool transwater, bool transsky) const = 0;
virtual bool contents_seals_map(const contentflags_t &contents) const = 0;
virtual bool contents_are_opaque(const contentflags_t &contents, bool transwater) const = 0;
enum class remap_type_t
{
brush,

View File

@ -174,6 +174,7 @@ public:
setting_int32 subdivide;
setting_bool nofill;
setting_bool nomerge;
setting_bool mergeacrosswater;
setting_bool noedgereuse;
setting_bool noclip;
setting_bool noskip;

View File

@ -72,12 +72,14 @@ static std::unique_ptr<face_t> TryMerge(const face_t *f1, const face_t *f2)
f1->original_side->lmshift != f2->original_side->lmshift)
return NULL;
// TODO: make this configurable?
if (qbsp_options.target_game->id != GAME_QUAKE_II) {
// Q1: don't merge across water boundaries; ezQuake/nQuake water caustics will leak onto
// above-water faces.
if (f1->contents[0].is_liquid(qbsp_options.target_game) != f2->contents[0].is_liquid(qbsp_options.target_game))
return nullptr;
if (!qbsp_options.mergeacrosswater.value()) {
// Q1: don't merge across water boundaries; ezQuake/nQuake water caustics will leak onto
// above-water faces.
if (f1->contents[0].is_liquid(qbsp_options.target_game) !=
f2->contents[0].is_liquid(qbsp_options.target_game))
return nullptr;
}
// Q1: don't merge across sky boundary - we delete faces inside sky
if (f1->contents[0].is_sky(qbsp_options.target_game) != f2->contents[0].is_sky(qbsp_options.target_game))

View File

@ -463,6 +463,7 @@ qbsp_settings::qbsp_settings()
"change the subdivide threshold, in luxels. 0 will disable subdivision entirely"},
nofill{this, "nofill", false, &debugging_group, "don't perform outside filling"},
nomerge{this, "nomerge", false, &debugging_group, "don't perform face merging"},
mergeacrosswater{this, "mergeacrosswater", false, &common_format_group, "merge faces that cross above and below water"},
noedgereuse{this, "noedgereuse", false, &debugging_group, "don't reuse edges (for debugging software rendering)"},
noclip{this, "noclip", false, &common_format_group, "don't write clip nodes (Q1-like BSP formats)"},
noskip{this, "noskip", false, &debugging_group, "don't remove faces with the 'skip' texture"},

View File

@ -143,21 +143,46 @@ static void FindEdgeVerts_BruteForce(
* tjunc fixes. func_detail_wall is meant to act like a separate mesh,
* so it shouldn't interact with solid.
*/
static bool Welds(const contentflags_t &a, const contentflags_t &b)
{
// FIXME: no clipping same type?
// all types weld with themselves
if (a.types_equal(b, qbsp_options.target_game))
return true;
// detail wall only welds with detail wall
if (qbsp_options.target_game->contents_are_detail_wall(a)
|| qbsp_options.target_game->contents_are_detail_wall(b))
return false;
// no need to weld translucent to opaque
// (because they could have void behind them due to visblocking.
// e.g. opaque water meeting solid)
if (!qbsp_options.target_game->contents_are_opaque(a, qbsp_options.transwater.value())
&& qbsp_options.target_game->contents_are_opaque(b, qbsp_options.transwater.value()))
return false;
if (!qbsp_options.target_game->contents_are_opaque(b, qbsp_options.transwater.value())
&& qbsp_options.target_game->contents_are_opaque(a, qbsp_options.transwater.value()))
return false;
// never weld with backfaces
if (qbsp_options.target_game->contents_are_empty(a)
|| qbsp_options.target_game->contents_are_empty(b))
return false;
// otherwise, weld
return true;
}
static bool HasTJuncInteraction(const face_t *f1, const face_t *f2)
{
// FIXME: handle func_detail_fence, func_detail_illusionary,
// liquids? make sure a combination of solid + func_detail_wall
// is treated as solid?
if (f1->contents.back.is_detail_wall(qbsp_options.target_game) &&
!f2->contents.back.is_detail_wall(qbsp_options.target_game))
return false;
if (f2->contents.back.is_detail_wall(qbsp_options.target_game) &&
!f1->contents.back.is_detail_wall(qbsp_options.target_game))
return false;
return true;
return Welds(f1->contents.back, f2->contents.back);
}
/*

1720
testmaps/q1_tjunc_matrix.map Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2122,3 +2122,195 @@ TEST_CASE("lq e3m4.map" * doctest::may_fail())
const auto [bsp, bspx, prt] = LoadTestmap("LibreQuake/lq1/maps/src/e3/e3m4.map");
CHECK(prt);
}
TEST_CASE("q1_tjunc_matrix")
{
// TODO: test opaque water in q1 mode
const auto [b, bspx, prt] = LoadTestmap("q1_tjunc_matrix.map", {"-mergeacrosswater"});
const mbsp_t &bsp = b; // workaround clang not allowing capturing bindings in lambdas
auto *game = bsp.loadversion->game;
CHECK(GAME_QUAKE == game->id);
const qvec3d face_midpoint_origin {-24, 0, 24};
const qvec3d face_midpoint_to_tjunc {8, 0, 8};
const qvec3d z_delta_to_next_face {0, 0, 64};
const qvec3d x_delta_to_next_face {-64, 0, 0};
enum index_t : int {
INDEX_SOLID = 0,
INDEX_SOLID_DETAIL,
INDEX_DETAIL_WALL,
INDEX_DETAIL_FENCE,
INDEX_DETAIL_FENCE_MIRRORINSIDE,
INDEX_DETAIL_ILLUSIONARY,
INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES,
INDEX_WATER,
INDEX_SKY
};
auto has_tjunc = [&](index_t horizontal, index_t vertical) -> bool {
const qvec3d face_midpoint = face_midpoint_origin
+ (x_delta_to_next_face * static_cast<int>(horizontal))
+ (z_delta_to_next_face * static_cast<int>(vertical));
auto *f = BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], face_midpoint);
const qvec3f tjunc_location = qvec3f(face_midpoint + face_midpoint_to_tjunc);
for (int i = 0; i < f->numedges; ++i) {
if (Face_PointAtIndex(&bsp, f, i) == tjunc_location) {
return true;
}
}
return false;
};
{
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));
// "-mergeacrosswater" is needed to prevent a weld between transparent water and solid
CHECK(!has_tjunc(INDEX_SOLID, INDEX_WATER));
CHECK( 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));
// see INDEX_SOLID, INDEX_WATER explanation
CHECK(!has_tjunc(INDEX_SOLID_DETAIL, INDEX_WATER));
CHECK( has_tjunc(INDEX_SOLID_DETAIL, INDEX_SKY));
}
{
INFO("INDEX_DETAIL_WALL horizontal");
// solid cuts a hole in detail_wall
CHECK( 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));
// see INDEX_SOLID, INDEX_WATER explanation
CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_WATER));
// sky cuts a hole in detail_wall
CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_SKY));
}
{
INFO("INDEX_DETAIL_FENCE horizontal");
// solid cuts a hole in fence
CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_SOLID));
// solid detail cuts a hole in fence
CHECK( 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));
// weld because both are translucent
CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_WATER));
// sky cuts a hole in fence
CHECK( has_tjunc(INDEX_DETAIL_FENCE, INDEX_SKY));
}
{
INFO("INDEX_DETAIL_FENCE_MIRRORINSIDE horizontal");
// solid cuts a hole in fence
CHECK( 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));
// 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));
// weld because both are translucent
CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_WATER));
// sky cuts a hole in fence
CHECK( has_tjunc(INDEX_DETAIL_FENCE_MIRRORINSIDE, INDEX_SKY));
}
{
INFO("INDEX_DETAIL_ILLUSIONARY horizontal");
// solid cuts a hole in illusionary
CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_SOLID));
// solid detail cuts a hole in illusionary
CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_SOLID_DETAIL));
// detail wall cuts a hole in illusionary
CHECK( 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));
// weld because both are translucent
CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_WATER));
// sky cuts a hole in illusionary
CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY, INDEX_SKY));
}
{
INFO("INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES horizontal");
// solid cuts a hole in illusionary
CHECK( 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));
// detail wall cuts a hole in illusionary
CHECK( 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));
// weld because both are translucent
CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_WATER));
// sky cuts a hole in illusionary
CHECK( has_tjunc(INDEX_DETAIL_ILLUSIONARY_NOCLIPFACES, INDEX_SKY));
}
{
INFO("INDEX_WATER horizontal");
// solid cuts a hole in water
CHECK( has_tjunc(INDEX_WATER, INDEX_SOLID));
// solid detail cuts a hole in illusionary
CHECK( 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));
}
{
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));
}
}

View File

@ -868,7 +868,7 @@ TEST_CASE("q2_mist_transwater" * doctest::test_suite("testmaps_q2"))
CHECK(Face_Winding(&bsp, down_faces[0]).directional_equal(top_of_water_dn));
}
TEST_CASE("q2_tjunc_matrix" * doctest::test_suite("testmaps_q2") * doctest::may_fail())
TEST_CASE("q2_tjunc_matrix" * doctest::test_suite("testmaps_q2"))
{
const auto [b, bspx, prt] = LoadTestmapQ2("q2_tjunc_matrix.map");
const mbsp_t &bsp = b; // workaround clang not allowing capturing bindings in lambdas
@ -911,16 +911,20 @@ TEST_CASE("q2_tjunc_matrix" * doctest::test_suite("testmaps_q2") * doctest::may_
};
{
INFO("INDEX_DETAIL_WALL horizontal - only welds with itself");
INFO("INDEX_DETAIL_WALL horizontal");
CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_DETAIL_WALL));
CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID));
CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID_DETAIL));
// 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));
// 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));
CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_SKY));
// same as INDEX_DETAIL_WALL, INDEX_SOLID
CHECK( has_tjunc(INDEX_DETAIL_WALL, INDEX_SKY));
}
{
@ -950,66 +954,83 @@ TEST_CASE("q2_tjunc_matrix" * doctest::test_suite("testmaps_q2") * doctest::may_
}
{
INFO("INDEX_TRANSPARENT_WATER horizontal - only welds with itself");
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));
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));
CHECK( 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));
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));
}
{
INFO("INDEX_OPAQUE_WATER horizontal - same as INDEX_SOLID");
CHECK(!has_tjunc(INDEX_OPAQUE_WATER, INDEX_DETAIL_WALL));
INFO("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));
CHECK(!has_tjunc(INDEX_OPAQUE_WATER, INDEX_TRANSPARENT_WATER));
// 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));
CHECK(!has_tjunc(INDEX_OPAQUE_WATER, INDEX_TRANSPARENT_WINDOW));
// window is stronger and cuts a hole in the water
CHECK( 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));
}
{
INFO("INDEX_OPAQUE_MIST horizontal - same as INDEX_SOLID");
CHECK(!has_tjunc(INDEX_OPAQUE_MIST, INDEX_DETAIL_WALL));
INFO("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));
CHECK(!has_tjunc(INDEX_OPAQUE_MIST, INDEX_TRANSPARENT_WATER));
// 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));
CHECK(!has_tjunc(INDEX_OPAQUE_MIST, INDEX_TRANSPARENT_WINDOW));
// 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));
}
{
INFO("INDEX_TRANSPARENT_WINDOW horizontal - only welds with itself");
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_DETAIL_WALL));
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SOLID));
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SOLID_DETAIL));
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_TRANSPARENT_WATER));
INFO("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));
// solid cuts a hole in the window
CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SOLID));
CHECK( 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));
// note, aux is lower priority than window, so bottom face of aux gets cut away
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_OPAQUE_AUX));
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SKY));
// sky cuts hole in window
CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SKY));
}
{
INFO("INDEX_OPAQUE_AUX horizontal - same as INDEX_SOLID");
CHECK(!has_tjunc(INDEX_OPAQUE_AUX, INDEX_DETAIL_WALL));
INFO("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));
CHECK(!has_tjunc(INDEX_OPAQUE_AUX, INDEX_TRANSPARENT_WINDOW));
// 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));
}