qbsp: "-add additional.map" feature
This commit is contained in:
parent
52dff47a86
commit
e05a2bdf75
|
|
@ -688,7 +688,7 @@ public:
|
||||||
return create_solid_contents();
|
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
|
// Q1-like games don't care about the local
|
||||||
// filesystem.
|
// filesystem.
|
||||||
|
|
@ -699,6 +699,13 @@ public:
|
||||||
fs::addArchive(path, true);
|
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);
|
img::init_palette(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -352,6 +352,7 @@ public:
|
||||||
// since the max world size in Q3 is {-65536, -65536, -65536, 65536, 65536, 65536}. should we dynamically change this?
|
// since the max world size in Q3 is {-65536, -65536, -65536, 65536, 65536, 65536}. should we dynamically change this?
|
||||||
// should we automatically turn this on if the world gets too big but leave it off for smaller worlds?
|
// 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_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_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
|
void setParameters(int argc, const char **argv) override
|
||||||
{
|
{
|
||||||
|
|
|
||||||
30
qbsp/map.cc
30
qbsp/map.cc
|
|
@ -2051,6 +2051,36 @@ void LoadMapFile(void)
|
||||||
map.entities.pop_back();
|
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} faces\n", map.faces.size());
|
||||||
logging::print(logging::flag::STAT, " {:8} brushes\n", map.brushes.size());
|
logging::print(logging::flag::STAT, " {:8} brushes\n", map.brushes.size());
|
||||||
logging::print(logging::flag::STAT, " {:8} entities\n", map.entities.size());
|
logging::print(logging::flag::STAT, " {:8} entities\n", map.entities.size());
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -1766,6 +1766,33 @@ TEST_CASE("q1_wad_external", "[testmaps_q1]") {
|
||||||
CHECK(bsp.dtex.textures[3].data.size() == sizeof(dmiptex_t));
|
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]") {
|
TEST_CASE("winding", "[benchmark][.releaseonly]") {
|
||||||
ankerl::nanobench::Bench bench;
|
ankerl::nanobench::Bench bench;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue