Selaa lähdekoodia

Merge pull request #45576 from lawnjelly/bvh_visibility

BVH add support for visibility (activation)
Rémi Verschelde 4 vuotta sitten
vanhempi
commit
a37095dd5e

+ 83 - 20
core/math/bvh.h

@@ -88,7 +88,7 @@ public:
 		unpair_callback_userdata = p_userdata;
 		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
 		// 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.
 		// of waiting for update, so only uncomment this if there are bugs.
@@ -104,7 +104,7 @@ public:
 		}
 		}
 #endif
 #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) {
 		if (USE_PAIRS) {
 			// for safety initialize the expanded AABB
 			// for safety initialize the expanded AABB
@@ -113,9 +113,10 @@ public:
 			expanded_aabb.grow_by(tree._pairing_expansion);
 			expanded_aabb.grow_by(tree._pairing_expansion);
 
 
 			// force a collision check no matter the AABB
 			// 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;
 		return h;
@@ -136,6 +137,18 @@ public:
 		erase(h);
 		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) {
 	void set_pairable(uint32_t p_handle, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask) {
 		BVHHandle h;
 		BVHHandle h;
 		h.set(p_handle);
 		h.set(p_handle);
@@ -182,6 +195,54 @@ public:
 		_check_for_collisions(true);
 		_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)
 	// call e.g. once per frame (this does a trickle optimize)
 	void update() {
 	void update() {
 		tree.update();
 		tree.update();
@@ -206,21 +267,23 @@ public:
 			// of waiting for update, so only uncomment this if there are bugs.
 			// of waiting for update, so only uncomment this if there are bugs.
 			//_check_for_collisions();
 			//_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);
-
-			// 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);
+			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);
+
+				// 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
 		}
 		}
 	}
 	}
 
 

+ 4 - 0
core/math/bvh_logic.inc

@@ -5,6 +5,10 @@ void _logic_item_remove_and_reinsert(uint32_t p_ref_id) {
 	// get the reference
 	// get the reference
 	ItemRef &ref = _refs[p_ref_id];
 	ItemRef &ref = _refs[p_ref_id];
 
 
+	// no need to optimize inactive items
+	if (!ref.is_active())
+		return;
+
 	// special case of debug draw
 	// special case of debug draw
 	if (ref.item_id == BVHCommon::INVALID)
 	if (ref.item_id == BVHCommon::INVALID)
 		return;
 		return;

+ 87 - 15
core/math/bvh_public.inc

@@ -1,5 +1,5 @@
 public:
 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
 #ifdef BVH_VERBOSE_TREE
 	VERBOSE_PRINT("\nitem_add BEFORE");
 	VERBOSE_PRINT("\nitem_add BEFORE");
 	_debug_recursive_print_tree(0);
 	_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);
 	create_root_node(_current_tree);
 
 
 	// we must choose where to add to 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
 #ifdef BVH_VERBOSE
