Skip to content

[4.5 dev] FTI / Physics interpolation produces visual artifacts for objects that leave and re-enter SceneTree #105970

@sinewavey

Description

@sinewavey

Tested versions

  • Reproducible in 80a3d20 (the latest at time of writing available from a merged PR)
  • Reproducible in fe38b57 (the build associated with the PR for FTI change)
  • Not reproducible in ca15fcc (immediate preceding merged PR to FTI change)

System information

Godot v4.5.dev - Windows 11 - Single-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 2060 (NVIDIA; 32.0.15.6094)

Issue description

I first noticed this following #104269 in my game, where I have a pool of RigidBody3D nodes that are relatively short-lived (visually), but may be added or removed from the scene tree regularly.

It seems that when allowed to exit the tree, these bodies then have some intense visual artifacts any time they come back.

2025-04-30.23-01-08.mp4

Steps to reproduce

Here's a small example of a situation I've noticed difficulty with:

extends Node;

const POOL_SIZE: int = 5;

var pool: Array[RigidBody3D] = [];
var lifetimes: PackedFloat32Array = [];
var pool_index: int = 0;

func _ready() -> void:
	pool.resize(POOL_SIZE);
	lifetimes.resize(POOL_SIZE);
	var def: PackedScene = load("res://ball.tscn");
	for i in POOL_SIZE:
		pool[i] = def.instantiate();
	return;

func _physics_process(delta: float) -> void:
	for i in POOL_SIZE:
		var ball: RigidBody3D = pool[i];
		if !ball.is_inside_tree():
			continue;

		var lifetime: float = maxf(0.0, lifetimes[i] - delta);
		lifetimes[i] = lifetime;
		if (lifetime > 0.0):
			continue;
		
		ball.visible = false;
		ball.freeze = true;
		ball.linear_velocity = Vector3();
		ball.angular_velocity = Vector3();
		remove_child(ball);	
	return;

func get_random_vector() -> Vector3:
	return Vector3(randf_range(-1.0, 1.0), randf_range(-1.0, 1.0), randf_range(-1.0, 1.0));

func add_ball() -> void:
	var ball: RigidBody3D = pool[pool_index];;
	ball.position = Vector3.UP * 2.5;
	ball.visible = true;
	ball.freeze = false;

	if !ball.is_inside_tree():
		add_child(ball);	

	ball.apply_central_impulse(get_random_vector() * 2.0);
	ball.reset_physics_interpolation();

	lifetimes[pool_index] = 2.0; 

	pool_index = wrapi(pool_index + 1, 0, POOL_SIZE);
	return;

Minimal reproduction project (MRP)

fti-mrp.zip

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions