Skip to content

Commit

Permalink
Support for Dynamic BVH as 2D Physics broadphase
Browse files Browse the repository at this point in the history
List of changes:
- Modified bvh class to handle 2D and 3D as a template
- Changes in Rect2, Vector2, Vector3 interface to uniformize template
calls
- New option in Project Settings to enable BVH for 2D Physics (enabled
by default like in 3D)
  • Loading branch information
pouleyKetchoupp committed Apr 30, 2021
1 parent 595a74c commit d8f6810
Show file tree
Hide file tree
Showing 26 changed files with 425 additions and 173 deletions.
48 changes: 24 additions & 24 deletions core/math/bvh.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@

#include "bvh_tree.h"

#define BVHTREE_CLASS BVH_Tree<T, 2, MAX_ITEMS, USE_PAIRS>
#define BVHTREE_CLASS BVH_Tree<T, 2, MAX_ITEMS, USE_PAIRS, Bounds, Point>

template <class T, bool USE_PAIRS = false, int MAX_ITEMS = 32>
template <class T, bool USE_PAIRS = false, int MAX_ITEMS = 32, class Bounds = AABB, class Point = Vector3>
class BVH_Manager {

public:
Expand Down Expand Up @@ -88,7 +88,7 @@ class BVH_Manager {
unpair_callback_userdata = p_userdata;
}

BVHHandle create(T *p_userdata, bool p_active, const AABB &p_aabb = AABB(), 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) {

// not sure if absolutely necessary to flush collisions here. It will cost performance to, instead
// of waiting for update, so only uncomment this if there are bugs.
Expand All @@ -108,7 +108,7 @@ class BVH_Manager {

if (USE_PAIRS) {
// for safety initialize the expanded AABB
AABB &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
Bounds &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
expanded_aabb = p_aabb;
expanded_aabb.grow_by(tree._pairing_expansion);

Expand All @@ -125,7 +125,7 @@ class BVH_Manager {
////////////////////////////////////////////////////
// wrapper versions that use uint32_t instead of handle
// for backward compatibility. Less type safe
void move(uint32_t p_handle, const AABB &p_aabb) {
void move(uint32_t p_handle, const Bounds &p_aabb) {
BVHHandle h;
h.set(p_handle);
move(h, p_aabb);
Expand All @@ -143,7 +143,7 @@ class BVH_Manager {
force_collision_check(h);
}

bool activate(uint32_t p_handle, const AABB &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;
h.set(p_handle);
return activate(h, p_aabb, p_delay_collision_check);
Expand Down Expand Up @@ -180,7 +180,7 @@ class BVH_Manager {

////////////////////////////////////////////////////

void move(BVHHandle p_handle, const AABB &p_aabb) {
void move(BVHHandle p_handle, const Bounds &p_aabb) {

if (tree.item_move(p_handle, p_aabb)) {
if (USE_PAIRS) {
Expand All @@ -207,7 +207,7 @@ class BVH_Manager {
void force_collision_check(BVHHandle p_handle) {
if (USE_PAIRS) {
// the aabb should already be up to date in the BVH
AABB aabb;
Bounds aabb;
item_get_AABB(p_handle, aabb);

// add it as changed even if aabb not different
Expand All @@ -221,7 +221,7 @@ class BVH_Manager {
// these should be read as set_visible for render trees,
// but generically this makes items add or remove from the
// tree internally, to speed things up by ignoring inactive items
bool activate(BVHHandle p_handle, const AABB &p_aabb, bool p_delay_collision_check = false) {
bool activate(BVHHandle p_handle, const Bounds &p_aabb, bool p_delay_collision_check = false) {
// sending the aabb here prevents the need for the BVH to maintain
// a redundant copy of the aabb.
// returns success
Expand Down Expand Up @@ -294,7 +294,7 @@ class BVH_Manager {
// 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.
// We cannot depend on waiting for the next update, because that may come much later.
AABB aabb;
Bounds aabb;
item_get_AABB(p_handle, aabb);

// passing false disables the optimization which prevents collision checks if
Expand All @@ -311,7 +311,7 @@ class BVH_Manager {
}

// cull tests
int cull_aabb(const AABB &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) {
typename BVHTREE_CLASS::CullParams params;

params.result_count_overall = 0;
Expand All @@ -328,7 +328,7 @@ class BVH_Manager {
return params.result_count_overall;
}

int cull_segment(const Vector3 &p_from, const Vector3 &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) {
typename BVHTREE_CLASS::CullParams params;

params.result_count_overall = 0;
Expand All @@ -346,7 +346,7 @@ class BVH_Manager {
return params.result_count_overall;
}

int cull_point(const Vector3 &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) {
typename BVHTREE_CLASS::CullParams params;

params.result_count_overall = 0;
Expand Down Expand Up @@ -396,7 +396,7 @@ class BVH_Manager {
return;
}

AABB bb;
Bounds bb;

typename BVHTREE_CLASS::CullParams params;

Expand All @@ -411,8 +411,8 @@ class BVH_Manager {
const BVHHandle &h = changed_items[n];

// use the expanded aabb for pairing
const AABB &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
BVH_ABB abb;
const Bounds &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
BVHABB_CLASS abb;
abb.from(expanded_aabb);

// find all the existing paired aabbs that are no longer
Expand Down Expand Up @@ -457,8 +457,8 @@ class BVH_Manager {
}

public:
void item_get_AABB(BVHHandle p_handle, AABB &r_aabb) {
BVH_ABB abb;
void item_get_AABB(BVHHandle p_handle, Bounds &r_aabb) {
BVHABB_CLASS abb;
tree.item_get_ABB(p_handle, abb);
abb.to(r_aabb);
}
Expand Down Expand Up @@ -494,8 +494,8 @@ class BVH_Manager {
}

// returns true if unpair
bool _find_leavers_process_pair(typename BVHTREE_CLASS::ItemPairs &p_pairs_from, const BVH_ABB &p_abb_from, BVHHandle p_from, BVHHandle p_to, bool p_full_check) {
BVH_ABB abb_to;
bool _find_leavers_process_pair(typename BVHTREE_CLASS::ItemPairs &p_pairs_from, const BVHABB_CLASS &p_abb_from, BVHHandle p_from, BVHHandle p_to, bool p_full_check) {
BVHABB_CLASS abb_to;
tree.item_get_ABB(p_to, abb_to);

// do they overlap?
Expand Down Expand Up @@ -526,10 +526,10 @@ class BVH_Manager {

// find all the existing paired aabbs that are no longer
// paired, and send callbacks
void _find_leavers(BVHHandle p_handle, const BVH_ABB &expanded_abb_from, bool p_full_check) {
void _find_leavers(BVHHandle p_handle, const BVHABB_CLASS &expanded_abb_from, bool p_full_check) {
typename BVHTREE_CLASS::ItemPairs &p_from = tree._pairs[p_handle.id()];

BVH_ABB abb_from = expanded_abb_from;
BVHABB_CLASS abb_from = expanded_abb_from;

// remove from pairing list for every partner
for (unsigned int n = 0; n < p_from.extended_pairs.size(); n++) {
Expand Down Expand Up @@ -608,15 +608,15 @@ class BVH_Manager {
_tick++;
}

void _add_changed_item(BVHHandle p_handle, const AABB &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,
// so all types must be added to the list

// aabb check with expanded aabb. This greatly decreases processing
// at the cost of slightly less accurate pairing checks
// Note this pairing AABB is separate from the AABB in the actual tree
AABB &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
// the aabb hasn't changed. This is needed where set_pairable has been called, but the position
Expand Down
95 changes: 52 additions & 43 deletions core/math/bvh_abb.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define BVH_ABB_H

// special optimized version of axis aligned bounding box
template <class Bounds = AABB, class Point = Vector3>
struct BVH_ABB {
struct ConvexHull {
// convex hulls (optional)
Expand All @@ -42,8 +43,8 @@ struct BVH_ABB {
};

struct Segment {
Vector3 from;
Vector3 to;
Point from;
Point to;
};

enum IntersectResult {
Expand All @@ -53,49 +54,50 @@ struct BVH_ABB {
};

// we store mins with a negative value in order to test them with SIMD
Vector3 min;
Vector3 neg_max;
Point min;
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 (*this == o) == false; }

void set(const Vector3 &_min, const Vector3 &_max) {
void set(const Point &_min, const Point &_max) {
min = _min;
neg_max = -_max;
}

// to and from standard AABB
void from(const AABB &p_aabb) {
void from(const Bounds &p_aabb) {
min = p_aabb.position;
neg_max = -(p_aabb.position + p_aabb.size);
}

void to(AABB &r_aabb) const {
void to(Bounds &r_aabb) const {
r_aabb.position = min;
r_aabb.size = calculate_size();
}

void merge(const BVH_ABB &p_o) {
neg_max.x = MIN(neg_max.x, p_o.neg_max.x);
neg_max.y = MIN(neg_max.y, p_o.neg_max.y);
neg_max.z = MIN(neg_max.z, p_o.neg_max.z);

min.x = MIN(min.x, p_o.min.x);
min.y = MIN(min.y, p_o.min.y);
min.z = MIN(min.z, p_o.min.z);
for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
neg_max[axis] = MIN(neg_max[axis], p_o.neg_max[axis]);
min[axis] = MIN(min[axis], p_o.min[axis]);
}
}

Vector3 calculate_size() const {
Point calculate_size() const {
return -neg_max - min;
}

Vector3 calculate_centre() const {
return Vector3((calculate_size() * 0.5) + min);
Point calculate_centre() const {
return Point((calculate_size() * 0.5) + min);
}

real_t get_proximity_to(const BVH_ABB &p_b) const {
const Vector3 d = (min - neg_max) - (p_b.min - p_b.neg_max);
return (Math::abs(d.x) + Math::abs(d.y) + Math::abs(d.z));
const Point d = (min - neg_max) - (p_b.min - p_b.neg_max);
real_t proximity = 0.0;
for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
proximity += Math::abs(d[axis]);
}
return proximity;
}

int select_by_proximity(const BVH_ABB &p_a, const BVH_ABB &p_b) const {
Expand Down Expand Up @@ -158,7 +160,7 @@ struct BVH_ABB {
}

bool intersects_convex_partial(const ConvexHull &p_hull) const {
AABB bb;
Bounds bb;
to(bb);
return bb.intersects_convex_shape(p_hull.planes, p_hull.num_planes, p_hull.points, p_hull.num_points);
}
Expand All @@ -178,7 +180,7 @@ struct BVH_ABB {

bool is_within_convex(const ConvexHull &p_hull) const {
// use half extents routine
AABB bb;
Bounds bb;
to(bb);
return bb.inside_convex_shape(p_hull.planes, p_hull.num_planes);
}
Expand All @@ -192,59 +194,66 @@ struct BVH_ABB {
}

bool intersects_segment(const Segment &p_s) const {
AABB bb;
Bounds bb;
to(bb);
return bb.intersects_segment(p_s.from, p_s.to);
}

bool intersects_point(const Vector3 &p_pt) const {
if (_vector3_any_lessthan(-p_pt, neg_max)) return false;
if (_vector3_any_lessthan(p_pt, min)) return false;
bool intersects_point(const Point &p_pt) const {
if (_any_lessthan(-p_pt, neg_max)) return false;
if (_any_lessthan(p_pt, min)) return false;
return true;
}

bool intersects(const BVH_ABB &p_o) const {
if (_vector3_any_morethan(p_o.min, -neg_max)) return false;
if (_vector3_any_morethan(min, -p_o.neg_max)) return false;
if (_any_morethan(p_o.min, -neg_max)) return false;
if (_any_morethan(min, -p_o.neg_max)) return false;
return true;
}

bool is_other_within(const BVH_ABB &p_o) const {
if (_vector3_any_lessthan(p_o.neg_max, neg_max)) return false;
if (_vector3_any_lessthan(p_o.min, min)) return false;
if (_any_lessthan(p_o.neg_max, neg_max)) return false;
if (_any_lessthan(p_o.min, min)) return false;
return true;
}

void grow(const Vector3 &p_change) {
void grow(const Point &p_change) {
neg_max -= p_change;
min -= p_change;
}

void expand(real_t p_change) {
grow(Vector3(p_change, p_change, p_change));
Point change;
change.set_all(p_change);
grow(change);
}

float get_area() const // actually surface area metric
{
Vector3 d = calculate_size();
// Actually surface area metric.
float get_area() const {
Point d = calculate_size();
return 2.0f * (d.x * d.y + d.y * d.z + d.z * d.x);
}

void set_to_max_opposite_extents() {
neg_max = Vector3(FLT_MAX, FLT_MAX, FLT_MAX);
neg_max.set_all(FLT_MAX);
min = neg_max;
}

bool _vector3_any_morethan(const Vector3 &p_a, const Vector3 &p_b) const {
if (p_a.x > p_b.x) return true;
if (p_a.y > p_b.y) return true;
if (p_a.z > p_b.z) return true;
bool _any_morethan(const Point &p_a, const Point &p_b) const {
for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
if (p_a[axis] > p_b[axis]) {
return true;
}
}
return false;
}

bool _vector3_any_lessthan(const Vector3 &p_a, const Vector3 &p_b) const {
if (p_a.x < p_b.x) return true;
if (p_a.y < p_b.y) return true;
if (p_a.z < p_b.z) return true;
bool _any_lessthan(const Point &p_a, const Point &p_b) const {
for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
if (p_a[axis] < p_b[axis]) {
return true;
}
}
return false;
}
};
Expand Down
Loading

0 comments on commit d8f6810

Please sign in to comment.