From e3b128819ddf4f95fdeb841b4fba03806b7a9d8f Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Wed, 30 Apr 2025 20:09:25 +1000 Subject: [PATCH] Add documentation about OpenXR render models --- .../xr/img/openxr_render_models_setup.webp | Bin 0 -> 10888 bytes tutorials/xr/index.rst | 1 + tutorials/xr/openxr_render_models.rst | 275 ++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 tutorials/xr/img/openxr_render_models_setup.webp create mode 100644 tutorials/xr/openxr_render_models.rst diff --git a/tutorials/xr/img/openxr_render_models_setup.webp b/tutorials/xr/img/openxr_render_models_setup.webp new file mode 100644 index 0000000000000000000000000000000000000000..0e4d45b7cfbf0ef8dc84391c7860a232a71be502 GIT binary patch literal 10888 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�$567sS&ySZVx1_M_xH>}1UK6F4t@fpw{I>1TH2-P~9>G#MX5S z8oOR=_~|CUPJ#|UYp(NP{C>!VjoN$ueXvRYQ@e%PW4i zjAX(4L2W*{cEPu?jhVaMk zN1F`-A$X#^3<4eh@&90Y$8*lIq*BV>Sb&+Z_<^a;e8|gn7XP~Lj-NCbKi84Ht`pL{ zxLnXD_*Uo4+eKn|8K5BODb;OMi0s(wW;_vaX=)v{{d3BPe=d& literal 0 HcmV?d00001 diff --git a/tutorials/xr/index.rst b/tutorials/xr/index.rst index fe3fd6617..c0d93f0f4 100644 --- a/tutorials/xr/index.rst +++ b/tutorials/xr/index.rst @@ -33,6 +33,7 @@ Advanced topics openxr_composition_layers openxr_hand_tracking openxr_body_tracking + openxr_render_models Godot XR Tools -------------- diff --git a/tutorials/xr/openxr_render_models.rst b/tutorials/xr/openxr_render_models.rst new file mode 100644 index 000000000..2692d9bfa --- /dev/null +++ b/tutorials/xr/openxr_render_models.rst @@ -0,0 +1,275 @@ +.. _doc_openxr_render_models: + +OpenXR Render Models +==================== + +A cornerstone of OpenXR's API design is being as platform agnostic as possible. +A great example of this is OpenXR's action map system where XR runtimes +have to support core interaction profiles to fall back on, +if no interaction profile exists for the hardware being used. +This ensures that OpenXR applications keep functioning even when used on +hardware that didn't exist when the application was released, +or that the developers of the application did not have access too. + +A consequence of this is that the application developer doesn't know with any +certainty what hardware is being used, as the XR runtime could be mimicking +other hardware. +The application developer thus can't show anything in relation to the actual +hardware used, the most common use case being showing the controllers the user +is currently holding. + +Showing the correct controller models and having these models +correctly positioned is important to a proper sense of immersion. + +This is where OpenXR's `render models API `_ comes in. +This API allows us to query the XR runtime for 3D assets that are correct +for the physical hardware being used. +The API also allows us to query the position of this hardware within the +tracking volume and the correct positioning of subcomponents of this hardware. + +For instance, we can correctly position and animate the trigger or show buttons +being pressed. + +For those runtimes that support the +`controller data source for hand tracking `_ +, we can also correctly position the user's fingers and hand according to the +shape of the controller. +Do note that this works in combination with the +`hand joints motion range extension `_ +to prevent clipping of the fingers. + +OpenXR Render models node +------------------------- + +The :ref:`OpenXRRenderModelManager` +node can be used to automate most of the render models functionality. +This node keeps track of the active render models currently made +available by the XR runtime. + +It will create child nodes for each active render model resulting in +that render model being displayed. + +This node must have an :ref:`XROrigin3D` node as an +ancestor. + +If ``tracker`` is set to ``Any`` our node will show all render models +currently being tracked. In this scenario this node must be a direct +child of our :ref:`XROrigin3D` node. + +If ``tracker`` is set to ``None set`` our node will only show render +models for which no tracker has been identified. In this scenario this +node must also be a direct child of our +:ref:`XROrigin3D` node. + +If ``tracker`` is set to ``Left Hand`` or ``Right Hand`` our node will +only show render models related to our left or right hand respectively. +In this scenario, our node can be placed deeper in the scene tree. + +.. warning:: + + For most XR runtimes this means the render model represents a controller + that is actually being held by the user but this is not a guarantee. + Some XR runtimes will always set the tracker to either the left or right + hand even if the controller is not currently held but is being tracked. + You should always test this as this will lead to unwanted behavior. + +In this scenario we can also specify an action for a pose in the action map +by setting the ``make_local_to_pose`` property to the pose action. +Use this in combination with an :ref:`XRController3D` +node that is using the same pose and you can now add a layer that allows +you to deviate from the tracked position of both your controller and the +related render model (see example below). + +.. note:: + + Combining the above with hand tracking does introduce the problem + that hand tracking is completely independent from the action map + system. You will need to combine the hand tracking and controller + tracking poses to properly offset the render models. + + This falls beyond the scope of this documentation. + +Render model manager example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can download `our render models demo `_ +which implements the setup described below. + +.. image:: img/openxr_render_models_setup.webp + +In this setup we find an :ref:`OpenXRRenderModelManager` +node directly underneath our :ref:`XROrigin3D` node. +On this node our ``target`` property is set to ``None set`` and will handle +showing all render models that are currently not related to our left or +right hand controllers. + +We then see the same setup for our left and right hand so we'll focus on +just the left hand. + +We have an :ref:`XRController3D` that will track the +location of our hand. + +.. note:: + + We are using the ``grip`` pose in this example. The ``palm`` pose is + arguably more suitable and predictable however it is not supported + by all XR runtimes. See the hand tracking demo project for a + solution to switching between these poses based on what is supported. + +As a child of the node we have an :ref:`AnimatableBody3D` +node that follows the tracked location of the hand **but** will interact +with physics objects to stop the player's hand from going through walls etc. +This node has a collision shape that encapsulates the hand. + +.. note:: + + It is important to set the physics priority so that this logic runs + after any physics logic that moves the XROrigin3D node or the hand + will lag a frame behind. + +The script below shows a basic implementation for this that you can build +upon. + +.. code-block:: gdscript + + class_name CollisionHands3D + extends AnimatableBody3D + + func _ready(): + # Make sure these are set correctly. + top_level = true + sync_to_physics = false + process_physics_priority = -90 + + func _physics_process(_delta): + # Follow our parent node around. + var dest_transform = get_parent().global_transform + + # We just apply rotation for this example. + global_basis = dest_transform.basis + + # Attempt to move to where our tracked hand is. + move_and_collide(dest_transform.origin - global_position) + + +Finally we see another :ref:`OpenXRRenderModelManager` +node, this one with ``target`` set to the appropriate hand and +``make_local_to_pose`` set to the correct pose. +This will ensure that the render models related to this hand are properly +shown and offset if our collision handler has altered the location. + +.. raw:: html + +
+ +
+ + +Render model node +----------------- + +The :ref:`OpenXRRenderModel` node implements +all the logic to display and position a given render model provided by +the render models API. + +Instances of this node are added by the render model manager node we used up +above but you can interact with these directly if you wish. + +Whenever Godot obtains information about a new render model an RID is +created to reference that render model. + +By assigning that RID to the ``render_model`` property on this node, +the node will start displaying the render model and manage both the +transform that places the render model in the correct place and +animates all the sub objects. + +The ``get_top_level_path`` function will return the top level path +associated with this render model. This will point to either the +left or right hand. As the top level path can be set or cleared +depending on whether the user picks up, or puts down, the controller +you can connect to the ``render_model_top_level_path_changes`` signal +and react to these changes. + +Depending on your setup of the +:ref:`OpenXRRenderModelManager` nodes, +render models will be removed or added as their top level path changes. + +Backend access +-------------- + +The nodes we've detailed out above handle all the display logic +for us but it is possible to interact with the data that drives +this directly and create your own implementation. + +For this you can access the +:ref:`OpenXRRenderModelExtension` +singleton. + +This object also lets you query whether render models are +supported and enabled on the device currently being used by +calling the ``is_active`` function on this object. + +The built-in logic implements the +`interaction render model API `_ +that lists all render models related to controllers and similar +devices that are present in the action map. +It will automatically create and remove render model entities +that are exposed through this API. + +As other extensions become available these can be implemented +in a GDExtension plugin. Such a plugin can call +``render_model_create`` and ``render_model_destroy`` to +create the object that will provide access to that render +model through the core render models API. + +You should not destroy a render model outside of this logic. + +You can connect to the ``render_model_added`` and +``render_model_removed`` signals to be informed when new render +models are added or removed. + +The core methods for working with this API are listed +below: + +.. list-table:: Render modele extension functions + :header-rows: 1 + + * - Function + - Description + * - render_model_get_all + - Provides an array of RIDs for all render models + that are being tracked. + * - render_model_new_scene_instance + - Provides a new scene that contains all meshes + needed to display the render model. + * - render_model_get_subaction_paths + - Provides a list of subaction paths from your + action map related to this render mode. + * - render_model_get_top_level_path + - Returns the top level path associated with this + render model (if any). + Use the ``render_model_top_level_path_changed`` + signal to react to this changing. + * - render_model_get_confidence + - Returns the tracking confidence for the tracking + data for this render model. + * - render_model_get_root_transform + - Returns the root transform for this render model + within our current reference space. This can be + used to place the render model in space. + * - render_model_get_animatable_node_count + - Returns the number of nodes in our render model + scene that can be animated + * - render_model_get_animatable_node_name + - Returns the name of the node that we can animate. + Note that this node can be any number of levels + deep within the scene. + * - render_model_is_animatable_node_visible + - Returns true if this animatable node should be + visible + * - render_model_get_animatable_node_transform + - Returns the transform for this animatable node. + This is a local transform that can be directly + applied. + +