Browse Source

FTI - Fix 3D auto-resets

* Ensure NOTIFICATION_RESET_PHYSICS_INTERPOLATION is sent to derived classes
* Add deferred auto-resets for all `Spatials` on entering the tree
lawnjelly 4 months ago
parent
commit
a2a8bef10f

+ 8 - 0
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()) {

+ 4 - 18
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: {

+ 0 - 6
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());

+ 47 - 4
scene/main/scene_tree_fti.cpp

@@ -46,8 +46,12 @@ void SceneTreeFTI::_reset_flags(Node *p_node) {
 	Spatial *s = Object::cast_to<Spatial>(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();

+ 6 - 0
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<Spatial *> frame_property_list;
 
+		LocalVector<Spatial *> 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.