Merge branch 'type-cleanup' into brushbsp
This commit is contained in:
commit
4957622c50
|
|
@ -318,6 +318,10 @@ static void serialize_bsp(const bspdata_t &bspdata, const mbsp_t &bsp, const fs:
|
|||
node.push_back({"maxs", src_node.maxs});
|
||||
node.push_back({"firstface", src_node.firstface});
|
||||
node.push_back({"numfaces", src_node.numfaces});
|
||||
|
||||
// human-readable plane
|
||||
auto& plane = bsp.dplanes.at(src_node.planenum);
|
||||
node.push_back({"plane", json::array({plane.normal[0], plane.normal[1], plane.normal[2], plane.dist})});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -770,6 +770,13 @@ struct bsp2_dclipnode_t
|
|||
auto stream_data() { return std::tie(planenum, children); }
|
||||
};
|
||||
|
||||
/*
|
||||
* Clipnodes need to be stored as a 16-bit offset. Originally, this was a
|
||||
* signed value and only the positive values up to 32767 were available. Since
|
||||
* the negative range was unused apart from a few values reserved for flags,
|
||||
* this has been extended to allow up to 65520 (0xfff0) clipnodes (with a
|
||||
* suitably modified engine).
|
||||
*/
|
||||
struct bsp29_dclipnode_t
|
||||
{
|
||||
int32_t planenum;
|
||||
|
|
|
|||
|
|
@ -150,8 +150,6 @@ extern setting_group debugging_group;
|
|||
class qbsp_settings : public common_settings
|
||||
{
|
||||
public:
|
||||
inline qbsp_settings() { }
|
||||
|
||||
setting_bool hexen2{this, "hexen2", false, &game_target_group, "target Hexen II's BSP format"};
|
||||
setting_bool hlbsp{this, "hlbsp", false, &game_target_group, "target Half Life's BSP format"};
|
||||
setting_bool q2bsp{this, "q2bsp", false, &game_target_group, "target Quake II's BSP format"};
|
||||
|
|
@ -250,15 +248,6 @@ public:
|
|||
|
||||
extern settings::qbsp_settings options;
|
||||
|
||||
/*
|
||||
* Clipnodes need to be stored as a 16-bit offset. Originally, this was a
|
||||
* signed value and only the positive values up to 32767 were available. Since
|
||||
* the negative range was unused apart from a few values reserved for flags,
|
||||
* this has been extended to allow up to 65520 (0xfff0) clipnodes (with a
|
||||
* suitably modified engine).
|
||||
*/
|
||||
#define MAX_BSP_CLIPNODES 0xfff0
|
||||
|
||||
// 0-2 are axial planes
|
||||
// 3-5 are non-axial planes snapped to the nearest
|
||||
#define PLANE_X 0
|
||||
|
|
@ -268,7 +257,7 @@ extern settings::qbsp_settings options;
|
|||
#define PLANE_ANYY 4
|
||||
#define PLANE_ANYZ 5
|
||||
|
||||
// planenum for a leaf (?)
|
||||
// planenum for a leaf
|
||||
constexpr int32_t PLANENUM_LEAF = -1;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -119,5 +119,5 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
constexpr reference operator[](const size_t &index) { return {bits, index >> shift, 1u << (index & mask)}; }
|
||||
constexpr reference operator[](const size_t &index) { return {bits, index >> shift, static_cast<size_t>(1) << (index & mask)}; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -64,6 +64,15 @@ if (embree_FOUND)
|
|||
message(STATUS "Found embree license: ${EMBREE_LICENSE}")
|
||||
endif()
|
||||
|
||||
# HACK: Windows embree .dll's from https://github.com/embree/embree/releases ship with a tbb12.dll
|
||||
# and we need to copy it from the embree/bin directory to our light.exe/testlight.exe dir in order for them to run
|
||||
find_file(EMBREE_TBB_DLL tbb12.dll
|
||||
"${EMBREE_ROOT_DIR}/bin"
|
||||
NO_DEFAULT_PATH)
|
||||
if (NOT EMBREE_TBB_DLL STREQUAL EMBREE_TBB_DLL-NOTFOUND)
|
||||
message(STATUS "Found embree EMBREE_TBB_DLL: ${EMBREE_TBB_DLL}")
|
||||
endif()
|
||||
|
||||
add_custom_command(TARGET light POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:embree>" "$<TARGET_FILE_DIR:light>"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:TBB::tbb>" "$<TARGET_FILE_DIR:light>")
|
||||
|
|
@ -72,6 +81,10 @@ if (embree_FOUND)
|
|||
add_custom_command(TARGET light POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${EMBREE_LICENSE}" "$<TARGET_FILE_DIR:light>/LICENSE-embree.txt")
|
||||
endif()
|
||||
if (NOT EMBREE_TBB_DLL STREQUAL EMBREE_TBB_DLL-NOTFOUND)
|
||||
add_custom_command(TARGET light POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${EMBREE_TBB_DLL}" "$<TARGET_FILE_DIR:light>")
|
||||
endif()
|
||||
|
||||
# so the executable will search for dylib's in the same directory as the executable
|
||||
if(APPLE)
|
||||
|
|
@ -123,11 +136,9 @@ if (embree_FOUND)
|
|||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:TBB::tbb>" "$<TARGET_FILE_DIR:testlight>"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:embree>" "$<TARGET_FILE_DIR:testlight>")
|
||||
|
||||
# HACK: copy embree's tbb12.dll
|
||||
# FIXME: this is only desired with the .zip release of embree, not e.g. vcpkg
|
||||
if (WIN32)
|
||||
if (NOT EMBREE_TBB_DLL STREQUAL EMBREE_TBB_DLL-NOTFOUND)
|
||||
add_custom_command(TARGET testlight POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE_DIR:embree>/tbb12.dll" "$<TARGET_FILE_DIR:testlight>")
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${EMBREE_TBB_DLL}" "$<TARGET_FILE_DIR:testlight>")
|
||||
endif()
|
||||
|
||||
add_definitions(-DHAVE_EMBREE)
|
||||
|
|
|
|||
|
|
@ -2095,7 +2095,7 @@ void ConvertMapFile(void)
|
|||
}
|
||||
|
||||
fs::path filename = options.bsp_path;
|
||||
filename.replace_filename(options.bsp_path.stem().string() + append);
|
||||
filename.replace_filename(options.bsp_path.stem().string() + append).replace_extension(".map");
|
||||
|
||||
std::ofstream f(filename);
|
||||
|
||||
|
|
|
|||
28
qbsp/qbsp.cc
28
qbsp/qbsp.cc
|
|
@ -109,7 +109,8 @@ void qbsp_settings::postinitialize(int argc, const char **argv)
|
|||
set_target_version(&bspver_hl);
|
||||
}
|
||||
|
||||
if (q2bsp.value() || q2rtx.value()) {
|
||||
if (q2bsp.value() ||
|
||||
(q2rtx.value() && !q2bsp.isChanged() && !qbism.isChanged())) {
|
||||
set_target_version(&bspver_q2);
|
||||
}
|
||||
|
||||
|
|
@ -167,10 +168,6 @@ void qbsp_settings::postinitialize(int argc, const char **argv)
|
|||
if (!includeskip.isChanged()) {
|
||||
includeskip.setValueLocked(true);
|
||||
}
|
||||
|
||||
if (!notriggermodels.isChanged()) {
|
||||
notriggermodels.setValueLocked(true);
|
||||
}
|
||||
}
|
||||
|
||||
common_settings::postinitialize(argc, argv);
|
||||
|
|
@ -596,6 +593,23 @@ winding_t BaseWindingForPlane(const qplane3d &p)
|
|||
return winding_t::from_plane(p, options.worldextent.value());
|
||||
}
|
||||
|
||||
static bool IsTrigger(const mapentity_t *entity)
|
||||
{
|
||||
auto &tex = entity->mapbrush(0).face(0).texname;
|
||||
|
||||
if (tex.length() < 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t trigger_pos = tex.rfind("trigger");
|
||||
|
||||
if (trigger_pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return trigger_pos == (tex.size() - strlen("trigger"));
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
ProcessEntity
|
||||
|
|
@ -619,9 +633,9 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
|
|||
return;
|
||||
|
||||
// for notriggermodels: if we have at least one trigger-like texture, do special trigger stuff
|
||||
bool discarded_trigger = (entity != map.world_entity() &&
|
||||
bool discarded_trigger = entity != map.world_entity() &&
|
||||
options.notriggermodels.value() &&
|
||||
entity->mapbrush(0).face(0).texname.find_last_of("trigger") == entity->mapbrush(0).face(0).texname.size() - strlen("trigger"));
|
||||
IsTrigger(entity);
|
||||
|
||||
// Export a blank model struct, and reserve the index (only do this once, for all hulls)
|
||||
if (!discarded_trigger) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,21 @@
|
|||
#include <map>
|
||||
#include <list>
|
||||
|
||||
static bool ShouldOmitFace(face_t *f)
|
||||
{
|
||||
if (!options.includeskip.value() && map.mtexinfos.at(f->texinfo).flags.is_skip)
|
||||
return true;
|
||||
if (map.mtexinfos.at(f->texinfo).flags.is_hint)
|
||||
return true;
|
||||
|
||||
// HACK: to save a few faces, don't output the interior faces of sky brushes
|
||||
if (f->contents[0].is_sky(options.target_game)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SubdivideFace
|
||||
|
|
@ -312,9 +327,7 @@ FindFaceEdges
|
|||
*/
|
||||
static void FindFaceEdges(mapentity_t *entity, face_t *face)
|
||||
{
|
||||
if (!options.includeskip.value() && map.mtexinfos.at(face->texinfo).flags.is_skip)
|
||||
return;
|
||||
if (map.mtexinfos.at(face->texinfo).flags.is_hint)
|
||||
if (ShouldOmitFace(face))
|
||||
return;
|
||||
|
||||
FindFaceFragmentEdges(entity, face, face);
|
||||
|
|
@ -387,9 +400,7 @@ EmitFace
|
|||
*/
|
||||
static void EmitFace(mapentity_t *entity, face_t *face)
|
||||
{
|
||||
if (!options.includeskip.value() && map.mtexinfos.at(face->texinfo).flags.is_skip)
|
||||
return;
|
||||
if (map.mtexinfos.at(face->texinfo).flags.is_hint)
|
||||
if (ShouldOmitFace(face))
|
||||
return;
|
||||
|
||||
EmitFaceFragment(entity, face, face);
|
||||
|
|
@ -426,9 +437,7 @@ static void GrowNodeRegion(mapentity_t *entity, node_t *node)
|
|||
|
||||
static void CountFace(mapentity_t *entity, face_t *f, size_t &facesCount, size_t &vertexesCount)
|
||||
{
|
||||
if (!options.includeskip.value() && map.mtexinfos.at(f->texinfo).flags.is_skip)
|
||||
return;
|
||||
if (map.mtexinfos.at(f->texinfo).flags.is_hint)
|
||||
if (ShouldOmitFace(f))
|
||||
return;
|
||||
|
||||
if (f->lmshift[1] != 4)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
#include <set>
|
||||
#include <map>
|
||||
|
||||
using namespace testing;
|
||||
|
||||
// FIXME: Clear global data (planes, etc) between each test
|
||||
|
||||
static const mapface_t *Mapbrush_FirstFaceWithTextureName(const mapbrush_t *brush, const std::string &texname)
|
||||
|
|
@ -492,15 +494,26 @@ TEST(testmaps_q1, simple_worldspawn_sky)
|
|||
EXPECT_EQ(5, textureToFace.at("orangestuff8").size());
|
||||
|
||||
// leaf/node counts
|
||||
EXPECT_EQ(6, bsp.dnodes.size());
|
||||
// - we'd get 7 nodes if it's cut like a cube (solid outside), with 1 additional cut inside to divide sky / empty
|
||||
// - we'd get 11 if it's cut as the sky plane (1), then two open cubes (5 nodes each)
|
||||
// - 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
|
||||
EXPECT_THAT(bsp.dnodes.size(), AllOf(Ge(7), Le(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;
|
||||
|
||||
EXPECT_EQ(CONTENTS_EMPTY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos)->contents);
|
||||
|
||||
EXPECT_EQ(CONTENTS_SKY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0,0,500))->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.)
|
||||
EXPECT_EQ(CONTENTS_SOLID, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], player_pos + qvec3d(0,0,500))->contents);
|
||||
|
||||
EXPECT_EQ(CONTENTS_SKY, BSP_FindLeafAtPoint(&bsp, &bsp.dmodels[0], qvec3d(player_pos[0], player_pos[1], inside_sky_z))->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);
|
||||
|
|
@ -752,30 +765,31 @@ TEST(testmaps_q2, detail) {
|
|||
|
||||
// stats
|
||||
EXPECT_EQ(1, bsp.dmodels.size());
|
||||
// Q2 reserves leaf 0 as an invalid leaf
|
||||
|
||||
// leafs:
|
||||
// 6 solid leafs outside the room
|
||||
// 6 solid leafs outside the room (* can be more depending on when the "divider" is cut)
|
||||
// 1 empty leaf filling the room above the divider
|
||||
// 2 empty leafs + 1 solid leaf for divider
|
||||
// 1 detail leaf for button
|
||||
// 4 empty leafs around + 1 on top of button
|
||||
// total: 16
|
||||
// Q2 reserves leaf 0 as an invalid leaf, so dleafs size is 17
|
||||
EXPECT_EQ(17, bsp.dleafs.size());
|
||||
|
||||
std::map<int32_t, int> counts_by_contents;
|
||||
for (size_t i = 1; i < bsp.dleafs.size(); ++i) {
|
||||
++counts_by_contents[bsp.dleafs[i].contents];
|
||||
}
|
||||
EXPECT_EQ(3, counts_by_contents.size()); // number of types
|
||||
|
||||
EXPECT_EQ((std::map<int32_t, int>{{Q2_CONTENTS_SOLID, 7}, {Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL, 1}, {0, 8}}),
|
||||
counts_by_contents);
|
||||
|
||||
EXPECT_EQ(1, counts_by_contents.at(Q2_CONTENTS_SOLID | Q2_CONTENTS_DETAIL));
|
||||
EXPECT_EQ(8, counts_by_contents.at(0)); // empty leafs
|
||||
EXPECT_THAT(counts_by_contents.at(Q2_CONTENTS_SOLID), AllOf(Ge(7), Le(9)));
|
||||
|
||||
// clusters:
|
||||
// 6 solid leafs outside the room
|
||||
// 1 empty leaf filling the room above the divider
|
||||
// 2 empty leafs + 1 solid leaf for divider
|
||||
// 1 empty cluster filling the room above the divider
|
||||
// 2 empty clusters created by divider
|
||||
// 1 cluster for the part of the room with the button
|
||||
// total: 10
|
||||
|
||||
std::set<int> clusters;
|
||||
// first add the empty leafs
|
||||
for (size_t i = 1; i < bsp.dleafs.size(); ++i) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue