Merge pull request #105463 from lawnjelly/fti_fix_auto_resets2

[3.x] FTI - Fix 3D auto-resets
This commit is contained in:
lawnjelly
2025-04-20 09:44:03 +01:00
committed by GitHub
5 changed files with 65 additions and 28 deletions

View File

@@ -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()) {

View File

@@ -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: {

View File

@@ -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());

View File

@@ -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();

View File

@@ -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.