Bläddra i källkod

BVH - add option for expanded AABBs in leaves

This PR adds a define BVH_EXPAND_LEAF_AABBS which is set, which stores expanded AABBs in the tree instead of exact AABBs.

This makes the logic less error prone when considering reciprocal collisions in the pairing, as all collision detect is now taking place between expanded AABB against expanded AABB, rather than expanded AABB against exact AABB.

The flip side of this is that the intersection tests will now be less exact when expanded margins are set.

All margins are now user customizable via project settings, and take account of collision pairing density to adjust the margin dynamically.

(cherry picked from commit 211dc8cd2d64a9472c1b4cdb53b11535e1d53be1)
lawnjelly 3 år sedan
förälder
incheckning
07e5022cce

+ 6 - 0
core/math/bvh.h

@@ -704,6 +704,11 @@ private:
 		// 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
 
 
+#ifdef BVH_EXPAND_LEAF_AABBS
+		// if using expanded AABB in the leaf, the redundancy check will already have been made
+		BOUNDS &expanded_aabb = tree._pairs[p_handle.id()].expanded_aabb;
+		item_get_AABB(p_handle, expanded_aabb);
+#else
 		// 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
@@ -720,6 +725,7 @@ private:
 		// this tick, because it is vital that the AABB is kept up to date
 		// this tick, because it is vital that the AABB is kept up to date
 		expanded_aabb = aabb;
 		expanded_aabb = aabb;
 		expanded_aabb.grow_by(tree._pairing_expansion);
 		expanded_aabb.grow_by(tree._pairing_expansion);
+#endif
 
 
 		// this code is to ensure that changed items only appear once on the updated list
 		// this code is to ensure that changed items only appear once on the updated list
 		// collision checking them multiple times is not needed, and repeats the same thing
 		// collision checking them multiple times is not needed, and repeats the same thing

+ 10 - 0
core/math/bvh_pair.inc

@@ -59,4 +59,14 @@ struct ItemPairs {
 
 
 		return userdata;
 		return userdata;
 	}
 	}
+
+	// experiment : scale the pairing expansion by the number of pairs.
+	// when the number of pairs is high, the density is high and a lower collision margin is better.
+	// when there are few local pairs, a larger margin is more optimal.
+	real_t scale_expansion_margin(real_t p_margin) const {
+		real_t x = real_t(num_pairs) * (1.0 / 9.0);
+		x = MIN(x, 1.0);
+		x = 1.0 - x;
+		return p_margin * x;
+	}
 };
 };

+ 27 - 0
core/math/bvh_public.inc

