diff options
Diffstat (limited to 'core/math/dynamic_bvh.h')
-rw-r--r-- | core/math/dynamic_bvh.h | 125 |
1 files changed, 116 insertions, 9 deletions
diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h index 81d70d7469..cdea51c674 100644 --- a/core/math/dynamic_bvh.h +++ b/core/math/dynamic_bvh.h @@ -61,13 +61,10 @@ class DynamicBVH { public: struct ID { - Node *node; + Node *node = nullptr; public: _FORCE_INLINE_ bool is_valid() const { return node != nullptr; } - _FORCE_INLINE_ ID() { - node = nullptr; - } }; private: @@ -134,6 +131,48 @@ private: (min.z <= b.max.z) && (max.z >= b.min.z)); } + + _FORCE_INLINE_ bool intersects_convex(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const { + Vector3 half_extents = (max - min) * 0.5; + Vector3 ofs = min + half_extents; + + for (int i = 0; i < p_plane_count; i++) { + const Plane &p = p_planes[i]; + Vector3 point( + (p.normal.x > 0) ? -half_extents.x : half_extents.x, + (p.normal.y > 0) ? -half_extents.y : half_extents.y, + (p.normal.z > 0) ? -half_extents.z : half_extents.z); + point += ofs; + if (p.is_point_over(point)) { + return false; + } + } + + // Make sure all points in the shape aren't fully separated from the AABB on + // each axis. + int bad_point_counts_positive[3] = { 0 }; + int bad_point_counts_negative[3] = { 0 }; + + for (int k = 0; k < 3; k++) { + for (int i = 0; i < p_point_count; i++) { + if (p_points[i].coord[k] > ofs.coord[k] + half_extents.coord[k]) { + bad_point_counts_positive[k]++; + } + if (p_points[i].coord[k] < ofs.coord[k] - half_extents.coord[k]) { + bad_point_counts_negative[k]++; + } + } + + if (bad_point_counts_negative[k] == p_point_count) { + return false; + } + if (bad_point_counts_positive[k] == p_point_count) { + return false; + } + } + + return true; + } }; struct Node { @@ -144,14 +183,14 @@ private: void *data; }; - _FORCE_INLINE_ bool is_leaf() const { return data != nullptr; } + _FORCE_INLINE_ bool is_leaf() const { return childs[1] == nullptr; } _FORCE_INLINE_ bool is_internal() const { return (!is_leaf()); } _FORCE_INLINE_ int get_index_in_parent() const { ERR_FAIL_COND_V(!parent, 0); return (parent->childs[1] == this) ? 1 : 0; } - _FORCE_INLINE_ void get_max_depth(int depth, int &maxdepth) { + void get_max_depth(int depth, int &maxdepth) { if (is_internal()) { childs[0]->get_max_depth(depth + 1, maxdepth); childs[1]->get_max_depth(depth + 1, maxdepth); @@ -183,6 +222,7 @@ private: int lkhd = -1; int total_leaves = 0; uint32_t opath = 0; + uint32_t index = 0; enum { ALLOCA_STACK_SIZE = 128 @@ -245,6 +285,9 @@ public: void remove(const ID &p_id); void get_elements(List<ID> *r_elements); + int get_leaf_count() const; + int get_max_depth() const; + /* Discouraged, but works as a reference on how it must be used */ struct DefaultQueryResult { virtual bool operator()(void *p_data) = 0; //return true whether you want to continue the query @@ -254,9 +297,13 @@ public: template <class QueryResult> _FORCE_INLINE_ void aabb_query(const AABB &p_aabb, QueryResult &r_result); template <class QueryResult> + _FORCE_INLINE_ void convex_query(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, QueryResult &r_result); + template <class QueryResult> _FORCE_INLINE_ void ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResult &r_result); - DynamicBVH(); + void set_index(uint32_t p_index); + uint32_t get_index() const; + ~DynamicBVH(); }; @@ -278,8 +325,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { LocalVector<const Node *> aux_stack; //only used in rare occasions when you run out of alloca memory because tree is too unbalanced. Should correct itself over time. do { - const Node *n = stack[depth - 1]; depth--; + const Node *n = stack[depth]; if (n->volume.intersects(volume)) { if (n->is_internal()) { if (depth > threshold) { @@ -304,7 +351,66 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { } template <class QueryResult> +void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, QueryResult &r_result) { + if (!bvh_root) { + return; + } + + //generate a volume anyway to improve pre-testing + Volume volume; + for (int i = 0; i < p_point_count; i++) { + if (i == 0) { + volume.min = p_points[0]; + volume.max = p_points[0]; + } else { + volume.min.x = MIN(volume.min.x, p_points[i].x); + volume.min.y = MIN(volume.min.y, p_points[i].y); + volume.min.z = MIN(volume.min.z, p_points[i].z); + + volume.max.x = MAX(volume.max.x, p_points[i].x); + volume.max.y = MAX(volume.max.y, p_points[i].y); + volume.max.z = MAX(volume.max.z, p_points[i].z); + } + } + + const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + stack[0] = bvh_root; + int32_t depth = 1; + int32_t threshold = ALLOCA_STACK_SIZE - 2; + + LocalVector<const Node *> aux_stack; //only used in rare occasions when you run out of alloca memory because tree is too unbalanced. Should correct itself over time. + + do { + depth--; + const Node *n = stack[depth]; + if (n->volume.intersects(volume) && n->volume.intersects_convex(p_planes, p_plane_count, p_points, p_point_count)) { + if (n->is_internal()) { + if (depth > threshold) { + if (aux_stack.empty()) { + aux_stack.resize(ALLOCA_STACK_SIZE * 2); + copymem(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + } else { + aux_stack.resize(aux_stack.size() * 2); + } + stack = aux_stack.ptr(); + threshold = aux_stack.size() - 2; + } + stack[depth++] = n->childs[0]; + stack[depth++] = n->childs[1]; + } else { + if (r_result(n->data)) { + return; + } + } + } + } while (depth > 0); +} +template <class QueryResult> void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResult &r_result) { + if (!bvh_root) { + return; + } + Vector3 ray_dir = (p_to - p_from); ray_dir.normalize(); @@ -327,7 +433,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu LocalVector<const Node *> aux_stack; //only used in rare occasions when you run out of alloca memory because tree is too unbalanced. Should correct itself over time. do { - const Node *node = stack[--depth]; + depth--; + const Node *node = stack[depth]; bounds[0] = node->volume.min; bounds[1] = node->volume.max; real_t tmin = 1.f, lambda_min = 0.f; |