Skip to content

Commit 8aac142

Browse files
committed
Add documentation about OpenXR render models
1 parent b2de954 commit 8aac142

File tree

3 files changed

+275
-0
lines changed

3 files changed

+275
-0
lines changed
10.6 KB
Binary file not shown.

tutorials/xr/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Advanced topics
3333
openxr_composition_layers
3434
openxr_hand_tracking
3535
openxr_body_tracking
36+
openxr_render_models
3637

3738
Godot XR Tools
3839
--------------

tutorials/xr/openxr_render_models.rst

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
.. _doc_openxr_render_models:
2+
3+
OpenXR Render Models
4+
====================
5+
6+
A cornerstone of OpenXR's API design is being as platform agnostic as possible.
7+
A great example of this is OpenXR's action map system where XR runtimes
8+
have to support core interaction profiles to fall back on,
9+
if no interaction profile exists for the hardware being used.
10+
This ensures that OpenXR applications keep functioning even when used on
11+
hardware that didn't exist when the application was released,
12+
or that the developers of the application did not have access too.
13+
14+
A consequence of this is that the application developer doesn't know with any
15+
certainty what hardware is being used, as the XR runtime could be mimicking
16+
other hardware.
17+
The application developer thus can't show anything in relation to the actual
18+
hardware used, the most common use case being showing the controllers the user
19+
is currently holding.
20+
21+
Showing the correct controller models and having these models
22+
correctly positioned is important to a proper sense of immersion.
23+
24+
This is where OpenXR's `render models API <https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_render_models>`_ comes in.
25+
This API allows us to query the XR runtime for 3D assets that are correct
26+
for the physical hardware being used.
27+
The API also allows us to query the position of this hardware within the
28+
tracking volume and the correct positioning of subcomponents of this hardware.
29+
30+
For instance, we can correctly position and animate the trigger or show buttons
31+
being pressed.
32+
33+
For those runtimes that support the
34+
`controller data source for hand tracking <https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_hand_tracking_data_source>`_
35+
, we can also correctly position the user`s fingers and hand according to the
36+
shape of the controller.
37+
Do note that this works in combination with the
38+
`hand joints motion range extension <https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_hand_joints_motion_range>`_
39+
to prevent clipping of the fingers.
40+
41+
OpenXR Render models node
42+
-------------------------
43+
44+
The :ref:`OpenXRRenderModelManager<class_OpenXRRenderModelManager>`
45+
node can be used to automate most of the render models functionality.
46+
This node keeps track of the active render models currently made
47+
available by the XR runtime.
48+
49+
It will create child nodes for each active render model resulting in
50+
that render model being displayed.
51+
52+
This node must have an :ref:`XROrigin3D<class_XROrigin3D>` node as an
53+
ancestor.
54+
55+
If ``tracker`` is set to ``Any`` our node will show all render models
56+
currently being tracked. In this scenario this node must be a direct
57+
child of our :ref:`XROrigin3D<class_XROrigin3D>` node.
58+
59+
If ``tracker`` is set to ``None set`` our node will only show render
60+
models for which no tracker has been identified. In this scenario this
61+
node must also be a direct child of our
62+
:ref:`XROrigin3D<class_XROrigin3D>` node.
63+
64+
If ``tracker`` is set to ``Left Hand`` or ``Right Hand`` our node will
65+
only show render models related to our left or right hand respectively.
66+
In this scenario, our node can be placed deeper in the scene tree.
67+
68+
.. warning::
69+
70+
For most XR runtimes this means the render model represents a controller
71+
that is actually being held by the user but this is not a guarantee.
72+
Some XR runtimes will always set the tracker to either the left or right
73+
hand even if the controller is not currently held but is being tracked.
74+
You should always test this as this will lead to unwanted behavior.
75+
76+
In this scenario we can also specify an action for a pose in the action map
77+
by setting the ``make_local_to_pose`` property to the pose action.
78+
Use this in combination with an :ref:`XRController3D<class_XRController3D>`
79+
node that is using the same pose and you can now add a layer that allows
80+
you to deviate from the tracked position of both your controller and the
81+
related render model (see example below).
82+
83+
.. note::
84+
85+
Combining the above with hand tracking does introduce the problem
86+
that hand tracking is completely independent from the action map
87+
system. You will need to combine the hand tracking and controller
88+
tracking poses to properly offset the render models.
89+
90+
This falls beyond the scope of this documentation but an example
91+
implementation of this can be found in
92+
`our hand tracking demo <https://github.com/godotengine/godot-demo-projects/tree/master/xr/openxr_render_models>`_.
93+
94+
Render model manager example
95+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
96+
97+
.. image:: img/openxr_render_models_setup.webp
98+
99+
In this setup we find an :ref:`OpenXRRenderModelManager<class_OpenXRRenderModelManager>`
100+
node directly underneath our :ref:`XROrigin3D<class_XROrigin3D>` node.
101+
On this node our ``target`` property is set to ``None set`` and will handle
102+
showing all render models that are currently not related to our left or
103+
right hand controllers.
104+
105+
We then see the same setup for our left and right hand so we'll focus on
106+
just the left hand.
107+
108+
We have an :ref:`XRController3D<class_XRController3D>` that will track the
109+
location of our hand.
110+
111+
.. note::
112+
113+
We are using the ``grip`` pose in this example. The ``palm`` pose is
114+
arguably more suitable and predictable however it is not supported
115+
by all XR runtimes. See the hand tracking demo project for a
116+
solution to switching between these poses based on what is supported.
117+
118+
As a child of the node we have an :ref:`AnimatableBody3D<class_AnimatableBody3D>`
119+
node that follows the tracked location of the hand **but** will interact
120+
with physics objects to stop the player's hand from going through walls etc.
121+
This node has a collision shape that encapsulates the hand.
122+
123+
.. note::
124+
125+
It is important to set the physics priority so that this logic runs
126+
after any physics logic that moves the XROrigin3D node or the hand
127+
will lag a frame behind.
128+
129+
The script below shows a basic implementation for this that you can build
130+
upon.
131+
132+
.. code-block:: gdscript
133+
134+
class_name CollisionHands3D
135+
extends AnimatableBody3D
136+
137+
func _ready():
138+
# Make sure these are set correctly.
139+
top_level = true
140+
sync_to_physics = false
141+
process_physics_priority = -90
142+
143+
func _physics_process(_delta):
144+
# Follow our parent node around.
145+
var dest_transform = get_parent().global_transform
146+
147+
# We just apply rotation for this example.
148+
global_basis = dest_transform.basis
149+
150+
# Attempt to move to where our tracked hand is.
151+
move_and_collide(dest_transform.origin - global_position)
152+
153+
154+
Finally we see another :ref:`OpenXRRenderModelManager<class_OpenXRRenderModelManager>`
155+
node, this one with ``target`` set to the appropriate hand and
156+
``make_local_to_pose`` set to the correct pose.
157+
This will ensure that the render models related to this hand are properly
158+
shown and offset if our collision handler has altered the location.
159+
160+
.. raw:: html
161+
162+
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; height: auto;">
163+
<iframe src="https://www.youtube.com/embed/_gNOd7wQ62M" frameborder="0" allowfullscreen style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></iframe>
164+
</div>
165+
166+
167+
Render model node
168+
-----------------
169+
170+
The :ref:`OpenXRRenderModel<class_OpenXRRenderModel>` node implements
171+
all the logic to display and position a given render model provided by
172+
the render models API.
173+
174+
Instances of this node are added by the render model manager node we used up
175+
above but you can interact with these directly if you wish.
176+
177+
Whenever Godot obtains information about a new render model an RID is
178+
created to reference that render model.
179+
180+
By assigning that RID to the ``render_model`` property on this node,
181+
the node will start displaying the render model and manage both the
182+
transform that places the render model in the correct place and
183+
animates all the sub objects.
184+
185+
The ``get_top_level_path`` function will return the top level path
186+
associated with this render model. This will point to either the
187+
left or right hand. As the top level path can be set or cleared
188+
depending on whether the user picks up, or puts down, the controller
189+
you can connect to the ``render_model_top_level_path_changes`` signal
190+
and react to these changes.
191+
192+
Depending on your setup of the
193+
:ref:`OpenXRRenderModelManager<class_OpenXRRenderModelManager>` nodes,
194+
render models will be removed or added as their top level path changes.
195+
196+
Backend access
197+
--------------
198+
199+
The nodes we've detailed out above handle all the display logic
200+
for us but it is possible to interact with the data that drives
201+
this directly and create your own implementation.
202+
203+
For this you can access the
204+
:ref:`OpenXRRenderModelExtension<class_OpenXRRenderModelExtension>`
205+
singleton.
206+
207+
This object also lets you query whether render models are
208+
supported and enabled on the device currently being used by
209+
calling the ``is_active`` function on this object.
210+
211+
The built-in logic implements the
212+
`interaction render model API <https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_interaction_render_model>`_
213+
that lists all render models related to controllers and similar
214+
devices that are present in the action map.
215+
It will automatically create and remove render model entities
216+
that are exposed through this API.
217+
218+
As other extensions become available these can be implemented
219+
in a GDExtension plugin. Such a plugin can call
220+
``render_model_create`` and ``render_model_destroy`` to
221+
create the object that will provide access to that render
222+
model through the core render models API.
223+
224+
You should not destroy a render model outside of this logic.
225+
226+
You can connect to the ``render_model_added`` and
227+
``render_model_removed`` signals to be informed when new render
228+
models are added or removed.
229+
230+
The core methods for working with this API are listed
231+
below:
232+
233+
.. list-table:: Render modele extension functions
234+
:header-rows: 1
235+
236+
* - Function
237+
- Description
238+
* - render_model_get_all
239+
- Provides an array of RIDs for all render models
240+
that are being tracked.
241+
* - render_model_new_scene_instance
242+
- Provides a new scene that contains all meshes
243+
needed to display the render model.
244+
* - render_model_get_subaction_paths
245+
- Provides a list of subaction paths from your
246+
action map related to this render mode.
247+
* - render_model_get_top_level_path
248+
- Returns the top level path associated with this
249+
render model (if any).
250+
Use the ``render_model_top_level_path_changed``
251+
signal to react to this changing.
252+
* - render_model_get_confidence
253+
- Returns the tracking confidence for the tracking
254+
data for this render model.
255+
* - render_model_get_root_transform
256+
- Returns the root transform for this render model
257+
within our current reference space. This can be
258+
used to place the render model in space.
259+
* - render_model_get_animatable_node_count
260+
- Returns the number of node in our render model
261+
scene that can be animated
262+
* - render_model_get_animatable_node_name
263+
- Returns the name of the node that we can animate.
264+
Note that this node can be any level deep within
265+
the scene.
266+
* - render_model_is_animatable_node_visible
267+
- Returns true if this animatable node should be
268+
visible
269+
* - render_model_get_animatable_node_transform
270+
- Returns the transform for this animatable node.
271+
This is a local transform that can be directly
272+
applied.
273+
274+

0 commit comments

Comments
 (0)