qbsp: adjust tjunc logic
This commit is contained in:
parent
a8443ef0cf
commit
0aeb0acd94
|
|
@ -628,6 +628,22 @@ public:
|
||||||
return contents_are_solid(contents) || contents_are_sky(contents);
|
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
|
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);
|
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
|
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
|
// HACK: borrowing Q2_CONTENTS_MONSTER for func_detail_wall
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ maputil - utiltiy for working with Quake MAP files
|
||||||
Synopsis
|
Synopsis
|
||||||
========
|
========
|
||||||
|
|
||||||
**bsputil** [OPTION]... MAPFILE
|
**maputil** MAPFILE [OPTION]...
|
||||||
|
|
||||||
Options
|
Options
|
||||||
=======
|
=======
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,7 @@ struct gamedef_t
|
||||||
virtual bool portal_can_see_through(
|
virtual bool portal_can_see_through(
|
||||||
const contentflags_t &contents0, const contentflags_t &contents1, bool transwater, bool transsky) const = 0;
|
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_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
|
enum class remap_type_t
|
||||||
{
|
{
|
||||||
brush,
|
brush,
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ public:
|
||||||
setting_int32 subdivide;
|
setting_int32 subdivide;
|
||||||
setting_bool nofill;
|
setting_bool nofill;
|
||||||
setting_bool nomerge;
|
setting_bool nomerge;
|
||||||
|
setting_bool mergeacrosswater;
|
||||||
setting_bool noedgereuse;
|
setting_bool noedgereuse;
|
||||||
setting_bool noclip;
|
setting_bool noclip;
|
||||||
setting_bool noskip;
|
setting_bool noskip;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
f1->original_side->lmshift != f2->original_side->lmshift)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// TODO: make this configurable?
|
|
||||||
if (qbsp_options.target_game->id != GAME_QUAKE_II) {
|
if (qbsp_options.target_game->id != GAME_QUAKE_II) {
|
||||||
// Q1: don't merge across water boundaries; ezQuake/nQuake water caustics will leak onto
|
if (!qbsp_options.mergeacrosswater.value()) {
|
||||||
// above-water faces.
|
// Q1: don't merge across water boundaries; ezQuake/nQuake water caustics will leak onto
|
||||||
if (f1->contents[0].is_liquid(qbsp_options.target_game) != f2->contents[0].is_liquid(qbsp_options.target_game))
|
// above-water faces.
|
||||||
return nullptr;
|
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
|
// 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))
|
if (f1->contents[0].is_sky(qbsp_options.target_game) != f2->contents[0].is_sky(qbsp_options.target_game))
|
||||||
|
|
|
||||||
|
|
@ -463,6 +463,7 @@ qbsp_settings::qbsp_settings()
|
||||||
"change the subdivide threshold, in luxels. 0 will disable subdivision entirely"},
|
"change the subdivide threshold, in luxels. 0 will disable subdivision entirely"},
|
||||||
nofill{this, "nofill", false, &debugging_group, "don't perform outside filling"},
|
nofill{this, "nofill", false, &debugging_group, "don't perform outside filling"},
|
||||||
nomerge{this, "nomerge", false, &debugging_group, "don't perform face merging"},
|
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)"},
|
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)"},
|
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"},
|
noskip{this, "noskip", false, &debugging_group, "don't remove faces with the 'skip' texture"},
|
||||||
|
|
|
||||||
|
|
@ -143,21 +143,46 @@ static void FindEdgeVerts_BruteForce(
|
||||||
* tjunc fixes. func_detail_wall is meant to act like a separate mesh,
|
* tjunc fixes. func_detail_wall is meant to act like a separate mesh,
|
||||||
* so it shouldn't interact with solid.
|
* 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)
|
static bool HasTJuncInteraction(const face_t *f1, const face_t *f2)
|
||||||
{
|
{
|
||||||
// FIXME: handle func_detail_fence, func_detail_illusionary,
|
// FIXME: handle func_detail_fence, func_detail_illusionary,
|
||||||
// liquids? make sure a combination of solid + func_detail_wall
|
// liquids? make sure a combination of solid + func_detail_wall
|
||||||
// is treated as solid?
|
// is treated as solid?
|
||||||
|
|
||||||
if (f1->contents.back.is_detail_wall(qbsp_options.target_game) &&
|
return Welds(f1->contents.back, f2->contents.back);
|
||||||
!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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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");
|
const auto [bsp, bspx, prt] = LoadTestmap("LibreQuake/lq1/maps/src/e3/e3m4.map");
|
||||||
CHECK(prt);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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));
|
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 auto [b, bspx, prt] = LoadTestmapQ2("q2_tjunc_matrix.map");
|
||||||
const mbsp_t &bsp = b; // workaround clang not allowing capturing bindings in lambdas
|
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_DETAIL_WALL));
|
||||||
CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID));
|
// this one is tricky - the solid cuts a hole in the top
|
||||||
CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_SOLID_DETAIL));
|
// 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_TRANSPARENT_WATER));
|
||||||
CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_OPAQUE_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_OPAQUE_MIST));
|
||||||
CHECK(!has_tjunc(INDEX_DETAIL_WALL, INDEX_TRANSPARENT_WINDOW));
|
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_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");
|
INFO("INDEX_TRANSPARENT_WATER horizontal");
|
||||||
CHECK(!has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_DETAIL_WALL));
|
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));
|
||||||
CHECK(!has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_SOLID_DETAIL));
|
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_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_OPAQUE_MIST));
|
||||||
CHECK(!has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_TRANSPARENT_WINDOW));
|
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_OPAQUE_AUX));
|
||||||
CHECK(!has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_SKY));
|
CHECK( has_tjunc(INDEX_TRANSPARENT_WATER, INDEX_SKY));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
INFO("INDEX_OPAQUE_WATER horizontal - same as INDEX_SOLID");
|
INFO("INDEX_OPAQUE_WATER horizontal");
|
||||||
CHECK(!has_tjunc(INDEX_OPAQUE_WATER, INDEX_DETAIL_WALL));
|
// 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));
|
||||||
CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_SOLID_DETAIL));
|
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_WATER));
|
||||||
CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_OPAQUE_MIST));
|
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_OPAQUE_AUX));
|
||||||
CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_SKY));
|
CHECK( has_tjunc(INDEX_OPAQUE_WATER, INDEX_SKY));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
INFO("INDEX_OPAQUE_MIST horizontal - same as INDEX_SOLID");
|
INFO("INDEX_OPAQUE_MIST horizontal");
|
||||||
CHECK(!has_tjunc(INDEX_OPAQUE_MIST, INDEX_DETAIL_WALL));
|
// 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));
|
||||||
CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_SOLID_DETAIL));
|
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_WATER));
|
||||||
CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_OPAQUE_MIST));
|
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_OPAQUE_AUX));
|
||||||
CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_SKY));
|
CHECK( has_tjunc(INDEX_OPAQUE_MIST, INDEX_SKY));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
INFO("INDEX_TRANSPARENT_WINDOW horizontal - only welds with itself");
|
INFO("INDEX_TRANSPARENT_WINDOW horizontal");
|
||||||
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_DETAIL_WALL));
|
// detail wall is stronger than window, cuts a hole in the window, so window
|
||||||
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SOLID));
|
// tjuncs with itself
|
||||||
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_SOLID_DETAIL));
|
CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_DETAIL_WALL));
|
||||||
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_TRANSPARENT_WATER));
|
// 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_WATER));
|
||||||
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_OPAQUE_MIST));
|
CHECK(!has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_OPAQUE_MIST));
|
||||||
CHECK( has_tjunc(INDEX_TRANSPARENT_WINDOW, INDEX_TRANSPARENT_WINDOW));
|
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_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");
|
INFO("INDEX_OPAQUE_AUX horizontal");
|
||||||
CHECK(!has_tjunc(INDEX_OPAQUE_AUX, INDEX_DETAIL_WALL));
|
// 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));
|
||||||
CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_SOLID_DETAIL));
|
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_TRANSPARENT_WATER));
|
||||||
CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_OPAQUE_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_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_OPAQUE_AUX));
|
||||||
CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_SKY));
|
CHECK( has_tjunc(INDEX_OPAQUE_AUX, INDEX_SKY));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue