|
| 1 | +.. _doc_openxr_render_models: |
| 2 | + |
| 3 | +OpenXR Render Models |
| 4 | +==================== |
| 5 | + |
| 6 | +A cornerstone in 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 respecively. |
| 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 of |
| 91 | + this implementation can be found in |
| 92 | + `our hand tracking demo <https://github.com/godotengine/godot-demo-projects/tree/master/xr/openxr_hand_tracking_demo>`_. |
| 93 | + |
| 94 | + **Need to submit a PR to the hand tracking demo for this implementation once render models is merged** |
| 95 | + |
| 96 | +Render models example |
| 97 | +~~~~~~~~~~~~~~~~~~~~~ |
| 98 | + |
| 99 | +.. image:: img/openxr_render_models_setup.webp |
| 100 | + |
| 101 | +In this setup we find an :ref:`OpenXRRenderModelManager<class_OpenXRRenderModelManager>` |
| 102 | +node directly underneath our :ref:`XROrigin3D<class_XROrigin3D>` node. |
| 103 | +On this node our ``target`` property is set to ``None set`` and will handle |
| 104 | +showing all render models that are currently not related to our left or |
| 105 | +right hand controllers. |
| 106 | + |
| 107 | +We then see the same setup for our left and right hand so we'll focus on |
| 108 | +just the left hand. |
| 109 | + |
| 110 | +We have an :ref:`XRController3D<class_XRController3D>` that will track the |
| 111 | +location of our hand. |
| 112 | + |
| 113 | +.. note:: |
| 114 | + |
| 115 | + We are using the ``grip`` pose in this example. The ``palm`` pose is |
| 116 | + arguably more suitable and predictable however it is not supported |
| 117 | + by all XR runtimes. See the hand tracking demo project for a |
| 118 | + solution to switching between these poses based on what is supported. |
| 119 | + |
| 120 | +As a child of the node we have an :ref:`AnimatableBody3D<class_AnimatableBody3D>` |
| 121 | +node that follows the tracked location of the hand **but** will interact |
| 122 | +with physics objects to stop the player's hand from going through walls etc. |
| 123 | +This node has a collision shape that encapsulates the hand. |
| 124 | + |
| 125 | +.. note:: |
| 126 | + |
| 127 | + It is important to set the physics priority such that this logic runs |
| 128 | + after any physics logic that moves the XROrigin3D node or the hand |
| 129 | + will lag a frame behind. |
| 130 | + |
| 131 | +The script below shows a basic implementation for this that you can build |
| 132 | +upon. |
| 133 | + |
| 134 | +.. code-block:: gdscript |
| 135 | +
|
| 136 | + class_name CollisionHands3D |
| 137 | + extends AnimatableBody3D |
| 138 | +
|
| 139 | + func _ready(): |
| 140 | + # Make sure these are set correctly. |
| 141 | + top_level = true |
| 142 | + sync_to_physics = false |
| 143 | + process_physics_priority = -90 |
| 144 | +
|
| 145 | + func _physics_process(_delta): |
| 146 | + # Follow our parent node around. |
| 147 | + var dest_transform = get_parent().global_transform |
| 148 | +
|
| 149 | + # We just apply rotation for this example. |
| 150 | + global_basis = dest_transform.basis |
| 151 | +
|
| 152 | + # Attempt to move to where our tracked hand is. |
| 153 | + move_and_collide(dest_transform.origin - global_position) |
| 154 | +
|
| 155 | +
|
| 156 | +Finally we see another :ref:`OpenXRRenderModelManager<class_OpenXRRenderModelManager>` |
| 157 | +node, this one with ``target`` set to the appropriate hand and |
| 158 | +``make_local_to_pose`` set to the correct pose. |
| 159 | +This will ensure that the render models related to this hand are properly |
| 160 | +shown and offset if our collision handler has altered the location. |
| 161 | + |
| 162 | +**Add video or animation to see this in practice** |
| 163 | + |
| 164 | +Render model node |
| 165 | +----------------- |
| 166 | + |
| 167 | +The :ref:`OpenXRRenderModel<class_OpenXRRenderModel>` node implements |
| 168 | +all the logic to display and position a given render model provided by |
| 169 | +the render models API. |
| 170 | + |
| 171 | +Instances of this node are added by the render model manager node we used up |
| 172 | +above but you can interact with these directly if you wish. |
| 173 | + |
| 174 | +Whenever Godot obtains information about a new render model an RID is |
| 175 | +created to reference that render model. |
| 176 | + |
| 177 | +By assigning that RID to the ``render_model`` property on this node, |
| 178 | +the node will start displaying the render model and manage both the |
| 179 | +transform that places the render model in the correct place and |
| 180 | +animates all the sub objects. |
| 181 | + |
| 182 | +The ``get_top_level_path`` function will return the top level path |
| 183 | +associated with this render model. This will point to either the |
| 184 | +left or right hand. As the top level path can be set or cleared |
| 185 | +depending on whether the user picks up, or puts down, the controller |
| 186 | +you can connect the ``render_model_top_level_path_changes`` signal |
| 187 | +and react to these changes. |
| 188 | + |
| 189 | +Depending on your setup of the |
| 190 | +:ref:`OpenXRRenderModelManager<class_OpenXRRenderModelManager>` nodes, |
| 191 | +render models will be removed or added as their top level path changes. |
| 192 | + |
| 193 | +Backend access |
| 194 | +-------------- |
| 195 | + |
| 196 | +The nodes we've detailed out above handle all the display logic |
| 197 | +for us but it is possible to interact with the data that drives |
| 198 | +this directly and create your own implementation. |
| 199 | + |
| 200 | +For this you can access the |
| 201 | +:ref:`OpenXRRenderModelExtension<class_OpenXRRenderModelExtension>` |
| 202 | +singleton. |
| 203 | + |
| 204 | +This object also lets you query whether render models are |
| 205 | +supported and enabled on the device currently being used by |
| 206 | +calling the ``is_active`` function on this object. |
| 207 | + |
| 208 | +The built-in logic implements the |
| 209 | +`interaction render model API <https://registry.khronos.org/OpenXR/specs/1.1/html/xrspec.html#XR_EXT_interaction_render_model>`_ |
| 210 | +that lists all render models related to controllers and similar |
| 211 | +devices that are present in the action map. |
| 212 | +It will automatically create and remove render model entities |
| 213 | +that are exposed through this API. |
| 214 | + |
| 215 | +As other extensions become available these can be implemented |
| 216 | +in a GDExtension plugin. Such a plugin can call |
| 217 | +``render_model_create`` and ``render_model_destroy`` to |
| 218 | +create the object that will provide access to that render |
| 219 | +model through the core render models API. |
| 220 | + |
| 221 | +You should not destroy a render model outside of this logic. |
| 222 | + |
| 223 | +You can connect to the ``render_model_added`` and |
| 224 | +``render_model_removed`` signals to be informed when new render |
| 225 | +models are added or removed. |
| 226 | + |
| 227 | +The core methods for working with this API are listed |
| 228 | +below: |
| 229 | + |
| 230 | +.. list-table:: Title |
| 231 | + :header-rows: 1 |
| 232 | + |
| 233 | + * - Function |
| 234 | + - Description |
| 235 | + * - render_model_get_all |
| 236 | + - Provides an array of RIDs for all render models |
| 237 | + that are being tracked. |
| 238 | + * - render_model_new_scene_instance |
| 239 | + - Provides a new scene that contains all meshes |
| 240 | + needed to display the render model. |
| 241 | + * - render_model_get_subaction_paths |
| 242 | + - Provides a list of subaction paths from your |
| 243 | + action map related to this render mode. |
| 244 | + * - render_model_get_top_level_path |
| 245 | + - Returns the top level path associated with this |
| 246 | + render model (if any). |
| 247 | + Use the ``render_model_top_level_path_changed`` |
| 248 | + signal to react to this changing. |
| 249 | + * - render_model_get_confidence |
| 250 | + - Returns the tracking confidence for the tracking |
| 251 | + data for this render model. |
| 252 | + * - render_model_get_root_transform |
| 253 | + - Returns the root transform for this render model |
| 254 | + within our current reference space. This can be |
| 255 | + used to place the render model in space. |
| 256 | + * - render_model_get_animatable_node_count |
| 257 | + - Returns the number of node in our render model |
| 258 | + scene that can be animated |
| 259 | + * - render_model_get_animatable_node_name |
| 260 | + - Returns the name of the node that we can animate. |
| 261 | + Note that this node can be any level deep within |
| 262 | + the scene. |
| 263 | + * - render_model_is_animatable_node_visible |
| 264 | + - Returns true if this animatable node should be |
| 265 | + visible |
| 266 | + * - render_model_get_animatable_node_transform |
| 267 | + - Returns the transform for this animatable node. |
| 268 | + This is a local transform that can be directly |
| 269 | + applied. |
| 270 | + |
| 271 | + |
0 commit comments