diff --git a/common/bspfile.cc b/common/bspfile.cc index 9c5ee867..73374f4c 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -86,6 +86,8 @@ struct gamedef_generic_t : public gamedef_t bool contents_are_empty(const contentflags_t &) const { throw std::bad_cast(); } + bool contents_are_mirrored(const contentflags_t &) const { throw std::bad_cast(); } + bool contents_are_solid(const contentflags_t &) const { throw std::bad_cast(); } bool contents_are_sky(const contentflags_t &) const { throw std::bad_cast(); } @@ -263,6 +265,18 @@ struct gamedef_q1_like_t : public gamedef_t return contents.native == CONTENTS_EMPTY; } + bool contents_are_mirrored(const contentflags_t &contents) const + { + if (contents.extended & CFLAGS_BMODEL_MIRROR_INSIDE) { + return true; + } + + // If the brush is non-solid, mirror faces for the inside view + return (contents.native == CONTENTS_WATER) + || (contents.native == CONTENTS_SLIME) + || (contents.native == CONTENTS_LAVA); + } + bool contents_are_solid(const contentflags_t &contents) const { if (contents.extended & CFLAGS_CONTENTS_MASK) @@ -650,6 +664,14 @@ struct gamedef_q2_t : public gamedef_t return !(contents.native & Q2_ALL_VISIBLE_CONTENTS); } + bool contents_are_mirrored(const contentflags_t &contents) const + { + // fixme-brushbsp: support some way of opting out of mirrorinside + + // If the brush is non-solid, mirror faces for the inside view + return !(contents.native & Q2_CONTENTS_SOLID); + } + bool contents_are_solid(const contentflags_t &contents) const { if (contents.extended & CFLAGS_CONTENTS_MASK) @@ -1124,6 +1146,11 @@ bool contentflags_t::is_empty(const gamedef_t *game) const return game->contents_are_empty(*this); } +bool contentflags_t::is_mirrored(const gamedef_t *game) const +{ + return game->contents_are_mirrored(*this); +} + bool contentflags_t::is_solid(const gamedef_t *game) const { return game->contents_are_solid(*this); diff --git a/include/common/bspfile.hh b/include/common/bspfile.hh index 67c303b0..a860973e 100644 --- a/include/common/bspfile.hh +++ b/include/common/bspfile.hh @@ -605,6 +605,8 @@ struct contentflags_t bool is_empty(const gamedef_t *game) const; + bool is_mirrored(const gamedef_t *game) const; + // detail solid or structural solid bool is_any_solid(const gamedef_t *game) const { return is_solid(game) @@ -1815,6 +1817,7 @@ struct gamedef_t virtual bool contents_are_detail_fence(const contentflags_t &contents) const = 0; virtual bool contents_are_detail_illusionary(const contentflags_t &contents) const = 0; virtual bool contents_are_empty(const contentflags_t &contents) const = 0; + virtual bool contents_are_mirrored(const contentflags_t &contents) const = 0; virtual bool contents_are_solid(const contentflags_t &contents) const = 0; virtual bool contents_are_sky(const contentflags_t &contents) const = 0; virtual bool contents_are_liquid(const contentflags_t &contents) const = 0; diff --git a/include/common/bsputils.hh b/include/common/bsputils.hh index 59300abf..9377fe9d 100644 --- a/include/common/bsputils.hh +++ b/include/common/bsputils.hh @@ -54,7 +54,7 @@ bool Light_PointInSolid(const mbsp_t *bsp, const dmodelh2_t *model, const qvec3d 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); + const mbsp_t *bsp, const dmodelh2_t *model, const qvec3d &point, const qvec3d &wantedNormal = qvec3d(0,0,0)); /** * 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/surfaces.cc b/qbsp/surfaces.cc index 7fe7ddae..50c26922 100644 --- a/qbsp/surfaces.cc +++ b/qbsp/surfaces.cc @@ -654,26 +654,10 @@ static void AddFaceToTree_r(mapentity_t* entity, face_t *face, brush_t *srcbrush for (face_t *part : faces) { node->facelist.push_back(part); - // fixme-brushbsp: move to contentflags_t helper - /* - * If the brush is non-solid, mirror faces for the inside view - */ - bool mirror = (srcbrush->contents.extended & CFLAGS_BMODEL_MIRROR_INSIDE); - - if (!(srcbrush->contents.is_solid(options.target_game) || - srcbrush->contents.is_any_detail(options.target_game) || - srcbrush->contents.is_sky(options.target_game))) { - mirror = true; - } - - if (mirror) { + if (srcbrush->contents.is_mirrored(options.target_game)) { node->facelist.push_back(MirrorFace(part)); } } - - // fixme-brushbsp: need to continue clipping it down the bsp tree, - // this currently leaves bits floating in the void that happen to touch splitting nodes - return; } diff --git a/qbsp/test_qbsp.cc b/qbsp/test_qbsp.cc index 943e0369..8067a0af 100644 --- a/qbsp/test_qbsp.cc +++ b/qbsp/test_qbsp.cc @@ -1179,6 +1179,7 @@ TEST_CASE("lavawater", "[testmaps_q2]") { /** * 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("qbsp_q2_bmodel_collision", "[testmaps_q2]") { const mbsp_t bsp = LoadTestmapQ2("qbsp_q2_bmodel_collision.map"); @@ -1190,6 +1191,15 @@ TEST_CASE("qbsp_q2_bmodel_collision", "[testmaps_q2]") { CHECK(Q2_CONTENTS_SOLID == BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[1], in_bmodel)->contents); } +TEST_CASE("q2_liquids", "[testmaps_q2]") +{ + const mbsp_t bsp = LoadTestmapQ2("q2_liquids.map"); + + // water is two sided + const qvec3d water_top {-116, -168, 144}; + CHECK(2 == BSP_FindFacesAtPoint(&bsp, &bsp.dmodels[0], water_top).size()); +} + TEST_CASE("winding", "[benchmark]") { ankerl::nanobench::Bench bench;