Merge pull request #107308 from lawnjelly/scenetreefti_get_interp_hidden4

FTI - `global_transform_interpolated()` on demand for invisible nodes
This commit is contained in:
Rémi Verschelde 2025-06-12 22:48:49 +02:00
commit 53da681e89
No known key found for this signature in database
GPG Key ID: C3336907360768E1

View File

@ -502,13 +502,98 @@ Transform3D Node3D::_get_global_transform_interpolated(real_t p_interpolation_fr
return res; return res;
} }
// Visible nodes - get_global_transform_interpolated is cheap.
// Invisible nodes - get_global_transform_interpolated is expensive, try to avoid.
Transform3D Node3D::get_global_transform_interpolated() { Transform3D Node3D::get_global_transform_interpolated() {
#if 1 #if 1
// Pass through if physics interpolation is switched off. // Pass through if physics interpolation is switched off.
// This is a convenience, as it allows you to easy turn off interpolation // This is a convenience, as it allows you to easy turn off interpolation
// without changing any code. // without changing any code.
if (data.fti_global_xform_interp_set && is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame() && is_visible_in_tree()) { if (is_inside_tree() && get_tree()->is_physics_interpolation_enabled() && !Engine::get_singleton()->is_in_physics_frame()) {
return data.global_transform_interpolated; // Note that with SceneTreeFTI, we may want to calculate interpolated transform for a node
// with physics interpolation set to OFF, if it has a parent that is ON.
// Cheap case.
// We already pre-cache the visible_in_tree for VisualInstances, but NOT for Node3Ds, so we have to
// deal with non-VIs the slow way.
if (Object::cast_to<VisualInstance3D>(this) && _is_vi_visible() && data.fti_global_xform_interp_set) {
return data.global_transform_interpolated;
}
// Find out if visible in tree.
// If not visible in tree, find the FIRST ancestor that is visible in tree.
const Node3D *visible_parent = nullptr;
const Node3D *s = this;
bool visible = true;
bool visible_in_tree = true;
while (s) {
if (!s->data.visible) {
visible_in_tree = false;
visible = false;
} else {
if (!visible) {
visible_parent = s;
visible = true;
}
}
s = s->data.parent;
}
// Simplest case, we can return the interpolated xform calculated by SceneTreeFTI.
if (visible_in_tree) {
return data.fti_global_xform_interp_set ? data.global_transform_interpolated : get_global_transform();
} else if (visible_parent) {
// INVISIBLE case. Not visible, but there is a visible ancestor somewhere in the chain.
if (_get_scene_tree_depth() < 1) {
// This should not happen unless there a problem has been introduced in the scene tree depth code.
// Print a non-spammy error and return something reasonable.
ERR_PRINT_ONCE("depth is < 1.");
return get_global_transform();
}
// The interpolated xform is not already calculated for invisible nodes, but we can calculate this
// manually on demand if there is a visible parent.
// First create the chain (backwards), from the node up to first visible parent.
const Node3D **parents = (const Node3D **)alloca((sizeof(const Node3D *) * _get_scene_tree_depth()));
int32_t num_parents = 0;
s = this;
while (s) {
if (s == visible_parent) {
// Finished.
break;
}
parents[num_parents++] = s;
s = s->data.parent;
}
// Now calculate the interpolated chain forwards.
float interpolation_fraction = Engine::get_singleton()->get_physics_interpolation_fraction();
// Seed the xform with the visible parent.
Transform3D xform = visible_parent->data.fti_global_xform_interp_set ? visible_parent->data.global_transform_interpolated : visible_parent->get_global_transform();
Transform3D local_interp;
// Backwards through the list is forwards through the chain through the tree.
for (int32_t n = num_parents - 1; n >= 0; n--) {
s = parents[n];
if (s->is_physics_interpolated()) {
// Make sure to call `get_transform()` rather than using local_transform directly, because
// local_transform may be dirty and need updating from rotation / scale.
TransformInterpolator::interpolate_transform_3d(s->data.local_transform_prev, s->get_transform(), local_interp, interpolation_fraction);
} else {
local_interp = s->get_transform();
}
xform *= local_interp;
}
// We could save this in case of multiple calls,
// but probably not necessary.
return xform;
}
} }
return get_global_transform(); return get_global_transform();