Merge pull request #45576 from lawnjelly/bvh_visibility
BVH add support for visibility (activation)
This commit is contained in:
commit
a37095dd5e
8 changed files with 214 additions and 35 deletions
|
@ -88,7 +88,7 @@ public:
|
|||
unpair_callback_userdata = p_userdata;
|
||||
}
|
||||
|
||||
BVHHandle create(T *p_userdata, 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 AABB &p_aabb = AABB(), 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.
|
||||
|
@ -104,7 +104,7 @@ public:
|
|||
}
|
||||
#endif
|
||||
|
||||
BVHHandle h = tree.item_add(p_userdata, p_aabb, p_subindex, p_pairable, p_pairable_type, p_pairable_mask);
|
||||
BVHHandle h = tree.item_add(p_userdata, p_active, p_aabb, p_subindex, p_pairable, p_pairable_type, p_pairable_mask);
|
||||
|
||||
if (USE_PAIRS) {
|
||||
// for safety initialize the expanded AABB
|
||||
|
@ -113,9 +113,10 @@ public:
|
|||
expanded_aabb.grow_by(tree._pairing_expansion);
|
||||
|
||||
// force a collision check no matter the AABB
|
||||
_add_changed_item(h, p_aabb, false);
|
||||
|
||||
_check_for_collisions(true);
|
||||
if (p_active) {
|
||||
_add_changed_item(h, p_aabb, false);
|
||||
_check_for_collisions(true);
|
||||
}
|
||||
}
|
||||
|
||||
return h;
|
||||
|
@ -136,6 +137,18 @@ public:
|
|||
erase(h);
|
||||
}
|
||||
|
||||
bool activate(uint32_t p_handle, const AABB &p_aabb, bool p_delay_collision_check = false) {
|
||||
BVHHandle h;
|
||||
h.set(p_handle);
|
||||
return activate(h, p_aabb, p_delay_collision_check);
|
||||
}
|
||||
|
||||
bool deactivate(uint32_t p_handle) {
|
||||
BVHHandle h;
|
||||
h.set(p_handle);
|
||||
return deactivate(h);
|
||||
}
|
||||
|
||||
void set_pairable(uint32_t p_handle, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask) {
|
||||
BVHHandle h;
|
||||
h.set(p_handle);
|
||||
|
@ -182,6 +195,54 @@ public:
|
|||
_check_for_collisions(true);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// sending the aabb here prevents the need for the BVH to maintain
|
||||
// a redundant copy of the aabb.
|
||||
// returns success
|
||||
if (tree.item_activate(p_handle, p_aabb)) {
|
||||
if (USE_PAIRS) {
|
||||
|
||||
// in the special case of the render tree, when setting visibility we are using the combination of
|
||||
// activate then set_pairable. This would case 2 sets of collision checks. For efficiency here we allow
|
||||
// deferring to have a single collision check at the set_pairable call.
|
||||
// Watch for bugs! This may cause bugs if set_pairable is not called.
|
||||
if (!p_delay_collision_check) {
|
||||
_add_changed_item(p_handle, p_aabb, false);
|
||||
|
||||
// force an immediate collision check, much like calls to set_pairable
|
||||
_check_for_collisions(true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool deactivate(BVHHandle p_handle) {
|
||||
// returns success
|
||||
if (tree.item_deactivate(p_handle)) {
|
||||
// call unpair and remove all references to the item
|
||||
// before deleting from the tree
|
||||
if (USE_PAIRS) {
|
||||
_remove_changed_item(p_handle);
|
||||
|
||||
// force check for collisions, much like an erase was called
|
||||
_check_for_collisions(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get_active(BVHHandle p_handle) const {
|
||||
return tree.item_get_active(p_handle);
|
||||
}
|
||||
|
||||
// call e.g. once per frame (this does a trickle optimize)
|
||||
void update() {
|
||||
tree.update();
|
||||
|
@ -206,21 +267,23 @@ public:
|
|||
// of waiting for update, so only uncomment this if there are bugs.
|
||||
//_check_for_collisions();
|
||||
|
||||
// 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;
|
||||
item_get_AABB(p_handle, aabb);
|
||||
if (get_active(p_handle)) {
|
||||
// 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;
|
||||
item_get_AABB(p_handle, aabb);
|
||||
|
||||
// passing false disables the optimization which prevents collision checks if
|
||||
// the aabb hasn't changed
|
||||
_add_changed_item(p_handle, aabb, false);
|
||||
// passing false disables the optimization which prevents collision checks if
|
||||
// the aabb hasn't changed
|
||||
_add_changed_item(p_handle, aabb, false);
|
||||
|
||||
// force an immediate collision check (probably just for this one item)
|
||||
// but it must be a FULL collision check, also checking pairable state and masks.
|
||||
// This is because AABB intersecting objects may have changed pairable state / mask
|
||||
// such that they should no longer be paired. E.g. lights.
|
||||
_check_for_collisions(true);
|
||||
// force an immediate collision check (probably just for this one item)
|
||||
// but it must be a FULL collision check, also checking pairable state and masks.
|
||||
// This is because AABB intersecting objects may have changed pairable state / mask
|
||||
// such that they should no longer be paired. E.g. lights.
|
||||
_check_for_collisions(true);
|
||||
} // only if active
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,10 @@ void _logic_item_remove_and_reinsert(uint32_t p_ref_id) {
|
|||
// get the reference
|
||||
ItemRef &ref = _refs[p_ref_id];
|
||||
|
||||
// no need to optimize inactive items
|
||||
if (!ref.is_active())
|
||||
return;
|
||||
|
||||
// special case of debug draw
|
||||
if (ref.item_id == BVHCommon::INVALID)
|
||||
return;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
public:
|
||||
BVHHandle item_add(T *p_userdata, const AABB &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 AABB &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
|
||||
VERBOSE_PRINT("\nitem_add BEFORE");
|
||||
_debug_recursive_print_tree(0);
|
||||
|
@ -60,15 +60,19 @@ BVHHandle item_add(T *p_userdata, const AABB &p_aabb, int32_t p_subindex, bool p
|
|||
create_root_node(_current_tree);
|
||||
|
||||
// we must choose where to add to tree
|
||||
ref->tnode_id = _logic_choose_item_add_node(_root_node_id[_current_tree], abb);
|
||||
if (p_active) {
|
||||
ref->tnode_id = _logic_choose_item_add_node(_root_node_id[_current_tree], abb);
|
||||
|
||||
bool refit = _node_add_item(ref->tnode_id, ref_id, abb);
|
||||
bool refit = _node_add_item(ref->tnode_id, ref_id, abb);
|
||||
|
||||
if (refit) {
|
||||
// only need to refit from the parent
|
||||
const TNode &add_node = _nodes[ref->tnode_id];
|
||||
if (add_node.parent_id != BVHCommon::INVALID)
|
||||
refit_upward_and_balance(add_node.parent_id);
|
||||
if (refit) {
|
||||
// only need to refit from the parent
|
||||
const TNode &add_node = _nodes[ref->tnode_id];
|
||||
if (add_node.parent_id != BVHCommon::INVALID)
|
||||
refit_upward_and_balance(add_node.parent_id);
|
||||
}
|
||||
} else {
|
||||
ref->set_inactive();
|
||||
}
|
||||
|
||||
#ifdef BVH_VERBOSE
|
||||
|
@ -100,11 +104,14 @@ void _debug_print_refs() {
|
|||
bool item_move(BVHHandle p_handle, const AABB &p_aabb) {
|
||||
uint32_t ref_id = p_handle.id();
|
||||
|
||||
BVH_ABB abb;
|
||||
abb.from(p_aabb);
|
||||
|
||||
// get the reference
|
||||
ItemRef &ref = _refs[ref_id];
|
||||
if (!ref.is_active()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BVH_ABB abb;
|
||||
abb.from(p_aabb);
|
||||
|
||||
BVH_ASSERT(ref.tnode_id != BVHCommon::INVALID);
|
||||
TNode &tnode = _nodes[ref.tnode_id];
|
||||
|
@ -117,7 +124,14 @@ bool item_move(BVHHandle p_handle, const AABB &p_aabb) {
|
|||
// for accurate collision detection
|
||||
TLeaf &leaf = _node_get_leaf(tnode);
|
||||
|
||||
leaf.get_aabb(ref.item_id) = abb;
|
||||
BVH_ABB &leaf_abb = leaf.get_aabb(ref.item_id);
|
||||
|
||||
// no change?
|
||||
if (leaf_abb == abb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
leaf_abb = abb;
|
||||
_integrity_check_all();
|
||||
|
||||
return true;
|
||||
|
@ -168,8 +182,10 @@ void item_remove(BVHHandle p_handle) {
|
|||
_extra[ref_id_moved_back].active_ref_id = active_ref_id;
|
||||
////////////////////////////////////////
|
||||
|
||||
// remove the item from the node
|
||||
node_remove_item(ref_id);
|
||||
// remove the item from the node (only if active)
|
||||
if (_refs[ref_id].is_active()) {
|
||||
node_remove_item(ref_id);
|
||||
}
|
||||
|
||||
// remove the item reference
|
||||
_refs.free(ref_id);
|
||||
|
@ -186,6 +202,54 @@ void item_remove(BVHHandle p_handle) {
|
|||
#endif
|
||||
}
|
||||
|
||||
// returns success
|
||||
bool item_activate(BVHHandle p_handle, const AABB &p_aabb) {
|
||||
uint32_t ref_id = p_handle.id();
|
||||
ItemRef &ref = _refs[ref_id];
|
||||
if (ref.is_active()) {
|
||||
// noop
|
||||
return false;
|
||||
}
|
||||
|
||||
// add to tree
|
||||
BVH_ABB abb;
|
||||
abb.from(p_aabb);
|
||||
|
||||
_current_tree = _handle_get_tree_id(p_handle);
|
||||
|
||||
// we must choose where to add to tree
|
||||
ref.tnode_id = _logic_choose_item_add_node(_root_node_id[_current_tree], abb);
|
||||
_node_add_item(ref.tnode_id, ref_id, abb);
|
||||
|
||||
refit_upward_and_balance(ref.tnode_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// returns success
|
||||
bool item_deactivate(BVHHandle p_handle) {
|
||||
uint32_t ref_id = p_handle.id();
|
||||
ItemRef &ref = _refs[ref_id];
|
||||
if (!ref.is_active()) {
|
||||
// noop
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove from tree
|
||||
BVH_ABB abb;
|
||||
node_remove_item(ref_id, &abb);
|
||||
|
||||
// mark as inactive
|
||||
ref.set_inactive();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool item_get_active(BVHHandle p_handle) const {
|
||||
uint32_t ref_id = p_handle.id();
|
||||
const ItemRef &ref = _refs[ref_id];
|
||||
return ref.is_active();
|
||||
}
|
||||
|
||||
// during collision testing, we want to set the mask and whether pairable for the item testing from
|
||||
void item_fill_cullparams(BVHHandle p_handle, CullParams &r_params) const {
|
||||
uint32_t ref_id = p_handle.id();
|
||||
|
@ -226,7 +290,10 @@ void item_set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pa
|
|||
ex.pairable_type = p_pairable_type;
|
||||
ex.pairable_mask = p_pairable_mask;
|
||||
|
||||
if ((ex.pairable != 0) != p_pairable) {
|
||||
bool active = ref.is_active();
|
||||
bool pairable_changed = (ex.pairable != 0) != p_pairable;
|
||||
|
||||
if (active && pairable_changed) {
|
||||
// record abb
|
||||
TNode &tnode = _nodes[ref.tnode_id];
|
||||
TLeaf &leaf = _node_get_leaf(tnode);
|
||||
|
@ -238,6 +305,8 @@ void item_set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pa
|
|||
// remove from old tree
|
||||
node_remove_item(ref_id);
|
||||
|
||||
// we must set the pairable AFTER getting the current tree
|
||||
// because the pairable status determines which tree
|
||||
ex.pairable = p_pairable;
|
||||
|
||||
// add to new tree
|
||||
|
@ -255,6 +324,9 @@ void item_set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pa
|
|||
if (add_node.parent_id != BVHCommon::INVALID)
|
||||
refit_upward_and_balance(add_node.parent_id);
|
||||
}
|
||||
} else {
|
||||
// always keep this up to date
|
||||
ex.pairable = p_pairable;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,12 @@ public:
|
|||
struct ItemRef {
|
||||
uint32_t tnode_id; // -1 is invalid
|
||||
uint32_t item_id; // in the leaf
|
||||
|
||||
bool is_active() const { return tnode_id != BVHCommon::INACTIVE; }
|
||||
void set_inactive() {
|
||||
tnode_id = BVHCommon::INACTIVE;
|
||||
item_id = BVHCommon::INACTIVE;
|
||||
}
|
||||
};
|
||||
|
||||
// extra info kept in separate parallel list to the references,
|
||||
|
|
|
@ -73,7 +73,11 @@
|
|||
|
||||
// really just a namespace
|
||||
struct BVHCommon {
|
||||
// these could possibly also be the same constant,
|
||||
// although this may be useful for debugging.
|
||||
// or use zero for invalid and +1 based indices.
|
||||
static const uint32_t INVALID = (0xffffffff);
|
||||
static const uint32_t INACTIVE = (0xfffffffe);
|
||||
};
|
||||
|
||||
// really a handle, can be anything
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
BroadPhaseSW::ID BroadPhaseBVH::create(CollisionObjectSW *p_object, int p_subindex, const AABB &p_aabb) {
|
||||
|
||||
ID oid = bvh.create(p_object, p_aabb, p_subindex, false, 1 << p_object->get_type(), 0);
|
||||
ID oid = bvh.create(p_object, true, p_aabb, p_subindex, false, 1 << p_object->get_type(), 0);
|
||||
return oid + 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,12 @@ void VisualServerScene::camera_set_use_vertical_aspect(RID p_camera, bool p_enab
|
|||
|
||||
/* SPATIAL PARTITIONING */
|
||||
VisualServerScene::SpatialPartitionID VisualServerScene::SpatialPartitioningScene_BVH::create(Instance *p_userdata, const AABB &p_aabb, int p_subindex, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask) {
|
||||
return _bvh.create(p_userdata, p_aabb, p_subindex, p_pairable, p_pairable_type, p_pairable_mask) + 1;
|
||||
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
|
||||
// we are relying on this instance to be valid in order to pass
|
||||
// the visible flag to the bvh.
|
||||
CRASH_COND(!p_userdata);
|
||||
#endif
|
||||
return _bvh.create(p_userdata, p_userdata->visible, p_aabb, p_subindex, p_pairable, p_pairable_type, p_pairable_mask) + 1;
|
||||
}
|
||||
|
||||
void VisualServerScene::SpatialPartitioningScene_BVH::erase(SpatialPartitionID p_handle) {
|
||||
|
@ -116,6 +121,17 @@ void VisualServerScene::SpatialPartitioningScene_BVH::move(SpatialPartitionID p_
|
|||
_bvh.move(p_handle - 1, p_aabb);
|
||||
}
|
||||
|
||||
void VisualServerScene::SpatialPartitioningScene_BVH::activate(SpatialPartitionID p_handle, const AABB &p_aabb) {
|
||||
// be very careful here, we are deferring the collision check, expecting a set_pairable to be called
|
||||
// immediately after.
|
||||
// see the notes in the BVH function.
|
||||
_bvh.activate(p_handle - 1, p_aabb, true);
|
||||
}
|
||||
|
||||
void VisualServerScene::SpatialPartitioningScene_BVH::deactivate(SpatialPartitionID p_handle) {
|
||||
_bvh.deactivate(p_handle - 1);
|
||||
}
|
||||
|
||||
void VisualServerScene::SpatialPartitioningScene_BVH::update() {
|
||||
_bvh.update();
|
||||
}
|
||||
|
@ -764,6 +780,16 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) {
|
|||
|
||||
instance->visible = p_visible;
|
||||
|
||||
// give the opportunity for the spatial paritioning scene to use a special implementation of visibility
|
||||
// for efficiency (supported in BVH but not octree)
|
||||
if (instance->spatial_partition_id) {
|
||||
if (p_visible) {
|
||||
instance->scenario->sps->activate(instance->spatial_partition_id, instance->transformed_aabb);
|
||||
} else {
|
||||
instance->scenario->sps->deactivate(instance->spatial_partition_id);
|
||||
}
|
||||
}
|
||||
|
||||
// when showing or hiding geometry, lights must be kept up to date to show / hide shadows
|
||||
if ((1 << instance->base_type) & VS::INSTANCE_GEOMETRY_MASK) {
|
||||
InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data);
|
||||
|
|
|
@ -117,6 +117,8 @@ public:
|
|||
virtual SpatialPartitionID create(Instance *p_userdata, const AABB &p_aabb = AABB(), int p_subindex = 0, bool p_pairable = false, uint32_t p_pairable_type = 0, uint32_t pairable_mask = 1) = 0;
|
||||
virtual void erase(SpatialPartitionID p_handle) = 0;
|
||||
virtual void move(SpatialPartitionID p_handle, const AABB &p_aabb) = 0;
|
||||
virtual void activate(SpatialPartitionID p_handle, const AABB &p_aabb) {}
|
||||
virtual void deactivate(SpatialPartitionID p_handle) {}
|
||||
virtual void update() {}
|
||||
virtual void update_collisions() {}
|
||||
virtual void set_pairable(SpatialPartitionID p_handle, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask) = 0;
|
||||
|
@ -164,6 +166,8 @@ public:
|
|||
SpatialPartitionID create(Instance *p_userdata, 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);
|
||||
void erase(SpatialPartitionID p_handle);
|
||||
void move(SpatialPartitionID p_handle, const AABB &p_aabb);
|
||||
void activate(SpatialPartitionID p_handle, const AABB &p_aabb);
|
||||
void deactivate(SpatialPartitionID p_handle);
|
||||
void update();
|
||||
void update_collisions();
|
||||
void set_pairable(SpatialPartitionID p_handle, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask);
|
||||
|
|
Loading…
Reference in a new issue