Skip to content

Conversation

@lawnjelly
Copy link
Member

Also uses the helper to provide FTI for debug collision shapes.

Fixes #110824 (for 3.x)
Supersedes #104518
(This PR seems a little easier to use than #104518, and the PR is a bit more polished, and handles debug collision shapes as an example.)

Introduction

#103685 by design removes server side physics interpolation, which has the side effect of removing in-built physics interpolation for users who create 3D nodes directly using servers, rather than via SceneTree nodes, and ideally we don't want these users to feel they are "losing out".

This use case is far simpler than SceneTree interpolation, because only global xforms need be supported.

There are several options to deal with this area, including:

  1. Provide no engine support and leave it to users (as was the case before physics interpolation PRs)
  2. Provide support via an addon
  3. Provide a simple helper in engine code to make this task much easier

This PR provides a simple solution for (3) for instances on the server, via a wrapper object called fti_instance.
It should be fairly simple to use, although admittedly there are a couple more lines to type than the old system.

The fti_instance is created in VisualServer like an instance, and it creates a regular instance internally.
Then the user should call fti_set_transform() on the fti_instance instead of instance_set_transform(), and can call fti_instance_reset() to reset physics interpolation.

All the regular instance functions can be called after retrieving the regular instance via fti_instance_get_instance().

Everything else should work automagically. Nearly everything in 3D works via instances so this should cover most cases. Cameras are not covered and would have to be interpolated manually, but as far as I remember this was also the case before #103685.

Model at 5 ticks per second with FTI

2025-04-03.09-27-22.mp4

Usage Example

# VisualServer expects references to be kept around.
var _mesh
var _instance
var _fti_instance
var _time = 0

func _enter_tree():
	# Create an FTI instance and link it to the actual instance,
	# so we can use the interpolation functionality.
	_fti_instance = VisualServer.fti_instance_create()
	_instance = VisualServer.fti_instance_get_instance(_fti_instance)
	
	# Set the scenario from the world, this ensures it
	# appears with the same objects as the scene.
	var scenario = get_world().scenario
	VisualServer.instance_set_scenario(_instance, scenario)
	
	# Add a mesh to it.
	# Remember, keep the reference.
	_mesh = load("res://Model/scarab.obj")
	VisualServer.instance_set_base(_instance, _mesh)
	
func _exit_tree():
	# Make sure to free rids when we are finished,
	# to prevent memory leaks.
	VisualServer.free_rid(_fti_instance)
	_fti_instance = RID()
	_instance = RID()

func _physics_process(delta):
	_time += delta

	# Move the model around.
	VisualServer.fti_instance_set_transform(_fti_instance, Transform(Basis(), Vector3(sin(_time) * 4, 0, 0)))
	
	# Test resetting.
	if Input.is_action_just_pressed("ui_accept"):
		VisualServer.fti_instance_reset(_fti_instance)

Notes

  • This also applies in 4.x so a port can be made once we have decided to go ahead.
  • I did test making the multithreaded versions of the functions just act single threaded and push the wrapped commands directly on the queue, but I don't think this should be necessary as the queue can add commands to the tail while being processed, and I don't see any obvious reason the ordering should matter. If there are any problems in the wild though it's fairly easy to change (and it won't cause anything catastrophic, worst case some jitter in multithread mode).
  • There are several ways of doing this. This is the second version after [3.x] Physics Interpolation - Add 3D helper for using servers directly. #104518, that version left managing the lifetime of the instance to the user, whereas this version handles the instance lifetime automatically.
  • One downside is that calling instance_get_instance() can possibly cause a stall as it is retrieving data from the server, but it should be called as a one off when creating the fti_instance.
  • I haven't found a straightforward way of combining this and returning both RIDs in the create_fti_instance() call, however I'm welcome to suggestions. (The existing create functions assume returning a single RID. It is just possible we could add a specific macro for this case, but I'm not sure it is worth it.)

Also uses the helper to provide FTI for debug collision shapes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant