diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index 1b8a0f96011..d1e279794e1 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -191,7 +191,15 @@ void Spatial::_notification(int p_what) { // Make sure servers are up to date. fti_update_servers_xform(); + + // As well as a reset based on the transform when adding, the user may change + // the transform during the first tick / frame, and we want to reset automatically + // at the end of the frame / tick (unless the user manually called `reset_physics_interpolation()`). + if (is_physics_interpolated()) { + get_tree()->get_scene_tree_fti().spatial_request_reset(this); + } } + } break; case NOTIFICATION_EXIT_TREE: { if (is_inside_tree()) { diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp index 13865ec2804..fdf4366c14f 100644 --- a/scene/3d/visual_instance.cpp +++ b/scene/3d/visual_instance.cpp @@ -103,24 +103,10 @@ void VisualInstance::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { - if (_is_vi_visible()) { - if (!_is_using_identity_transform()) { - // Physics interpolation cases. - if (is_inside_tree() && get_tree()->is_physics_interpolation_enabled()) { - // Physics interpolated VIs don't need to send their transform immediately after setting, - // indeed it is counterproductive, because the interpolated transform will be sent - // to the VisualServer immediately prior to rendering. - if (is_physics_interpolated() && _is_physics_interpolation_reset_requested()) { - // For instance when first adding to the tree, when the previous transform is - // unset, to prevent streaking from the origin. - _notification(NOTIFICATION_RESET_PHYSICS_INTERPOLATION); - _set_physics_interpolation_reset_requested(false); - } - } else { - // Physics interpolation global off, always send. - VisualServer::get_singleton()->instance_set_transform(instance, get_global_transform()); - } - } + // ToDo : Can we turn off notify transform for physics interpolated cases? + if (_is_vi_visible() && !(is_inside_tree() && get_tree()->is_physics_interpolation_enabled()) && !_is_using_identity_transform()) { + // Physics interpolation global off, always send. + VisualServer::get_singleton()->instance_set_transform(instance, get_global_transform()); } } break; case NOTIFICATION_EXIT_WORLD: { diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 0ead3088c87..b13e61ddf2f 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -101,12 +101,6 @@ void Node::_notification(int p_notification) { get_tree()->node_count++; orphan_node_count--; - // Allow physics interpolated nodes to automatically reset when added to the tree - // (this is to save the user doing this manually each time). - if (get_tree()->is_physics_interpolation_enabled()) { - _set_physics_interpolation_reset_requested(true); - } - } break; case NOTIFICATION_EXIT_TREE: { ERR_FAIL_COND(!get_viewport()); diff --git a/scene/main/scene_tree_fti.cpp b/scene/main/scene_tree_fti.cpp index ef224f737ed..b8151c490d3 100644 --- a/scene/main/scene_tree_fti.cpp +++ b/scene/main/scene_tree_fti.cpp @@ -46,8 +46,12 @@ void SceneTreeFTI::_reset_flags(Node *p_node) { Spatial *s = Object::cast_to(p_node); if (s) { - s->data.fti_on_frame_xform_list = false; s->data.fti_on_tick_xform_list = false; + s->data.fti_on_tick_property_list = false; + s->data.fti_on_frame_xform_list = false; + s->data.fti_on_frame_property_list = false; + s->data.fti_global_xform_interp_set = false; + s->data.fti_frame_xform_force_update = false; // In most cases the later NOTIFICATION_RESET_PHYSICS_INTERPOLATION // will reset this, but this should help cover hidden nodes. @@ -80,6 +84,8 @@ void SceneTreeFTI::tick_update() { return; } + _update_request_resets(); + uint32_t curr_mirror = data.mirror; uint32_t prev_mirror = curr_mirror ? 0 : 1; @@ -161,6 +167,36 @@ void SceneTreeFTI::tick_update() { data.mirror = prev_mirror; } +void SceneTreeFTI::_update_request_resets() { + // For instance when first adding to the tree, when the previous transform is + // unset, to prevent streaking from the origin. + for (uint32_t n = 0; n < data.request_reset_list.size(); n++) { + Spatial *s = data.request_reset_list[n]; + if (s->_is_physics_interpolation_reset_requested()) { + if (s->_is_vi_visible() && !s->_is_using_identity_transform()) { + s->notification(Spatial::NOTIFICATION_RESET_PHYSICS_INTERPOLATION); + } + + s->_set_physics_interpolation_reset_requested(false); + } + } + + data.request_reset_list.clear(); +} + +void SceneTreeFTI::spatial_request_reset(Spatial *p_spatial) { + DEV_ASSERT(p_spatial); + DEV_CHECK_ONCE(data.enabled); + + if (!p_spatial->_is_physics_interpolation_reset_requested()) { + p_spatial->_set_physics_interpolation_reset_requested(true); +#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS + DEV_CHECK_ONCE(data.request_reset_list.find(p_spatial) == -1); +#endif + data.request_reset_list.push_back(p_spatial); + } +} + void SceneTreeFTI::_spatial_notify_set_property(Spatial &r_spatial) { if (!r_spatial.is_physics_interpolated()) { return; @@ -230,9 +266,12 @@ void SceneTreeFTI::spatial_notify_delete(Spatial *p_spatial) { ERR_FAIL_NULL(p_spatial); - if (p_spatial->data.fti_on_frame_xform_list) { - p_spatial->data.fti_on_frame_xform_list = false; - } + p_spatial->data.fti_on_frame_xform_list = false; + + // Ensure this is kept in sync with the lists, in case a node + // is removed and readded to the scene tree multiple times + // on the same frame / tick. + p_spatial->_set_physics_interpolation_reset_requested(false); // This can potentially be optimized for large scenes with large churn, // as it will be doing a linear search through the lists. @@ -243,6 +282,7 @@ void SceneTreeFTI::spatial_notify_delete(Spatial *p_spatial) { data.tick_property_list[1].erase_unordered(p_spatial); data.frame_property_list.erase_unordered(p_spatial); + data.request_reset_list.erase_unordered(p_spatial); #ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS // There should only be one occurrence on the lists. @@ -254,6 +294,7 @@ void SceneTreeFTI::spatial_notify_delete(Spatial *p_spatial) { DEV_CHECK_ONCE(data.tick_property_list[1].find(p_spatial) == -1); DEV_CHECK_ONCE(data.frame_property_list.find(p_spatial) == -1); + DEV_CHECK_ONCE(data.request_reset_list.find(p_spatial) == -1); #endif } @@ -382,6 +423,8 @@ void SceneTreeFTI::frame_update(Node *p_root, bool p_frame_start) { return; } + _update_request_resets(); + data.frame_start = p_frame_start; float f = Engine::get_singleton()->get_physics_interpolation_fraction(); diff --git a/scene/main/scene_tree_fti.h b/scene/main/scene_tree_fti.h index d6c4cfa84c9..22828b492c8 100644 --- a/scene/main/scene_tree_fti.h +++ b/scene/main/scene_tree_fti.h @@ -48,6 +48,7 @@ public: bool is_enabled() const { return false; } void spatial_notify_changed(Spatial &r_spatial, bool p_transform_changed) {} + void spatial_request_reset(Spatial *p_spatial) {} void spatial_notify_delete(Spatial *p_spatial) {} }; #else @@ -73,6 +74,8 @@ class SceneTreeFTI { LocalVector frame_property_list; + LocalVector request_reset_list; + uint32_t mirror = 0; bool enabled = false; @@ -87,6 +90,8 @@ class SceneTreeFTI { } data; void _update_dirty_spatials(Node *p_node, uint32_t p_current_frame, float p_interpolation_fraction, bool p_active, const Transform *p_parent_global_xform = nullptr, int p_depth = 0); + void _update_request_resets(); + void _reset_flags(Node *p_node); void _spatial_notify_set_xform(Spatial &r_spatial); void _spatial_notify_set_property(Spatial &r_spatial); @@ -104,6 +109,7 @@ public: } } + void spatial_request_reset(Spatial *p_spatial); void spatial_notify_delete(Spatial *p_spatial); // Calculate interpolated xforms, send to visual server.