Compare commits

...

10 Commits

26 changed files with 759 additions and 872 deletions

View File

@ -9,6 +9,7 @@ A GDExtension plugin to use Ultraleap hand tracking in your project. See GIF bel
- [:wine\_glass: Windows extra instructions](#wine-glass-windows-extra-instructions)
- [:robot: Android instructions](#robot-android-instructions)
- [:fast\_forward: Quick start](#fast-forward-quick-start)
- [:crown: Credits](#crown-credits)
![GIF of Ultraleap hand tracking in Godot](res/ul-godot.gif)
@ -108,4 +109,8 @@ func _process(_delta):
print(frame.right_hand.palm.position)
```
* Hit play, make sure your hand tracking camera is plugged in, flat on your desk, and show your right hand. It should print the palm position in the editor's output.
* Hit play, make sure your hand tracking camera is plugged in, flat on your desk, and show your right hand. It should print the palm position in the editor's output.
## :crown: Credits
[Glove](https://poly.pizza/m/l1zv4LaA4I) by [Quaternius](https://poly.pizza/u/Quaternius)

13
demo/.gitignore vendored
View File

@ -1,3 +1,16 @@
# Godot 4+ specific ignores
.godot/
addons/godot_ultraleap_plugin/bin/*
android/
# Created by https://www.toptal.com/developers/gitignore/api/blender
# Edit at https://www.toptal.com/developers/gitignore?templates=blender
### Blender ###
# Ignore temporary Blender files.
*.blend[0-9]*
# End of https://www.toptal.com/developers/gitignore/api/blender
# LeapC error logs
leapc_log.txt

BIN
demo/models/glove.blend Normal file

Binary file not shown.

View File

@ -0,0 +1,48 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://dyb0lbho7xd81"
path="res://.godot/imported/glove.blend-e768135381ce58f5766a76b31a0d248a.scn"
[deps]
source_file="res://models/glove.blend"
dest_files=["res://.godot/imported/glove.blend-e768135381ce58f5766a76b31a0d248a.scn"]
[params]
nodes/root_type="Node3D"
nodes/root_name="Armature"
nodes/apply_root_scale=true
nodes/root_scale=0.03
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
import_script/path=""
_subresources={}
gltf/embedded_image_handling=1
blender/nodes/visible=0
blender/nodes/punctual_lights=true
blender/nodes/cameras=true
blender/nodes/custom_properties=true
blender/nodes/modifiers=1
blender/meshes/colors=false
blender/meshes/uvs=true
blender/meshes/normals=true
blender/meshes/tangents=true
blender/meshes/skins=2
blender/meshes/export_bones_deforming_mesh_only=false
blender/materials/unpack_enabled=true
blender/materials/export_materials=1
blender/animation/limit_playback=true
blender/animation/always_sample=true
blender/animation/group_tracks=true

View File

@ -9,7 +9,7 @@ wrist = "Wrist_L"
palm = "Palm_L"
thumb_metacarpal = ""
thumb_proximal = "Thumb_Metacarpal_L"
thumb_intermediate = "Thumb_Proximal_L"
thumb_intermediate = "Thumb_Intermediate_L"
thumb_distal = "Thumb_Distal_L"
thumb_tip = "Thumb_Tip_L"
index_metacarpal = "Index_Metacarpal_L"

View File

@ -9,7 +9,7 @@ wrist = "Wrist_R"
palm = "Palm_R"
thumb_metacarpal = ""
thumb_proximal = "Thumb_Metacarpal_R"
thumb_intermediate = "Thumb_Proximal_R"
thumb_intermediate = "Thumb_Intermediate_R"
thumb_distal = "Thumb_Distal_R"
thumb_tip = "Thumb_Tip_R"
index_metacarpal = "Index_Metacarpal_R"

View File

@ -0,0 +1,11 @@
[gd_resource type="Resource" script_class="RigSettings" load_steps=2 format=3 uid="uid://dlmksvtwwtpp3"]
[ext_resource type="Script" path="res://scripts/RigSettings.gd" id="1_j8gtg"]
[resource]
script = ExtResource("1_j8gtg")
bone_transform = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0)
set_palm = true
set_arm = false
keep_original_arm_size = false
set_joints_positions = true

View File

@ -1,23 +1,22 @@
[gd_scene load_steps=60 format=3 uid="uid://8skajwns477"]
[gd_scene load_steps=19 format=3 uid="uid://8skajwns477"]
[ext_resource type="Script" path="res://scripts/CubeApplication.gd" id="1_58k8d"]
[ext_resource type="Script" path="res://scripts/Starter.gd" id="1_r2upn"]
[ext_resource type="PhysicsMaterial" uid="uid://6k8xjvrlw20i" path="res://materials/physics/Cube.tres" id="2_cn4n8"]
[ext_resource type="Script" path="res://scripts/HandRigger.gd" id="2_tavpj"]
[ext_resource type="Sky" uid="uid://bhsbdowctuwnw" path="res://materials/synthwave_shader_sky.tres" id="4_7qc5t"]
[ext_resource type="Resource" uid="uid://calogg0183u0c" path="res://resources/hand_mappings/default_hands/DefaultLeftHandMapping.tres" id="4_t22n4"]
[ext_resource type="Material" uid="uid://btbe5pq8cyrl3" path="res://materials/hand.tres" id="5_fla0v"]
[ext_resource type="Resource" uid="uid://b30gqri16oypr" path="res://resources/hand_mappings/default_hands/DefaultRightHandMapping.tres" id="5_mnjpi"]
[ext_resource type="Script" path="res://scripts/OriginHands.gd" id="6_7qixu"]
[ext_resource type="Resource" uid="uid://cffd7giqey3m1" path="res://resources/hand_rigging_settings/XRToolsLeftHandRigSettings.tres" id="6_iolh5"]
[ext_resource type="Resource" uid="uid://bsxpetlexducm" path="res://resources/hand_rigging_settings/XRToolsRightHandRigSettings.tres" id="7_3rn24"]
[ext_resource type="Script" path="res://scripts/PinchDetector.gd" id="8_msnd6"]
[ext_resource type="PackedScene" uid="uid://8phfwsorh3hp" path="res://scenes/PinchDisplay.tscn" id="11_jj1t8"]
[ext_resource type="PackedScene" uid="uid://m8f8c3c6sd4d" path="res://models/hand_l.gltf" id="13_6nyhp"]
[ext_resource type="Script" path="res://scripts/HandRiggerTwo.gd" id="14_5pdoj"]
[ext_resource type="Script" path="res://scripts/CubeSpawner.gd" id="14_d8lm2"]
[ext_resource type="PackedScene" uid="uid://camq2pwvs2mjs" path="res://models/hand_r.gltf" id="14_jr4j5"]
[ext_resource type="PackedScene" uid="uid://bgh27uj3dhagi" path="res://scenes/Floor.tscn" id="14_oblky"]
[ext_resource type="Resource" uid="uid://dlmksvtwwtpp3" path="res://resources/hand_rigging_settings/BlenderRightHandRigSettings.tres" id="15_cfof7"]
[ext_resource type="Material" uid="uid://cbko4lro38pl0" path="res://materials/cube.tres" id="16_vxkw4"]
[ext_resource type="Script" path="res://scripts/TreeHands.gd" id="18_gnxc3"]
[ext_resource type="PackedScene" uid="uid://dyb0lbho7xd81" path="res://models/glove.blend" id="22_mxyke"]
[sub_resource type="Environment" id="Environment_rgujr"]
background_mode = 2
@ -27,170 +26,9 @@ ssao_enabled = true
ssil_enabled = true
glow_enabled = true
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_3kea0"]
radius = 0.00351904
height = 0.0351904
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ugce1"]
radius = 0.00449165
height = 0.0449165
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_0o5wh"]
radius = 0.00421479
height = 0.0421479
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_vecey"]
radius = 0.00307495
height = 0.0307495
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_23f8v"]
radius = 0.00803676
height = 0.0803676
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_um8xn"]
radius = 0.00381163
height = 0.0381163
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_h2gkd"]
radius = 0.00313457
height = 0.0313457
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_3qsmt"]
radius = 0.0027395
height = 0.027395
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_vh3jl"]
radius = 0.00804632
height = 0.0804632
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_7fv0f"]
radius = 0.00450034
height = 0.0450034
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_4xuus"]
radius = 0.00324167
height = 0.0324167
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_8mpk0"]
radius = 0.00233466
height = 0.0233466
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_wwf6d"]
radius = 0.00739438
height = 0.0739438
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_bo7ag"]
radius = 0.00401312
height = 0.0401312
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_0723u"]
radius = 0.00277976
height = 0.0277976
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_yplv6"]
radius = 0.00270494
height = 0.0270494
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_j707p"]
radius = 0.00657112
height = 0.0657112
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cgukp"]
radius = 0.00331235
height = 0.0331235
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_r306b"]
radius = 0.00180244
height = 0.0180244
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ablcc"]
radius = 0.00197651
height = 0.0197651
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ucm3s"]
radius = 0.00351904
height = 0.0351904
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cdv4p"]
radius = 0.00449165
height = 0.0449165
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_kygny"]
radius = 0.00421479
height = 0.0421479
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ns6ok"]
radius = 0.00307495
height = 0.0307495
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ks1mn"]
radius = 0.00803676
height = 0.0803676
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_y1gdr"]
radius = 0.00381163
height = 0.0381163
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_v4dnc"]
radius = 0.00313457
height = 0.0313457
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_0sebb"]
radius = 0.0027395
height = 0.027395
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_nljr0"]
radius = 0.00804632
height = 0.0804632
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_iru7b"]
radius = 0.00450034
height = 0.0450034
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_i7l1o"]
radius = 0.00324167
height = 0.0324167
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_8ejtr"]
radius = 0.00233466
height = 0.0233466
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_8yhqs"]
radius = 0.00739438
height = 0.0739438
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ixd66"]
radius = 0.00401312
height = 0.0401312
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_jmqs6"]
radius = 0.00277976
height = 0.0277976
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_0c5ky"]
radius = 0.00270494
height = 0.0270494
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_pyrql"]
radius = 0.00657112
height = 0.0657112
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_dm3kd"]
radius = 0.00331235
height = 0.0331235
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_3r4ji"]
radius = 0.00180244
height = 0.0180244
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_yy4pl"]
radius = 0.00197651
height = 0.0197651
[node name="RiggedHand" type="Node3D" node_paths=PackedStringArray("origin_hands", "rigged_hands", "spawner")]
[node name="RiggedHand" type="Node3D" node_paths=PackedStringArray("origin_hands", "spawner")]
script = ExtResource("1_58k8d")
origin_hands = NodePath("HandTracking/Debug/OriginHands")
rigged_hands = NodePath("Scene/XRToolsHands")
spawner = NodePath("CubeSpawner")
cube_material = ExtResource("2_cn4n8")
@ -208,649 +46,79 @@ directional_shadow_max_distance = 10.0
[node name="WorldEnvironment" type="WorldEnvironment" parent="Scene"]
environment = SubResource("Environment_rgujr")
[node name="XRToolsHands" type="Node3D" parent="Scene"]
[node name="Hands" parent="Scene" instance=ExtResource("22_mxyke")]
[node name="hand_l" parent="Scene/XRToolsHands" instance=ExtResource("13_6nyhp")]
[node name="Skeleton3D" parent="Scene/XRToolsHands/hand_l/Armature" index="0"]
bones/0/rotation = Quaternion(-0.706517, 6.47275e-06, 6.47275e-06, 0.707696)
bones/0/scale = Vector3(1, 1, 1)
bones/1/rotation = Quaternion(0.323537, -2.56575e-05, -0.0272204, 0.945824)
bones/2/rotation = Quaternion(-0.0459503, -0.0271357, -0.0752572, 0.995735)
bones/3/rotation = Quaternion(0.0556407, 0.0103261, 0.0139847, 0.9983)
bones/4/rotation = Quaternion(-0.0757687, -0.019257, -0.0337124, 0.996369)
bones/4/scale = Vector3(1, 1, 1)
bones/5/scale = Vector3(1, 1, 1)
bones/6/rotation = Quaternion(0.111223, -0.00277812, 0.117574, 0.986812)
bones/7/rotation = Quaternion(-0.0136832, -0.0246682, -0.235071, 0.971569)
bones/7/scale = Vector3(1, 1, 1)
bones/8/rotation = Quaternion(0.014226, -0.0119908, -0.134541, 0.990733)
bones/9/rotation = Quaternion(-0.0522884, -0.000557213, 0.103621, 0.993241)
bones/10/rotation = Quaternion(-0.0358546, 4.20051e-05, 0.0499776, 0.998107)
bones/11/rotation = Quaternion(-0.0119466, 0.000966737, -0.0105008, 0.999873)
bones/12/rotation = Quaternion(0.0394554, 0.00492874, -0.137827, 0.989658)
bones/13/rotation = Quaternion(-0.0139315, -0.000142422, -0.168612, 0.985584)
bones/14/rotation = Quaternion(0.0136882, 7.99237e-05, 0.168411, 0.985622)
[node name="Skeleton3D" parent="Scene/Hands/Hand_R" index="0"]
bones/1/rotation = Quaternion(0.0148708, 0.0555304, 0.124875, 0.990506)
bones/1/scale = Vector3(1, 1, 1)
bones/2/rotation = Quaternion(0.0492641, -0.0609617, -0.0313721, 0.99643)
bones/2/scale = Vector3(1, 1, 1)
bones/3/rotation = Quaternion(-0.0410244, 0.0105776, 4.70588e-05, 0.999102)
bones/4/rotation = Quaternion(0.120922, -0.0105837, -0.0250514, 0.992289)
bones/5/rotation = Quaternion(-0.0062748, 0.000262642, 0.00175339, 0.999979)
bones/6/rotation = Quaternion(-0.00698136, -0.000436042, 0.0629245, 0.997994)
bones/6/scale = Vector3(1, 1, 1)
bones/7/rotation = Quaternion(0.000761069, 2.27135e-05, 0.023783, 0.999717)
bones/8/rotation = Quaternion(0.0942945, -0.00647312, -0.0713323, 0.992964)
bones/8/scale = Vector3(1, 1, 1)
bones/9/rotation = Quaternion(0.080584, 0.000863212, 0.0170846, 0.996601)
bones/10/rotation = Quaternion(-0.00988194, 0.000912695, 0.00551936, 0.999936)
bones/11/rotation = Quaternion(-0.00711419, -1.65559e-05, 0.00433084, 0.999965)
bones/11/scale = Vector3(1, 1, 1)
bones/12/rotation = Quaternion(0.0452136, 2.84688e-05, 0.000615496, 0.998977)
bones/12/scale = Vector3(1, 1, 1)
bones/13/rotation = Quaternion(0.0487925, -0.0032748, -0.0268792, 0.998442)
bones/13/scale = Vector3(1, 1, 1)
bones/14/rotation = Quaternion(0.0878815, 0.00360548, 0.0158406, 0.995999)
bones/14/scale = Vector3(1, 1, 1)
bones/15/rotation = Quaternion(-0.0711949, 1.57336e-05, 0.0180856, 0.997298)
bones/16/rotation = Quaternion(-0.0510567, -0.00202299, 0.0418045, 0.997818)
bones/16/scale = Vector3(1, 1, 1)
bones/17/rotation = Quaternion(0.0173263, 0.0186085, -0.160829, 0.986655)
bones/17/scale = Vector3(1, 1, 1)
bones/18/rotation = Quaternion(-0.0113518, 0.0126214, -0.131984, 0.991107)
bones/15/rotation = Quaternion(-0.0471006, -0.000381474, -0.00279166, 0.998886)
bones/16/rotation = Quaternion(-0.0142445, -0.0969636, -0.0737366, 0.992451)
bones/17/rotation = Quaternion(0.0775611, 0.0958066, -0.0339574, 0.991792)
bones/18/rotation = Quaternion(0.0646157, 0.0109749, 0.0537625, 0.996401)
bones/18/scale = Vector3(1, 1, 1)
bones/19/rotation = Quaternion(0.0330517, -0.00671975, 0.126351, 0.991412)
bones/19/rotation = Quaternion(-0.0369754, 0.00858932, -0.0199233, 0.999081)
bones/19/scale = Vector3(1, 1, 1)
bones/20/rotation = Quaternion(-0.0917695, 2.45622e-05, 0.0284477, 0.995374)
bones/20/scale = Vector3(1, 1, 1)
bones/21/rotation = Quaternion(-0.089286, 0.00316807, -0.00673912, 0.995978)
bones/21/scale = Vector3(1, 1, 1)
bones/22/rotation = Quaternion(0.0449258, 0.032808, -0.185059, 0.981152)
bones/23/rotation = Quaternion(-0.0180548, 0.0114553, -0.107075, 0.994021)
bones/20/rotation = Quaternion(0.0569544, 0.00986803, 0.0573357, 0.99668)
bones/22/scale = Vector3(1, 1, 1)
bones/23/rotation = Quaternion(0.134974, 0.0393908, -0.0475089, 0.988925)
bones/23/scale = Vector3(1, 1, 1)
bones/24/rotation = Quaternion(0.0158363, -0.0193397, 0.153777, 0.987789)
bones/24/rotation = Quaternion(0.0804022, 0.0142776, -0.0117548, 0.996591)
[node name="mesh_Hand_L" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="0"]
surface_material_override/0 = ExtResource("5_fla0v")
[node name="Physical Bone Wrist_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="1"]
transform = Transform3D(-0.894427, -0.328111, -0.284155, 0.45, -0.68627, -0.569097, 0.000767299, -0.635755, 0.770956, 0.0046894, 0.00962776, 0.0135056)
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.000119256, -0.000431016, 0.0173759)
body_offset = Transform3D(-0.894427, -0.328099, -0.284169, -4.41447e-07, 0.634615, -0.771898, 0.45, -0.68733, -0.567811, 0.00468961, 0.013686, 0.00962365)
bone_name = "Wrist_L"
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Wrist_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_3kea0")
[node name="Physical Bone Thumb_Metacarpal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="2"]
transform = Transform3D(0.998519, -0.0176556, -0.0514608, -0.0176508, 0.789625, -0.613335, 0.0514602, 0.613335, 0.788145, 0.0111553, 0.033801, -0.0176645)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.64335e-08, 3.94061e-08, 0.0224583)
body_offset = Transform3D(1, -4.66034e-06, -3.46452e-07, 2.71946e-07, -8.34465e-07, -1, -1.49012e-08, 1, -1.78814e-07, 2.42144e-08, 0.0224583, -3.53903e-08)
bone_name = "Thumb_Metacarpal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Thumb_Metacarpal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_ugce1")
[node name="Physical Bone Thumb_Proximal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="3"]
transform = Transform3D(0.977072, -0.0596789, -0.204293, -0.0596847, 0.844592, -0.532007, 0.204339, 0.532052, 0.821444, 0.0166173, 0.0587883, -0.0526805)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.74011e-08, 1.33086e-06, 0.0210788)
body_offset = Transform3D(0.999993, 1.2666e-06, 3.27826e-07, 2.72691e-06, 2.0802e-05, -0.999769, -1.85519e-06, 0.999989, -6.44624e-05, 2.04891e-08, 0.0210739, 2.79397e-08)
bone_name = "Thumb_Proximal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Thumb_Proximal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_0o5wh")
[node name="Physical Bone Thumb_Distal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="4"]
transform = Transform3D(0.983664, -0.0602819, -0.17022, -0.0602855, 0.778919, -0.624345, 0.170209, 0.624254, 0.762592, 0.0235403, 0.0795988, -0.0817191)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.83344e-09, 2.44007e-08, 0.0153722)
body_offset = Transform3D(1.0001, -1.14739e-06, -4.91738e-07, 1.63913e-07, 9.83477e-07, -1.00016, 4.47035e-08, 1.00002, -1.10269e-06, 3.72529e-09, 0.0153747, -7.45058e-09)
bone_name = "Thumb_Distal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Thumb_Distal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_vecey")
[node name="Physical Bone Index_Metacarpal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="5"]
transform = Transform3D(0.998759, 1.23566e-05, 0.0504416, 1.23529e-05, 1, -0.000489544, -0.0504434, 0.000489542, 0.998759, -0.0120277, 0.0224307, -0.0402555)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -7.64561e-06, 2.08032e-05, 0.0403411)
body_offset = Transform3D(1.00003, -2.29193e-10, -1.53109e-06, 3.01749e-07, -4.44124e-08, -1.00003, 2.65572e-10, 1, -3.02098e-08, 7.70763e-06, 0.0403424, -2.0802e-05)
bone_name = "Index_Metacarpal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Index_Metacarpal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_23f8v")
[node name="Physical Bone Index_Proximal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="6"]
transform = Transform3D(0.959421, 0.0317731, 0.280169, 0.0317659, 0.975142, -0.219322, -0.280181, 0.219338, 0.934561, -0.0193939, 0.0266509, -0.0980405)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.02987e-08, 3.78956e-08, 0.0190583)
body_offset = Transform3D(0.999999, 5.52088e-06, 4.05312e-06, 1.71065e-05, -1.90884e-05, -1, 7.82311e-08, 1.00001, -2.08616e-06, -8.75443e-08, 0.0190583, 1.86265e-09)
bone_name = "Index_Proximal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Index_Proximal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_um8xn")
[node name="Physical Bone Index_Intermediate_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="7"]
transform = Transform3D(0.981736, -0.0188037, -0.189316, -0.0188038, 0.98064, -0.194909, 0.189317, 0.194911, 0.962376, -0.0217661, 0.0338857, -0.130935)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.2221e-08, -1.45816e-07, 0.0156729)
body_offset = Transform3D(1, -1.11759e-08, -4.17233e-07, -5.96046e-07, -8.9407e-08, -0.999999, -1.52737e-07, 1, 1.3411e-06, -8.56817e-08, 0.0156729, 1.24797e-07)
bone_name = "Index_Intermediate_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Index_Intermediate_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_h2gkd")
[node name="Physical Bone Index_Distal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="8"]
transform = Transform3D(0.895003, -0.0505997, -0.443181, -0.0505995, 0.975614, -0.213576, 0.443181, 0.213575, 0.870618, -0.0127286, 0.039866, -0.157943)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.4303e-08, -1.40158e-07, 0.0136975)
body_offset = Transform3D(1, -2.6077e-07, -7.15256e-07, -3.8743e-07, 2.68221e-07, -1, 2.08616e-07, 0.999999, -2.38419e-07, -7.45058e-08, 0.0136975, 1.43424e-07)
bone_name = "Index_Distal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Index_Distal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_3qsmt")
[node name="Physical Bone Middle_Metacarpal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="9"]
transform = Transform3D(0.994997, -0.00350133, 0.0997857, -0.00350155, 0.997547, 0.0699121, -0.0997868, -0.0699142, 0.992497, -0.0140149, 0.000741391, -0.0398954)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.91443e-07, -2.26466e-08, 0.0402332)
body_offset = Transform3D(0.999994, -1.86265e-08, 3.72529e-06, 2.30968e-07, 1.11759e-07, -0.999948, -2.44472e-07, 1, 1.74344e-06, 1.41561e-07, 0.0402311, -4.74975e-08)
bone_name = "Middle_Metacarpal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Middle_Metacarpal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_vh3jl")
[node name="Physical Bone Middle_Proximal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="10"]
transform = Transform3D(0.996886, -0.00370814, 0.0787784, -0.0037077, 0.99558, 0.093801, -0.0787696, -0.0938002, 0.99245, -0.0198062, -0.00418107, -0.10216)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.80444e-06, -1.03029e-06, 0.0225024)
body_offset = Transform3D(1, 5.68107e-07, -1.9446e-06, -1.23605e-05, -3.35276e-06, -0.999981, 1.32248e-07, 0.999996, -1.06543e-06, -3.76068e-06, 0.022502, 1.05426e-06)
bone_name = "Middle_Proximal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Middle_Proximal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_7fv0f")
[node name="Physical Bone Middle_Intermediate_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="11"]
transform = Transform3D(0.9805, 0.00144519, -0.196513, 0.0014446, 0.999893, 0.0145677, 0.196511, -0.0145641, 0.980391, -0.0183899, -0.00653144, -0.140384)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.10588e-07, 2.46853e-06, 0.0162092)
body_offset = Transform3D(0.999999, 9.31323e-09, -3.8743e-07, 2.65241e-06, 0, -0.999998, -5.50644e-07, 1, 3.61633e-06, -1.04308e-07, 0.0162091, -2.52714e-06)
bone_name = "Middle_Intermediate_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Middle_Intermediate_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_4xuus")
[node name="Physical Bone Middle_Distal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="12"]
transform = Transform3D(0.859443, 0.0111824, -0.511109, 0.0111817, 0.99917, 0.0406604, 0.511109, -0.0406654, 0.858553, -0.00923841, -0.00723905, -0.166295)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.49379e-08, -5.47541e-07, 0.0116733)
body_offset = Transform3D(1, -1.37836e-06, 2.38419e-07, 5.96046e-08, 2.22027e-06, -1, -5.7742e-08, 1.00006, -3.72529e-09, 5.21541e-08, 0.0116733, 5.47618e-07)
bone_name = "Middle_Distal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Middle_Distal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_8mpk0")
[node name="Physical Bone Ring_Metacarpal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="13"]
transform = Transform3D(0.999345, -0.00254654, 0.0360913, -0.00254641, 0.990095, 0.140358, -0.0360938, -0.140355, 0.989451, -0.0113347, -0.0182626, -0.0365457)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.25742e-07, -7.96737e-08, 0.0369716)
body_offset = Transform3D(1, -9.0804e-08, -2.84612e-06, -6.70552e-08, 1.19209e-06, -1.00001, -1.21072e-08, 0.999997, 2.20537e-06, 2.30968e-07, 0.0369719, -1.86265e-09)
bone_name = "Ring_Metacarpal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Ring_Metacarpal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_wwf6d")
[node name="Physical Bone Ring_Proximal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="14"]
transform = Transform3D(0.992826, -0.014505, 0.118691, -0.0145051, 0.970639, 0.239961, -0.118685, -0.239963, 0.963492, -0.0150509, -0.0282668, -0.0924602)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.06737e-07, 4.92861e-08, 0.0200655)
body_offset = Transform3D(1, -1.49012e-07, 6.94394e-06, -7.45058e-08, 1.51992e-06, -0.999993, 3.72529e-08, 0.999966, -7.37607e-06, -3.25963e-08, 0.0200654, 9.87202e-08)
bone_name = "Ring_Proximal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Ring_Proximal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_bo7ag")
[node name="Physical Bone Ring_Intermediate_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="15"]
transform = Transform3D(0.978981, 0.0212057, -0.20285, 0.0212061, 0.978606, 0.204665, 0.202846, -0.204647, 0.957593, -0.0146131, -0.0359265, -0.125103)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.55555e-08, -1.49379e-07, 0.0138987)
body_offset = Transform3D(1, 1.2666e-07, -2.47359e-06, -8.04663e-07, 1.49012e-08, -1.00001, 3.57628e-07, 1, 1.63764e-05, -1.11759e-08, 0.0138988, -7.82311e-08)
bone_name = "Ring_Intermediate_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Ring_Intermediate_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_0723u")
[node name="Physical Bone Ring_Distal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="16"]
transform = Transform3D(0.890969, 0.0525079, -0.451019, 0.0525077, 0.974714, 0.217205, 0.451018, -0.217203, 0.865681, -0.00569391, -0.0417084, -0.15012)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.981e-08, -2.98654e-09, 0.0135247)
body_offset = Transform3D(1, 8.9407e-08, -2.98023e-08, 1.19209e-06, -1.49012e-07, -1, 1.04308e-07, 1, 1.46031e-06, -8.9407e-08, 0.0135247, -1.67638e-08)
bone_name = "Ring_Distal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Ring_Distal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_yplv6")
[node name="Physical Bone Little_Metacarpal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="17"]
transform = Transform3D(0.99838, -0.00517571, 0.0566563, -0.0051757, 0.983461, 0.181058, -0.0566547, -0.181051, 0.981891, -0.00186194, -0.0359219, -0.0322231)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.729e-08, 7.06004e-09, 0.032854)
body_offset = Transform3D(1, -1.02445e-08, -1.30385e-06, 1.11759e-08, -1.19209e-07, -1.00005, -9.31323e-10, 1, -2.36928e-06, -5.44533e-08, 0.0328557, 7.07805e-08)
bone_name = "Little_Metacarpal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Little_Metacarpal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_j707p")
[node name="Physical Bone Little_Proximal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="18"]
transform = Transform3D(0.999119, -0.00766528, 0.0419904, -0.00766681, 0.935528, 0.353171, -0.0419913, -0.353169, 0.934616, -0.00441872, -0.0477197, -0.0799608)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.18104e-08, -5.29351e-10, 0.0165618)
body_offset = Transform3D(1.00003, -2.70084e-07, 2.34321e-06, 2.47732e-06, -1.19209e-07, -1, -7.36676e-07, 1, 2.05636e-06, -1.69966e-08, 0.0165618, -3.35276e-08)
bone_name = "Little_Proximal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Little_Proximal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_cgukp")
[node name="Physical Bone Little_Intermediate_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="19"]
transform = Transform3D(0.944251, 0.0443014, -0.326231, 0.0443017, 0.964834, 0.259245, 0.326232, -0.259253, 0.909046, -0.00217368, -0.0559051, -0.103633)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.24323e-07, -6.70526e-08, 0.00901262)
body_offset = Transform3D(1, -8.86619e-07, 8.9407e-08, -1.49012e-07, -3.60608e-06, -1, 0, 1.00004, 0, 2.23517e-07, 0.00901262, 6.70552e-08)
bone_name = "Little_Intermediate_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Little_Intermediate_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_r306b")
[node name="Physical Bone Little_Distal_L" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D" index="20"]
transform = Transform3D(0.854312, 0.0804069, -0.517508, 0.0817176, 0.956642, 0.280723, 0.52251, -0.280784, 0.80837, 0.00587975, -0.0610156, -0.119813)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.06259e-07, -1.73672e-08, 0.0098821)
body_offset = Transform3D(1.00476, 5.21988e-05, -4.1306e-05, -0.0032497, 3.51518e-05, -1.00004, 0.000102505, 1.00023, -1.63913e-06, 0, 0.00988252, 3.35276e-08)
bone_name = "Little_Distal_L"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_l/Armature/Skeleton3D/Physical Bone Little_Distal_L"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_ablcc")
[node name="hand_r" parent="Scene/XRToolsHands" instance=ExtResource("14_jr4j5")]
[node name="Skeleton3D" parent="Scene/XRToolsHands/hand_r/Armature" index="0"]
bones/0/rotation = Quaternion(-0.706517, -6.47275e-06, -6.47275e-06, 0.707696)
bones/0/scale = Vector3(1, 1, 1)
bones/1/rotation = Quaternion(0.323537, 2.56575e-05, 0.0272204, 0.945824)
bones/2/rotation = Quaternion(-0.0459503, 0.0271357, 0.0752572, 0.995735)
bones/3/rotation = Quaternion(0.0556407, -0.0103261, -0.0139847, 0.9983)
bones/4/rotation = Quaternion(-0.0757687, 0.019257, 0.0337124, 0.996369)
bones/4/scale = Vector3(1, 1, 1)
bones/5/scale = Vector3(1, 1, 1)
bones/6/rotation = Quaternion(0.111223, 0.00277812, -0.117574, 0.986812)
bones/7/rotation = Quaternion(-0.0136832, 0.0246682, 0.235071, 0.971569)
bones/7/scale = Vector3(1, 1, 1)
bones/8/rotation = Quaternion(0.014226, 0.0119908, 0.134541, 0.990733)
bones/9/rotation = Quaternion(-0.0522884, 0.000557213, -0.103621, 0.993241)
bones/10/rotation = Quaternion(-0.0358546, -4.20051e-05, -0.0499776, 0.998107)
bones/11/rotation = Quaternion(-0.0119466, -0.000966737, 0.0105008, 0.999873)
bones/12/rotation = Quaternion(0.0394554, -0.00492874, 0.137827, 0.989658)
bones/13/rotation = Quaternion(-0.0139315, 0.000142422, 0.168612, 0.985584)
bones/14/rotation = Quaternion(0.0136882, -7.99237e-05, -0.168411, 0.985622)
[node name="Skeleton3D" parent="Scene/Hands/Hand_L" index="0"]
bones/1/rotation = Quaternion(0.0148708, -0.0555304, -0.124875, 0.990506)
bones/1/scale = Vector3(1, 1, 1)
bones/2/rotation = Quaternion(0.0492641, 0.0609617, 0.0313721, 0.99643)
bones/2/scale = Vector3(1, 1, 1)
bones/3/rotation = Quaternion(-0.0410244, -0.0105776, -4.70588e-05, 0.999102)
bones/4/rotation = Quaternion(0.120922, 0.0105837, 0.0250514, 0.992289)
bones/5/rotation = Quaternion(-0.0062748, -0.000262642, -0.00175339, 0.999979)
bones/6/rotation = Quaternion(-0.00698136, 0.000436042, -0.0629245, 0.997994)
bones/6/scale = Vector3(1, 1, 1)
bones/7/rotation = Quaternion(0.000761069, -2.27135e-05, -0.023783, 0.999717)
bones/8/rotation = Quaternion(0.0942945, 0.00647312, 0.0713323, 0.992964)
bones/8/scale = Vector3(1, 1, 1)
bones/9/rotation = Quaternion(0.080584, -0.000863212, -0.0170846, 0.996601)
bones/10/rotation = Quaternion(-0.00988194, -0.000912695, -0.00551936, 0.999936)
bones/11/rotation = Quaternion(-0.00711419, 1.65559e-05, -0.00433084, 0.999965)
bones/11/scale = Vector3(1, 1, 1)
bones/12/rotation = Quaternion(0.0452136, -2.84688e-05, -0.000615496, 0.998977)
bones/12/scale = Vector3(1, 1, 1)
bones/13/rotation = Quaternion(0.0487925, 0.0032748, 0.0268792, 0.998442)
bones/13/scale = Vector3(1, 1, 1)
bones/14/rotation = Quaternion(0.0878815, -0.00360548, -0.0158406, 0.995999)
bones/14/scale = Vector3(1, 1, 1)
bones/15/rotation = Quaternion(-0.0711949, -1.57336e-05, -0.0180856, 0.997298)
bones/16/rotation = Quaternion(-0.0510567, 0.00202299, -0.0418045, 0.997818)
bones/16/scale = Vector3(1, 1, 1)
bones/17/rotation = Quaternion(0.0173263, -0.0186085, 0.160829, 0.986655)
bones/17/scale = Vector3(1, 1, 1)
bones/18/rotation = Quaternion(-0.0113518, -0.0126214, 0.131984, 0.991107)
bones/15/rotation = Quaternion(-0.0471006, 0.000381474, 0.00279166, 0.998886)
bones/16/rotation = Quaternion(-0.0142445, 0.0969636, 0.0737366, 0.992451)
bones/17/rotation = Quaternion(0.0775611, -0.0958066, 0.0339574, 0.991792)
bones/18/rotation = Quaternion(0.0646157, -0.0109749, -0.0537625, 0.996401)
bones/18/scale = Vector3(1, 1, 1)
bones/19/rotation = Quaternion(0.0330517, 0.00671975, -0.126351, 0.991412)
bones/19/rotation = Quaternion(-0.0369754, -0.00858932, 0.0199233, 0.999081)
bones/19/scale = Vector3(1, 1, 1)
bones/20/rotation = Quaternion(-0.0917695, -2.45622e-05, -0.0284477, 0.995374)
bones/20/scale = Vector3(1, 1, 1)
bones/21/rotation = Quaternion(-0.089286, -0.00316807, 0.00673912, 0.995978)
bones/21/scale = Vector3(1, 1, 1)
bones/22/rotation = Quaternion(0.0449258, -0.032808, 0.185059, 0.981152)
bones/23/rotation = Quaternion(-0.0180548, -0.0114553, 0.107075, 0.994021)
bones/20/rotation = Quaternion(0.0569544, -0.00986803, -0.0573357, 0.99668)
bones/22/scale = Vector3(1, 1, 1)
bones/23/rotation = Quaternion(0.134974, -0.0393908, 0.0475089, 0.988925)
bones/23/scale = Vector3(1, 1, 1)
bones/24/rotation = Quaternion(0.0158363, 0.0193397, -0.153777, 0.987789)
[node name="mesh_Hand_R" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="0"]
surface_material_override/0 = ExtResource("5_fla0v")
[node name="Physical Bone Wrist_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="1"]
transform = Transform3D(-0.894427, 0.329417, 0.27117, -0.45, -0.698944, -0.568182, -0.000767299, -0.635776, 0.759248, -0.004742, 0.00972582, 0.0135063)
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8.08761e-05, -0.000381693, 0.0176839)
body_offset = Transform3D(-0.894427, 0.329405, 0.271184, 4.41447e-07, 0.634615, -0.76019, -0.45, -0.700004, -0.566915, -0.00474221, 0.0136854, 0.0097217)
bone_name = "Wrist_R"
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Wrist_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_ucm3s")
[node name="Physical Bone Thumb_Metacarpal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="2"]
transform = Transform3D(0.998519, 0.0176556, 0.0514608, 0.0176509, 0.789625, -0.613335, -0.0514602, 0.613335, 0.788145, -0.0111553, 0.033801, -0.0176644)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.92277e-08, -1.64732e-08, 0.0224583)
body_offset = Transform3D(1, 4.66034e-06, 3.46452e-07, -1.49012e-07, -8.34465e-07, -1, 9.31323e-08, 1, -1.78814e-07, -2.70084e-08, 0.0224583, 2.04891e-08)
bone_name = "Thumb_Metacarpal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Thumb_Metacarpal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_cdv4p")
[node name="Physical Bone Thumb_Proximal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="3"]
transform = Transform3D(0.977074, 0.0596813, 0.204293, 0.0596848, 0.844589, -0.532007, -0.204341, 0.532063, 0.821444, -0.0166173, 0.0587883, -0.0526805)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.06371e-08, 1.33831e-06, 0.0210788)
body_offset = Transform3D(0.999996, -1.19954e-06, -3.27826e-07, -1.80304e-06, 1.00136e-05, -0.999769, 1.18464e-06, 0.999992, -6.44624e-05, -3.72529e-09, 0.0210739, 2.04891e-08)
bone_name = "Thumb_Proximal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Thumb_Proximal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_kygny")
[node name="Physical Bone Thumb_Distal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="4"]
transform = Transform3D(0.983664, 0.0602819, 0.17022, 0.0602855, 0.778919, -0.624345, -0.170209, 0.624254, 0.762592, -0.0235403, 0.0795988, -0.0817191)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.83344e-09, 2.44007e-08, 0.0153722)
body_offset = Transform3D(1.0001, 1.14739e-06, 4.91738e-07, -1.63913e-07, 9.83477e-07, -1.00016, -4.47035e-08, 1.00002, -1.10269e-06, -3.72529e-09, 0.0153747, -7.45058e-09)
bone_name = "Thumb_Distal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Thumb_Distal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_ns6ok")
[node name="Physical Bone Index_Metacarpal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="5"]
transform = Transform3D(0.998759, -1.23563e-05, -0.0504416, -1.23528e-05, 1, -0.000489532, 0.0504434, 0.000489534, 0.998759, 0.0120277, 0.0224307, -0.0402555)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.64651e-06, 2.08028e-05, 0.0403411)
body_offset = Transform3D(1.00003, 1.56433e-10, 1.50874e-06, -3.01749e-07, -3.63216e-08, -1.00003, -9.09495e-11, 1, -1.91503e-08, -7.70763e-06, 0.0403424, -2.0802e-05)
bone_name = "Index_Metacarpal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Index_Metacarpal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_ks1mn")
[node name="Physical Bone Index_Proximal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="6"]
transform = Transform3D(0.959421, -0.0317698, -0.280167, -0.0317659, 0.975133, -0.21932, 0.280181, 0.219334, 0.934562, 0.0193939, 0.0266509, -0.0980405)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.19852e-08, 7.22504e-09, 0.0190583)
body_offset = Transform3D(0.999999, -3.23728e-06, -1.3411e-06, -1.71065e-05, -1.62423e-05, -1, -7.82311e-08, 1, -4.76837e-07, 8.75443e-08, 0.0190583, 1.86265e-09)
bone_name = "Index_Proximal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Index_Proximal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_y1gdr")
[node name="Physical Bone Index_Intermediate_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="7"]
transform = Transform3D(0.981736, 0.0188037, 0.189316, 0.0188038, 0.98064, -0.194909, -0.189317, 0.194911, 0.962376, 0.0217661, 0.0338856, -0.130934)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.51656e-08, -7.13102e-08, 0.0156727)
body_offset = Transform3D(1, 1.11759e-08, 4.17233e-07, 5.96046e-07, -8.9407e-08, -0.999999, 1.52737e-07, 1, 1.3411e-06, 1.86265e-08, 0.0156727, 5.02914e-08)
bone_name = "Index_Intermediate_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Index_Intermediate_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_v4dnc")
[node name="Physical Bone Index_Distal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="8"]
transform = Transform3D(0.895003, 0.0506001, 0.443191, 0.0505995, 0.975611, -0.213573, -0.443181, 0.213575, 0.870614, 0.0127286, 0.039866, -0.157943)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.2922e-07, -1.73428e-07, 0.0136975)
body_offset = Transform3D(1, 5.73695e-07, 1.12951e-05, 3.8743e-07, -2.98023e-07, -1, -2.08616e-07, 0.999996, 2.19047e-06, 7.45058e-08, 0.0136975, 1.43424e-07)
bone_name = "Index_Distal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Index_Distal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_0sebb")
[node name="Physical Bone Middle_Metacarpal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="9"]
transform = Transform3D(0.994997, 0.00350132, -0.0997857, 0.00350141, 0.997547, 0.0699122, 0.0997876, -0.0699142, 0.992497, 0.0140149, 0.000741375, -0.0398954)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.35563e-07, -9.81168e-09, 0.0402332)
body_offset = Transform3D(0.999994, 6.98492e-09, -3.72529e-06, -1.08033e-06, 1.04308e-07, -0.999948, 4.74975e-08, 1, 1.81794e-06, -8.56817e-08, 0.0402311, -6.33299e-08)
bone_name = "Middle_Metacarpal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Middle_Metacarpal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_nljr0")
[node name="Physical Bone Middle_Proximal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="10"]
transform = Transform3D(0.996886, 0.00370751, -0.078777, 0.0037077, 0.99558, 0.093801, 0.0787696, -0.0938004, 0.992453, 0.0198026, -0.00418212, -0.10216)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.58524e-07, 4.1205e-08, 0.0225022)
body_offset = Transform3D(1, -1.21025e-06, 3.58373e-06, 1.23605e-05, -3.2559e-06, -0.999984, -1.32248e-07, 0.999996, -1.43796e-06, 1.77883e-07, 0.0225018, -8.84756e-09)
bone_name = "Middle_Proximal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Middle_Proximal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_iru7b")
[node name="Physical Bone Middle_Intermediate_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="11"]
transform = Transform3D(0.9805, -0.00144519, 0.196512, -0.0014446, 0.999893, 0.0145684, -0.196511, -0.0145641, 0.980387, 0.01839, -0.00655322, -0.140383)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.21105e-07, 2.4244e-05, 0.0162088)
body_offset = Transform3D(0.999999, -9.31323e-09, -3.42727e-07, -2.65241e-06, 0, -0.999995, 5.50644e-07, 1, 4.34741e-06, 1.2666e-07, 0.0162087, -2.43145e-05)
bone_name = "Middle_Intermediate_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Middle_Intermediate_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_i7l1o")
[node name="Physical Bone Middle_Distal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="12"]
transform = Transform3D(0.859443, -0.01118, 0.511109, -0.0111817, 0.99916, 0.0406604, -0.511109, -0.040661, 0.858553, 0.00923841, -0.00723905, -0.166295)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.49379e-08, -5.47547e-07, 0.0116733)
body_offset = Transform3D(1, 1.2368e-06, -2.38419e-07, -5.96046e-08, -2.43261e-06, -1, 5.7742e-08, 1.00005, -3.72529e-09, -5.21541e-08, 0.0116733, 5.47618e-07)
bone_name = "Middle_Distal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Middle_Distal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_8ejtr")
[node name="Physical Bone Ring_Metacarpal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="13"]
transform = Transform3D(0.999345, 0.00254691, -0.0360913, 0.00254641, 0.990167, 0.140358, 0.0360938, -0.140374, 0.98945, 0.0113347, -0.0182626, -0.0365457)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.2588e-07, -7.14046e-08, 0.0369716)
body_offset = Transform3D(1, -1.25729e-08, 2.8424e-06, 6.70552e-08, 9.23872e-06, -1.00001, 1.21072e-08, 1.00007, 1.98185e-06, -2.30968e-07, 0.0369719, -1.86265e-09)
bone_name = "Ring_Metacarpal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Ring_Metacarpal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_8yhqs")
[node name="Physical Bone Ring_Proximal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="14"]
transform = Transform3D(0.992826, 0.014505, -0.118691, 0.0145051, 0.970621, 0.239961, 0.118685, -0.239958, 0.963492, 0.0150509, -0.0282669, -0.0924603)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.1512e-07, 1.18209e-07, 0.0200656)
body_offset = Transform3D(1, 5.25266e-07, -6.94394e-06, 7.45058e-08, 9.38773e-07, -0.999993, -3.72529e-08, 0.999948, -7.37607e-06, 2.42144e-08, 0.0200655, 2.98023e-08)
bone_name = "Ring_Proximal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Ring_Proximal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_ixd66")
[node name="Physical Bone Ring_Intermediate_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="15"]
transform = Transform3D(0.978981, -0.0212056, 0.202848, -0.0212059, 0.978606, 0.204671, -0.202845, -0.204647, 0.957578, 0.0146131, -0.0359265, -0.125103)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.89592e-08, -2.75304e-07, 0.0138988)
body_offset = Transform3D(1, 3.72529e-09, 4.15742e-06, -2.83122e-07, 2.98023e-07, -0.999998, -3.98606e-07, 1, 2.54363e-05, 1.11759e-08, 0.0138988, -7.82311e-08)
bone_name = "Ring_Intermediate_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Ring_Intermediate_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_jmqs6")
[node name="Physical Bone Ring_Distal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="16"]
transform = Transform3D(0.890969, -0.0525079, 0.451019, -0.0525077, 0.974714, 0.217205, -0.451018, -0.217203, 0.865681, 0.00569391, -0.0417084, -0.15012)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8.981e-08, -2.98654e-09, 0.0135247)
body_offset = Transform3D(1, -8.9407e-08, 2.98023e-08, -1.19209e-06, -1.49012e-07, -1, -1.04308e-07, 1, 1.46031e-06, 8.9407e-08, 0.0135247, -1.67638e-08)
bone_name = "Ring_Distal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Ring_Distal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_0c5ky")
[node name="Physical Bone Little_Metacarpal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="17"]
transform = Transform3D(0.99838, 0.00517571, -0.056656, 0.0051757, 0.983461, 0.181058, 0.0566547, -0.181051, 0.981881, 0.00186188, -0.035922, -0.0322231)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.87333e-08, -3.37417e-08, 0.0328544)
body_offset = Transform3D(1, 1.02445e-08, 1.02073e-06, -1.11759e-08, -1.19209e-07, -1.00004, 9.31323e-10, 1, -4.47035e-07, -4.80213e-09, 0.0328557, 4.84288e-08)
bone_name = "Little_Metacarpal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Little_Metacarpal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_pyrql")
[node name="Physical Bone Little_Proximal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="18"]
transform = Transform3D(0.999119, 0.00766528, -0.041989, 0.00766523, 0.935528, 0.353169, 0.041989, -0.353169, 0.934616, 0.00441872, -0.0477197, -0.0799608)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.553e-10, 2.9579e-08, 0.0165618)
body_offset = Transform3D(1.00003, 2.70084e-07, -9.68575e-07, 3.1665e-07, -1.19209e-07, -1, 9.59262e-08, 1, 2.38419e-07, 1.69966e-08, 0.0165618, -3.35276e-08)
bone_name = "Little_Proximal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Little_Proximal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_dm3kd")
[node name="Physical Bone Little_Intermediate_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="19"]
transform = Transform3D(0.944251, -0.0443013, 0.326231, -0.0443017, 0.964795, 0.259245, -0.326232, -0.259244, 0.909046, 0.00217368, -0.0559051, -0.103633)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.24323e-07, -6.70552e-08, 0.00901262)
body_offset = Transform3D(1, 4.47035e-08, -8.9407e-08, 1.49012e-07, -9.38773e-07, -1, 0, 1, 0, -2.23517e-07, 0.00901262, 6.70552e-08)
bone_name = "Little_Intermediate_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Little_Intermediate_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_3r4ji")
[node name="Physical Bone Little_Distal_R" type="PhysicalBone3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D" index="20"]
transform = Transform3D(0.854312, -0.0804069, 0.517506, -0.0817176, 0.956642, 0.280724, -0.52251, -0.280784, 0.808371, -0.00587974, -0.0610156, -0.119813)
joint_type = 1
joint_offset = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.93743e-07, -2.21802e-08, 0.0098821)
body_offset = Transform3D(1.00476, -5.21988e-05, 3.92795e-05, 0.0032497, 3.51518e-05, -1.00004, -0.000102505, 1.00023, -7.7486e-07, 7.45058e-09, 0.00988252, 2.98023e-08)
bone_name = "Little_Distal_R"
joint_constraints/bias = 0.3
joint_constraints/damping = 1.0
joint_constraints/impulse_clamp = 0.0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Scene/XRToolsHands/hand_r/Armature/Skeleton3D/Physical Bone Little_Distal_R"]
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0)
shape = SubResource("CapsuleShape3D_yy4pl")
[node name="ReflectionProbe" type="ReflectionProbe" parent="Scene"]
visible = false
update_mode = 1
bones/24/rotation = Quaternion(0.0804022, -0.0142776, 0.0117548, 0.996591)
[node name="HandTracking" type="Node3D" parent="."]
@ -860,25 +128,26 @@ script = ExtResource("1_r2upn")
[node name="UltraleapDeviceNode" type="UltraleapDeviceNode" parent="HandTracking"]
tracker = NodePath("../UltraleapHandTracking")
rigging_transform = Transform3D(-1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0)
[node name="HandRiggers" type="Node3D" parent="HandTracking"]
[node name="XRToolsHandRiggers" type="Node3D" parent="HandTracking/HandRiggers"]
[node name="GloveHandsRiggers" type="Node3D" parent="HandTracking/HandRiggers"]
[node name="LeftHandRiggerXRTools" type="Node3D" parent="HandTracking/HandRiggers/XRToolsHandRiggers" node_paths=PackedStringArray("tracking", "skeleton")]
script = ExtResource("2_tavpj")
[node name="LeftHandRiggerGlove" type="Node3D" parent="HandTracking/HandRiggers/GloveHandsRiggers" node_paths=PackedStringArray("tracking", "skeleton")]
script = ExtResource("14_5pdoj")
tracking = NodePath("../../../UltraleapDeviceNode")
skeleton = NodePath("../../../../Scene/XRToolsHands/hand_l/Armature/Skeleton3D")
skeleton = NodePath("../../../../Scene/Hands/Hand_L/Skeleton3D")
ex_map = ExtResource("4_t22n4")
rig_settings = ExtResource("6_iolh5")
[node name="RightHandRiggerXRTools" type="Node3D" parent="HandTracking/HandRiggers/XRToolsHandRiggers" node_paths=PackedStringArray("tracking", "skeleton")]
script = ExtResource("2_tavpj")
[node name="RightHandRiggerGlove" type="Node3D" parent="HandTracking/HandRiggers/GloveHandsRiggers" node_paths=PackedStringArray("tracking", "skeleton")]
script = ExtResource("14_5pdoj")
tracking = NodePath("../../../UltraleapDeviceNode")
handedness = 1
skeleton = NodePath("../../../../Scene/XRToolsHands/hand_r/Armature/Skeleton3D")
skeleton = NodePath("../../../../Scene/Hands/Hand_R/Skeleton3D")
ex_map = ExtResource("5_mnjpi")
rig_settings = ExtResource("7_3rn24")
rig_settings = ExtResource("15_cfof7")
[node name="Detectors" type="Node3D" parent="HandTracking"]
@ -898,7 +167,7 @@ display = NodePath("../../../Debug/RightPinchDisplay")
[node name="Debug" type="Node3D" parent="HandTracking"]
[node name="OriginHands" type="Node3D" parent="HandTracking/Debug" node_paths=PackedStringArray("tracking")]
visible = false
process_mode = 4
script = ExtResource("6_7qixu")
tracking = NodePath("../../UltraleapDeviceNode")
@ -908,6 +177,15 @@ transform = Transform3D(0.01, 0, 0, 0, 0.01, 0, 0, 0, 0.01, 0, 0, 0)
[node name="RightPinchDisplay" parent="HandTracking/Debug" instance=ExtResource("11_jj1t8")]
transform = Transform3D(0.01, 0, 0, 0, 0.01, 0, 0, 0, 0.01, 0, 0, 0)
[node name="LeftTreeHands" type="Node3D" parent="HandTracking/Debug" node_paths=PackedStringArray("tracking")]
script = ExtResource("18_gnxc3")
tracking = NodePath("../../UltraleapDeviceNode")
[node name="RightTreeHands" type="Node3D" parent="HandTracking/Debug" node_paths=PackedStringArray("tracking")]
script = ExtResource("18_gnxc3")
tracking = NodePath("../../UltraleapDeviceNode")
handedness = 1
[node name="CubeSpawner" type="Node" parent="." node_paths=PackedStringArray("left_hand_pinch_detector", "right_hand_pinch_detector")]
script = ExtResource("14_d8lm2")
left_hand_pinch_detector = NodePath("../HandTracking/Detectors/PinchDetectors/LeftHandDetector")
@ -983,5 +261,4 @@ ticks_on_borders = true
[connection signal="toggled" from="DevPanel/VBoxContainer/SpawnerSetTorque" to="." method="set_spawner_set_torque"]
[connection signal="value_changed" from="DevPanel/VBoxContainer/HSlider" to="." method="on_springiness_value_changed"]
[editable path="Scene/XRToolsHands/hand_l"]
[editable path="Scene/XRToolsHands/hand_r"]
[editable path="Scene/Hands"]

View File

@ -111,8 +111,8 @@ func set_bone_transform(bone : UltraleapBone, model : Node3D, colour : Color = C
model.global_position = transform_m * pos
# Multiply by quaternion, which is this object rotation, to rotate the hands
model.global_rotation = (quaternion * bone.rotation * Quaternion(Vector3.RIGHT, deg_to_rad(90))).get_euler()
# Multiply by quaternion, which is this object orientation, to rotate the hands
model.global_rotation = (quaternion * bone.orientation * Quaternion(Vector3.RIGHT, deg_to_rad(90))).get_euler()
var mesh
var material

View File

@ -78,7 +78,7 @@ func set_arm(hand : UltraleapHand) -> Transform3D:
var bone_idx : int = skeleton.find_bone(arm_name)
var base : Basis = Basis(hand.arm.rotation)
var base : Basis = Basis(hand.arm.orientation)
var origin : Vector3 = hand.arm.prev_joint
if rig_settings.keep_original_arm_size:
var dir : Vector3 = hand.arm.prev_joint - hand.arm.next_joint
@ -129,11 +129,11 @@ func set_tip(finger_name : String, finger : UltraleapDigit):
if tip_name == "":
return
var prev_base : Basis = Basis(finger.get("distal").rotation)
var prev_base : Basis = Basis(finger.get("distal").orientation)
var prev_origin : Vector3 = finger.get("distal").prev_joint
var prev_t : Transform3D = Transform3D(prev_base, prev_origin) * rig_settings.bone_transform
var base : Basis = Basis(finger.get("distal").rotation)
var base : Basis = Basis(finger.get("distal").orientation)
var origin : Vector3 = finger.get("distal").next_joint
var t : Transform3D = prev_t.inverse() * (Transform3D(base, origin) * rig_settings.bone_transform)
@ -143,7 +143,7 @@ func set_tip(finger_name : String, finger : UltraleapDigit):
#set_bone_scale(ex_map.get(finger_name + "_tip"), global_bone_scale)
func set_metacarpal(finger_name : String, bone_name : String, finger : UltraleapDigit, wrist : Transform3D):
var base : Basis = Basis(finger.get(bone_name).rotation)
var base : Basis = Basis(finger.get(bone_name).orientation)
var origin : Vector3 = finger.get(bone_name).prev_joint
var t : Transform3D = wrist.inverse() * Transform3D(base, origin) * rig_settings.bone_transform
@ -160,11 +160,11 @@ func set_bone(finger_name : String, bone_name : String, prev_bone_name : String,
):
return
var prev_base : Basis = Basis(finger.get(prev_bone_name).rotation)
var prev_base : Basis = Basis(finger.get(prev_bone_name).orientation)
var prev_origin : Vector3 = finger.get(prev_bone_name).prev_joint
var prev_t : Transform3D = Transform3D(prev_base, prev_origin) * rig_settings.bone_transform
var base : Basis = Basis(finger.get(bone_name).rotation)
var base : Basis = Basis(finger.get(bone_name).orientation)
var origin : Vector3 = finger.get(bone_name).prev_joint
var t : Transform3D = prev_t.inverse() * Transform3D(base, origin) * rig_settings.bone_transform

View File

@ -0,0 +1,133 @@
@tool
extends Node3D
@export var tracking : UltraleapDeviceNode
@export var handedness : UltraleapTypes.Chirality = UltraleapTypes.Chirality.Left
@export var skeleton : Skeleton3D
@export var ex_map : RigMap
@export var rig_settings : RigSettings
@export var use_interpolation : bool = true
func _ready():
if skeleton == null:
return
skeleton.animate_physical_bones = true
func _process(_delta):
if tracking == null:
skeleton.hide()
return
if Engine.is_editor_hint() and not tracking.run_in_editor:
skeleton.reset_bone_poses()
skeleton.show()
return
var frame : UltraleapFrame = tracking.get_last_frame()
if frame == null:
skeleton.hide()
return
if use_interpolation:
var interpolated_frame : UltraleapFrame = tracking.get_interpolated_frame(Time.get_ticks_usec())
if interpolated_frame != null:
frame = interpolated_frame
if handedness == UltraleapTypes.Chirality.Left:
if frame.is_left_hand_visible:
skeleton.show()
set_hand(frame.left_hand)
else:
skeleton.hide()
else:
if frame.is_right_hand_visible:
skeleton.show()
set_hand(frame.right_hand)
else:
skeleton.hide()
func set_hand(hand : UltraleapHand):
set_wrist(hand)
set_finger(hand.thumb)
set_finger(hand.index)
set_finger(hand.middle)
set_finger(hand.ring)
set_finger(hand.pinky)
set_palm(hand)
func set_palm(hand : UltraleapHand):
var palm_name : String = ex_map.get("palm")
var bone_idx : int = skeleton.find_bone(palm_name)
if bone_idx == -1:
return
# Leaving it for now, we might find a need for a palm bone after all...
#skeleton.set_bone_pose_rotation(bone_idx, hand.palm.orientation)
#skeleton.set_bone_pose_position(bone_idx, hand.palm.position)
#skeleton.set_bone_pose_scale(bone_idx, global_bone_scale)
func set_wrist(hand : UltraleapHand):
var wrist_name : String = ex_map.get("wrist")
var bone_idx : int = skeleton.find_bone(wrist_name)
var wrist_transform : Transform3D = hand.wrist * tracking.rigging_transform
skeleton.set_bone_global_pose_override(bone_idx, wrist_transform, 1.0)
func set_finger(finger : UltraleapDigit):
if finger.type != UltraleapDigit.FingerType.Thumb:
set_bone(finger, finger.metacarpal)
set_bone(finger, finger.proximal)
set_bone(finger, finger.intermediate)
set_bone(finger, finger.distal)
func set_bone(finger : UltraleapDigit, bone : UltraleapBone):
var bone_name : String = bone_name_from_type(bone.type)
var finger_name : String = finger_name_from_type(finger.type)
var bone_mapped_name : String = ex_map.get(finger_name + "_" + bone_name)
if (bone == null or bone_mapped_name == ""):
return
set_bone_transform(ex_map.get(finger_name + "_" + bone_name), bone.get_transform())
func set_bone_transform(bone_name : String, bone_transform : Transform3D):
var bone_idx : int = skeleton.find_bone(bone_name)
if bone_idx == -1:
print("Couldn't set bone " + bone_name)
return
skeleton.set_bone_pose_rotation(bone_idx, bone_transform.basis.get_rotation_quaternion())
if rig_settings.set_joints_positions:
skeleton.set_bone_pose_position(bone_idx, bone_transform.origin)
func finger_name_from_type(finger_type: UltraleapDigit.FingerType) -> String:
return {
UltraleapDigit.FingerType.Thumb: "thumb",
UltraleapDigit.FingerType.Index: "index",
UltraleapDigit.FingerType.Middle: "middle",
UltraleapDigit.FingerType.Ring: "ring",
UltraleapDigit.FingerType.Pinky: "pinky"
}[finger_type] if finger_type != UltraleapDigit.FingerType.None else null
func bone_name_from_type(bone_type : UltraleapBone.BoneType) -> String:
return {
UltraleapBone.BoneType.Metacarpal: "metacarpal",
UltraleapBone.BoneType.Proximal: "proximal",
UltraleapBone.BoneType.Intermediate: "intermediate",
UltraleapBone.BoneType.Distal: "distal",
UltraleapBone.BoneType.Arm: "arm"
}[bone_type] if bone_type != UltraleapBone.BoneType.None else null

View File

@ -86,7 +86,7 @@ func set_bone(bone_name : String, finger : UltraleapDigit, index : int):
bone = UltraleapBone.new()
var bone_transform : Transform3D = tracking.global_transform * Transform3D(
Basis(bone.rotation),
Basis(bone.orientation),
bone.prev_joint
)
@ -103,7 +103,7 @@ func set_tip(finger : UltraleapDigit, index : int) -> int:
tip = UltraleapBone.new()
var tip_transform : Transform3D = tracking.global_transform * Transform3D(
Basis(tip.rotation),
Basis(tip.orientation),
tip.next_joint
)
@ -120,12 +120,12 @@ func set_arm(hand : UltraleapHand, index : int) -> int:
arm = UltraleapBone.new()
var wrist_transform : Transform3D = tracking.global_transform * Transform3D(
Basis(arm.rotation),
Basis(arm.orientation),
arm.prev_joint
)
var elbow_transform : Transform3D = tracking.global_transform * Transform3D(
Basis(arm.rotation),
Basis(arm.orientation),
arm.next_joint
)

View File

@ -39,7 +39,7 @@ func _process(_delta):
var pinch_strength : float = hand.pinch_strength
var pinch_position : Vector3 = (hand.index.distal.next_joint + hand.thumb.distal.next_joint) / 2.0
var pinch_base : Basis = Basis(hand.thumb.distal.rotation.slerp(hand.index.distal.rotation, 0.5))
var pinch_base : Basis = Basis(hand.thumb.distal.orientation.slerp(hand.index.distal.orientation, 0.5))
pinch_transform = tracking.global_transform * Transform3D(pinch_base, pinch_position)

66
demo/scripts/TreeHands.gd Normal file
View File

@ -0,0 +1,66 @@
extends Node3D
class_name TreeHands
@export var tracking : UltraleapDeviceNode
@export var handedness : UltraleapTypes.Chirality = UltraleapTypes.Chirality.Left
var bones : Array[Node3D] = []
var gizmo : PackedScene
func _ready():
gizmo = preload("res://models/Origin.tscn")
spawn_finger_gizmos(UltraleapDigit.new(), "thumb")
spawn_finger_gizmos(UltraleapDigit.new(), "index")
spawn_finger_gizmos(UltraleapDigit.new(), "middle")
spawn_finger_gizmos(UltraleapDigit.new(), "ring")
spawn_finger_gizmos(UltraleapDigit.new(), "pinky")
func _process(_delta):
if tracking == null:
return
var frame : UltraleapFrame = tracking.get_last_frame()
# Dirty hack to check if the device is initialised.
# Should implement something like a flag later
if frame == null:
return
if handedness == UltraleapTypes.Chirality.Left:
if frame.is_left_hand_visible:
self.show()
set_hand(frame.left_hand)
else:
self.hide()
else:
if frame.is_right_hand_visible:
self.show()
set_hand(frame.right_hand)
else:
self.hide()
func spawn_finger_gizmos(finger : UltraleapDigit, fname : String):
var parent : Node3D = self as Node3D
if fname != "thumb":
parent = spawn_bone_gizmo(finger.metacarpal, parent, fname, "metacarpal")
parent = spawn_bone_gizmo(finger.proximal, parent, fname, "proximal")
parent = spawn_bone_gizmo(finger.intermediate, parent, fname, "intermediate")
parent = spawn_bone_gizmo(finger.distal, parent, fname, "distal")
func spawn_bone_gizmo(bone : UltraleapBone, parent : Node3D, fname : String, bname : String):
var instance : Node3D = gizmo.instantiate()
parent.add_child(instance)
instance.owner = parent
instance.name = fname + "_" + bname
bones.append(instance)
return instance
func set_hand(hand : UltraleapHand):
position = hand.wrist.origin
quaternion = hand.wrist.basis.get_rotation_quaternion()
for bone in bones:
var splitted : PackedStringArray = bone.name.split("_")
bone.position = hand.get(splitted[0]).get(splitted[1]).translation
bone.quaternion = hand.get(splitted[0]).get(splitted[1]).rotation

View File

@ -19,12 +19,27 @@ void UltraleapBone::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_next_joint"), &UltraleapBone::get_next_joint);
ClassDB::bind_method(D_METHOD("set_next_joint", "next_joint"), &UltraleapBone::set_next_joint);
ClassDB::bind_method(D_METHOD("get_center"), &UltraleapBone::get_center);
ClassDB::bind_method(D_METHOD("set_center", "center"), &UltraleapBone::set_center);
ClassDB::bind_method(D_METHOD("get_translation"), &UltraleapBone::get_translation);
ClassDB::bind_method(D_METHOD("set_translation", "translation"), &UltraleapBone::set_translation);
ClassDB::bind_method(D_METHOD("get_width"), &UltraleapBone::get_width);
ClassDB::bind_method(D_METHOD("set_width", "width"), &UltraleapBone::set_width);
ClassDB::bind_method(D_METHOD("get_length"), &UltraleapBone::get_length);
ClassDB::bind_method(D_METHOD("set_length", "length"), &UltraleapBone::set_length);
ClassDB::bind_method(D_METHOD("get_orientation"), &UltraleapBone::get_orientation);
ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &UltraleapBone::set_orientation);
ClassDB::bind_method(D_METHOD("get_rotation"), &UltraleapBone::get_rotation);
ClassDB::bind_method(D_METHOD("set_rotation", "rotation"), &UltraleapBone::set_rotation);
ClassDB::bind_method(D_METHOD("get_global_transform"), &UltraleapBone::get_global_transform);
ClassDB::bind_method(D_METHOD("get_transform"), &UltraleapBone::get_transform);
ClassDB::add_property(
"UltraleapBone",
PropertyInfo(
@ -57,6 +72,26 @@ void UltraleapBone::_bind_methods() {
"get_next_joint"
);
ClassDB::add_property(
"UltraleapBone",
PropertyInfo(
Variant::VECTOR3,
"center"
),
"set_center",
"get_center"
);
ClassDB::add_property(
"UltraleapBone",
PropertyInfo(
Variant::VECTOR3,
"translation"
),
"set_translation",
"get_translation"
);
ClassDB::add_property(
"UltraleapBone",
PropertyInfo(
@ -67,6 +102,26 @@ void UltraleapBone::_bind_methods() {
"get_width"
);
ClassDB::add_property(
"UltraleapBone",
PropertyInfo(
Variant::FLOAT,
"length"
),
"set_length",
"get_length"
);
ClassDB::add_property(
"UltraleapBone",
PropertyInfo(
Variant::QUATERNION,
"orientation"
),
"set_orientation",
"get_orientation"
);
ClassDB::add_property(
"UltraleapBone",
PropertyInfo(
@ -76,12 +131,104 @@ void UltraleapBone::_bind_methods() {
"set_rotation",
"get_rotation"
);
BIND_ENUM_CONSTANT(Metacarpal);
BIND_ENUM_CONSTANT(Proximal);
BIND_ENUM_CONSTANT(Intermediate);
BIND_ENUM_CONSTANT(Distal);
BIND_ENUM_CONSTANT(Arm);
BIND_ENUM_CONSTANT(None);
}
void UltraleapBone::fill_bone_data(Ref<UltraleapBone> ul_bone, LEAP_BONE* bone, BoneType type) {
ul_bone->prev_joint = UltraleapTypes::ultraleap_vector3_to_godot_vector3(&bone->prev_joint);
ul_bone->next_joint = UltraleapTypes::ultraleap_vector3_to_godot_vector3(&bone->next_joint);
ul_bone->width = bone->width / 1000;
ul_bone->rotation = UltraleapTypes::ultraleap_quaternion_to_godot_quaternion(&bone->rotation);
ul_bone->type = type;
void UltraleapBone::fill_bone_data(Ref<UltraleapBone> bone, LEAP_BONE* leap_bone, BoneType type) {
bone->type = type;
bone->prev_joint = UltraleapTypes::ultraleap_vector3_to_godot_vector3(&leap_bone->prev_joint);
bone->next_joint = UltraleapTypes::ultraleap_vector3_to_godot_vector3(&leap_bone->next_joint);
bone->width = leap_bone->width / 1000;
bone->orientation = UltraleapTypes::ultraleap_quaternion_to_godot_quaternion(&leap_bone->rotation);
}
void UltraleapBone::fill_bone_data(Ref<UltraleapBone> bone, Transform3D root, LEAP_BONE* leap_bone, BoneType type, Transform3D rigging_transform) {
// This function is for metacarpals or arms, this way root can be changed to be anything
fill_bone_data(bone, leap_bone, type);
// Set rotation and translation
Transform3D current_bone_transform = Transform3D(
Basis(bone->orientation),
type == UltraleapBone::BoneType::Arm ? bone->next_joint : bone->prev_joint
);
Transform3D relative_bone_transform = root.inverse() * current_bone_transform;
bone->rotation = relative_bone_transform.basis.get_quaternion();
bone->translation = relative_bone_transform.origin;
compute_bone_data(bone);
// Set transform for rigging
bone->set_global_transform(current_bone_transform * rigging_transform, root * rigging_transform);
}
void UltraleapBone::fill_bone_data(Ref<UltraleapBone> bone, Ref<UltraleapBone> previous_bone, LEAP_BONE* leap_bone, BoneType type, Transform3D rigging_transform) {
fill_bone_data(bone, leap_bone, type);
// Set previous bone (and set its next bone to current bone)
previous_bone->next_bone = bone;
bone->previous_bone = previous_bone;
// Set rotation and translation
Transform3D previous_bone_transform = Transform3D(
Basis(bone->previous_bone->orientation),
bone->previous_bone->prev_joint
);
Transform3D current_bone_transform = Transform3D(
Basis(bone->orientation),
bone->prev_joint
);
Transform3D relative_bone_transform = previous_bone_transform.inverse() * current_bone_transform;
bone->rotation = relative_bone_transform.basis.get_quaternion();
bone->translation = relative_bone_transform.origin;
compute_bone_data(bone);
// Set transform for rigging
bone->set_global_transform(current_bone_transform * rigging_transform);
}
void UltraleapBone::compute_bone_data(Ref<UltraleapBone> bone) {
bone->center = (bone->prev_joint + bone->next_joint) / 2.0f;
bone->length = (bone->prev_joint - bone->next_joint).length();
}
void UltraleapBone::set_transform(Transform3D new_transform) {
if (previous_bone != NULL) {
global_transform = previous_bone->global_transform * new_transform;
}
// Ideally, we want to add a flag to allow propagating the transform
// to next bones
}
void UltraleapBone::set_global_transform(Transform3D new_transform) {
global_transform = new_transform;
// Set the local transform based on the global transform with the previous bone
if (previous_bone != NULL) {
transform = previous_bone->global_transform.inverse() * new_transform;
}
// Ideally, we want to add a flag to allow propagating the transform
// to next bones
}
void UltraleapBone::set_global_transform(Transform3D new_transform, Transform3D parent) {
global_transform = new_transform;
// Set the local transform based on the global transform with the previous transform
transform = parent.inverse() * new_transform;
// Ideally, we want to add a flag to allow propagating the transform
// to next bones
}

View File

@ -10,6 +10,7 @@
#include <godot_cpp/variant/vector3.hpp>
#include <godot_cpp/variant/quaternion.hpp>
#include <godot_cpp/variant/transform3d.hpp>
#include <LeapC.h>
@ -33,9 +34,28 @@ public:
Vector3 prev_joint;
Vector3 next_joint;
Vector3 center;
Vector3 translation;
float width;
float length;
Quaternion orientation;
Quaternion rotation;
Ref<UltraleapBone> previous_bone;
Ref<UltraleapBone> next_bone;
Transform3D transform;
Transform3D global_transform;
Transform3D get_global_transform() { return global_transform; }
void set_global_transform(Transform3D new_transform);
void set_global_transform(Transform3D new_transform, Transform3D parent);
Transform3D get_transform() { return transform; }
void set_transform(Transform3D new_transform);
// Getters / Setters
BoneType get_type() { return type; }
void set_type(BoneType value) { type = value; }
@ -46,13 +66,28 @@ public:
Vector3 get_next_joint() { return next_joint; }
void set_next_joint(Vector3 value) { next_joint = value; }
Vector3 get_center() { return center; }
void set_center(Vector3 value) { center = value; }
Vector3 get_translation() { return translation; }
void set_translation(Vector3 value) { translation = value; }
float get_width() { return width; }
void set_width(float value) { width = value; }
float get_length() { return length; }
void set_length(float value) { length = value; }
Quaternion get_orientation() { return orientation; }
void set_orientation(Quaternion value) { orientation = value; }
Quaternion get_rotation() { return rotation; }
void set_rotation(Quaternion value) { rotation = value; }
static void fill_bone_data(Ref<UltraleapBone> ul_bone, LEAP_BONE* bone, BoneType type);
static void fill_bone_data(Ref<UltraleapBone> bone, LEAP_BONE* leap_bone, BoneType type);
static void fill_bone_data(Ref<UltraleapBone> bone, Transform3D root, LEAP_BONE* leap_bone, BoneType type, Transform3D rigging_transform);
static void fill_bone_data(Ref<UltraleapBone> bone, Ref<UltraleapBone> previous_bone, LEAP_BONE* leap_bone, BoneType type, Transform3D rigging_transform);
static void compute_bone_data(Ref<UltraleapBone> bone);
protected:
static void _bind_methods();
};

View File

@ -14,6 +14,7 @@
using namespace godot;
UltraleapDevice::~UltraleapDevice() {
UtilityFunctions::print("Destroying device");
unsubscribe();
close();
}
@ -37,6 +38,9 @@ void UltraleapDevice::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_left_image"), &UltraleapDevice::get_left_image);
ClassDB::bind_method(D_METHOD("get_right_image"), &UltraleapDevice::get_right_image);
ClassDB::bind_method(D_METHOD("get_rigging_transform"), &UltraleapDevice::get_rigging_transform);
ClassDB::bind_method(D_METHOD("set_rigging_transform"), &UltraleapDevice::set_rigging_transform);
ClassDB::bind_method(D_METHOD("open"), &UltraleapDevice::open);
ClassDB::bind_method(D_METHOD("close"), &UltraleapDevice::close);
ClassDB::bind_method(D_METHOD("subscribe"), &UltraleapDevice::subscribe);
@ -72,6 +76,16 @@ void UltraleapDevice::_bind_methods() {
"get_id"
);
ClassDB::add_property(
"UltraleapDevice",
PropertyInfo(
Variant::TRANSFORM3D,
"rigging_transform"
),
"set_rigging_transform",
"get_rigging_transform"
);
// Signals
ClassDB::add_signal(
@ -201,7 +215,7 @@ void UltraleapDevice::close() {
void UltraleapDevice::on_frame_received(const LEAP_TRACKING_EVENT* frame) {
Ref<UltraleapFrame> new_frame = Ref<UltraleapFrame>(memnew(UltraleapFrame));
UltraleapFrame::fill_frame_data(new_frame, frame);
UltraleapFrame::fill_frame_data(new_frame, frame, rigging_transform);
frame_mutex.lock();
last_frame_ref = new_frame;
frame_mutex.unlock();
@ -252,7 +266,7 @@ Ref<UltraleapFrame> UltraleapDevice::get_interpolated_frame(int64_t time) {
result = LeapInterpolateFrameEx(connection, device, time, interpolatedFrame, targetFrameSize);
if (result == eLeapRS_Success) {
Ref<UltraleapFrame> new_frame = memnew(UltraleapFrame);
UltraleapFrame::fill_frame_data(new_frame, interpolatedFrame);
UltraleapFrame::fill_frame_data(new_frame, interpolatedFrame, rigging_transform);
return new_frame;
}
}

View File

@ -53,6 +53,9 @@ public:
uint32_t get_id() { return id; }
void set_id(uint32_t value) { id = value; }
Transform3D get_rigging_transform() { return rigging_transform; }
void set_rigging_transform(Transform3D t) { rigging_transform = t; }
void subscribe();
void unsubscribe();
@ -83,6 +86,8 @@ private:
bool opened = false;
bool subscribed = false;
bool interpolation_available = false;
Transform3D rigging_transform;
};

View File

@ -23,23 +23,14 @@ void UltraleapDeviceNode::device_added(Ref<UltraleapDevice> device) {
// If user has chosen "Any", the strategy here is to use the newly connected device
if (chosen_serial == 0) {
if (current_device != NULL && should_run()) {
unsubscribe_and_close();
}
current_device = device;
if (should_run()) {
open_and_subscribe();
}
setup_device(device);
}
// Otherwise we check for the serial and subscribe to the device
else {
if (get_tracking() != NULL) {
if ((chosen_serial - 1) < get_tracking()->devices->size()) {
if (devices[chosen_serial - 1] == device->serial) {
current_device = device;
if (should_run()) {
open_and_subscribe();
}
setup_device(device);
}
}
}
@ -56,7 +47,7 @@ void UltraleapDeviceNode::device_removed(Ref<UltraleapDevice> device) {
if (current_device != NULL) {
if (device->serial == current_device->serial) {
unsubscribe_and_close();
current_device = Ref<UltraleapDevice>();
current_device = Ref<UltraleapDevice>(NULL);
}
}
@ -67,15 +58,34 @@ void UltraleapDeviceNode::device_removed(Ref<UltraleapDevice> device) {
void UltraleapDeviceNode::open_and_subscribe() {
if (current_device != NULL) {
current_device->open();
current_device->subscribe();
if (!current_device->is_opened()) {
current_device->open();
}
if (!current_device->is_subscribed()) {
current_device->subscribe();
}
}
}
void UltraleapDeviceNode::unsubscribe_and_close() {
if (current_device != NULL) {
current_device->unsubscribe();
current_device->close();
if (current_device->is_subscribed()) {
current_device->unsubscribe();
}
if (current_device->is_opened()) {
current_device->close();
}
}
}
void UltraleapDeviceNode::setup_device(Ref<UltraleapDevice> device) {
if (current_device != NULL && should_run()) {
unsubscribe_and_close();
}
current_device = device;
current_device->set_rigging_transform(rigging_transform);
if (should_run()) {
open_and_subscribe();
}
}
@ -104,6 +114,9 @@ void UltraleapDeviceNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_run_in_editor", "run_in_editor"), &UltraleapDeviceNode::set_run_in_editor, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_run_in_editor"), &UltraleapDeviceNode::get_run_in_editor);
ClassDB::bind_method(D_METHOD("get_rigging_transform"), &UltraleapDeviceNode::get_rigging_transform);
ClassDB::bind_method(D_METHOD("set_rigging_transform"), &UltraleapDeviceNode::set_rigging_transform);
ClassDB::add_property(
"UltraleapDeviceNode",
PropertyInfo(
@ -133,6 +146,25 @@ void UltraleapDeviceNode::_bind_methods() {
"get_run_in_editor"
);
ClassDB::add_property_group(
"UltraleapDeviceNode",
"Rigging",
"rigging"
);
ClassDB::add_property(
"UltraleapDeviceNode",
PropertyInfo(
Variant::TRANSFORM3D,
"rigging_transform",
PROPERTY_HINT_NONE,
"",
PROPERTY_USAGE_NO_EDITOR
),
"set_rigging_transform",
"get_rigging_transform"
);
// Signals
ClassDB::add_signal(
@ -244,6 +276,24 @@ void UltraleapDeviceNode::_get_property_list(List<PropertyInfo> *p_list) const {
String(",").join(devices).insert(0, "Any,")
)
);
p_list->push_back(
PropertyInfo(
Variant::INT,
"rigging/presets",
PROPERTY_HINT_ENUM,
"Blender,Custom"
)
);
if (rigging_transform_type == 1) {
p_list->push_back(
PropertyInfo(
Variant::TRANSFORM3D,
"rigging/transform"
)
);
}
}
bool UltraleapDeviceNode::_get(const StringName &p_name, Variant &r_ret) const {
@ -252,6 +302,14 @@ bool UltraleapDeviceNode::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = chosen_serial;
return true;
}
else if (name == "rigging/presets") {
r_ret = rigging_transform_type;
return true;
}
else if (name == "rigging/transform") {
r_ret = rigging_transform;
return true;
}
return false;
}
@ -262,6 +320,15 @@ bool UltraleapDeviceNode::_set(const StringName &p_name, const Variant &p_value)
notify_property_list_changed();
return true;
}
else if (name == "rigging/presets") {
rigging_transform_type = p_value;
notify_property_list_changed();
return true;
}
else if (name == "rigging/transform") {
rigging_transform = p_value;
return true;
}
return false;
}
@ -324,4 +391,20 @@ void UltraleapDeviceNode::set_run_in_editor(bool value) {
if (should_run() && current_device != NULL) {
open_and_subscribe();
}
}
Transform3D UltraleapDeviceNode::get_rigging_transform() {
if (current_device != NULL) {
return current_device->get_rigging_transform();
}
return rigging_transform;
}
void UltraleapDeviceNode::set_rigging_transform(Transform3D t) {
if (current_device != NULL) {
current_device->set_rigging_transform(t);
}
rigging_transform = t;
}

View File

@ -49,6 +49,9 @@ public:
void set_run_in_editor(bool value);
bool get_run_in_editor() { return run_in_editor; }
Transform3D get_rigging_transform();
void set_rigging_transform(Transform3D t);
protected:
static void _bind_methods();
@ -64,8 +67,14 @@ private:
void open_and_subscribe();
void unsubscribe_and_close();
void setup_device(Ref<UltraleapDevice> device);
bool should_run();
int rigging_transform_type = 0;
// Used as a cache for when/if no device is connected yet (in editor for example)
Transform3D rigging_transform;
};
#endif

View File

@ -117,9 +117,16 @@ void UltraleapDigit::_bind_methods() {
"",
"get_bones"
);
BIND_ENUM_CONSTANT(Thumb);
BIND_ENUM_CONSTANT(Index);
BIND_ENUM_CONSTANT(Middle);
BIND_ENUM_CONSTANT(Ring);
BIND_ENUM_CONSTANT(Pinky);
BIND_ENUM_CONSTANT(None);
}
void UltraleapDigit::fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* digit, FingerType type) {
void UltraleapDigit::fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* digit, FingerType type, Transform3D root, Transform3D rigging_transform) {
if (ul_digit->metacarpal_ref == NULL) {
ul_digit->metacarpal_ref = Ref<UltraleapBone>(memnew(UltraleapBone));
ul_digit->proximal_ref = Ref<UltraleapBone>(memnew(UltraleapBone));
@ -129,9 +136,19 @@ void UltraleapDigit::fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* d
ul_digit->is_extended = digit->is_extended == 1;
ul_digit->type = type;
UltraleapBone::fill_bone_data(ul_digit->metacarpal_ref, &digit->metacarpal, UltraleapBone::BoneType::Metacarpal);
UltraleapBone::fill_bone_data(ul_digit->proximal_ref, &digit->proximal, UltraleapBone::BoneType::Proximal);
UltraleapBone::fill_bone_data(ul_digit->intermediate_ref, &digit->intermediate, UltraleapBone::BoneType::Intermediate);
UltraleapBone::fill_bone_data(ul_digit->distal_ref, &digit->distal, UltraleapBone::BoneType::Distal);
// We need some extra logic here because the metacarpal doesn't exist in Ultraleap's hand data
if (type == UltraleapDigit::FingerType::Thumb) {
UltraleapBone::fill_bone_data(ul_digit->proximal_ref, root, &digit->proximal, UltraleapBone::BoneType::Proximal, rigging_transform);
// Still filling it out but shouldn't be used
UltraleapBone::fill_bone_data(ul_digit->metacarpal_ref, root, &digit->metacarpal, UltraleapBone::BoneType::Metacarpal, rigging_transform);
}
else {
UltraleapBone::fill_bone_data(ul_digit->metacarpal_ref, root, &digit->metacarpal, UltraleapBone::BoneType::Metacarpal, rigging_transform);
UltraleapBone::fill_bone_data(ul_digit->proximal_ref, ul_digit->metacarpal_ref, &digit->proximal, UltraleapBone::BoneType::Proximal, rigging_transform);
}
UltraleapBone::fill_bone_data(ul_digit->intermediate_ref, ul_digit->proximal_ref, &digit->intermediate, UltraleapBone::BoneType::Intermediate, rigging_transform);
UltraleapBone::fill_bone_data(ul_digit->distal_ref, ul_digit->intermediate_ref, &digit->distal, UltraleapBone::BoneType::Distal, rigging_transform);
}

View File

@ -8,6 +8,7 @@
#include <godot_cpp/variant/vector3.hpp>
#include <godot_cpp/variant/quaternion.hpp>
#include <godot_cpp/variant/transform3d.hpp>
#include <LeapC.h>
@ -66,7 +67,7 @@ public:
return bones;
}
static void fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* digit, FingerType type);
static void fill_digit_data(Ref<UltraleapDigit> ul_digit, LEAP_DIGIT* digit, FingerType type, Transform3D root, Transform3D rigging_transform);
protected:
static void _bind_methods();

View File

@ -80,7 +80,7 @@ void UltraleapFrame::_bind_methods() {
);
}
void UltraleapFrame::fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TRACKING_EVENT* frame) {
void UltraleapFrame::fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TRACKING_EVENT* frame, Transform3D rigging_transform) {
ul_frame->id = frame->tracking_frame_id;
ul_frame->framerate = frame->framerate;
ul_frame->is_left_hand_visible = false;
@ -92,14 +92,14 @@ void UltraleapFrame::fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TR
if (ul_frame->get_left_hand() == NULL) {
ul_frame->set_left_hand(Ref<UltraleapHand>(memnew(UltraleapHand)));
}
UltraleapHand::fill_hand_data(ul_frame->get_left_hand(), &frame->pHands[i]);
UltraleapHand::fill_hand_data(ul_frame->get_left_hand(), &frame->pHands[i], rigging_transform);
ul_frame->is_left_hand_visible = true;
}
else {
if (ul_frame->get_right_hand() == NULL) {
ul_frame->set_right_hand(Ref<UltraleapHand>(memnew(UltraleapHand)));
}
UltraleapHand::fill_hand_data(ul_frame->get_right_hand(), &frame->pHands[i]);
UltraleapHand::fill_hand_data(ul_frame->get_right_hand(), &frame->pHands[i], rigging_transform);
ul_frame->is_right_hand_visible = true;
}
}

View File

@ -41,7 +41,7 @@ public:
float get_framerate() { return framerate; }
void set_framerate(float value) { return; }
static void fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TRACKING_EVENT* frame);
static void fill_frame_data(Ref<UltraleapFrame> ul_frame, const LEAP_TRACKING_EVENT* frame, Transform3D rigging_transform);
protected:
static void _bind_methods();

View File

@ -55,6 +55,9 @@ void UltraleapHand::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_digits"), &UltraleapHand::get_digits);
ClassDB::bind_method(D_METHOD("set_wrist", "wrist"), &UltraleapHand::set_wrist);
ClassDB::bind_method(D_METHOD("get_wrist"), &UltraleapHand::get_wrist);
ClassDB::add_property(
"UltraleapHand",
PropertyInfo(
@ -223,9 +226,19 @@ void UltraleapHand::_bind_methods() {
"",
"get_digits"
);
ClassDB::add_property(
"UltraleapHand",
PropertyInfo(
Variant::TRANSFORM3D,
"wrist"
),
"set_wrist",
"get_wrist"
);
};
void UltraleapHand::fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand) {
void UltraleapHand::fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand, Transform3D rigging_transform) {
// For now we just check if the arm ref is not set
if (ul_hand->arm_ref == NULL) {
ul_hand->arm_ref = Ref<UltraleapBone>(memnew(UltraleapBone));
@ -247,11 +260,16 @@ void UltraleapHand::fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand)
UltraleapPalm::fill_palm_data(ul_hand->palm_ref, &hand->palm);
UltraleapDigit::fill_digit_data(ul_hand->get_thumb(), &hand->thumb, UltraleapDigit::FingerType::Thumb);
UltraleapDigit::fill_digit_data(ul_hand->get_index(), &hand->index, UltraleapDigit::FingerType::Index);
UltraleapDigit::fill_digit_data(ul_hand->get_middle(), &hand->middle, UltraleapDigit::FingerType::Middle);
UltraleapDigit::fill_digit_data(ul_hand->get_ring(), &hand->ring, UltraleapDigit::FingerType::Ring);
UltraleapDigit::fill_digit_data(ul_hand->get_pinky(), &hand->pinky, UltraleapDigit::FingerType::Pinky);
ul_hand->wrist = Transform3D(
Basis(UltraleapTypes::ultraleap_quaternion_to_godot_quaternion(&hand->palm.orientation)),
UltraleapTypes::ultraleap_vector3_to_godot_vector3(&hand->arm.next_joint)
);
UltraleapBone::fill_bone_data(ul_hand->arm_ref, &hand->arm, UltraleapBone::BoneType::Arm);
UltraleapDigit::fill_digit_data(ul_hand->get_thumb(), &hand->thumb, UltraleapDigit::FingerType::Thumb, ul_hand->wrist, rigging_transform);
UltraleapDigit::fill_digit_data(ul_hand->get_index(), &hand->index, UltraleapDigit::FingerType::Index, ul_hand->wrist, rigging_transform);
UltraleapDigit::fill_digit_data(ul_hand->get_middle(), &hand->middle, UltraleapDigit::FingerType::Middle, ul_hand->wrist, rigging_transform);
UltraleapDigit::fill_digit_data(ul_hand->get_ring(), &hand->ring, UltraleapDigit::FingerType::Ring, ul_hand->wrist, rigging_transform);
UltraleapDigit::fill_digit_data(ul_hand->get_pinky(), &hand->pinky, UltraleapDigit::FingerType::Pinky, ul_hand->wrist, rigging_transform);
UltraleapBone::fill_bone_data(ul_hand->arm_ref, ul_hand->wrist, &hand->arm, UltraleapBone::BoneType::Arm, rigging_transform);
}

View File

@ -30,6 +30,8 @@ public:
float pinch_strength;
float grab_strength;
Transform3D wrist;
// Getters / Setters
float get_confidence() { return confidence; }
void set_confidence(float value) { confidence = value; }
@ -87,7 +89,10 @@ public:
return digits_array;
}
static void fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand);
Transform3D get_wrist() { return wrist; }
void set_wrist(Transform3D value) { wrist = value; }
static void fill_hand_data(Ref<UltraleapHand> ul_hand, LEAP_HAND* hand, Transform3D rigging_transform);
protected:
static void _bind_methods();