@@ -100,11 +104,14 @@ void _debug_print_refs() {
 bool item_move(BVHHandle p_handle, const AABB &p_aabb) {
 bool item_move(BVHHandle p_handle, const AABB &p_aabb) {
 	uint32_t ref_id = p_handle.id();
 	uint32_t ref_id = p_handle.id();
 
 
-	BVH_ABB abb;
-	abb.from(p_aabb);
-
 	// get the reference
 	// get the reference
 	ItemRef &ref = _refs[ref_id];
 	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);
 	BVH_ASSERT(ref.tnode_id != BVHCommon::INVALID);
 	TNode &tnode = _nodes[ref.tnode_id];
 	TNode &tnode = _nodes[ref.tnode_id];
@@ -117,7 +124,14 @@ bool item_move(BVHHandle p_handle, const AABB &p_aabb) {
 		// for accurate collision detection
 		// for accurate collision detection
 		TLeaf &leaf = _node_get_leaf(tnode);
 		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();
 		_integrity_check_all();
 
 
 		return true;
 		return true;
@@ -168,8 +182,10 @@ void item_remove(BVHHandle p_handle) {
 	_extra[ref_id_moved_back].active_ref_id = active_ref_id;
 	_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
 	// remove the item reference
 	_refs.free(ref_id);
 	_refs.free(ref_id);
@@ -186,6 +202,54 @@ void item_remove(BVHHandle p_handle) {
 #endif
 #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
 // 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 {
 void item_fill_cullparams(BVHHandle p_handle, CullParams &r_params) const {
 	uint32_t ref_id = p_handle.id();
 	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_type = p_pairable_type;
 	ex.pairable_mask = p_pairable_mask;
 	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
 		// record abb
 		TNode &tnode = _nodes[ref.tnode_id];
 		TNode &tnode = _nodes[ref.tnode_id];
 		TLeaf &leaf = _node_get_leaf(tnode);
 		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
 		// remove from old tree
 		node_remove_item(ref_id);
 		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;
 		ex.pairable = p_pairable;
 
 
 		// add to new tree
 		// 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)
 			if (add_node.parent_id != BVHCommon::INVALID)
 				refit_upward_and_balance(add_node.parent_id);
 				refit_upward_and_balance(add_node.parent_id);
 		}
 		}
+	} else {
+		// always keep this up to date
+		ex.pairable = p_pairable;
 	}
 	}
 }
 }
 
 

+ 6 - 0
core/math/bvh_structs.inc

@@ -3,6 +3,12 @@ public:
 struct ItemRef {
 struct ItemRef {
 	uint32_t tnode_id; // -1 is invalid
 	uint32_t tnode_id; // -1 is invalid
 	uint32_t item_id; // in the leaf
 	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,
 // extra info kept in separate parallel list to the references,

+ 4 - 0
core/math/bvh_tree.h

@@ -73,7 +73,11 @@
 
 
 // really just a namespace
 // really just a namespace
 struct BVHCommon {
 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 INVALID = (0xffffffff);
+	static const uint32_t INACTIVE = (0xfffffffe);
 };
 };
 
 
 // really a handle, can be anything
 // really a handle, can be anything

+ 1 - 1
servers/physics/broad_phase_bvh.cpp

@@ -34,7 +34,7 @@
 
 
 BroadPhaseSW::ID BroadPhaseBVH::create(CollisionObjectSW *p_object, int p_subindex, const AABB &p_aabb) {
 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;
 	return oid + 1;
 }
 }
 
 

+ 27 - 1
servers/visual/visual_server_scene.cpp

@@ -105,7 +105,12 @@ void VisualServerScene::camera_set_use_vertical_aspect(RID p_camera, bool p_enab
 
 
 /* SPATIAL PARTITIONING */
 /* 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) {
 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) {
 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);
 	_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() {
 void VisualServerScene::SpatialPartitioningScene_BVH::update() {
 	_bvh.update();
 	_bvh.update();
 }
 }
@@ -764,6 +780,16 @@ void VisualServerScene::instance_set_visible(RID p_instance, bool p_visible) {
 
 
 	instance->visible = 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
 	// 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) {
 	if ((1 << instance->base_type) & VS::INSTANCE_GEOMETRY_MASK) {
 		InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data);
 		InstanceGeometryData *geom = static_cast<InstanceGeometryData *>(instance->base_data);

+ 4 - 0
servers/visual/visual_server_scene.h

@@ -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 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 erase(SpatialPartitionID p_handle) = 0;
 		virtual void move(SpatialPartitionID p_handle, const AABB &p_aabb) = 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() {}
 		virtual void update_collisions() {}
 		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;
 		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);
 		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 erase(SpatialPartitionID p_handle);
 		void move(SpatialPartitionID p_handle, const AABB &p_aabb);
 		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();
 		void update_collisions();
 		void update_collisions();
 		void set_pairable(SpatialPartitionID p_handle, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask);
 		void set_pairable(SpatialPartitionID p_handle, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask);