Fix interpolation in XR#103233
Conversation
84c9554 to
ef111f6
Compare
ef111f6 to
362d1cf
Compare
362d1cf to
511c07f
Compare
|
|
511c07f to
b1c68a5
Compare
e6df661 to
b1c68a5
Compare
b1c68a5 to
3e7da32
Compare
|
Firstly I've noticed you've used For Cameras and likely XRNodes (I'm guessing like hand markers?) you may need to have better control over the prev/curr xforms and may end up needing to handle this data manually as in To be clear, using If we end up needing several nodes with full client interpolation, it's possible we can "genericize" some of the boiler plate to make it easier and less bug prone to add to nodes. Combining interpolated and non-interpolated on same nodeMy first impressions are the approach of trying to have Nodes are currently intended to be either physics interpolated (operating on the physics tick) or non-interpolated (operating on the frame) but not both. You are essentially in uncharted waters here, other parts of Godot may not work correctly. I suspect this needs a proper discussion of what problems are trying to be solved to be sure that this is the correct avenue to solve them, probably via a proposal. I must apologize as you are on a difficult boundary here - effectively I'm the goto guy for interpolation, but I don't have a clue how the current XR stuff works, and I don't have XR equipment, so you have to explain it like to a newbie and explain what problems you are trying to solve. Essentially the current paradigm for 3D interpolation in the engine is this in a nutshell:
Now, there may be some specific leeway to move outside this if a specific use case requires it, but there isn't existing code to work outside this framework. How XR is meant to work (at a guess)At a guess for how things are meant to operate with XR:
If this is correct:
This is just trying to explain some of how things currently work interpolation-wise, but I think I would need some further description / research into how XR currently works in order to come up with some coherent design here. |
There can be several of them but only one will be active at a time: https://docs.godotengine.org/en/stable/classes/class_xrorigin3d.html#class-xrorigin3d-property-current
I went with this approach due to this comment: #103232 (comment)
This approach comes from this comment: #103232 (comment)
This is correct, the benefit from physics interpolation being: interpolate the global transform so it follows the parent correctly while not interpolating the local transform so the tracked movement is in realtime. This current approach works fine because it just acts as any other non interpolated node, but setting its global transform to "follow" the interpolated transform of the parent. This shouldn't have any side effect as it's the same as changing the transform of an non-interpolated node inside The only issue is the transform being updated on physics frame by one of the interpolated ancestors changing its own transform, but this is a separate issue that happens outside of XR as you already know: #103351 |
scene/3d/xr_nodes.cpp
Outdated
| // If we want interpolated physics nodes like RayCast3D to follow this node properly, | ||
| // the transform must be relative to the non interpolated parent when we're on physics process | ||
| if (parent && parent->is_physics_interpolated_and_enabled() && !is_set_as_top_level()) { | ||
| set_transform(pose_offset); |
There was a problem hiding this comment.
To be clear, this is the (imo) contentious area I am referring to - setting transforms on BOTH the frame and the physics tick.
Imo the issue of trying to get RayCast3D to work is a separate issue and should be fixed in a separate follow up PR, for all the nodes you are changing.
In general we try and keep PRs as self-contained and non-divisible as possible, and focused on fixing one bug at a time.
If we get a decent implementation for the xforms for display I can approve that, but architectural changes as above will probably need further discussion with other maintainers, as there are a number of alternative (perhaps less hacky) ways of addressing that problem.
As is I can see a number of potential pitfalls with this approach, and it needs a full discussion in its own right.
There was a problem hiding this comment.
To be clear, this is the (imo) contentious area I am referring to - setting transforms on BOTH the frame and the physics tick.
Imo the issue of trying to get
RayCast3Dto work is a separate issue and should be fixed in a separate follow up PR, for all the nodes you are changing.In general we try and keep PRs as self-contained and non-divisible as possible, and focused on fixing one bug at a time.
If we get a decent implementation for the xforms for display I can approve that, but architectural changes as above will probably need further discussion with other maintainers, as there are a number of alternative (perhaps less hacky) ways of addressing that problem.
As is I can see a number of potential pitfalls with this approach, and it needs a full discussion in its own right.
Oh so this is what you meant, now I get it
I totally forgot I was updating the transform in both render frame and physics frame 😅
The reason behind this change is because the order of execution is this:
- Pose update received
- Physics update
- Render update (we apply the interpolated transform of the parent here)
Because we're setting the transform at render frame, which happens after physics, when you try to access the transform of the node inside physics it has a transform that hasn't been updated yet.
It's also possible that the node being relative to the interpolated transform of the parent at the time of a physics frame might have an impact too, since get_global_transform_interpolated returns the non-interpolated transform when called inside physics frame it might also be helping with that, but I'm not completely sure.
I agree that it's a bit hacky and to be honest I was just waiting for review to see if anyone here could suggest something better, maybe if we go the top_level route this can be avoided but I still have the doubt I commented here: #103232 (comment)
Doing that would prevent users from using top_level in the intended way as the node would always follow the parent
|
I think there are some language issues between us (I admit it's quite difficult to put across in words so apologies for any confusion 😁 ), I wasn't clear on the definitions I was using, and now I can see it was ambiguous, so to be more clear: When I mention "interpolated / non-interpolated" I was trying to refer to the physics interpolation mode of the node. InterpolatedBy saying a node that is interpolated I was meaning Non-interpolatedA node that is non-interpolated is a node that is An From now on I'll try and refer to the
|
I misunderstood you, yeah, sorry 😅 |
|
Been discussing and learning a bit more how XR works with @BastiaanOlij on rocket chat (where the data comes in before the ticks etc). I'm kind of coming around to your idea of setting the transform on the ticks as a bodge for XR as being a possibility (best of a difficult situation) but would be good to discuss on a follow up PR. In particular it might even be possible to do a light version of |
I've just read the conversation, I want to point out that the reason why I'm doing it also on the camera is because childs have the same problem as in the hands when following it, for example a raycast. About possible alternatives to setting the transform at physics process I'm all ears 🫡 About a followup PR, you want me to separate this into 2 PRs one for the |
Essentially only setting the transform on the frame for this PR. I understand that this means some physics aspects may not work correctly with physics interpolation. That's fine, a PR doesn't have to fix all outstanding bugs. The engine is (as I understand it) broken already with physics interpolation, so we are just making it slightly less broken. |
Alright that makes sense, with this PR even if we remove the transform change on the physics frame it should be easy to workaround if needed from GDScript, although it should work out of the box |
3e7da32 to
66208e9
Compare
|
I've updated the PR to remove what was requested. extends Node3D
@onready var controller: XRController3D = $".."
var origin: XROrigin3D
func _ready() -> void:
set_physics_interpolation_mode(Node.PHYSICS_INTERPOLATION_MODE_ON)
if controller.get_parent() is XROrigin3D:
origin = controller.get_parent() as XROrigin3D
# set as top level to ignore parent transform!!!
top_level = true
pass
func _physics_process(delta: float) -> void:
if !origin || !controller || !controller.get_pose():
return
global_transform = origin.global_transform * controller.get_pose().transform
passThis can be added to a node child of the 14892235_storage_emulated_0_Movies_ScreenRecording_ScreenRecording_2025.03.03-19.31.32.mov |
There was a problem hiding this comment.
This looks fine to me although will need a second pass by an XR guy, and maybe a check for style.
Just to throw a spanner in the works (sorry! 😁 ), partly as a result of this XR issue and IK and ragdoll, I've revisited the overall 3D physics interpolation design.
After doing some testbed work today and speaking with other maintainers, we've decided to evaluate changing the 3D physics interpolation paradigm to scene side rather than server side.
If this works out ok, it will make this XR code much easier to do and simpler for XR maintainers to understand (and will need to be remodified), but I don't think there's a major problem with this version being available in the interim. (Alternatively you guys may be happy with the workaround, but this PR is fine too.)
The current provisional plan is to try out new version in 3.x then if successful, look at porting to 4.x.
Interpolation on the scene side makes more sense to me, and will indeed be easier to mantain without having to make workarounds on some parts like we're doing here. Thank you for the review 🤝 |
|
I will wait for @BastiaanOlij to review so I can make a final commit and don't trigger CI/CD for a comment change |
BastiaanOlij
left a comment
There was a problem hiding this comment.
Sorry it took me awhile to get to this.
I think this is as good as we'll get it, it solves the core problem and the only question mark I have is related to a scenario that wouldn't work well with interpolation anyway (using physics following hand tracking).
It is also important to note that the approach used in XR Tools, and described in the OpenXR origin centric movement demo will not play nice with interpolation regardless of what we do.
Users must use the OpenXR character centric movement approach, which is superior anyway.
We may wish to add a remark around this to the room scale page in the manual that details the two approaches.
| Node3D *parent = Object::cast_to<Node3D>(get_parent()); | ||
|
|
||
| if (is_inside_tree() && parent && parent->is_physics_interpolated_and_enabled() && !is_set_as_top_level() && !is_physics_interpolated()) { | ||
| pose_offset = p_pose->get_adjusted_transform(); |
There was a problem hiding this comment.
My worry here with not setting the pose at this stage, is that any of the logic that needs to be done during physics process that uses the nodes location, won't have it until the interpolated version is set in internal process.
Maybe that is an unavoidable consequence of using interpolation and it may have no real impact as user who turn interpolation will likely need to stay away from such processes anyway.
There was a problem hiding this comment.
My worry here with not setting the pose at this stage, is that any of the logic that needs to be done during physics process that uses the nodes location, won't have it until the interpolated version is set in internal process.
Maybe that is an unavoidable consequence of using interpolation and it may have no real impact as user who turn interpolation will likely need to stay away from such processes anyway.
Initially the PR addressed this issue by setting the transform on physics process too, but I removed it by request of @lawnjelly , because in the current interpolation implementation you shouldn't process a non-interpolared node inside physics_process. They're planning to change the behavior of the interpolation system in a way that shouldn't be a problem anymore.
In the meantime there is a workaround I'm using that works perfectly fine for things like physics based hands that follow the XRController3D node, you can see it here: #103233 (comment) 😀
Thank you for the review 🤝 |
|
Looks like it needs a rebase. Also reminder to anyone that can to review #103685, as the sooner we get that merged in 3.x the sooner we can get it in 4.x and it should make the XR stuff a lot easier and perhaps open up opportunities to do things with less lag. |
66208e9 to
30ff4ad
Compare
|
Rebased and fixed the comments, should be ready to merge |
|
You'll need to squash the commits, a single commit is preferred. See PR workflow docs for how-to. |
30ff4ad to
9e1b9fb
Compare
Fixed. |
|
Thanks! And congrats for your first merged Godot contribution 🎉 |
|
Cherry-picked for 4.4.1. |
Fixes #103232
This is currently WIP as it only fixes the problem withXRServer::world_originnot being interpolated.I'm out of ideas about how to fix the second part of the issue of the tracked XR nodes, please read the issue for more info.
The PR has been updated and fixes all the issues I found.
Any tips are appreciated as this is my first PR and I'm sure there are things to improve.