From 3a308914764b712e30942e71b62f87191d4cee5c Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 1 May 2022 21:44:58 -0600 Subject: [PATCH] testqbsp: add more detail_illusionary tests --- common/bsputils.cc | 55 +++++++---- include/common/bsputils.hh | 8 +- qbsp/test_qbsp.cc | 39 ++++++++ .../qbsp_detail_illusionary_intersecting.map | 92 ++++++++++++++++++ ...l_illusionary_noclipfaces_intersecting.map | 93 +++++++++++++++++++ 5 files changed, 267 insertions(+), 20 deletions(-) create mode 100644 testmaps/qbsp_detail_illusionary_intersecting.map create mode 100644 testmaps/qbsp_detail_illusionary_noclipfaces_intersecting.map diff --git a/common/bsputils.cc b/common/bsputils.cc index c6b0dd57..c6137e6e 100644 --- a/common/bsputils.cc +++ b/common/bsputils.cc @@ -319,21 +319,28 @@ static bool EdgePlanes_PointInside(const std::vector &edgeplanes, cons return true; } -static const mface_t *BSP_FindFaceAtPoint_r( - const mbsp_t *bsp, const int nodenum, const qvec3d &point, const qvec3d &wantedNormal) +/** + * pass 0,0,0 for wantedNormal to disable the normal check + */ +static void BSP_FindFaceAtPoint_r( + const mbsp_t *bsp, const int nodenum, const qvec3d &point, const qvec3d &wantedNormal, std::vector &result) { if (nodenum < 0) { // we're only interested in nodes, since faces are owned by nodes. - return nullptr; + return; } const bsp2_dnode_t *node = &bsp->dnodes[nodenum]; const vec_t dist = bsp->dplanes[node->planenum].distance_to_fast(point); - if (dist > 0.1) - return BSP_FindFaceAtPoint_r(bsp, node->children[0], point, wantedNormal); - if (dist < -0.1) - return BSP_FindFaceAtPoint_r(bsp, node->children[1], point, wantedNormal); + if (dist > 0.1) { + BSP_FindFaceAtPoint_r(bsp, node->children[0], point, wantedNormal, result); + return; + } + if (dist < -0.1) { + BSP_FindFaceAtPoint_r(bsp, node->children[1], point, wantedNormal, result); + return; + } // Point is close to this node plane. Check all faces on the plane. for (int i = 0; i < node->numfaces; i++) { @@ -341,9 +348,11 @@ static const mface_t *BSP_FindFaceAtPoint_r( // First check if it's facing the right way qvec3d faceNormal = Face_Normal(bsp, face); - if (qv::dot(faceNormal, wantedNormal) < 0) { - // Opposite, so not the right face. - continue; + if (wantedNormal != qvec3d{0, 0, 0}) { + if (qv::dot(faceNormal, wantedNormal) < 0) { + // Opposite, so not the right face. + continue; + } } // Next test if it's within the boundaries of the face @@ -352,23 +361,33 @@ static const mface_t *BSP_FindFaceAtPoint_r( // Found a match? if (insideFace) { - return face; + result.push_back(face); } } // No match found on this plane. Check both sides of the tree. - const mface_t *side0Match = BSP_FindFaceAtPoint_r(bsp, node->children[0], point, wantedNormal); - if (side0Match != nullptr) { - return side0Match; - } else { - return BSP_FindFaceAtPoint_r(bsp, node->children[1], point, wantedNormal); - } + BSP_FindFaceAtPoint_r(bsp, node->children[0], point, wantedNormal, result); + BSP_FindFaceAtPoint_r(bsp, node->children[1], point, wantedNormal, result); +} + +std::vector BSP_FindFacesAtPoint( + const mbsp_t *bsp, const dmodelh2_t *model, const qvec3d &point, const qvec3d &wantedNormal) +{ + std::vector result; + BSP_FindFaceAtPoint_r(bsp, model->headnode[0], point, wantedNormal, result); + return result; } const mface_t *BSP_FindFaceAtPoint( const mbsp_t *bsp, const dmodelh2_t *model, const qvec3d &point, const qvec3d &wantedNormal) { - return BSP_FindFaceAtPoint_r(bsp, model->headnode[0], point, wantedNormal); + std::vector result; + BSP_FindFaceAtPoint_r(bsp, model->headnode[0], point, wantedNormal, result); + + if (result.empty()) { + return nullptr; + } + return result[0]; } static const bsp2_dnode_t *BSP_FindNodeAtPoint_r( diff --git a/include/common/bsputils.hh b/include/common/bsputils.hh index 17e1b0d2..99b5cd4b 100644 --- a/include/common/bsputils.hh +++ b/include/common/bsputils.hh @@ -21,10 +21,11 @@ #include #include -#include - #include +#include +#include + const dmodelh2_t *BSP_GetWorldModel(const mbsp_t *bsp); int Face_GetNum(const mbsp_t *bsp, const mface_t *f); @@ -51,6 +52,9 @@ int Face_ContentsOrSurfaceFlags( const dmodelh2_t *BSP_DModelForModelString(const mbsp_t *bsp, const std::string &submodel_str); bool Light_PointInSolid(const mbsp_t *bsp, const dmodelh2_t *model, const qvec3d &point); bool Light_PointInWorld(const mbsp_t *bsp, const qvec3d &point); + +std::vector BSP_FindFacesAtPoint( + const mbsp_t *bsp, const dmodelh2_t *model, const qvec3d &point, const qvec3d &wantedNormal); /** * Searches for a face touching a point and facing a certain way. * Sometimes (water, sky?) there will be 2 overlapping candidates facing opposite ways, the provided normal diff --git a/qbsp/test_qbsp.cc b/qbsp/test_qbsp.cc index 219df7c2..b5ad8911 100644 --- a/qbsp/test_qbsp.cc +++ b/qbsp/test_qbsp.cc @@ -510,6 +510,45 @@ TEST(testmaps_q1, noclipfaces_mirrorinside) } } +TEST(testmaps_q1, detail_illusionary_intersecting) +{ + const mbsp_t bsp = LoadTestmap("qbsp_detail_illusionary_intersecting.map"); + + EXPECT_FALSE(map.leakfile); + + // sides: 3*4 = 12 + // top: 3 + // bottom: 3 + EXPECT_EQ(bsp.dfaces.size(), 18); + + for (auto &face : bsp.dfaces) { + EXPECT_STREQ("{trigger", Face_TextureName(&bsp, &face)); + } + + // top of cross + 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 + EXPECT_EQ(0, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -52, 116), qvec3d(0, -1, 0)).size()); +} + +TEST(testmaps_q1, detail_illusionary_noclipfaces_intersecting) +{ + const mbsp_t bsp = LoadTestmap("qbsp_detail_illusionary_noclipfaces_intersecting.map"); + + EXPECT_FALSE(map.leakfile); + + for (auto &face : bsp.dfaces) { + EXPECT_STREQ("{trigger", Face_TextureName(&bsp, &face)); + } + + // top of cross has 2 faces Z-fighting, because we disabled clipping + EXPECT_EQ(2, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -50, 120), qvec3d(0, 0, 1)).size()); + + // interior face not clipped away + EXPECT_EQ(1, BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], qvec3d(-58, -52, 116), qvec3d(0, -1, 0)).size()); +} + TEST(testmaps_q1, detail_doesnt_seal) { const mbsp_t bsp = LoadTestmap("qbsp_detail_doesnt_seal.map"); diff --git a/testmaps/qbsp_detail_illusionary_intersecting.map b/testmaps/qbsp_detail_illusionary_intersecting.map new file mode 100644 index 00000000..18866c78 --- /dev/null +++ b/testmaps/qbsp_detail_illusionary_intersecting.map @@ -0,0 +1,92 @@ +// Game: Quake +// Format: Valve +// entity 0 +{ +"mapversion" "220" +"classname" "worldspawn" +"wad" "deprecated/free_wad.wad;deprecated/fence.wad;deprecated/origin.wad;deprecated/hintskip.wad" +"_wateralpha" "0.5" +"_tb_def" "builtin:Quake.fgd" +"message" "Intersecting detail illusionary. Intersection should be clipped away" +// brush 0 +{ +( 204 0 232 ) ( 204 -192 232 ) ( 204 0 32 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 208 -192 32 ) ( 204 -192 32 ) ( 208 -192 232 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 208 0 32 ) ( 204 0 32 ) ( 208 -192 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 208 -192 232 ) ( 204 -192 232 ) ( 208 0 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 208 0 232 ) ( 204 0 232 ) ( 208 0 32 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 208 -192 232 ) ( 208 0 232 ) ( 208 -192 32 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 1 +{ +( -176 -192 32 ) ( -176 0 32 ) ( -176 -192 232 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -176 -192 232 ) ( -172 -192 232 ) ( -176 -192 32 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -176 -192 32 ) ( -172 -192 32 ) ( -176 0 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -176 0 232 ) ( -172 0 232 ) ( -176 -192 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -176 0 32 ) ( -172 0 32 ) ( -176 0 232 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -172 -192 32 ) ( -172 -192 232 ) ( -172 0 32 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 2 +{ +( -172 0 232 ) ( -172 -4 232 ) ( -172 0 32 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -4 232 ) ( 204 -4 32 ) ( -172 -4 232 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -172 0 32 ) ( -172 -4 32 ) ( 204 0 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 204 0 232 ) ( 204 -4 232 ) ( -172 0 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 204 0 232 ) ( -172 0 232 ) ( 204 0 32 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 0 32 ) ( 204 -4 32 ) ( 204 0 232 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 3 +{ +( -172 -192 32 ) ( -172 -188 32 ) ( -172 -192 232 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -192 32 ) ( -172 -192 32 ) ( 204 -192 232 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -192 32 ) ( 204 -188 32 ) ( -172 -192 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -172 -192 232 ) ( -172 -188 232 ) ( 204 -192 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -172 -188 32 ) ( 204 -188 32 ) ( -172 -188 232 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -192 232 ) ( 204 -188 232 ) ( 204 -192 32 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 4 +{ +( -172 -188 232 ) ( -172 -188 228 ) ( -172 -4 232 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -188 232 ) ( 204 -188 228 ) ( -172 -188 232 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -4 228 ) ( -172 -4 228 ) ( 204 -188 228 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 204 -4 232 ) ( 204 -188 232 ) ( -172 -4 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -172 -4 232 ) ( -172 -4 228 ) ( 204 -4 232 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -4 232 ) ( 204 -4 228 ) ( 204 -188 232 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 5 +{ +( -172 -4 32 ) ( -172 -4 36 ) ( -172 -188 32 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -172 -188 32 ) ( -172 -188 36 ) ( 204 -188 32 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -172 -4 32 ) ( -172 -188 32 ) ( 204 -4 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -172 -188 36 ) ( -172 -4 36 ) ( 204 -188 36 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 204 -4 32 ) ( 204 -4 36 ) ( -172 -4 32 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -188 32 ) ( 204 -188 36 ) ( 204 -4 32 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +} +// entity 1 +{ +"classname" "info_player_start" +"origin" "-120 -80 120" +} +// entity 2 +{ +"classname" "func_detail_illusionary" +// brush 0 +{ +( -60 -36 120 ) ( -60 -68 120 ) ( -60 -68 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 0 1.0000000000000002 0 0 ] 0 1 1 +( -60 -68 120 ) ( -56 -68 120 ) ( -56 -68 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ -1.0000000000000002 0 0 0 ] 0 1 1 +( -56 -68 72 ) ( -56 -36 72 ) ( -60 -36 72 ) {trigger [ -1.0000000000000002 0 0 12 ] [ 0 1.0000000000000002 0 0 ] 0 1 1 +( -60 -36 120 ) ( -56 -36 120 ) ( -56 -68 120 ) {trigger [ 1.0000000000000002 0 0 52 ] [ 0 1.0000000000000002 0 0 ] 0 1 1 +( -56 -36 72 ) ( -56 -36 120 ) ( -60 -36 120 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 1.0000000000000002 0 0 24 ] 0 1 1 +( -56 -68 120 ) ( -56 -36 120 ) ( -56 -36 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 0 1.0000000000000002 0 0 ] 0 1 1 +} +// brush 1 +{ +( -72 -48 120 ) ( -72 -52 120 ) ( -72 -52 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 0 1.0000000000000002 0 20 ] 0 1 1 +( -68 -52 120 ) ( -28 -52 120 ) ( -28 -52 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ -1.0000000000000002 0 0 24 ] 0 1 1 +( -28 -52 72 ) ( -28 -48 72 ) ( -68 -48 72 ) {trigger [ 0 -1.0000000000000002 0 20 ] [ -1.0000000000000002 0 0 24 ] 0 1 1 +( -68 -48 120 ) ( -28 -48 120 ) ( -28 -52 120 ) {trigger [ 0 1.0000000000000002 0 44 ] [ -1.0000000000000002 0 0 24 ] 0 1 1 +( -28 -48 72 ) ( -28 -48 120 ) ( -68 -48 120 ) {trigger [ 0 0 1.0000000000000002 0 ] [ -1.0000000000000002 0 0 24 ] 0 1 1 +( -28 -52 120 ) ( -28 -48 120 ) ( -28 -48 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +} +} diff --git a/testmaps/qbsp_detail_illusionary_noclipfaces_intersecting.map b/testmaps/qbsp_detail_illusionary_noclipfaces_intersecting.map new file mode 100644 index 00000000..a904cf33 --- /dev/null +++ b/testmaps/qbsp_detail_illusionary_noclipfaces_intersecting.map @@ -0,0 +1,93 @@ +// Game: Quake +// Format: Valve +// entity 0 +{ +"mapversion" "220" +"classname" "worldspawn" +"wad" "deprecated/free_wad.wad;deprecated/fence.wad;deprecated/origin.wad;deprecated/hintskip.wad" +"_wateralpha" "0.5" +"_tb_def" "builtin:Quake.fgd" +"message" "Intersecting detail illusionary with _noclipfaces. Should have 2 z-fighting faces where they intersect" +// brush 0 +{ +( 204 0 232 ) ( 204 -192 232 ) ( 204 0 32 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 208 -192 32 ) ( 204 -192 32 ) ( 208 -192 232 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 208 0 32 ) ( 204 0 32 ) ( 208 -192 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 208 -192 232 ) ( 204 -192 232 ) ( 208 0 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 208 0 232 ) ( 204 0 232 ) ( 208 0 32 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 208 -192 232 ) ( 208 0 232 ) ( 208 -192 32 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 1 +{ +( -176 -192 32 ) ( -176 0 32 ) ( -176 -192 232 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -176 -192 232 ) ( -172 -192 232 ) ( -176 -192 32 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -176 -192 32 ) ( -172 -192 32 ) ( -176 0 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -176 0 232 ) ( -172 0 232 ) ( -176 -192 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -176 0 32 ) ( -172 0 32 ) ( -176 0 232 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -172 -192 32 ) ( -172 -192 232 ) ( -172 0 32 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 2 +{ +( -172 0 232 ) ( -172 -4 232 ) ( -172 0 32 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -4 232 ) ( 204 -4 32 ) ( -172 -4 232 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -172 0 32 ) ( -172 -4 32 ) ( 204 0 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 204 0 232 ) ( 204 -4 232 ) ( -172 0 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 204 0 232 ) ( -172 0 232 ) ( 204 0 32 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 0 32 ) ( 204 -4 32 ) ( 204 0 232 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 3 +{ +( -172 -192 32 ) ( -172 -188 32 ) ( -172 -192 232 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -192 32 ) ( -172 -192 32 ) ( 204 -192 232 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -192 32 ) ( 204 -188 32 ) ( -172 -192 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -172 -192 232 ) ( -172 -188 232 ) ( 204 -192 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -172 -188 32 ) ( 204 -188 32 ) ( -172 -188 232 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -192 232 ) ( 204 -188 232 ) ( 204 -192 32 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 4 +{ +( -172 -188 232 ) ( -172 -188 228 ) ( -172 -4 232 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -188 232 ) ( 204 -188 228 ) ( -172 -188 232 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -4 228 ) ( -172 -4 228 ) ( 204 -188 228 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 204 -4 232 ) ( 204 -188 232 ) ( -172 -4 232 ) skip [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -172 -4 232 ) ( -172 -4 228 ) ( 204 -4 232 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -4 232 ) ( 204 -4 228 ) ( 204 -188 232 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +// brush 5 +{ +( -172 -4 32 ) ( -172 -4 36 ) ( -172 -188 32 ) skip [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -172 -188 32 ) ( -172 -188 36 ) ( 204 -188 32 ) skip [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( -172 -4 32 ) ( -172 -188 32 ) ( 204 -4 32 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( -172 -188 36 ) ( -172 -4 36 ) ( 204 -188 36 ) skip [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1 +( 204 -4 32 ) ( 204 -4 36 ) ( -172 -4 32 ) skip [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1 +( 204 -188 32 ) ( 204 -188 36 ) ( 204 -4 32 ) skip [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1 +} +} +// entity 1 +{ +"classname" "info_player_start" +"origin" "-120 -80 120" +} +// entity 2 +{ +"classname" "func_detail_illusionary" +"_noclipfaces" "1" +// brush 0 +{ +( -60 -36 120 ) ( -60 -68 120 ) ( -60 -68 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 0 1.0000000000000002 0 0 ] 0 1 1 +( -60 -68 120 ) ( -56 -68 120 ) ( -56 -68 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ -1.0000000000000002 0 0 0 ] 0 1 1 +( -56 -68 72 ) ( -56 -36 72 ) ( -60 -36 72 ) {trigger [ -1.0000000000000002 0 0 12 ] [ 0 1.0000000000000002 0 0 ] 0 1 1 +( -60 -36 120 ) ( -56 -36 120 ) ( -56 -68 120 ) {trigger [ 1.0000000000000002 0 0 52 ] [ 0 1.0000000000000002 0 0 ] 0 1 1 +( -56 -36 72 ) ( -56 -36 120 ) ( -60 -36 120 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 1.0000000000000002 0 0 24 ] 0 1 1 +( -56 -68 120 ) ( -56 -36 120 ) ( -56 -36 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 0 1.0000000000000002 0 0 ] 0 1 1 +} +// brush 1 +{ +( -72 -48 120 ) ( -72 -52 120 ) ( -72 -52 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 0 1.0000000000000002 0 20 ] 0 1 1 +( -68 -52 120 ) ( -28 -52 120 ) ( -28 -52 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ -1.0000000000000002 0 0 24 ] 0 1 1 +( -28 -52 72 ) ( -28 -48 72 ) ( -68 -48 72 ) {trigger [ 0 -1.0000000000000002 0 20 ] [ -1.0000000000000002 0 0 24 ] 0 1 1 +( -68 -48 120 ) ( -28 -48 120 ) ( -28 -52 120 ) {trigger [ 0 1.0000000000000002 0 44 ] [ -1.0000000000000002 0 0 24 ] 0 1 1 +( -28 -48 72 ) ( -28 -48 120 ) ( -68 -48 120 ) {trigger [ 0 0 1.0000000000000002 0 ] [ -1.0000000000000002 0 0 24 ] 0 1 1 +( -28 -52 120 ) ( -28 -48 120 ) ( -28 -48 72 ) {trigger [ 0 0 1.0000000000000002 0 ] [ 0 -1.0000000000000002 0 0 ] 0 1 1 +} +}