this all matches release now
This commit is contained in:
parent
cc9dd986c6
commit
8476e2861c
|
|
@ -41,4 +41,4 @@ bool WindingIsTiny(const winding_t &w, double size = EDGE_LENGTH_EPSILON);
|
||||||
std::unique_ptr<bspbrush_t> BrushFromBounds(const aabb3d &bounds);
|
std::unique_ptr<bspbrush_t> BrushFromBounds(const aabb3d &bounds);
|
||||||
|
|
||||||
// compatibility version
|
// compatibility version
|
||||||
std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, bool use_mid_split);
|
std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, std::optional<bool> forced_quick_tree);
|
||||||
|
|
|
||||||
|
|
@ -352,7 +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_int32 midsplitbrushes{this, "midsplitbrushes", 128, &common_format_group, "switch to cheaper partitioning if a node contains this many brushes"};
|
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"};
|
||||||
|
|
||||||
void setParameters(int argc, const char **argv) override
|
void setParameters(int argc, const char **argv) override
|
||||||
{
|
{
|
||||||
|
|
|
||||||
110
qbsp/brushbsp.cc
110
qbsp/brushbsp.cc
|
|
@ -626,7 +626,7 @@ static void CheckPlaneAgainstParents(const qbsp_plane_t &plane, node_t *node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CheckPlaneAgainstVolume(const qbsp_plane_t &plane, node_t *node)
|
static bool CheckPlaneAgainstVolume(const qbsp_plane_t &plane, const node_t *node)
|
||||||
{
|
{
|
||||||
auto [front, back] = SplitBrush(node->volume->copy_unique(), plane);
|
auto [front, back] = SplitBrush(node->volume->copy_unique(), plane);
|
||||||
|
|
||||||
|
|
@ -712,7 +712,7 @@ ChooseMidPlaneFromList
|
||||||
The clipping hull BSP doesn't worry about avoiding splits
|
The clipping hull BSP doesn't worry about avoiding splits
|
||||||
==================
|
==================
|
||||||
*/
|
*/
|
||||||
static std::optional<qbsp_plane_t> ChooseMidPlaneFromList(const std::vector<std::unique_ptr<bspbrush_t>> &brushes, const aabb3d &bounds)
|
static std::optional<qbsp_plane_t> ChooseMidPlaneFromList(const std::vector<std::unique_ptr<bspbrush_t>> &brushes, const node_t *node)
|
||||||
{
|
{
|
||||||
vec_t bestaxialmetric = VECT_MAX;
|
vec_t bestaxialmetric = VECT_MAX;
|
||||||
std::optional<qbsp_plane_t> bestaxialplane;
|
std::optional<qbsp_plane_t> bestaxialplane;
|
||||||
|
|
@ -730,8 +730,13 @@ static std::optional<qbsp_plane_t> ChooseMidPlaneFromList(const std::vector<std:
|
||||||
}
|
}
|
||||||
|
|
||||||
const qbsp_plane_t &plane = side.plane;
|
const qbsp_plane_t &plane = side.plane;
|
||||||
|
|
||||||
|
if (!CheckPlaneAgainstVolume(plane, node)) {
|
||||||
|
continue; // would produce a tiny volume
|
||||||
|
}
|
||||||
|
|
||||||
/* calculate the split metric, smaller values are better */
|
/* calculate the split metric, smaller values are better */
|
||||||
const vec_t metric = SplitPlaneMetric(plane, bounds);
|
const vec_t metric = SplitPlaneMetric(plane, node->bounds);
|
||||||
|
|
||||||
if (metric < bestanymetric) {
|
if (metric < bestanymetric) {
|
||||||
bestanymetric = metric;
|
bestanymetric = metric;
|
||||||
|
|
@ -761,46 +766,77 @@ Using heuristics, chooses a plane to partition the brushes with.
|
||||||
Returns nullopt if there are no valid planes to split with.
|
Returns nullopt if there are no valid planes to split with.
|
||||||
================
|
================
|
||||||
*/
|
*/
|
||||||
static std::optional<qbsp_plane_t> SelectSplitPlane(const std::vector<std::unique_ptr<bspbrush_t>> &brushes, node_t *node, bool use_mid_split, bspstats_t &stats)
|
static std::optional<qbsp_plane_t> SelectSplitPlane(const std::vector<std::unique_ptr<bspbrush_t>> &brushes, node_t *node, std::optional<bool> forced_quick_tree, bspstats_t &stats)
|
||||||
{
|
{
|
||||||
// no brushes left to split, so we can't use any plane.
|
// no brushes left to split, so we can't use any plane.
|
||||||
if (!brushes.size()) {
|
if (!brushes.size()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it is crossing a block boundary, force a split;
|
// if forced_quick_tree is nullopt, we will choose fast/slow based on
|
||||||
// this is optional q3map2 mode
|
// certain parameters.
|
||||||
for (size_t i = 0; i < 3; i++) {
|
if (!forced_quick_tree.has_value() || forced_quick_tree.value() == true) {
|
||||||
if (qbsp_options.blocksize.value()[i] <= 0) {
|
// if it is crossing a block boundary, force a split;
|
||||||
continue;
|
// this is optional q3map2 mode that is disabled by default.
|
||||||
}
|
if (qbsp_options.blocksize.isChanged()) {
|
||||||
|
for (size_t i = 0; i < 3; i++) {
|
||||||
|
if (qbsp_options.blocksize.value()[i] <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
vec_t dist = qbsp_options.blocksize.value()[i] * (floor(node->bounds.mins()[i] / qbsp_options.blocksize.value()[i]) + 1);
|
vec_t dist = qbsp_options.blocksize.value()[i] * (floor(node->bounds.mins()[i] / qbsp_options.blocksize.value()[i]) + 1);
|
||||||
|
|
||||||
if (node->bounds.maxs()[i] > dist) {
|
if (node->bounds.maxs()[i] > dist) {
|
||||||
qplane3d plane{};
|
qplane3d plane{};
|
||||||
plane.normal[i] = 1.0;
|
plane.normal[i] = 1.0;
|
||||||
plane.dist = dist;
|
plane.dist = dist;
|
||||||
qbsp_plane_t bsp_plane = plane;
|
qbsp_plane_t bsp_plane = plane;
|
||||||
stats.c_blocksplit++;
|
|
||||||
|
|
||||||
for (auto &b : brushes) {
|
if (!CheckPlaneAgainstVolume(bsp_plane, node)) {
|
||||||
b->side = TestBrushToPlanenum(*b, bsp_plane, nullptr, nullptr, nullptr);
|
continue; // would produce a tiny volume
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.c_blocksplit++;
|
||||||
|
|
||||||
|
for (auto &b : brushes) {
|
||||||
|
b->side = TestBrushToPlanenum(*b, bsp_plane, nullptr, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bsp_plane;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!forced_quick_tree.has_value()) {
|
||||||
|
|
||||||
|
// decide if we should switch to the midsplit method
|
||||||
|
if (qbsp_options.midsplitbrushfraction.value() != 0.0) {
|
||||||
|
// new way (opt-in)
|
||||||
|
// how much of the map are we partitioning?
|
||||||
|
double fractionOfMap = brushes.size() / (double) map.brushes.size();
|
||||||
|
forced_quick_tree = (fractionOfMap > qbsp_options.midsplitbrushfraction.value());
|
||||||
|
} else {
|
||||||
|
// old way (ericw-tools 0.15.2+)
|
||||||
|
if (qbsp_options.maxnodesize.value() >= 64) {
|
||||||
|
const vec_t maxnodesize = qbsp_options.maxnodesize.value() - qbsp_options.epsilon.value();
|
||||||
|
|
||||||
|
forced_quick_tree = (node->bounds.maxs()[0] - node->bounds.mins()[0]) > maxnodesize
|
||||||
|
|| (node->bounds.maxs()[1] - node->bounds.mins()[1]) > maxnodesize
|
||||||
|
|| (node->bounds.maxs()[2] - node->bounds.mins()[2]) > maxnodesize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return bsp_plane;
|
if (forced_quick_tree.value()) {
|
||||||
}
|
if (auto mid_plane = ChooseMidPlaneFromList(brushes, node)) {
|
||||||
}
|
stats.c_midsplit++;
|
||||||
|
|
||||||
if (brushes.size() >= qbsp_options.midsplitbrushes.value() || use_mid_split) {
|
for (auto &b : brushes) {
|
||||||
if (auto mid_plane = ChooseMidPlaneFromList(brushes, node->bounds)) {
|
b->side = TestBrushToPlanenum(*b, mid_plane.value(), nullptr, nullptr, nullptr);
|
||||||
stats.c_midsplit++;
|
}
|
||||||
|
|
||||||
for (auto &b : brushes) {
|
return mid_plane;
|
||||||
b->side = TestBrushToPlanenum(*b, mid_plane.value(), nullptr, nullptr, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return mid_plane;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -991,10 +1027,10 @@ BuildTree_r
|
||||||
Called in parallel.
|
Called in parallel.
|
||||||
==================
|
==================
|
||||||
*/
|
*/
|
||||||
static void BuildTree_r(node_t *node, std::vector<std::unique_ptr<bspbrush_t>> brushes, bool use_mid_split, bspstats_t &stats)
|
static void BuildTree_r(node_t *node, std::vector<std::unique_ptr<bspbrush_t>> brushes, std::optional<bool> forced_quick_tree, bspstats_t &stats)
|
||||||
{
|
{
|
||||||
// find the best plane to use as a splitter
|
// find the best plane to use as a splitter
|
||||||
auto bestplane = SelectSplitPlane(brushes, node, use_mid_split, stats);
|
auto bestplane = SelectSplitPlane(brushes, node, forced_quick_tree, stats);
|
||||||
|
|
||||||
if (!bestplane) {
|
if (!bestplane) {
|
||||||
// this is a leaf node
|
// this is a leaf node
|
||||||
|
|
@ -1034,8 +1070,8 @@ static void BuildTree_r(node_t *node, std::vector<std::unique_ptr<bspbrush_t>> b
|
||||||
|
|
||||||
// recursively process children
|
// recursively process children
|
||||||
tbb::task_group g;
|
tbb::task_group g;
|
||||||
g.run([&]() { BuildTree_r(node->children[0].get(), std::move(children[0]), use_mid_split, stats); });
|
g.run([&]() { BuildTree_r(node->children[0].get(), std::move(children[0]), forced_quick_tree, stats); });
|
||||||
g.run([&]() { BuildTree_r(node->children[1].get(), std::move(children[1]), use_mid_split, stats); });
|
g.run([&]() { BuildTree_r(node->children[1].get(), std::move(children[1]), forced_quick_tree, stats); });
|
||||||
g.wait();
|
g.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1044,7 +1080,7 @@ static void BuildTree_r(node_t *node, std::vector<std::unique_ptr<bspbrush_t>> b
|
||||||
BrushBSP
|
BrushBSP
|
||||||
==================
|
==================
|
||||||
*/
|
*/
|
||||||
static std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, std::vector<std::unique_ptr<bspbrush_t>> brushlist, bool use_mid_split)
|
static std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, std::vector<std::unique_ptr<bspbrush_t>> brushlist, std::optional<bool> forced_quick_tree)
|
||||||
{
|
{
|
||||||
auto tree = std::make_unique<tree_t>();
|
auto tree = std::make_unique<tree_t>();
|
||||||
|
|
||||||
|
|
@ -1120,7 +1156,7 @@ static std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, std::vector<std::un
|
||||||
|
|
||||||
bspstats_t stats{};
|
bspstats_t stats{};
|
||||||
stats.leafstats = qbsp_options.target_game->create_content_stats();
|
stats.leafstats = qbsp_options.target_game->create_content_stats();
|
||||||
BuildTree_r(tree->headnode.get(), std::move(brushlist), use_mid_split, stats);
|
BuildTree_r(tree->headnode.get(), std::move(brushlist), forced_quick_tree, stats);
|
||||||
|
|
||||||
logging::print(logging::flag::STAT, " {:8} visible nodes\n", stats.c_nodes - stats.c_nonvis);
|
logging::print(logging::flag::STAT, " {:8} visible nodes\n", stats.c_nodes - stats.c_nonvis);
|
||||||
logging::print(logging::flag::STAT, " {:8} nonvis nodes\n", stats.c_nonvis);
|
logging::print(logging::flag::STAT, " {:8} nonvis nodes\n", stats.c_nonvis);
|
||||||
|
|
@ -1133,7 +1169,7 @@ static std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, std::vector<std::un
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, bool use_mid_split)
|
std::unique_ptr<tree_t> BrushBSP(mapentity_t *entity, std::optional<bool> forced_quick_tree)
|
||||||
{
|
{
|
||||||
return BrushBSP(entity, MakeBspBrushList(entity), use_mid_split);
|
return BrushBSP(entity, MakeBspBrushList(entity), forced_quick_tree);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -609,7 +609,7 @@ static void ProcessEntity(mapentity_t *entity, const int hullnum)
|
||||||
if (qbsp_options.forcegoodtree.value()) {
|
if (qbsp_options.forcegoodtree.value()) {
|
||||||
tree = BrushBSP(entity, false);
|
tree = BrushBSP(entity, false);
|
||||||
} else {
|
} else {
|
||||||
tree = BrushBSP(entity, entity == map.world_entity());
|
tree = BrushBSP(entity, entity == map.world_entity() ? std::nullopt : std::optional<bool>(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// build all the portals in the bsp tree
|
// build all the portals in the bsp tree
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue