From 07e5022cce2e059320bf7c1b62d073e04a635513 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Thu, 18 Nov 2021 14:32:33 +0000 Subject: [PATCH] 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) --- core/math/bvh.h | 6 +++++ core/math/bvh_pair.inc | 10 ++++++++ core/math/bvh_public.inc | 27 +++++++++++++++++++++ core/math/bvh_tree.h | 3 +++ doc/classes/ProjectSettings.xml | 16 ++++++++++++ main/main.cpp | 5 +++- servers/physics/broad_phase_bvh.cpp | 1 + servers/physics_2d/broad_phase_2d_bvh.cpp | 1 + servers/physics_2d/physics_2d_server_sw.cpp | 2 ++ servers/visual/visual_server_scene.cpp | 4 +++ 10 files changed, 74 insertions(+), 1 deletion(-) diff --git a/core/math/bvh.h b/core/math/bvh.h index 8d0134f78ba..90052a46aa8 100644 --- a/core/math/bvh.h +++ b/core/math/bvh.h @@ -704,6 +704,11 @@ private: // Note that non pairable items can pair with pairable, // 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 // at the cost of slightly less accurate pairing checks // 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 expanded_aabb = aabb; expanded_aabb.grow_by(tree._pairing_expansion); +#endif // 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 diff --git a/core/math/bvh_pair.inc b/core/math/bvh_pair.inc index 7175edbfe16..e6f1d6bd490 100644 --- a/core/math/bvh_pair.inc +++ b/core/math/bvh_pair.inc @@ -59,4 +59,14 @@ struct ItemPairs { 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; + } }; diff --git a/core/math/bvh_public.inc b/core/math/bvh_public.inc index 6a8ecb05555..db4de03b39d 100644 --- a/core/math/bvh_public.inc +++ b/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; 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 BVHHandle handle; @@ -115,6 +122,15 @@ bool item_move(BVHHandle p_handle, const BOUNDS &p_aabb) { BVHABB_CLASS abb; 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); 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); // 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) { return false; } +#endif #ifdef BVH_VERBOSE_MOVES print_line("item_move " + itos(p_handle.id()) + "(within tnode aabb) : " + _debug_aabb_to_string(abb)); diff --git a/core/math/bvh_tree.h b/core/math/bvh_tree.h index 8acf076cc4f..1751837fa38 100644 --- a/core/math/bvh_tree.h +++ b/core/math/bvh_tree.h @@ -50,6 +50,9 @@ #define BVHABB_CLASS BVH_ABB +// not sure if this is better yet so making optional +#define BVH_EXPAND_LEAF_AABBS + // never do these checks in release #if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED) //#define BVH_VERBOSE diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 3c84df2eb5f..d60e0c05860 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1016,6 +1016,11 @@ 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. + + 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. + 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. @@ -1095,6 +1100,11 @@ 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. + + 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. + Enables the use of bounding volume hierarchy instead of octree for 3D physics spatial partitioning. This may give better performance. @@ -1430,9 +1440,15 @@ 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. + + 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. + 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. + [b]Note:[/b] Not used if [member ProjectSettings.rendering/quality/spatial_partitioning/use_bvh] is enabled. Enables the use of bounding volume hierarchy instead of octree for rendering spatial partitioning. This may give better performance. diff --git a/main/main.cpp b/main/main.cpp index b4d111d3507..442774a2648 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -175,8 +175,11 @@ static String get_full_version_string() { // FIXME: Could maybe be moved to PhysicsServerManager and Physics2DServerManager directly // to have less code in main.cpp. 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/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 physics_server = PhysicsServerManager::new_server(ProjectSettings::get_singleton()->get(PhysicsServerManager::setting_property_name)); diff --git a/servers/physics/broad_phase_bvh.cpp b/servers/physics/broad_phase_bvh.cpp index 0154a931c57..162684dfa8a 100644 --- a/servers/physics/broad_phase_bvh.cpp +++ b/servers/physics/broad_phase_bvh.cpp @@ -127,6 +127,7 @@ BroadPhaseSW *BroadPhaseBVH::_create() { BroadPhaseBVH::BroadPhaseBVH() { 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_unpair_callback(_unpair_callback, this); bvh.set_check_pair_callback(_check_pair_callback, this); diff --git a/servers/physics_2d/broad_phase_2d_bvh.cpp b/servers/physics_2d/broad_phase_2d_bvh.cpp index 6b2fe8a812e..83a5a98a713 100644 --- a/servers/physics_2d/broad_phase_2d_bvh.cpp +++ b/servers/physics_2d/broad_phase_2d_bvh.cpp @@ -123,6 +123,7 @@ BroadPhase2DSW *BroadPhase2DBVH::_create() { BroadPhase2DBVH::BroadPhase2DBVH() { 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_unpair_callback(_unpair_callback, this); bvh.set_check_pair_callback(_check_pair_callback, this); diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp index 21c9be258f5..2912053a3f3 100644 --- a/servers/physics_2d/physics_2d_server_sw.cpp +++ b/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/cell_size", 128); 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"); diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index 639d3d51076..1a87d7f5bcf 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/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() { _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) { @@ -4095,6 +4096,9 @@ VisualServerScene::VisualServerScene() { render_pass = 1; singleton = this; _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; }