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 d2e96584ba5..8983396ca63 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1030,6 +1030,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. @@ -1109,6 +1114,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. @@ -1477,9 +1487,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 41c20813fc4..982432f47fd 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -176,8 +176,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 a295b0c8236..56c4f73644e 100644 --- a/servers/physics_2d/physics_2d_server_sw.cpp +++ b/servers/physics_2d/physics_2d_server_sw.cpp @@ -1317,6 +1317,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 1c3131466a5..d796f64644d 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) { @@ -4119,6 +4120,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; }