Skip to content

Commit 2674ffe

Browse files
committed
FTI - global_transform_interpolated() on demand for invisible nodes
1 parent 8f87e60 commit 2674ffe

File tree

1 file changed

+87
-2
lines changed

1 file changed

+87
-2
lines changed

scene/3d/node_3d.cpp

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,13 +502,98 @@ Transform3D Node3D::_get_global_transform_interpolated(real_t p_interpolation_fr
502502
return res;
503503
}
504504

505+
// Visible nodes - get_global_transform_interpolated is cheap.
506+
// Invisible nodes - get_global_transform_interpolated is expensive, try to avoid.
505507
Transform3D Node3D::get_global_transform_interpolated() {
506508
#if 1
507509
// Pass through if physics interpolation is switched off.
508510
// This is a convenience, as it allows you to easy turn off interpolation
509511
// without changing any code.
510-
if (data.fti_global_xform_interp_set && is_physics_interpolated_and_enabled() && !Engine::get_singleton()->is_in_physics_frame() && is_visible_in_tree()) {
511-
return data.global_transform_interpolated;
512+
if (is_inside_tree() && get_tree()->is_physics_interpolation_enabled() && !Engine::get_singleton()->is_in_physics_frame()) {
513+
// Note that with SceneTreeFTI, we may want to calculate interpolated transform for a node
514+
// with physics interpolation set to OFF, if it has a parent that is ON.
515+
516+
// Cheap case.
517+
// We already pre-cache the visible_in_tree for VisualInstances, but NOT for Node3Ds, so we have to
518+
// deal with non-VIs the slow way.
519+
if (Object::cast_to<VisualInstance3D>(this) && _is_vi_visible() && data.fti_global_xform_interp_set) {
520+
return data.global_transform_interpolated;
521+
}
522+
523+
// Find out if visible in tree.
524+
// If not visible in tree, find the FIRST ancestor that is visible in tree.
525+
const Node3D *visible_parent = nullptr;
526+
const Node3D *s = this;
527+
bool visible = true;
528+
bool visible_in_tree = true;
529+
530+
while (s) {
531+
if (!s->data.visible) {
532+
visible_in_tree = false;
533+
visible = false;
534+
} else {
535+
if (!visible) {
536+
visible_parent = s;
537+
visible = true;
538+
}
539+
}
540+
s = s->data.parent;
541+
}
542+
543+
// Simplest case, we can return the interpolated xform calculated by SceneTreeFTI.
544+
if (visible_in_tree) {
545+
return data.fti_global_xform_interp_set ? data.global_transform_interpolated : get_global_transform();
546+
} else if (visible_parent) {
547+
// INVISIBLE case. Not visible, but there is a visible ancestor somewhere in the chain.
548+
if (_get_scene_tree_depth() < 1) {
549+
// This should not happen unless there a problem has been introduced in the scene tree depth code.
550+
// Print a non-spammy error and return something reasonable.
551+
ERR_PRINT_ONCE("depth is < 1.");
552+
return get_global_transform();
553+
}
554+
555+
// The interpolated xform is not already calculated for invisible nodes, but we can calculate this
556+
// manually on demand if there is a visible parent.
557+
// First create the chain (backwards), from the node up to first visible parent.
558+
const Node3D **parents = (const Node3D **)alloca((sizeof(const Node3D *) * _get_scene_tree_depth()));
559+
int32_t num_parents = 0;
560+
561+
s = this;
562+
while (s) {
563+
if (s == visible_parent) {
564+
// Finished.
565+
break;
566+
}
567+
568+
parents[num_parents++] = s;
569+
s = s->data.parent;
570+
}
571+
572+
// Now calculate the interpolated chain forwards.
573+
float interpolation_fraction = Engine::get_singleton()->get_physics_interpolation_fraction();
574+
575+
// Seed the xform with the visible parent.
576+
Transform3D xform = visible_parent->data.fti_global_xform_interp_set ? visible_parent->data.global_transform_interpolated : visible_parent->get_global_transform();
577+
Transform3D local_interp;
578+
579+
// Backwards through the list is forwards through the chain through the tree.
580+
for (int32_t n = num_parents - 1; n >= 0; n--) {
581+
s = parents[n];
582+
583+
if (s->is_physics_interpolated()) {
584+
// Make sure to call `get_transform()` rather than using local_transform directly, because
585+
// local_transform may be dirty and need updating from rotation / scale.
586+
TransformInterpolator::interpolate_transform_3d(s->data.local_transform_prev, s->get_transform(), local_interp, interpolation_fraction);
587+
} else {
588+
local_interp = s->get_transform();
589+
}
590+
xform *= local_interp;
591+
}
592+
593+
// We could save this in case of multiple calls,
594+
// but probably not necessary.
595+
return xform;
596+
}
512597
}
513598

514599
return get_global_transform();

0 commit comments

Comments
 (0)