@@ -9,6 +9,13 @@ BVHHandle item_add(T *p_userdata, bool p_active, const BOUNDS &p_aabb, int32_t p
 	BVHABB_CLASS abb;
 	BVHABB_CLASS abb;
 	abb.from(p_aabb);
 	abb.from(p_aabb);
 
 
+	// NOTE that we do not expand the AABB for the first create even if
+	// leaf expansion is switched on. This is for two reasons:
+	// (1) We don't know if this object will move in future, in which case a non-expanded
+	// bound would be better...
+	// (2) We don't yet know how many objects will be paired, which is used to modify
+	// the expansion margin.
+
 	// handle to be filled with the new item ref
 	// handle to be filled with the new item ref
 	BVHHandle handle;
 	BVHHandle handle;
 
 
@@ -115,6 +122,15 @@ bool item_move(BVHHandle p_handle, const BOUNDS &p_aabb) {
 	BVHABB_CLASS abb;
 	BVHABB_CLASS abb;
 	abb.from(p_aabb);
 	abb.from(p_aabb);
 
 
+#ifdef BVH_EXPAND_LEAF_AABBS
+	if (USE_PAIRS) {
+		// scale the pairing expansion by the number of pairs.
+		abb.expand(_pairs[ref_id].scale_expansion_margin(_pairing_expansion));
+	} else {
+		abb.expand(_pairing_expansion);
+	}
+#endif
+
 	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];
 
 
@@ -129,9 +145,20 @@ bool item_move(BVHHandle p_handle, const BOUNDS &p_aabb) {
 		BVHABB_CLASS &leaf_abb = leaf.get_aabb(ref.item_id);
 		BVHABB_CLASS &leaf_abb = leaf.get_aabb(ref.item_id);
 
 
 		// no change?
 		// no change?
+#ifdef BVH_EXPAND_LEAF_AABBS
+		BOUNDS leaf_aabb;
+		leaf_abb.to(leaf_aabb);
+
+		// This test should pass in a lot of cases, and by returning false we can avoid
+		// collision pairing checks later, which greatly reduces processing.
+		if (expanded_aabb_encloses_not_shrink(leaf_aabb, p_aabb)) {
+			return false;
+		}
+#else
 		if (leaf_abb == abb) {
 		if (leaf_abb == abb) {
 			return false;
 			return false;
 		}
 		}
+#endif
 
 
 #ifdef BVH_VERBOSE_MOVES
 #ifdef BVH_VERBOSE_MOVES
 		print_line("item_move " + itos(p_handle.id()) + "(within tnode aabb) : " + _debug_aabb_to_string(abb));
 		print_line("item_move " + itos(p_handle.id()) + "(within tnode aabb) : " + _debug_aabb_to_string(abb));

+ 3 - 0
core/math/bvh_tree.h

@@ -50,6 +50,9 @@
 
 
 #define BVHABB_CLASS BVH_ABB<BOUNDS, POINT>
 #define BVHABB_CLASS BVH_ABB<BOUNDS, POINT>
 
 
+// not sure if this is better yet so making optional
+#define BVH_EXPAND_LEAF_AABBS
+
 // 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

+ 16 - 0
doc/classes/ProjectSettings.xml

@@ -1016,6 +1016,11 @@
 			Size of the hash table used for the broad-phase 2D hash grid algorithm.
 			Size of the hash table used for the broad-phase 2D hash grid algorithm.
 			[b]Note:[/b] Not used if [member ProjectSettings.physics/2d/use_bvh] is enabled.
 			[b]Note:[/b] Not used if [member ProjectSettings.physics/2d/use_bvh] is enabled.
 		</member>
 		</member>
+		<member name="physics/2d/bvh_collision_margin" type="float" setter="" getter="" default="1.0">
+			Additional expansion applied to object bounds in the 2D physics bounding volume hierarchy. This can reduce BVH processing at the cost of a slightly coarser broadphase, which can stress the physics more in some situations.
+			The default value will work well in most situations. A value of 0.0 will turn this optimization off, and larger values may work better for larger, faster moving objects.
+			[b]Note:[/b] Used only if [member ProjectSettings.physics/2d/use_bvh] is enabled.
+		</member>
 		<member name="physics/2d/cell_size" type="int" setter="" getter="" default="128">
 		<member name="physics/2d/cell_size" type="int" setter="" getter="" default="128">
 			Cell size used for the broad-phase 2D hash grid algorithm (in pixels).
 			Cell size used for the broad-phase 2D hash grid algorithm (in pixels).
 			[b]Note:[/b] Not used if [member ProjectSettings.physics/2d/use_bvh] is enabled.
 			[b]Note:[/b] Not used if [member ProjectSettings.physics/2d/use_bvh] is enabled.
@@ -1095,6 +1100,11 @@
 			The default linear damp in 3D.
 			The default linear damp in 3D.
 			[b]Note:[/b] Good values are in the range [code]0[/code] to [code]1[/code]. At value [code]0[/code] objects will keep moving with the same velocity. Values greater than [code]1[/code] will aim to reduce the velocity to [code]0[/code] in less than a second e.g. a value of [code]2[/code] will aim to reduce the velocity to [code]0[/code] in half a second. A value equal to or greater than the physics frame rate ([member ProjectSettings.physics/common/physics_fps], [code]60[/code] by default) will bring the object to a stop in one iteration.
 			[b]Note:[/b] Good values are in the range [code]0[/code] to [code]1[/code]. At value [code]0[/code] objects will keep moving with the same velocity. Values greater than [code]1[/code] will aim to reduce the velocity to [code]0[/code] in less than a second e.g. a value of [code]2[/code] will aim to reduce the velocity to [code]0[/code] in half a second. A value equal to or greater than the physics frame rate ([member ProjectSettings.physics/common/physics_fps], [code]60[/code] by default) will bring the object to a stop in one iteration.
 		</member>
 		</member>
+		<member name="physics/3d/godot_physics/bvh_collision_margin" type="float" setter="" getter="" default="0.1">
+			Additional expansion applied to object bounds in the 3D physics bounding volume hierarchy. This can reduce BVH processing at the cost of a slightly coarser broadphase, which can stress the physics more in some situations.
+			The default value will work well in most situations. A value of 0.0 will turn this optimization off, and larger values may work better for larger, faster moving objects.
+			[b]Note:[/b] Used only if [member ProjectSettings.physics/3d/godot_physics/use_bvh] is enabled.
+		</member>
 		<member name="physics/3d/godot_physics/use_bvh" type="bool" setter="" getter="" default="true">
 		<member name="physics/3d/godot_physics/use_bvh" type="bool" setter="" getter="" default="true">
 			Enables the use of bounding volume hierarchy instead of octree for 3D physics spatial partitioning. This may give better performance.
 			Enables the use of bounding volume hierarchy instead of octree for 3D physics spatial partitioning. This may give better performance.
 		</member>
 		</member>
@@ -1430,9 +1440,15 @@
 			See also [member rendering/quality/skinning/force_software_skinning].
 			See also [member rendering/quality/skinning/force_software_skinning].
 			[b]Note:[/b] When the software skinning fallback is triggered, custom vertex shaders will behave in a different way, because the bone transform will be already applied to the modelview matrix.
 			[b]Note:[/b] When the software skinning fallback is triggered, custom vertex shaders will behave in a different way, because the bone transform will be already applied to the modelview matrix.
 		</member>
 		</member>
+		<member name="rendering/quality/spatial_partitioning/bvh_collision_margin" type="float" setter="" getter="" default="0.1">
+			Additional expansion applied to object bounds in the 3D rendering bounding volume hierarchy. This can reduce BVH processing at the cost of a slightly reduced accuracy.
+			The default value will work well in most situations. A value of 0.0 will turn this optimization off, and larger values may work better for larger, faster moving objects.
+			[b]Note:[/b] Used only if [member ProjectSettings.rendering/quality/spatial_partitioning/use_bvh] is enabled.
+		</member>
 		<member name="rendering/quality/spatial_partitioning/render_tree_balance" type="float" setter="" getter="" default="0.0">
 		<member name="rendering/quality/spatial_partitioning/render_tree_balance" type="float" setter="" getter="" default="0.0">
 			The rendering octree balance can be changed to favor smaller ([code]0[/code]), or larger ([code]1[/code]) branches.
 			The rendering octree balance can be changed to favor smaller ([code]0[/code]), or larger ([code]1[/code]) branches.
 			Larger branches can increase performance significantly in some projects.
 			Larger branches can increase performance significantly in some projects.
+			[b]Note:[/b] Not used if [member ProjectSettings.rendering/quality/spatial_partitioning/use_bvh] is enabled.
 		</member>
 		</member>
 		<member name="rendering/quality/spatial_partitioning/use_bvh" type="bool" setter="" getter="" default="true">
 		<member name="rendering/quality/spatial_partitioning/use_bvh" type="bool" setter="" getter="" default="true">
 			Enables the use of bounding volume hierarchy instead of octree for rendering spatial partitioning. This may give better performance.
 			Enables the use of bounding volume hierarchy instead of octree for rendering spatial partitioning. This may give better performance.

+ 4 - 1
main/main.cpp

@@ -175,8 +175,11 @@ static String get_full_version_string() {
 // FIXME: Could maybe be moved to PhysicsServerManager and Physics2DServerManager directly
 // FIXME: Could maybe be moved to PhysicsServerManager and Physics2DServerManager directly
 // to have less code in main.cpp.
 // to have less code in main.cpp.
 void initialize_physics() {
 void initialize_physics() {
-	// This must be defined BEFORE the 3d physics server is created
+	// This must be defined BEFORE the 3d physics server is created,
+	// otherwise it won't always show up in the project settings page.
 	GLOBAL_DEF("physics/3d/godot_physics/use_bvh", true);
 	GLOBAL_DEF("physics/3d/godot_physics/use_bvh", true);
+	GLOBAL_DEF("physics/3d/godot_physics/bvh_collision_margin", 0.1);
+	ProjectSettings::get_singleton()->set_custom_property_info("physics/3d/godot_physics/bvh_collision_margin", PropertyInfo(Variant::REAL, "physics/3d/godot_physics/bvh_collision_margin", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"));
 
 
 	/// 3D Physics Server
 	/// 3D Physics Server
 	physics_server = PhysicsServerManager::new_server(ProjectSettings::get_singleton()->get(PhysicsServerManager::setting_property_name));
 	physics_server = PhysicsServerManager::new_server(ProjectSettings::get_singleton()->get(PhysicsServerManager::setting_property_name));

+ 1 - 0
servers/physics/broad_phase_bvh.cpp

@@ -127,6 +127,7 @@ BroadPhaseSW *BroadPhaseBVH::_create() {
 
 
 BroadPhaseBVH::BroadPhaseBVH() {
 BroadPhaseBVH::BroadPhaseBVH() {
 	bvh.params_set_thread_safe(GLOBAL_GET("rendering/threads/thread_safe_bvh"));
 	bvh.params_set_thread_safe(GLOBAL_GET("rendering/threads/thread_safe_bvh"));
+	bvh.params_set_pairing_expansion(GLOBAL_GET("physics/3d/godot_physics/bvh_collision_margin"));
 	bvh.set_pair_callback(_pair_callback, this);
 	bvh.set_pair_callback(_pair_callback, this);
 	bvh.set_unpair_callback(_unpair_callback, this);
 	bvh.set_unpair_callback(_unpair_callback, this);
 	bvh.set_check_pair_callback(_check_pair_callback, this);
 	bvh.set_check_pair_callback(_check_pair_callback, this);

+ 1 - 0
servers/physics_2d/broad_phase_2d_bvh.cpp

@@ -123,6 +123,7 @@ BroadPhase2DSW *BroadPhase2DBVH::_create() {
 
 
 BroadPhase2DBVH::BroadPhase2DBVH() {
 BroadPhase2DBVH::BroadPhase2DBVH() {
 	bvh.params_set_thread_safe(GLOBAL_GET("rendering/threads/thread_safe_bvh"));
 	bvh.params_set_thread_safe(GLOBAL_GET("rendering/threads/thread_safe_bvh"));
+	bvh.params_set_pairing_expansion(GLOBAL_GET("physics/2d/bvh_collision_margin"));
 	bvh.set_pair_callback(_pair_callback, this);
 	bvh.set_pair_callback(_pair_callback, this);
 	bvh.set_unpair_callback(_unpair_callback, this);
 	bvh.set_unpair_callback(_unpair_callback, this);
 	bvh.set_check_pair_callback(_check_pair_callback, this);
 	bvh.set_check_pair_callback(_check_pair_callback, this);

+ 2 - 0
servers/physics_2d/physics_2d_server_sw.cpp

@@ -1325,6 +1325,8 @@ Physics2DServerSW::Physics2DServerSW() {
 	GLOBAL_DEF("physics/2d/bp_hash_table_size", 4096);
 	GLOBAL_DEF("physics/2d/bp_hash_table_size", 4096);
 	GLOBAL_DEF("physics/2d/cell_size", 128);
 	GLOBAL_DEF("physics/2d/cell_size", 128);
 	GLOBAL_DEF("physics/2d/large_object_surface_threshold_in_cells", 512);
 	GLOBAL_DEF("physics/2d/large_object_surface_threshold_in_cells", 512);
+	GLOBAL_DEF("physics/2d/bvh_collision_margin", 1.0);
+	ProjectSettings::get_singleton()->set_custom_property_info("physics/2d/bvh_collision_margin", PropertyInfo(Variant::REAL, "physics/2d/bvh_collision_margin", PROPERTY_HINT_RANGE, "0.0,20.0,0.1"));
 
 
 	bool use_bvh = GLOBAL_GET("physics/2d/use_bvh");
 	bool use_bvh = GLOBAL_GET("physics/2d/use_bvh");
 
 

+ 4 - 0
servers/visual/visual_server_scene.cpp

@@ -100,6 +100,7 @@ void VisualServerScene::camera_set_use_vertical_aspect(RID p_camera, bool p_enab
 
 
 VisualServerScene::SpatialPartitioningScene_BVH::SpatialPartitioningScene_BVH() {
 VisualServerScene::SpatialPartitioningScene_BVH::SpatialPartitioningScene_BVH() {
 	_bvh.params_set_thread_safe(GLOBAL_GET("rendering/threads/thread_safe_bvh"));
 	_bvh.params_set_thread_safe(GLOBAL_GET("rendering/threads/thread_safe_bvh"));
+	_bvh.params_set_pairing_expansion(GLOBAL_GET("rendering/quality/spatial_partitioning/bvh_collision_margin"));
 }
 }
 
 
 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) {
@@ -4095,6 +4096,9 @@ VisualServerScene::VisualServerScene() {
 	render_pass = 1;
 	render_pass = 1;
 	singleton = this;
 	singleton = this;
 	_use_bvh = GLOBAL_DEF("rendering/quality/spatial_partitioning/use_bvh", true);
 	_use_bvh = GLOBAL_DEF("rendering/quality/spatial_partitioning/use_bvh", true);
+	GLOBAL_DEF("rendering/quality/spatial_partitioning/bvh_collision_margin", 0.1);
+	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/spatial_partitioning/bvh_collision_margin", PropertyInfo(Variant::REAL, "rendering/quality/spatial_partitioning/bvh_collision_margin", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"));
+
 	_visual_server_callbacks = nullptr;
 	_visual_server_callbacks = nullptr;
 }
 }