Skip to content

Commit

Permalink
Add OpenXR hand tracking demo
Browse files Browse the repository at this point in the history
  • Loading branch information
BastiaanOlij committed Oct 5, 2023
1 parent 0dfb54f commit a5add68
Show file tree
Hide file tree
Showing 39 changed files with 977 additions and 0 deletions.
2 changes: 2 additions & 0 deletions xr/openxr_hand_tracking_demo/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf
8 changes: 8 additions & 0 deletions xr/openxr_hand_tracking_demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Godot 4+ specific ignores
.godot/

# Ignore our Android build folder, should be installed by user if needed
android/

# Ignore our OpenXR loader add-on, should be installed by user if needed
addons/godotopenxr
65 changes: 65 additions & 0 deletions xr/openxr_hand_tracking_demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# OpenXR hand tracking demo

This demo is an OpenXR project that demonstrates how to use the hand tracking extension and the hand interaction profile extension.
These extensions can be used separately however used together the compliment eachother incredibly well.

Godot version: 4.2.0
Language: GDScript
Renderer: compatibility

## How does it work?

Hand tracking in OpenXR has been a somewhat divisive subject over the years as there have been differences in platform implementation. Since OpenXR 1.0.28 things have started to come together and as support for new extension increases we're getting a more holistic approach.

There are two sides to hand tracking, each serviced by different APIs.

### Hand visualisation

The ability to accurately visualise the hand of the user, with accurate positioning of fingers, is important to the feeling of emersion in XR.

In the early days of XR, tracking was limited to controllers held by the user, visualising the users hand was accomplished by attaching a hand model to the controller model and animating the fingers based on which buttons were pressed on the controller. This later was improved by additional sensors on the controller to further improve positioning of fingers. In earlier VR APIs this was put in the hands of the developer however this has become increasingly more difficult with OpenXR as access to the sensor is often not possible.

With the advant of optical hand tracking, where full information about finger orientation became possible, a new and more accurate way of visualising the users hand became possible. For this OpenXR added the hand tracking extensions, which provides an API to obtain the position and orientation of all joints of the users hand. This in turn allows the creation of a bone armature that can be applied to a skinned hand.
This demo uses this API to visualise the users hand by making use of the `OpenXRHand` helper node that is part of the OpenXR module.

As haptic gloves are slowly introduced into the market we see devices that cross/merge the controller and hand tracking paradigm. This will likely lead to further developments on this functionality.

**Warning** XR runtimes that allow optical hand tracking until recently treated this as separate to controller tracking and the hand tracking extension would only function when optical hand tracking was used. This resulted in a problem when the user switched between controller and optical hand tracking. Only SteamVR provided inferred hand tracking as standard providing a seamless transition between the two. Recently the [Hand tracking data source](https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hand_tracking_data_source) extension was added. Once Godot supports this extension and more runtimes implement this, we will update this demo with support for this.

**Note** XR runtimes that perform optical hand tracking will often adjust the bone information to conform fully to the users hand, while runtimes that infer hand tracking from controller input will use a fixed armature. This can lead to deformation issues when a hand model is incorrectly skinned and the bone data results in a much smaller, or larger armature than the model was based on. Be sure to test your hand models with a wide variaty of devices and XR runtimes!

### Hand interaction

OpenXR from its inception used an action map to handle interacting with the virtual world however the action map was always limited to controller based systems.

When optical hand tracking was introduced, only joint data could be retrieved through the aforementioned API. This meant that a disjoint was introduced between user interaction when controllers were used, and interaction when optical hand tracking was used with the latter requiring the user to implement their own strategies for detecting gestures that triggered actions.

OpenXR 1.0.28 introduced the [Hand interaction extension](https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_EXT_hand_interaction) which adds support to the action map for basic gesture recognition. This is what we use in this demo.

This extension has two ways of being used:

First, you can add the new interaction profile to an existing action map, normal controller profiles will be used for controllers and OpenXR will switch to the hand interaction profile after we switch to optical hand tracking.

Second, you can setup an action map that only contains the hand interaction profile. The extension guarantees that if controllers are used, all inputs are emulated.

## Action map

This demo contains a custom action map tailered to this demo. It is fully based on the hand interaction profile approach and will thus only function if the hand interaction extension is available.

## Running on PCVR

This project can be run as normal for PCVR. Ensure that an OpenXR runtime has been installed.
This project has been tested with the SteamVR OpenXR runtime.
The Oculus desktop runtime currently doesn't support hand tracking.

Note that Godot currently can't run using the WMR OpenXR runtime. Install SteamVR with WMR support.

## Running on standalone VR

