Merge branch 'brushbsp' of https://github.com/ericwa/ericw-tools into brushbsp

# Conflicts:
#	include/qbsp/qbsp.hh
This commit is contained in:
Jonathan 2022-08-02 06:24:46 -04:00
commit 93655913c0
11 changed files with 135 additions and 8 deletions

View File

@ -688,7 +688,7 @@ public:
return create_solid_contents();
}
void init_filesystem(const fs::path &, const settings::common_settings &options) const override
void init_filesystem(const fs::path &map_or_bsp, const settings::common_settings &options) const override
{
// Q1-like games don't care about the local
// filesystem.
@ -699,6 +699,13 @@ public:
fs::addArchive(path, true);
}
// certain features like '-add additional.map' search relative to the map we're compiling
// so add the map directory to the search path
auto map_or_bsp_dir = map_or_bsp.parent_path();
if (!map_or_bsp_dir.empty()) {
fs::addArchive(map_or_bsp_dir);
}
img::init_palette(this);
}

View File

@ -17,6 +17,8 @@
See file, 'COPYING', for details.
*/
#pragma once
#include "common/fs.hh"
struct bspdata_t;

View File

@ -26,6 +26,7 @@
#pragma once
#include <atomic>
#include <cstdarg>
#include <filesystem>
#include <fmt/format.h>
@ -123,9 +124,9 @@ void percent(uint64_t count, uint64_t max, bool displayElapsed = true);
// simple wrapper to percent() to use it in an object-oriented manner.
struct percent_clock
{
std::atomic_uint64_t max = 0;
std::atomic<uint64_t> max {0};
bool displayElapsed = true;
std::atomic_uint64_t count = 0;
std::atomic<uint64_t> count {0};
inline void increase()
{

View File

@ -22,6 +22,8 @@
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
#include <atomic>
// parallel extensions to logging
namespace logging
{
@ -29,7 +31,7 @@ template<typename TS, typename TE, typename Body>
void parallel_for(const TS &start, const TE &end, const Body &func)
{
auto length = end - start;
std::atomic_uint64_t progress = 0;
std::atomic<uint64_t> progress = 0;
tbb::parallel_for(start, end, [&](const auto &it) {
percent(progress++, length);
@ -43,7 +45,7 @@ template<typename Container, typename Body>
void parallel_for_each(Container &container, const Body &func)
{
auto length = std::size(container);
std::atomic_uint64_t progress = 0;
std::atomic<uint64_t> progress = 0;
tbb::parallel_for_each(container, [&](auto &f) {
percent(progress++, length);
@ -57,7 +59,7 @@ template<typename Container, typename Body>
void parallel_for_each(const Container &container, const Body &func)
{
auto length = std::size(container);
std::atomic_uint64_t progress = 0;
std::atomic<uint64_t> progress = 0;
tbb::parallel_for_each(container, [&](const auto &f) {
percent(progress++, length);

View File

@ -353,6 +353,7 @@ public:
// should we automatically turn this on if the world gets too big but leave it off for smaller worlds?
setting_blocksize blocksize{this, "blocksize", { 0, 0, 0 }, &common_format_group, "from q3map2; split the world by x/y/z sized chunks, speeding up split decisions"};
setting_numeric<vec_t> midsplitbrushfraction{this, "midsplitbrushfraction", 0.0, &common_format_group, "switch to cheaper partitioning if a node contains this % of brushes in the map"};
setting_string add{this, "add", "", "", &common_format_group, "the given map file will be appended to the base map"};
void setParameters(int argc, const char **argv) override
{

View File

@ -28,6 +28,7 @@
#include <common/bsputils.hh>
#include <common/qvec.hh>
#include <atomic>
#include <cassert>
#include <cmath>
#include <algorithm>

View File

@ -2051,6 +2051,36 @@ void LoadMapFile(void)
map.entities.pop_back();
}
// -add function
if (!qbsp_options.add.value().empty()) {
auto file = fs::load(qbsp_options.add.value());
if (!file) {
FError("Couldn't load map file \"{}\".\n", qbsp_options.add.value());
return;
}
parser_t parser(file->data(), file->size());
for (int i = 0;; i++) {
mapentity_t &entity = map.entities.emplace_back();
if (!ParseEntity(parser, &entity)) {
break;
}
if (entity.epairs.get("classname") == "worldspawn") {
// The easiest way to get the additional map's worldspawn brushes
// into the base map's is to rename the additional map's worldspawn classname to func_group
entity.epairs.set("classname", "func_group");
}
}
// Remove dummy entity inserted above
assert(!map.entities.back().epairs.size());
assert(map.entities.back().brushes.empty());
map.entities.pop_back();
}
logging::print(logging::flag::STAT, " {:8} faces\n", map.faces.size());
logging::print(logging::flag::STAT, " {:8} brushes\n", map.brushes.size());
logging::print(logging::flag::STAT, " {:8} entities\n", map.entities.size());

View File

@ -27,6 +27,8 @@
#include <qbsp/portals.hh>
#include <tbb/task_group.h>
#include <atomic>
//============================================================================
portal_t *tree_t::create_portal()
@ -79,7 +81,7 @@ static void ConvertNodeToLeaf(node_t *node, const contentflags_t &contents)
Q_assert(node->markfaces.empty());
}
static void PruneNodes_R(node_t *node, std::atomic_int32_t &count_pruned)
static void PruneNodes_R(node_t *node, std::atomic<int32_t> &count_pruned)
{
if (node->is_leaf) {
return;
@ -113,7 +115,7 @@ void PruneNodes(node_t *node)
{
logging::funcheader();
std::atomic_int32_t count_pruned = 0;
std::atomic<int32_t> count_pruned = 0;
PruneNodes_R(node, count_pruned);

View File

@ -0,0 +1,31 @@
// Game: Quake
// Format: Valve
// entity 0
{
"mapversion" "220"
"classname" "worldspawn"
"message" "merge maps addition. The worldspawn keys here should be ignored"
"wad" "deprecated/free_wad.wad"
// brush 0
{
( 0 -64 -16 ) ( 0 -63 -16 ) ( 0 -64 -15 ) bolt11 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 0 -64 -16 ) ( 0 -64 -15 ) ( 1 -64 -16 ) bolt11 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 0 -64 -16 ) ( 1 -64 -16 ) ( 0 -63 -16 ) bolt11 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 128 64 16 ) ( 128 65 16 ) ( 129 64 16 ) bolt11 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 128 64 16 ) ( 129 64 16 ) ( 128 64 17 ) bolt11 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 64 64 16 ) ( 64 64 17 ) ( 64 65 16 ) bolt11 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
}
}
// entity 1
{
"classname" "func_wall"
// brush 0
{
( 96 32 0 ) ( 96 33 0 ) ( 96 32 1 ) bolt11 [ 0 -1 0 0 ] [ 0 0 -1 -16 ] 0 1 1
( 96 32 0 ) ( 96 32 1 ) ( 97 32 0 ) bolt11 [ 1 0 0 0 ] [ 0 0 -1 -16 ] 0 1 1
( 96 32 0 ) ( 97 32 0 ) ( 96 33 0 ) bolt11 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 128 64 16 ) ( 128 65 16 ) ( 129 64 16 ) bolt11 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 128 64 16 ) ( 129 64 16 ) ( 128 64 17 ) bolt11 [ -1 0 0 0 ] [ 0 0 -1 -16 ] 0 1 1
( 128 64 16 ) ( 128 64 17 ) ( 128 65 16 ) bolt11 [ 0 1 0 0 ] [ 0 0 -1 -16 ] 0 1 1
}
}

View File

@ -0,0 +1,23 @@
// Game: Quake
// Format: Valve
// entity 0
{
"mapversion" "220"
"classname" "worldspawn"
"message" "merge maps base"
"wad" "deprecated/free_wad.wad"
// brush 0
{
( -64 -64 -16 ) ( -64 -63 -16 ) ( -64 -64 -15 ) bolt11 [ 0 -1 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -64 -64 -16 ) ( -64 -64 -15 ) ( -63 -64 -16 ) bolt11 [ 1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( -64 -64 -16 ) ( -63 -64 -16 ) ( -64 -63 -16 ) bolt11 [ -1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 64 64 16 ) ( 64 65 16 ) ( 65 64 16 ) bolt11 [ 1 0 0 0 ] [ 0 -1 0 0 ] 0 1 1
( 64 64 16 ) ( 65 64 16 ) ( 64 64 17 ) bolt11 [ -1 0 0 0 ] [ 0 0 -1 0 ] 0 1 1
( 0 64 16 ) ( 0 64 17 ) ( 0 65 16 ) bolt11 [ 0 1 0 0 ] [ 0 0 -1 0 ] 0 1 1
}
}
// entity 1
{
"classname" "info_player_start"
"origin" "-16 -16 40"
}

View File

@ -1766,6 +1766,33 @@ TEST_CASE("q1_wad_external", "[testmaps_q1]") {
CHECK(bsp.dtex.textures[3].data.size() == sizeof(dmiptex_t));
}
TEST_CASE("q1_merge_maps", "[testmaps_q1]") {
const auto [bsp, bspx, prt] = LoadTestmapQ1("q1_merge_maps_base.map", { "-add", "q1_merge_maps_addition.map" });
CHECK(GAME_QUAKE == bsp.loadversion->game->id);
// check brushwork from the two maps is merged
REQUIRE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {5,0,16}, {0, 0, 1}));
REQUIRE(BSP_FindFaceAtPoint(&bsp, &bsp.dmodels[0], {-5,0,16}, {0, 0, 1}));
// check that the worldspawn keys from the base map are used
auto ents = EntData_Parse(bsp.dentdata);
REQUIRE(ents.size() == 3); // worldspawn, info_player_start, func_wall
REQUIRE(ents[0].get("classname") == "worldspawn");
CHECK(ents[0].get("message") == "merge maps base");
// check info_player_start
auto it = std::find_if(ents.begin(), ents.end(),
[](const entdict_t &dict) -> bool { return dict.get("classname") == "info_player_start"; });
REQUIRE(it != ents.end());
// check func_wall entity from addition map is included
it = std::find_if(ents.begin(), ents.end(),
[](const entdict_t &dict) -> bool { return dict.get("classname") == "func_wall"; });
REQUIRE(it != ents.end());
}
TEST_CASE("winding", "[benchmark][.releaseonly]") {
ankerl::nanobench::Bench bench;