BVH - detect shrinkage within expanded bounds
Although the expanded bounds were working in normal use, for moving and growing objects, there was one case which was not dealt with properly - significant shrinkage of exact bounds within an expanded bound. This PR detects significant shrinkage and forces a new expanded bound to be created.
This commit is contained in:
parent
c3a6b0f418
commit
ed47570266
9 changed files with 146 additions and 77 deletions
|
@ -49,10 +49,10 @@
|
||||||
#include "bvh_tree.h"
|
#include "bvh_tree.h"
|
||||||
#include "core/os/mutex.h"
|
#include "core/os/mutex.h"
|
||||||
|
|
||||||
#define BVHTREE_CLASS BVH_Tree<T, 2, MAX_ITEMS, USE_PAIRS, Bounds, Point>
|
#define BVHTREE_CLASS BVH_Tree<T, 2, MAX_ITEMS, USE_PAIRS, BOUNDS, POINT>
|
||||||
#define BVH_LOCKED_FUNCTION BVHLockedFunction(&_mutex, BVH_THREAD_SAFE &&_thread_safe);
|
#define BVH_LOCKED_FUNCTION BVHLockedFunction(&_mutex, BVH_THREAD_SAFE &&_thread_safe);
|
||||||
|
|
||||||
template <class T, bool USE_PAIRS = false, int MAX_ITEMS = 32, class Bounds = AABB, class Point = Vector3, bool BVH_THREAD_SAFE = true>
|
template <class T, bool USE_PAIRS = false, int MAX_ITEMS = 32, class BOUNDS = AABB, class POINT = Vector3, bool BVH_THREAD_SAFE = true>
|
||||||
class BVH_Manager {
|
class BVH_Manager {
|
||||||
public:
|
public:
|
||||||
// note we are using uint32_t instead of BVHHandle, losing type safety, but this
|
// note we are using uint32_t instead of BVHHandle, losing type safety, but this
|
||||||
|
@ -80,12 +80,7 @@ public:
|
||||||
|
|
||||||
void params_set_pairing_expansion(real_t p_value) {
|
void params_set_pairing_expansion(real_t p_value) {
|
||||||
BVH_LOCKED_FUNCTION
|
BVH_LOCKED_FUNCTION
|
||||||
if (p_value >= 0.0) {
|
tree.params_set_pairing_expansion(p_value);
|
||||||
tree._pairing_expansion = p_value;
|
|
||||||
tree._auto_pairing_expansion = false;
|
|
||||||
} else {
|
|
||||||
tree._auto_pairing_expansion = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_pair_callback(PairCallback p_callback, void *p_userdata) {
|
void set_pair_callback(PairCallback p_callback, void *p_userdata) {
|
||||||
|
@ -104,7 +99,7 @@ public:
|
||||||
check_pair_callback_userdata = p_userdata;
|
check_pair_callback_userdata = p_userdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
BVHHandle create(T *p_userdata, bool p_active, const Bounds &p_aabb = Bounds(), int p_subindex = 0, bool p_pairable = false, uint32_t p_pairable_type = 0, uint32_t p_pairable_mask = 1) {
|
BVHHandle create(T *p_userdata, bool p_active, const BOUNDS &p_aabb = BOUNDS(), int p_subindex = 0, bool p_pairable = false, uint32_t p_pairable_type = 0, uint32_t p_pairable_mask = 1) {
|
||||||
BVH_LOCKED_FUNCTION
|
BVH_LOCKED_FUNCTION
|
||||||
|
|
||||||
// not sure if absolutely necessary to flush collisions here. It will cost performance to, instead
|
// not sure if absolutely necessary to flush collisions here. It will cost performance to, instead
|
||||||
|
@ -125,7 +120,7 @@ public:
|
||||||
|
|
||||||
if (USE_PAIRS) {
|
if (USE_PAIRS) {
|
||||||
// for safety initialize the expanded AABB
|
// for safety initialize the expanded AABB
|
||||||
Bounds &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
|
BOUNDS &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
|
||||||
expanded_aabb = p_aabb;
|
expanded_aabb = p_aabb;
|
||||||
expanded_aabb.grow_by(tree._pairing_expansion);
|
expanded_aabb.grow_by(tree._pairing_expansion);
|
||||||
|
|
||||||
|
@ -142,7 +137,7 @@ public:
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
// wrapper versions that use uint32_t instead of handle
|
// wrapper versions that use uint32_t instead of handle
|
||||||
// for backward compatibility. Less type safe
|
// for backward compatibility. Less type safe
|
||||||
void move(uint32_t p_handle, const Bounds &p_aabb) {
|
void move(uint32_t p_handle, const BOUNDS &p_aabb) {
|
||||||
BVHHandle h;
|
BVHHandle h;
|
||||||
h.set(p_handle);
|
h.set(p_handle);
|
||||||
move(h, p_aabb);
|
move(h, p_aabb);
|
||||||
|
@ -166,7 +161,7 @@ public:
|
||||||
force_collision_check(h);
|
force_collision_check(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool activate(uint32_t p_handle, const Bounds &p_aabb, bool p_delay_collision_check = false) {
|
bool activate(uint32_t p_handle, const BOUNDS &p_aabb, bool p_delay_collision_check = false) {
|
||||||
BVHHandle h;
|
BVHHandle h;
|
||||||
h.set(p_handle);
|
h.set(p_handle);
|
||||||
return activate(h, p_aabb, p_delay_collision_check);
|
return activate(h, p_aabb, p_delay_collision_check);
|
||||||
|
@ -203,7 +198,7 @@ public:
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
////////////////////////////////////////////////////
|
||||||
|
|
||||||
void move(BVHHandle p_handle, const Bounds &p_aabb) {
|
void move(BVHHandle p_handle, const BOUNDS &p_aabb) {
|
||||||
BVH_LOCKED_FUNCTION
|
BVH_LOCKED_FUNCTION
|
||||||
if (tree.item_move(p_handle, p_aabb)) {
|
if (tree.item_move(p_handle, p_aabb)) {
|
||||||
if (USE_PAIRS) {
|
if (USE_PAIRS) {
|
||||||
|
@ -239,7 +234,7 @@ public:
|
||||||
BVH_LOCKED_FUNCTION
|
BVH_LOCKED_FUNCTION
|
||||||
if (USE_PAIRS) {
|
if (USE_PAIRS) {
|
||||||
// the aabb should already be up to date in the BVH
|
// the aabb should already be up to date in the BVH
|
||||||
Bounds aabb;
|
BOUNDS aabb;
|
||||||
item_get_AABB(p_handle, aabb);
|
item_get_AABB(p_handle, aabb);
|
||||||
|
|
||||||
// add it as changed even if aabb not different
|
// add it as changed even if aabb not different
|
||||||
|
@ -253,7 +248,7 @@ public:
|
||||||
// these should be read as set_visible for render trees,
|
// these should be read as set_visible for render trees,
|
||||||
// but generically this makes items add or remove from the
|
// but generically this makes items add or remove from the
|
||||||
// tree internally, to speed things up by ignoring inactive items
|
// tree internally, to speed things up by ignoring inactive items
|
||||||
bool activate(BVHHandle p_handle, const Bounds &p_aabb, bool p_delay_collision_check = false) {
|
bool activate(BVHHandle p_handle, const BOUNDS &p_aabb, bool p_delay_collision_check = false) {
|
||||||
BVH_LOCKED_FUNCTION
|
BVH_LOCKED_FUNCTION
|
||||||
// sending the aabb here prevents the need for the BVH to maintain
|
// sending the aabb here prevents the need for the BVH to maintain
|
||||||
// a redundant copy of the aabb.
|
// a redundant copy of the aabb.
|
||||||
|
@ -331,7 +326,7 @@ public:
|
||||||
// when the pairable state changes, we need to force a collision check because newly pairable
|
// when the pairable state changes, we need to force a collision check because newly pairable
|
||||||
// items may be in collision, and unpairable items might move out of collision.
|
// items may be in collision, and unpairable items might move out of collision.
|
||||||
// We cannot depend on waiting for the next update, because that may come much later.
|
// We cannot depend on waiting for the next update, because that may come much later.
|
||||||
Bounds aabb;
|
BOUNDS aabb;
|
||||||
item_get_AABB(p_handle, aabb);
|
item_get_AABB(p_handle, aabb);
|
||||||
|
|
||||||
// passing false disables the optimization which prevents collision checks if
|
// passing false disables the optimization which prevents collision checks if
|
||||||
|
@ -348,7 +343,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// cull tests
|
// cull tests
|
||||||
int cull_aabb(const Bounds &p_aabb, T **p_result_array, int p_result_max, int *p_subindex_array = nullptr, uint32_t p_mask = 0xFFFFFFFF) {
|
int cull_aabb(const BOUNDS &p_aabb, T **p_result_array, int p_result_max, int *p_subindex_array = nullptr, uint32_t p_mask = 0xFFFFFFFF) {
|
||||||
BVH_LOCKED_FUNCTION
|
BVH_LOCKED_FUNCTION
|
||||||
typename BVHTREE_CLASS::CullParams params;
|
typename BVHTREE_CLASS::CullParams params;
|
||||||
|
|
||||||
|
@ -366,7 +361,7 @@ public:
|
||||||
return params.result_count_overall;
|
return params.result_count_overall;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cull_segment(const Point &p_from, const Point &p_to, T **p_result_array, int p_result_max, int *p_subindex_array = nullptr, uint32_t p_mask = 0xFFFFFFFF) {
|
int cull_segment(const POINT &p_from, const POINT &p_to, T **p_result_array, int p_result_max, int *p_subindex_array = nullptr, uint32_t p_mask = 0xFFFFFFFF) {
|
||||||
BVH_LOCKED_FUNCTION
|
BVH_LOCKED_FUNCTION
|
||||||
typename BVHTREE_CLASS::CullParams params;
|
typename BVHTREE_CLASS::CullParams params;
|
||||||
|
|
||||||
|
@ -385,7 +380,7 @@ public:
|
||||||
return params.result_count_overall;
|
return params.result_count_overall;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cull_point(const Point &p_point, T **p_result_array, int p_result_max, int *p_subindex_array = nullptr, uint32_t p_mask = 0xFFFFFFFF) {
|
int cull_point(const POINT &p_point, T **p_result_array, int p_result_max, int *p_subindex_array = nullptr, uint32_t p_mask = 0xFFFFFFFF) {
|
||||||
BVH_LOCKED_FUNCTION
|
BVH_LOCKED_FUNCTION
|
||||||
typename BVHTREE_CLASS::CullParams params;
|
typename BVHTREE_CLASS::CullParams params;
|
||||||
|
|
||||||
|
@ -439,7 +434,7 @@ private:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bounds bb;
|
BOUNDS bb;
|
||||||
|
|
||||||
typename BVHTREE_CLASS::CullParams params;
|
typename BVHTREE_CLASS::CullParams params;
|
||||||
|
|
||||||
|
@ -454,7 +449,7 @@ private:
|
||||||
const BVHHandle &h = changed_items[n];
|
const BVHHandle &h = changed_items[n];
|
||||||
|
|
||||||
// use the expanded aabb for pairing
|
// use the expanded aabb for pairing
|
||||||
const Bounds &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
|
const BOUNDS &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
|
||||||
BVHABB_CLASS abb;
|
BVHABB_CLASS abb;
|
||||||
abb.from(expanded_aabb);
|
abb.from(expanded_aabb);
|
||||||
|
|
||||||
|
@ -501,7 +496,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void item_get_AABB(BVHHandle p_handle, Bounds &r_aabb) {
|
void item_get_AABB(BVHHandle p_handle, BOUNDS &r_aabb) {
|
||||||
BVHABB_CLASS abb;
|
BVHABB_CLASS abb;
|
||||||
tree.item_get_ABB(p_handle, abb);
|
tree.item_get_ABB(p_handle, abb);
|
||||||
abb.to(r_aabb);
|
abb.to(r_aabb);
|
||||||
|
@ -530,6 +525,10 @@ private:
|
||||||
void *ud_from = pairs_from.remove_pair_to(p_to);
|
void *ud_from = pairs_from.remove_pair_to(p_to);
|
||||||
pairs_to.remove_pair_to(p_from);
|
pairs_to.remove_pair_to(p_from);
|
||||||
|
|
||||||
|
#ifdef BVH_VERBOSE_PAIRING
|
||||||
|
print_line("_unpair " + itos(p_from.id()) + " from " + itos(p_to.id()));
|
||||||
|
#endif
|
||||||
|
|
||||||
// callback
|
// callback
|
||||||
if (unpair_callback) {
|
if (unpair_callback) {
|
||||||
unpair_callback(pair_callback_userdata, p_from, exa.userdata, exa.subindex, p_to, exb.userdata, exb.subindex, ud_from);
|
unpair_callback(pair_callback_userdata, p_from, exa.userdata, exa.subindex, p_to, exb.userdata, exb.subindex, ud_from);
|
||||||
|
@ -637,6 +636,10 @@ private:
|
||||||
// callback
|
// callback
|
||||||
void *callback_userdata = nullptr;
|
void *callback_userdata = nullptr;
|
||||||
|
|
||||||
|
#ifdef BVH_VERBOSE_PAIRING
|
||||||
|
print_line("_pair " + itos(p_ha.id()) + " to " + itos(p_hb.id()));
|
||||||
|
#endif
|
||||||
|
|
||||||
if (pair_callback) {
|
if (pair_callback) {
|
||||||
callback_userdata = pair_callback(pair_callback_userdata, p_ha, exa.userdata, exa.subindex, p_hb, exb.userdata, exb.subindex);
|
callback_userdata = pair_callback(pair_callback_userdata, p_ha, exa.userdata, exa.subindex, p_hb, exb.userdata, exb.subindex);
|
||||||
}
|
}
|
||||||
|
@ -697,19 +700,19 @@ private:
|
||||||
_tick++;
|
_tick++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _add_changed_item(BVHHandle p_handle, const Bounds &aabb, bool p_check_aabb = true) {
|
void _add_changed_item(BVHHandle p_handle, const BOUNDS &aabb, bool p_check_aabb = true) {
|
||||||
// Note that non pairable items can pair with pairable,
|
// Note that non pairable items can pair with pairable,
|
||||||
// so all types must be added to the list
|
// so all types must be added to the list
|
||||||
|
|
||||||
// aabb check with expanded aabb. This greatly decreases processing
|
// aabb check with expanded aabb. This greatly decreases processing
|
||||||
// at the cost of slightly less accurate pairing checks
|
// at the cost of slightly less accurate pairing checks
|
||||||
// Note this pairing AABB is separate from the AABB in the actual tree
|
// Note this pairing AABB is separate from the AABB in the actual tree
|
||||||
Bounds &expanded_aabb = tree._pairs[p_handle.id()].expanded_aabb;
|
BOUNDS &expanded_aabb = tree._pairs[p_handle.id()].expanded_aabb;
|
||||||
|
|
||||||
// passing p_check_aabb false disables the optimization which prevents collision checks if
|
// passing p_check_aabb false disables the optimization which prevents collision checks if
|
||||||
// the aabb hasn't changed. This is needed where set_pairable has been called, but the position
|
// the aabb hasn't changed. This is needed where set_pairable has been called, but the position
|
||||||
// has not changed.
|
// has not changed.
|
||||||
if (p_check_aabb && expanded_aabb.encloses(aabb)) {
|
if (p_check_aabb && tree.expanded_aabb_encloses_not_shrink(expanded_aabb, aabb)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
#define BVH_ABB_H
|
#define BVH_ABB_H
|
||||||
|
|
||||||
// special optimized version of axis aligned bounding box
|
// special optimized version of axis aligned bounding box
|
||||||
template <class Bounds = AABB, class Point = Vector3>
|
template <class BOUNDS = AABB, class POINT = Vector3>
|
||||||
struct BVH_ABB {
|
struct BVH_ABB {
|
||||||
struct ConvexHull {
|
struct ConvexHull {
|
||||||
// convex hulls (optional)
|
// convex hulls (optional)
|
||||||
|
@ -43,8 +43,8 @@ struct BVH_ABB {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Segment {
|
struct Segment {
|
||||||
Point from;
|
POINT from;
|
||||||
Point to;
|
POINT to;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum IntersectResult {
|
enum IntersectResult {
|
||||||
|
@ -54,47 +54,47 @@ struct BVH_ABB {
|
||||||
};
|
};
|
||||||
|
|
||||||
// we store mins with a negative value in order to test them with SIMD
|
// we store mins with a negative value in order to test them with SIMD
|
||||||
Point min;
|
POINT min;
|
||||||
Point neg_max;
|
POINT neg_max;
|
||||||
|
|
||||||
bool operator==(const BVH_ABB &o) const { return (min == o.min) && (neg_max == o.neg_max); }
|
bool operator==(const BVH_ABB &o) const { return (min == o.min) && (neg_max == o.neg_max); }
|
||||||
bool operator!=(const BVH_ABB &o) const { return (*this == o) == false; }
|
bool operator!=(const BVH_ABB &o) const { return (*this == o) == false; }
|
||||||
|
|
||||||
void set(const Point &_min, const Point &_max) {
|
void set(const POINT &_min, const POINT &_max) {
|
||||||
min = _min;
|
min = _min;
|
||||||
neg_max = -_max;
|
neg_max = -_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
// to and from standard AABB
|
// to and from standard AABB
|
||||||
void from(const Bounds &p_aabb) {
|
void from(const BOUNDS &p_aabb) {
|
||||||
min = p_aabb.position;
|
min = p_aabb.position;
|
||||||
neg_max = -(p_aabb.position + p_aabb.size);
|
neg_max = -(p_aabb.position + p_aabb.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void to(Bounds &r_aabb) const {
|
void to(BOUNDS &r_aabb) const {
|
||||||
r_aabb.position = min;
|
r_aabb.position = min;
|
||||||
r_aabb.size = calculate_size();
|
r_aabb.size = calculate_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void merge(const BVH_ABB &p_o) {
|
void merge(const BVH_ABB &p_o) {
|
||||||
for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
|
for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
|
||||||
neg_max[axis] = MIN(neg_max[axis], p_o.neg_max[axis]);
|
neg_max[axis] = MIN(neg_max[axis], p_o.neg_max[axis]);
|
||||||
min[axis] = MIN(min[axis], p_o.min[axis]);
|
min[axis] = MIN(min[axis], p_o.min[axis]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Point calculate_size() const {
|
POINT calculate_size() const {
|
||||||
return -neg_max - min;
|
return -neg_max - min;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point calculate_centre() const {
|
POINT calculate_centre() const {
|
||||||
return Point((calculate_size() * 0.5) + min);
|
return POINT((calculate_size() * 0.5) + min);
|
||||||
}
|
}
|
||||||
|
|
||||||
real_t get_proximity_to(const BVH_ABB &p_b) const {
|
real_t get_proximity_to(const BVH_ABB &p_b) const {
|
||||||
const Point d = (min - neg_max) - (p_b.min - p_b.neg_max);
|
const POINT d = (min - neg_max) - (p_b.min - p_b.neg_max);
|
||||||
real_t proximity = 0.0;
|
real_t proximity = 0.0;
|
||||||
for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
|
for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
|
||||||
proximity += Math::abs(d[axis]);
|
proximity += Math::abs(d[axis]);
|
||||||
}
|
}
|
||||||
return proximity;
|
return proximity;
|
||||||
|
@ -162,7 +162,7 @@ struct BVH_ABB {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool intersects_convex_partial(const ConvexHull &p_hull) const {
|
bool intersects_convex_partial(const ConvexHull &p_hull) const {
|
||||||
Bounds bb;
|
BOUNDS bb;
|
||||||
to(bb);
|
to(bb);
|
||||||
return bb.intersects_convex_shape(p_hull.planes, p_hull.num_planes, p_hull.points, p_hull.num_points);
|
return bb.intersects_convex_shape(p_hull.planes, p_hull.num_planes, p_hull.points, p_hull.num_points);
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ struct BVH_ABB {
|
||||||
|
|
||||||
bool is_within_convex(const ConvexHull &p_hull) const {
|
bool is_within_convex(const ConvexHull &p_hull) const {
|
||||||
// use half extents routine
|
// use half extents routine
|
||||||
Bounds bb;
|
BOUNDS bb;
|
||||||
to(bb);
|
to(bb);
|
||||||
return bb.inside_convex_shape(p_hull.planes, p_hull.num_planes);
|
return bb.inside_convex_shape(p_hull.planes, p_hull.num_planes);
|
||||||
}
|
}
|
||||||
|
@ -197,12 +197,12 @@ struct BVH_ABB {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool intersects_segment(const Segment &p_s) const {
|
bool intersects_segment(const Segment &p_s) const {
|
||||||
Bounds bb;
|
BOUNDS bb;
|
||||||
to(bb);
|
to(bb);
|
||||||
return bb.intersects_segment(p_s.from, p_s.to);
|
return bb.intersects_segment(p_s.from, p_s.to);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool intersects_point(const Point &p_pt) const {
|
bool intersects_point(const POINT &p_pt) const {
|
||||||
if (_any_lessthan(-p_pt, neg_max)) {
|
if (_any_lessthan(-p_pt, neg_max)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -232,20 +232,20 @@ struct BVH_ABB {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void grow(const Point &p_change) {
|
void grow(const POINT &p_change) {
|
||||||
neg_max -= p_change;
|
neg_max -= p_change;
|
||||||
min -= p_change;
|
min -= p_change;
|
||||||
}
|
}
|
||||||
|
|
||||||
void expand(real_t p_change) {
|
void expand(real_t p_change) {
|
||||||
Point change;
|
POINT change;
|
||||||
change.set_all(p_change);
|
change.set_all(p_change);
|
||||||
grow(change);
|
grow(change);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually surface area metric.
|
// Actually surface area metric.
|
||||||
float get_area() const {
|
float get_area() const {
|
||||||
Point d = calculate_size();
|
POINT d = calculate_size();
|
||||||
return 2.0f * (d.x * d.y + d.y * d.z + d.z * d.x);
|
return 2.0f * (d.x * d.y + d.y * d.z + d.z * d.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,8 +254,8 @@ struct BVH_ABB {
|
||||||
min = neg_max;
|
min = neg_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _any_morethan(const Point &p_a, const Point &p_b) const {
|
bool _any_morethan(const POINT &p_a, const POINT &p_b) const {
|
||||||
for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
|
for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
|
||||||
if (p_a[axis] > p_b[axis]) {
|
if (p_a[axis] > p_b[axis]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -263,8 +263,8 @@ struct BVH_ABB {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _any_lessthan(const Point &p_a, const Point &p_b) const {
|
bool _any_lessthan(const POINT &p_a, const POINT &p_b) const {
|
||||||
for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
|
for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
|
||||||
if (p_a[axis] < p_b[axis]) {
|
if (p_a[axis] < p_b[axis]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct CullParams {
|
||||||
uint32_t pairable_type;
|
uint32_t pairable_type;
|
||||||
|
|
||||||
// optional components for different tests
|
// optional components for different tests
|
||||||
Point point;
|
POINT point;
|
||||||
BVHABB_CLASS abb;
|
BVHABB_CLASS abb;
|
||||||
typename BVHABB_CLASS::ConvexHull hull;
|
typename BVHABB_CLASS::ConvexHull hull;
|
||||||
typename BVHABB_CLASS::Segment segment;
|
typename BVHABB_CLASS::Segment segment;
|
||||||
|
|
|
@ -6,12 +6,12 @@ void _debug_recursive_print_tree(int p_tree_id) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const {
|
String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const {
|
||||||
Point size = aabb.calculate_size();
|
POINT size = aabb.calculate_size();
|
||||||
|
|
||||||
String sz;
|
String sz;
|
||||||
float vol = 0.0;
|
float vol = 0.0;
|
||||||
|
|
||||||
for (int i = 0; i < Point::AXES_COUNT; ++i) {
|
for (int i = 0; i < POINT::AXIS_COUNT; ++i) {
|
||||||
sz += "(";
|
sz += "(";
|
||||||
sz += itos(aabb.min[i]);
|
sz += itos(aabb.min[i]);
|
||||||
sz += " ~ ";
|
sz += " ~ ";
|
||||||
|
|
|
@ -14,10 +14,10 @@ struct ItemPairs {
|
||||||
void clear() {
|
void clear() {
|
||||||
num_pairs = 0;
|
num_pairs = 0;
|
||||||
extended_pairs.reset();
|
extended_pairs.reset();
|
||||||
expanded_aabb = Bounds();
|
expanded_aabb = BOUNDS();
|
||||||
}
|
}
|
||||||
|
|
||||||
Bounds expanded_aabb;
|
BOUNDS expanded_aabb;
|
||||||
|
|
||||||
// maybe we can just use the number in the vector TODO
|
// maybe we can just use the number in the vector TODO
|
||||||
int32_t num_pairs;
|
int32_t num_pairs;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
public:
|
public:
|
||||||
BVHHandle item_add(T *p_userdata, bool p_active, const Bounds &p_aabb, int32_t p_subindex, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask, bool p_invisible = false) {
|
BVHHandle item_add(T *p_userdata, bool p_active, const BOUNDS &p_aabb, int32_t p_subindex, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask, bool p_invisible = false) {
|
||||||
#ifdef BVH_VERBOSE_TREE
|
#ifdef BVH_VERBOSE_TREE
|
||||||
VERBOSE_PRINT("\nitem_add BEFORE");
|
VERBOSE_PRINT("\nitem_add BEFORE");
|
||||||
_debug_recursive_print_tree(0);
|
_debug_recursive_print_tree(0);
|
||||||
|
@ -103,7 +103,7 @@ void _debug_print_refs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns false if noop
|
// returns false if noop
|
||||||
bool item_move(BVHHandle p_handle, const Bounds &p_aabb) {
|
bool item_move(BVHHandle p_handle, const BOUNDS &p_aabb) {
|
||||||
uint32_t ref_id = p_handle.id();
|
uint32_t ref_id = p_handle.id();
|
||||||
|
|
||||||
// get the reference
|
// get the reference
|
||||||
|
@ -118,7 +118,7 @@ bool item_move(BVHHandle p_handle, const Bounds &p_aabb) {
|
||||||
BVH_ASSERT(ref.tnode_id != BVHCommon::INVALID);
|
BVH_ASSERT(ref.tnode_id != BVHCommon::INVALID);
|
||||||
TNode &tnode = _nodes[ref.tnode_id];
|
TNode &tnode = _nodes[ref.tnode_id];
|
||||||
|
|
||||||
// does it fit within the current aabb?
|
// does it fit within the current leaf aabb?
|
||||||
if (tnode.aabb.is_other_within(abb)) {
|
if (tnode.aabb.is_other_within(abb)) {
|
||||||
// do nothing .. fast path .. not moved enough to need refit
|
// do nothing .. fast path .. not moved enough to need refit
|
||||||
|
|
||||||
|
@ -133,12 +133,20 @@ bool item_move(BVHHandle p_handle, const Bounds &p_aabb) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BVH_VERBOSE_MOVES
|
||||||
|
print_line("item_move " + itos(p_handle.id()) + "(within tnode aabb) : " + _debug_aabb_to_string(abb));
|
||||||
|
#endif
|
||||||
|
|
||||||
leaf_abb = abb;
|
leaf_abb = abb;
|
||||||
_integrity_check_all();
|
_integrity_check_all();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef BVH_VERBOSE_MOVES
|
||||||
|
print_line("item_move " + itos(p_handle.id()) + "(outside tnode aabb) : " + _debug_aabb_to_string(abb));
|
||||||
|
#endif
|
||||||
|
|
||||||
uint32_t tree_id = _handle_get_tree_id(p_handle);
|
uint32_t tree_id = _handle_get_tree_id(p_handle);
|
||||||
|
|
||||||
// remove and reinsert
|
// remove and reinsert
|
||||||
|
@ -206,7 +214,7 @@ void item_remove(BVHHandle p_handle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns success
|
// returns success
|
||||||
bool item_activate(BVHHandle p_handle, const Bounds &p_aabb) {
|
bool item_activate(BVHHandle p_handle, const BOUNDS &p_aabb) {
|
||||||
uint32_t ref_id = p_handle.id();
|
uint32_t ref_id = p_handle.id();
|
||||||
ItemRef &ref = _refs[ref_id];
|
ItemRef &ref = _refs[ref_id];
|
||||||
if (ref.is_active()) {
|
if (ref.is_active()) {
|
||||||
|
@ -403,7 +411,7 @@ void update() {
|
||||||
|
|
||||||
// if there are no nodes, do nothing, but if there are...
|
// if there are no nodes, do nothing, but if there are...
|
||||||
if (bound_valid) {
|
if (bound_valid) {
|
||||||
Bounds bb;
|
BOUNDS bb;
|
||||||
world_bound.to(bb);
|
world_bound.to(bb);
|
||||||
real_t size = bb.get_longest_axis_size();
|
real_t size = bb.get_longest_axis_size();
|
||||||
|
|
||||||
|
@ -421,3 +429,50 @@ void update() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void params_set_pairing_expansion(real_t p_value) {
|
||||||
|
if (p_value < 0.0) {
|
||||||
|
#ifdef BVH_ALLOW_AUTO_EXPANSION
|
||||||
|
_auto_pairing_expansion = true;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef BVH_ALLOW_AUTO_EXPANSION
|
||||||
|
_auto_pairing_expansion = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_pairing_expansion = p_value;
|
||||||
|
|
||||||
|
// calculate shrinking threshold
|
||||||
|
const real_t fudge_factor = 1.1;
|
||||||
|
_aabb_shrinkage_threshold = _pairing_expansion * POINT::AXIS_COUNT * 2.0 * fudge_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This routine is not just an enclose check, it also checks for special case of shrinkage
|
||||||
|
bool expanded_aabb_encloses_not_shrink(const BOUNDS &p_expanded_aabb, const BOUNDS &p_aabb) const {
|
||||||
|
if (!p_expanded_aabb.encloses(p_aabb)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for special case of shrinkage. If the aabb has shrunk
|
||||||
|
// significantly we want to create a new expanded bound, because
|
||||||
|
// the previous expanded bound will have diverged significantly.
|
||||||
|
const POINT &exp_size = p_expanded_aabb.size;
|
||||||
|
const POINT &new_size = p_aabb.size;
|
||||||
|
|
||||||
|
real_t exp_l = 0.0;
|
||||||
|
real_t new_l = 0.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < POINT::AXIS_COUNT; ++i) {
|
||||||
|
exp_l += exp_size[i];
|
||||||
|
new_l += new_size[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// is difference above some metric
|
||||||
|
real_t diff = exp_l - new_l;
|
||||||
|
if (diff < _aabb_shrinkage_threshold) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -25,16 +25,16 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point centre = full_bound.calculate_centre();
|
POINT centre = full_bound.calculate_centre();
|
||||||
Point size = full_bound.calculate_size();
|
POINT size = full_bound.calculate_size();
|
||||||
|
|
||||||
int order[Point::AXIS_COUNT];
|
int order[POINT::AXIS_COUNT];
|
||||||
|
|
||||||
order[0] = size.min_axis();
|
order[0] = size.min_axis();
|
||||||
order[Point::AXIS_COUNT - 1] = size.max_axis();
|
order[POINT::AXIS_COUNT - 1] = size.max_axis();
|
||||||
|
|
||||||
static_assert(Point::AXIS_COUNT <= 3, "BVH Point::AXIS_COUNT has unexpected size");
|
static_assert(POINT::AXIS_COUNT <= 3, "BVH POINT::AXIS_COUNT has unexpected size");
|
||||||
if (Point::AXIS_COUNT == 3) {
|
if (POINT::AXIS_COUNT == 3) {
|
||||||
order[1] = 3 - (order[0] + order[2]);
|
order[1] = 3 - (order[0] + order[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
|
||||||
|
|
||||||
// detect when split on longest axis failed
|
// detect when split on longest axis failed
|
||||||
int min_threshold = MAX_ITEMS / 4;
|
int min_threshold = MAX_ITEMS / 4;
|
||||||
int min_group_size[Point::AXIS_COUNT];
|
int min_group_size[POINT::AXIS_COUNT];
|
||||||
min_group_size[0] = MIN(num_a, num_b);
|
min_group_size[0] = MIN(num_a, num_b);
|
||||||
if (min_group_size[0] < min_threshold) {
|
if (min_group_size[0] < min_threshold) {
|
||||||
// slow but sure .. first move everything back into a
|
// slow but sure .. first move everything back into a
|
||||||
|
@ -68,7 +68,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
|
||||||
num_b = 0;
|
num_b = 0;
|
||||||
|
|
||||||
// now calculate the best split
|
// now calculate the best split
|
||||||
for (int axis = 1; axis < Point::AXIS_COUNT; axis++) {
|
for (int axis = 1; axis < POINT::AXIS_COUNT; axis++) {
|
||||||
split_axis = order[axis];
|
split_axis = order[axis];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
|
||||||
// best axis
|
// best axis
|
||||||
int best_axis = 0;
|
int best_axis = 0;
|
||||||
int best_min = min_group_size[0];
|
int best_min = min_group_size[0];
|
||||||
for (int axis = 1; axis < Point::AXIS_COUNT; axis++) {
|
for (int axis = 1; axis < POINT::AXIS_COUNT; axis++) {
|
||||||
if (min_group_size[axis] > best_min) {
|
if (min_group_size[axis] > best_min) {
|
||||||
best_min = min_group_size[axis];
|
best_min = min_group_size[axis];
|
||||||
best_axis = axis;
|
best_axis = axis;
|
||||||
|
|
|
@ -29,12 +29,6 @@ struct ItemExtra {
|
||||||
T *userdata;
|
T *userdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
// this is an item OR a child node depending on whether a leaf node
|
|
||||||
struct Item {
|
|
||||||
BVHABB_CLASS aabb;
|
|
||||||
uint32_t item_ref_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
// tree leaf
|
// tree leaf
|
||||||
struct TLeaf {
|
struct TLeaf {
|
||||||
uint16_t num_items;
|
uint16_t num_items;
|
||||||
|
@ -177,4 +171,14 @@ bool _auto_node_expansion = true;
|
||||||
// larger values gives more 'sticky' pairing, and is less likely to exhibit tunneling
|
// larger values gives more 'sticky' pairing, and is less likely to exhibit tunneling
|
||||||
// we can either use auto mode, where the expansion is based on the root node size, or specify manually
|
// we can either use auto mode, where the expansion is based on the root node size, or specify manually
|
||||||
real_t _pairing_expansion = 0.1;
|
real_t _pairing_expansion = 0.1;
|
||||||
|
|
||||||
|
#ifdef BVH_ALLOW_AUTO_EXPANSION
|
||||||
bool _auto_pairing_expansion = true;
|
bool _auto_pairing_expansion = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// when using an expanded bound, we must detect the condition where a new AABB
|
||||||
|
// is significantly smaller than the expanded bound, as this is a special case where we
|
||||||
|
// should override the optimization and create a new expanded bound.
|
||||||
|
// This threshold is derived from the _pairing_expansion, and should be recalculated
|
||||||
|
// if _pairing_expansion is changed.
|
||||||
|
real_t _aabb_shrinkage_threshold = 0.0;
|
||||||
|
|
|
@ -48,12 +48,14 @@
|
||||||
#include "core/print_string.h"
|
#include "core/print_string.h"
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#define BVHABB_CLASS BVH_ABB<Bounds, Point>
|
#define BVHABB_CLASS BVH_ABB<BOUNDS, POINT>
|
||||||
|
|
||||||
// never do these checks in release
|
// never do these checks in release
|
||||||
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
|
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
|
||||||
//#define BVH_VERBOSE
|
//#define BVH_VERBOSE
|
||||||
//#define BVH_VERBOSE_TREE
|
//#define BVH_VERBOSE_TREE
|
||||||
|
//#define BVH_VERBOSE_PAIRING
|
||||||
|
//#define BVH_VERBOSE_MOVES
|
||||||
|
|
||||||
//#define BVH_VERBOSE_FRAME
|
//#define BVH_VERBOSE_FRAME
|
||||||
//#define BVH_CHECKS
|
//#define BVH_CHECKS
|
||||||
|
@ -148,7 +150,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T, int MAX_CHILDREN, int MAX_ITEMS, bool USE_PAIRS = false, class Bounds = AABB, class Point = Vector3>
|
template <class T, int MAX_CHILDREN, int MAX_ITEMS, bool USE_PAIRS = false, class BOUNDS = AABB, class POINT = Vector3>
|
||||||
class BVH_Tree {
|
class BVH_Tree {
|
||||||
friend class BVH;
|
friend class BVH;
|
||||||
|
|
||||||
|
@ -165,6 +167,11 @@ public:
|
||||||
// (as these ids are stored as negative numbers in the node)
|
// (as these ids are stored as negative numbers in the node)
|
||||||
uint32_t dummy_leaf_id;
|
uint32_t dummy_leaf_id;
|
||||||
_leaves.request(dummy_leaf_id);
|
_leaves.request(dummy_leaf_id);
|
||||||
|
|
||||||
|
// In many cases you may want to change this default in the client code,
|
||||||
|
// or expose this value to the user.
|
||||||
|
// This default may make sense for a typically scaled 3d game, but maybe not for 2d on a pixel scale.
|
||||||
|
params_set_pairing_expansion(0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
Loading…
Reference in a new issue