This project is preconfigured for export to Quest however you must install the Android build templates and OpenXR loader plugin.
Please follow [the instructions for deploying on Android in the manual](https://docs.godotengine.org/en/latest/tutorials/xr/deploying_to_android.html).

## Screenshots

![Screenshot](screenshots/hand_interaction_demo.png)

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions xr/openxr_hand_tracking_demo/assets/images/pattern.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://be5eg5hugwheh"
path.s3tc="res://.godot/imported/pattern.png-4d94aeab07bbe0313c1636ed9890ceae.s3tc.ctex"
path.etc2="res://.godot/imported/pattern.png-4d94aeab07bbe0313c1636ed9890ceae.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}

[deps]

source_file="res://assets/images/pattern.png"
dest_files=["res://.godot/imported/pattern.png-4d94aeab07bbe0313c1636ed9890ceae.s3tc.ctex", "res://.godot/imported/pattern.png-4d94aeab07bbe0313c1636ed9890ceae.etc2.ctex"]

[params]

compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
27 changes: 27 additions & 0 deletions xr/openxr_hand_tracking_demo/assets/valve_hand_models/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright (c), Valve Corporation
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5 changes: 5 additions & 0 deletions xr/openxr_hand_tracking_demo/assets/valve_hand_models/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The model files and textures in this folder are Copyright (c) Valve. All Rights Reserved.

They are made available under the terms of the attached LICENSE file.

Original files including blend and fbx versions can be found at: https://github.com/JoeLudwig/xrsandbox/tree/master/projects/xrsandbox/assets/models/valve_hand_models
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[remap]

importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bs5xn2f4c7i42"
path="res://.godot/imported/left_hand.glb-3b74577681ea43528a21481079416e20.scn"

[deps]

source_file="res://assets/valve_hand_models/left_hand.glb"
dest_files=["res://.godot/imported/left_hand.glb-3b74577681ea43528a21481079416e20.scn"]

[params]

nodes/root_type="Spatial"
nodes/root_name="HandModel"
nodes/apply_root_scale=true
nodes/root_scale=1.0
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=0
meshes/lightmap_texel_size=0.1
skins/use_named_skins=true
animation/import=true
animation/fps=15
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/embedded_image_handling=1
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://cxycwpn8gjea0"
path.s3tc="res://.godot/imported/left_hand_file2.png-b8bef016bdaeec0e01d5d8be8ff7e586.s3tc.ctex"
path.etc2="res://.godot/imported/left_hand_file2.png-b8bef016bdaeec0e01d5d8be8ff7e586.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={}

[deps]

source_file="res://assets/valve_hand_models/left_hand_file2.png"
dest_files=["res://.godot/imported/left_hand_file2.png-b8bef016bdaeec0e01d5d8be8ff7e586.s3tc.ctex", "res://.godot/imported/left_hand_file2.png-b8bef016bdaeec0e01d5d8be8ff7e586.etc2.ctex"]

[params]

compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=1
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=1
roughness/src_normal="res://assets/valve_hand_models/left_hand_file2.png"
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://cy7pa0dxaw768"
path.s3tc="res://.godot/imported/left_hand_file3.jpg-56e2392868a70843211420a34f12e9b6.s3tc.ctex"
path.etc2="res://.godot/imported/left_hand_file3.jpg-56e2392868a70843211420a34f12e9b6.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={}

[deps]

source_file="res://assets/valve_hand_models/left_hand_file3.jpg"
dest_files=["res://.godot/imported/left_hand_file3.jpg-56e2392868a70843211420a34f12e9b6.s3tc.ctex", "res://.godot/imported/left_hand_file3.jpg-56e2392868a70843211420a34f12e9b6.etc2.ctex"]

[params]

compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[remap]

importer="scene"
importer_version=1
type="PackedScene"
uid="uid://28kilruyuw6p"
path="res://.godot/imported/right_hand.glb-861cb559eabc001de94b0f3cad13e387.scn"

[deps]

source_file="res://assets/valve_hand_models/right_hand.glb"
dest_files=["res://.godot/imported/right_hand.glb-861cb559eabc001de94b0f3cad13e387.scn"]

[params]

nodes/root_type="Spatial"
nodes/root_name="HandModel"
nodes/apply_root_scale=true
nodes/root_scale=1.0
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=0
meshes/lightmap_texel_size=0.1
skins/use_named_skins=true
animation/import=true
animation/fps=15
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/embedded_image_handling=1
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://brfle6b72mi8p"
path.s3tc="res://.godot/imported/right_hand_file2.png-e9bffa6f99c68052b4a534d935ca5b1c.s3tc.ctex"
path.etc2="res://.godot/imported/right_hand_file2.png-e9bffa6f99c68052b4a534d935ca5b1c.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={}

[deps]

source_file="res://assets/valve_hand_models/right_hand_file2.png"
dest_files=["res://.godot/imported/right_hand_file2.png-e9bffa6f99c68052b4a534d935ca5b1c.s3tc.ctex", "res://.godot/imported/right_hand_file2.png-e9bffa6f99c68052b4a534d935ca5b1c.etc2.ctex"]

[params]

compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=1
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=1
roughness/src_normal="res://assets/valve_hand_models/right_hand_file2.png"
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://c7gnqpbl7eurm"
path.s3tc="res://.godot/imported/right_hand_file3.jpg-7d5b59fe57bd602de3e7776887b1ee9b.s3tc.ctex"
path.etc2="res://.godot/imported/right_hand_file3.jpg-7d5b59fe57bd602de3e7776887b1ee9b.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
generator_parameters={}

[deps]

source_file="res://assets/valve_hand_models/right_hand_file3.jpg"
dest_files=["res://.godot/imported/right_hand_file3.jpg-7d5b59fe57bd602de3e7776887b1ee9b.s3tc.ctex", "res://.godot/imported/right_hand_file3.jpg-7d5b59fe57bd602de3e7776887b1ee9b.etc2.ctex"]

[params]

compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit a5add68

Please sign in to comment.