This commit is contained in:
sharpoff
2026-01-14 13:01:37 +09:00
commit 49d7e22132
660 changed files with 41243 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
The Hand Mesh was made using the Makehuman Community Edition
It was slightly modified inside Blender 3D and textured inside Substance Painter
If there should be concerns regarding License issues
https://github.com/makehumancommunity/makehuman/blob/master/LICENSE.md
under point D of the makehuman communtiy edition license
https://github.com/makehumancommunity/makehuman/blob/master/LICENSE.md#d-concerning-the-output-from-makehuman
As the assets have been released under CC0, there is no limitation on what you can do with this combined output.
The MakeHuman project makes no claim whatsoever over output such as:
Exports to files (FBX, OBJ, DAE, MHX2...)
Exports via direct integration (import via MPFB)
Graphical data generated via scripting or plugins
Renderings
Screenshots
Saved model files

View File

@@ -0,0 +1,126 @@
To the extent possible under law, DigitalN8m4r3 aka Miodrag Sejic
has waived all copyright and related or neighboring rights to Hand Models for the Godot XR Tools.
This work is published from: Austria.
Date: 7th November 2022
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@@ -0,0 +1,81 @@
[gd_scene load_steps=37 format=3 uid="uid://the6y7swe6j0"]
[ext_resource type="Animation" uid="uid://dgfeikrugfewi" path="res://addons/godot-xr-tools/hands/animations/left/Cup.res" id="1_7svyl"]
[ext_resource type="Animation" uid="uid://dlxa6f6hwurka" path="res://addons/godot-xr-tools/hands/animations/left/Default pose.res" id="2_ykhfd"]
[ext_resource type="Animation" uid="uid://dqa0h82y3qn1t" path="res://addons/godot-xr-tools/hands/animations/left/Grip 1.res" id="3_y80ds"]
[ext_resource type="Animation" uid="uid://di384xtde8ydf" path="res://addons/godot-xr-tools/hands/animations/left/Grip 2.res" id="4_1nbr8"]
[ext_resource type="Animation" uid="uid://dd67rufxwj2u" path="res://addons/godot-xr-tools/hands/animations/left/Grip 3.res" id="5_28lp1"]
[ext_resource type="Animation" uid="uid://db62hs5s4n2b3" path="res://addons/godot-xr-tools/hands/animations/left/Grip 4.res" id="6_uj648"]
[ext_resource type="Animation" uid="uid://bediglpx0rj7i" path="res://addons/godot-xr-tools/hands/animations/left/Grip 5.res" id="7_yxjeh"]
[ext_resource type="Animation" uid="uid://nq3xh1olqipq" path="res://addons/godot-xr-tools/hands/animations/left/Grip Shaft.res" id="8_i0ro5"]
[ext_resource type="Animation" uid="uid://plad1r85f7ws" path="res://addons/godot-xr-tools/hands/animations/left/Grip.res" id="9_811qe"]
[ext_resource type="Animation" uid="uid://bi1l6lre2w2lp" path="res://addons/godot-xr-tools/hands/animations/left/Hold.res" id="10_o5u3q"]
[ext_resource type="Animation" uid="uid://c3e6h0rv2uw2d" path="res://addons/godot-xr-tools/hands/animations/left/Horns.res" id="11_i2b8g"]
[ext_resource type="Animation" uid="uid://dfekure1r6q13" path="res://addons/godot-xr-tools/hands/animations/left/Metal.res" id="12_yoig6"]
[ext_resource type="Animation" uid="uid://b0rhk4r0r0t32" path="res://addons/godot-xr-tools/hands/animations/left/Middle.res" id="13_kvhwh"]
[ext_resource type="Animation" uid="uid://f5k0gh4qnmv5" path="res://addons/godot-xr-tools/hands/animations/left/OK.res" id="14_ofab3"]
[ext_resource type="Animation" uid="uid://1nlkfvitq7ku" path="res://addons/godot-xr-tools/hands/animations/left/Peace.res" id="15_a4q3y"]
[ext_resource type="Animation" uid="uid://dhjb0e334tfwl" path="res://addons/godot-xr-tools/hands/animations/left/Pinch Flat.res" id="16_ixe2d"]
[ext_resource type="Animation" uid="uid://dkjsnihi81b7p" path="res://addons/godot-xr-tools/hands/animations/left/Pinch Large.res" id="17_oifug"]
[ext_resource type="Animation" uid="uid://bn0fdhe2jwq3h" path="res://addons/godot-xr-tools/hands/animations/left/Pinch Middle.res" id="18_2ic2e"]
[ext_resource type="Animation" uid="uid://bo1b8w0s4ci81" path="res://addons/godot-xr-tools/hands/animations/left/Pinch Ring.res" id="19_4a8v4"]
[ext_resource type="Animation" uid="uid://m5x2m8x3tcel" path="res://addons/godot-xr-tools/hands/animations/left/Pinch Tight.res" id="20_ugc01"]
[ext_resource type="Animation" uid="uid://fi23m6i7orhw" path="res://addons/godot-xr-tools/hands/animations/left/Pinch Up.res" id="21_l3kgg"]
[ext_resource type="Animation" uid="uid://c8qmcuyaltdnw" path="res://addons/godot-xr-tools/hands/animations/left/PingPong.res" id="22_j83er"]
[ext_resource type="Animation" uid="uid://bqnoubqq7ogwu" path="res://addons/godot-xr-tools/hands/animations/left/Pinky.res" id="23_sm4sd"]
[ext_resource type="Animation" uid="uid://ddbo6ioa282en" path="res://addons/godot-xr-tools/hands/animations/left/Pistol.res" id="24_r4kss"]
[ext_resource type="Animation" uid="uid://brkptjihht3ae" path="res://addons/godot-xr-tools/hands/animations/left/Ring.res" id="25_2ncut"]
[ext_resource type="Animation" uid="uid://cnng6xumhw7cx" path="res://addons/godot-xr-tools/hands/animations/left/Rounded.res" id="26_02lfv"]
[ext_resource type="Animation" uid="uid://cevirj0eagdrq" path="res://addons/godot-xr-tools/hands/animations/left/Sign 1.res" id="27_o5si3"]
[ext_resource type="Animation" uid="uid://cc6phxovf1ban" path="res://addons/godot-xr-tools/hands/animations/left/Sign 2.res" id="28_ble5k"]
[ext_resource type="Animation" uid="uid://ohthjp8qbcc4" path="res://addons/godot-xr-tools/hands/animations/left/Sign 3.res" id="29_twq1x"]
[ext_resource type="Animation" uid="uid://dmx42g64577g5" path="res://addons/godot-xr-tools/hands/animations/left/Sign 4.res" id="30_eyeuv"]
[ext_resource type="Animation" uid="uid://dhsoxntrktx0p" path="res://addons/godot-xr-tools/hands/animations/left/Sign 5.res" id="31_50jrs"]
[ext_resource type="Animation" uid="uid://c0u2a3yc2vhg8" path="res://addons/godot-xr-tools/hands/animations/left/Sign_Point.res" id="32_7v122"]
[ext_resource type="Animation" uid="uid://4g211my0hoiw" path="res://addons/godot-xr-tools/hands/animations/left/Straight.res" id="33_m3lif"]
[ext_resource type="Animation" uid="uid://d06l7hygl4qt3" path="res://addons/godot-xr-tools/hands/animations/left/Surfer.res" id="34_b3p10"]
[ext_resource type="Animation" uid="uid://bxei4oebd4hu3" path="res://addons/godot-xr-tools/hands/animations/left/Thumb.res" id="35_huikq"]
[sub_resource type="AnimationLibrary" id="AnimationLibrary_kw48d"]
_data = {
&"Cup": ExtResource("1_7svyl"),
&"Default pose": ExtResource("2_ykhfd"),
&"Grip": ExtResource("9_811qe"),
&"Grip 1": ExtResource("3_y80ds"),
&"Grip 2": ExtResource("4_1nbr8"),
&"Grip 3": ExtResource("5_28lp1"),
&"Grip 4": ExtResource("6_uj648"),
&"Grip 5": ExtResource("7_yxjeh"),
&"Grip Shaft": ExtResource("8_i0ro5"),
&"Hold": ExtResource("10_o5u3q"),
&"Horns": ExtResource("11_i2b8g"),
&"Metal": ExtResource("12_yoig6"),
&"Middle": ExtResource("13_kvhwh"),
&"OK": ExtResource("14_ofab3"),
&"Peace": ExtResource("15_a4q3y"),
&"Pinch Flat": ExtResource("16_ixe2d"),
&"Pinch Large": ExtResource("17_oifug"),
&"Pinch Middle": ExtResource("18_2ic2e"),
&"Pinch Ring": ExtResource("19_4a8v4"),
&"Pinch Tight": ExtResource("20_ugc01"),
&"Pinch Up": ExtResource("21_l3kgg"),
&"PingPong": ExtResource("22_j83er"),
&"Pinky": ExtResource("23_sm4sd"),
&"Pistol": ExtResource("24_r4kss"),
&"Ring": ExtResource("25_2ncut"),
&"Rounded": ExtResource("26_02lfv"),
&"Sign 1": ExtResource("27_o5si3"),
&"Sign 2": ExtResource("28_ble5k"),
&"Sign 3": ExtResource("29_twq1x"),
&"Sign 4": ExtResource("30_eyeuv"),
&"Sign 5": ExtResource("31_50jrs"),
&"Sign_Point": ExtResource("32_7v122"),
&"Straight": ExtResource("33_m3lif"),
&"Surfer": ExtResource("34_b3p10"),
&"Thumb": ExtResource("35_huikq")
}
[node name="AnimationPlayer" type="AnimationPlayer"]
libraries = {
"": SubResource("AnimationLibrary_kw48d")
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,32 @@
[gd_resource type="AnimationNodeBlendTree" load_steps=6 format=3 uid="uid://dl8yf7ipqotd1"]
[sub_resource type="AnimationNodeAnimation" id="1"]
animation = &"Grip"
[sub_resource type="AnimationNodeAnimation" id="2"]
animation = &"Grip"
[sub_resource type="AnimationNodeBlend2" id="3"]
filter_enabled = true
filters = ["Armature/Skeleton3D:Little_Distal_L", "Armature/Skeleton3D:Little_Intermediate_L", "Armature/Skeleton3D:Little_Metacarpal_L", "Armature/Skeleton3D:Little_Proximal_L", "Armature/Skeleton3D:Middle_Distal_L", "Armature/Skeleton3D:Middle_Intermediate_L", "Armature/Skeleton3D:Middle_Metacarpal_L", "Armature/Skeleton3D:Middle_Proximal_L", "Armature/Skeleton3D:Ring_Distal_L", "Armature/Skeleton3D:Ring_Intermediate_L", "Armature/Skeleton3D:Ring_Metacarpal_L", "Armature/Skeleton3D:Ring_Proximal_L", "Armature/Skeleton3D:Thumb_Distal_L", "Armature/Skeleton3D:Thumb_Metacarpal_L", "Armature/Skeleton3D:Thumb_Proximal_L", "Armature/Skeleton:Little_Distal_L", "Armature/Skeleton:Little_Intermediate_L", "Armature/Skeleton:Little_Proximal_L", "Armature/Skeleton:Middle_Distal_L", "Armature/Skeleton:Middle_Intermediate_L", "Armature/Skeleton:Middle_Proximal_L", "Armature/Skeleton:Ring_Distal_L", "Armature/Skeleton:Ring_Intermediate_L", "Armature/Skeleton:Ring_Proximal_L", "Armature/Skeleton:Thumb_Distal_L", "Armature/Skeleton:Thumb_Proximal_L"]
[sub_resource type="AnimationNodeAnimation" id="4"]
animation = &"Grip 5"
[sub_resource type="AnimationNodeBlend2" id="5"]
filter_enabled = true
filters = ["Armature/Skeleton3D:Index_Distal_L", "Armature/Skeleton3D:Index_Intermediate_L", "Armature/Skeleton3D:Index_Metacarpal_L", "Armature/Skeleton3D:Index_Proximal_L", "Armature/Skeleton:Index_Distal_L", "Armature/Skeleton:Index_Intermediate_L", "Armature/Skeleton:Index_Proximal_L"]
[resource]
graph_offset = Vector2(-536, 11)
nodes/ClosedHand1/node = SubResource("1")
nodes/ClosedHand1/position = Vector2(-600, 300)
nodes/ClosedHand2/node = SubResource("2")
nodes/ClosedHand2/position = Vector2(-360, 300)
nodes/Grip/node = SubResource("3")
nodes/Grip/position = Vector2(0, 20)
nodes/OpenHand/node = SubResource("4")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("5")
nodes/Trigger/position = Vector2(-360, 20)
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]

View File

@@ -0,0 +1,81 @@
[gd_scene load_steps=37 format=3 uid="uid://bxipj7hyjwy6f"]
[ext_resource type="Animation" uid="uid://do01jton6rk42" path="res://addons/godot-xr-tools/hands/animations/right/Cup.res" id="1_mq7eu"]
[ext_resource type="Animation" uid="uid://ky28birj4su6" path="res://addons/godot-xr-tools/hands/animations/right/Default pose.res" id="2_kxlaj"]
[ext_resource type="Animation" uid="uid://daqddcrbpam0c" path="res://addons/godot-xr-tools/hands/animations/right/Grip 1.res" id="3_5xubn"]
[ext_resource type="Animation" uid="uid://r8hleealhrqt" path="res://addons/godot-xr-tools/hands/animations/right/Grip 2.res" id="4_y3ovo"]
[ext_resource type="Animation" uid="uid://rs7vpclot07o" path="res://addons/godot-xr-tools/hands/animations/right/Grip 3.res" id="5_0d4pg"]
[ext_resource type="Animation" uid="uid://d1xnpyc08njjx" path="res://addons/godot-xr-tools/hands/animations/right/Grip 4.res" id="6_nabm2"]
[ext_resource type="Animation" uid="uid://s1vqcxyqcvea" path="res://addons/godot-xr-tools/hands/animations/right/Grip 5.res" id="7_gy2yb"]
[ext_resource type="Animation" uid="uid://c8dghcftg1thx" path="res://addons/godot-xr-tools/hands/animations/right/Grip Shaft.res" id="8_ffwxq"]
[ext_resource type="Animation" uid="uid://ccds2u22gbxn7" path="res://addons/godot-xr-tools/hands/animations/right/Grip.res" id="9_y16mo"]
[ext_resource type="Animation" uid="uid://bv5tuc1kjv0k5" path="res://addons/godot-xr-tools/hands/animations/right/Hold.res" id="10_g8e21"]
[ext_resource type="Animation" uid="uid://cdjfhqnr4n2mr" path="res://addons/godot-xr-tools/hands/animations/right/Horns.res" id="11_b3lks"]
[ext_resource type="Animation" uid="uid://b1kqsodcr1til" path="res://addons/godot-xr-tools/hands/animations/right/Metal.res" id="12_p14fk"]
[ext_resource type="Animation" uid="uid://cg6fsqb2iuuih" path="res://addons/godot-xr-tools/hands/animations/right/Middle.res" id="13_4i278"]
[ext_resource type="Animation" uid="uid://c3isimdlxg54a" path="res://addons/godot-xr-tools/hands/animations/right/OK.res" id="14_qk7tg"]
[ext_resource type="Animation" uid="uid://bxmc3vlfih764" path="res://addons/godot-xr-tools/hands/animations/right/Peace.res" id="15_ejppt"]
[ext_resource type="Animation" uid="uid://dg4v7rqiaje2h" path="res://addons/godot-xr-tools/hands/animations/right/Pinch Flat.res" id="16_7cyln"]
[ext_resource type="Animation" uid="uid://cuwwpxi44vwpm" path="res://addons/godot-xr-tools/hands/animations/right/Pinch Large.res" id="17_amtgc"]
[ext_resource type="Animation" uid="uid://c6vghurk7t5yb" path="res://addons/godot-xr-tools/hands/animations/right/Pinch Middle.res" id="18_pnqcq"]
[ext_resource type="Animation" uid="uid://mjotm5uukoo3" path="res://addons/godot-xr-tools/hands/animations/right/Pinch Ring.res" id="19_aeaff"]
[ext_resource type="Animation" uid="uid://ca21ej1p3g2yt" path="res://addons/godot-xr-tools/hands/animations/right/Pinch Tight.res" id="20_mghnf"]
[ext_resource type="Animation" uid="uid://cseojxi8rrqc" path="res://addons/godot-xr-tools/hands/animations/right/Pinch Up.res" id="21_1v6tw"]
[ext_resource type="Animation" uid="uid://c8hn123uon74u" path="res://addons/godot-xr-tools/hands/animations/right/PingPong.res" id="22_ueac0"]
[ext_resource type="Animation" uid="uid://b6ra3rqia1gvb" path="res://addons/godot-xr-tools/hands/animations/right/Pinky.res" id="23_7vyi4"]
[ext_resource type="Animation" uid="uid://csp3fdknowmi5" path="res://addons/godot-xr-tools/hands/animations/right/Pistol.res" id="24_nc600"]
[ext_resource type="Animation" uid="uid://2ttepgvf634h" path="res://addons/godot-xr-tools/hands/animations/right/Ring.res" id="25_20nj0"]
[ext_resource type="Animation" uid="uid://wcwa3p1qrhwr" path="res://addons/godot-xr-tools/hands/animations/right/Rounded.res" id="26_648rb"]
[ext_resource type="Animation" uid="uid://bcwx6a6mhsoj2" path="res://addons/godot-xr-tools/hands/animations/right/Sign 1.res" id="27_liq3y"]
[ext_resource type="Animation" uid="uid://btnsp36fjkldf" path="res://addons/godot-xr-tools/hands/animations/right/Sign 2.res" id="28_vdv8x"]
[ext_resource type="Animation" uid="uid://c35i6glt1ov2m" path="res://addons/godot-xr-tools/hands/animations/right/Sign 3.res" id="29_uairn"]
[ext_resource type="Animation" uid="uid://b8mmk2ufmlyf" path="res://addons/godot-xr-tools/hands/animations/right/Sign 4.res" id="30_351lm"]
[ext_resource type="Animation" uid="uid://ddgl3lsnlfiny" path="res://addons/godot-xr-tools/hands/animations/right/Sign 5.res" id="31_o4yst"]
[ext_resource type="Animation" uid="uid://ccy3e7a0hvxyl" path="res://addons/godot-xr-tools/hands/animations/right/Sign_Point.res" id="32_02kj7"]
[ext_resource type="Animation" uid="uid://d3h36bkauutoq" path="res://addons/godot-xr-tools/hands/animations/right/Straight.res" id="33_gpxgh"]
[ext_resource type="Animation" uid="uid://bq7cjxdn28jcv" path="res://addons/godot-xr-tools/hands/animations/right/Surfer.res" id="34_ldn4j"]
[ext_resource type="Animation" uid="uid://d2ukhr3n2r1u8" path="res://addons/godot-xr-tools/hands/animations/right/Thumb.res" id="35_xnxq3"]
[sub_resource type="AnimationLibrary" id="AnimationLibrary_reqae"]
_data = {
&"Cup": ExtResource("1_mq7eu"),
&"Default pose": ExtResource("2_kxlaj"),
&"Grip": ExtResource("9_y16mo"),
&"Grip 1": ExtResource("3_5xubn"),
&"Grip 2": ExtResource("4_y3ovo"),
&"Grip 3": ExtResource("5_0d4pg"),
&"Grip 4": ExtResource("6_nabm2"),
&"Grip 5": ExtResource("7_gy2yb"),
&"Grip Shaft": ExtResource("8_ffwxq"),
&"Hold": ExtResource("10_g8e21"),
&"Horns": ExtResource("11_b3lks"),
&"Metal": ExtResource("12_p14fk"),
&"Middle": ExtResource("13_4i278"),
&"OK": ExtResource("14_qk7tg"),
&"Peace": ExtResource("15_ejppt"),
&"Pinch Flat": ExtResource("16_7cyln"),
&"Pinch Large": ExtResource("17_amtgc"),
&"Pinch Middle": ExtResource("18_pnqcq"),
&"Pinch Ring": ExtResource("19_aeaff"),
&"Pinch Tight": ExtResource("20_mghnf"),
&"Pinch Up": ExtResource("21_1v6tw"),
&"PingPong": ExtResource("22_ueac0"),
&"Pinky": ExtResource("23_7vyi4"),
&"Pistol": ExtResource("24_nc600"),
&"Ring": ExtResource("25_20nj0"),
&"Rounded": ExtResource("26_648rb"),
&"Sign 1": ExtResource("27_liq3y"),
&"Sign 2": ExtResource("28_vdv8x"),
&"Sign 3": ExtResource("29_uairn"),
&"Sign 4": ExtResource("30_351lm"),
&"Sign 5": ExtResource("31_o4yst"),
&"Sign_Point": ExtResource("32_02kj7"),
&"Straight": ExtResource("33_gpxgh"),
&"Surfer": ExtResource("34_ldn4j"),
&"Thumb": ExtResource("35_xnxq3")
}
[node name="AnimationPlayer" type="AnimationPlayer"]
libraries = {
"": SubResource("AnimationLibrary_reqae")
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,32 @@
[gd_resource type="AnimationNodeBlendTree" load_steps=6 format=3 uid="uid://m85b1gogdums"]
[sub_resource type="AnimationNodeAnimation" id="1"]
animation = &"Grip"
[sub_resource type="AnimationNodeAnimation" id="2"]
animation = &"Grip"
[sub_resource type="AnimationNodeBlend2" id="4"]
filter_enabled = true
filters = ["Armature/Skeleton3D:Little_Distal_R", "Armature/Skeleton3D:Little_Intermediate_R", "Armature/Skeleton3D:Little_Metacarpal_R", "Armature/Skeleton3D:Little_Proximal_R", "Armature/Skeleton3D:Middle_Distal_R", "Armature/Skeleton3D:Middle_Intermediate_R", "Armature/Skeleton3D:Middle_Metacarpal_R", "Armature/Skeleton3D:Middle_Proximal_R", "Armature/Skeleton3D:Ring_Distal_R", "Armature/Skeleton3D:Ring_Intermediate_R", "Armature/Skeleton3D:Ring_Metacarpal_R", "Armature/Skeleton3D:Ring_Proximal_R", "Armature/Skeleton3D:Thumb_Distal_R", "Armature/Skeleton3D:Thumb_Metacarpal_R", "Armature/Skeleton3D:Thumb_Proximal_R", "Armature/Skeleton:Little_Distal_R", "Armature/Skeleton:Little_Intermediate_R", "Armature/Skeleton:Little_Proximal_R", "Armature/Skeleton:Middle_Distal_R", "Armature/Skeleton:Middle_Intermediate_R", "Armature/Skeleton:Middle_Proximal_R", "Armature/Skeleton:Ring_Distal_R", "Armature/Skeleton:Ring_Intermediate_R", "Armature/Skeleton:Ring_Proximal_R", "Armature/Skeleton:Thumb_Distal_R", "Armature/Skeleton:Thumb_Proximal_R"]
[sub_resource type="AnimationNodeAnimation" id="3"]
animation = &"Grip 5"
[sub_resource type="AnimationNodeBlend2" id="5"]
filter_enabled = true
filters = ["Armature/Skeleton3D:Index_Distal_R", "Armature/Skeleton3D:Index_Intermediate_R", "Armature/Skeleton3D:Index_Metacarpal_R", "Armature/Skeleton3D:Index_Proximal_R", "Armature/Skeleton:Index_Distal_R", "Armature/Skeleton:Index_Intermediate_R", "Armature/Skeleton:Index_Proximal_R"]
[resource]
graph_offset = Vector2(-552.664, 107.301)
nodes/ClosedHand1/node = SubResource("1")
nodes/ClosedHand1/position = Vector2(-600, 300)
nodes/ClosedHand2/node = SubResource("2")
nodes/ClosedHand2/position = Vector2(-360, 300)
nodes/Grip/node = SubResource("4")
nodes/Grip/position = Vector2(0, 40)
nodes/OpenHand/node = SubResource("3")
nodes/OpenHand/position = Vector2(-600, 100)
nodes/Trigger/node = SubResource("5")
nodes/Trigger/position = Vector2(-360, 40)
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]

View File

@@ -0,0 +1,3 @@
[remap]
importer="keep"

View File

@@ -0,0 +1,3 @@
[remap]
importer="keep"

Binary file not shown.

View File

@@ -0,0 +1,3 @@
[remap]
importer="keep"

Binary file not shown.

View File

@@ -0,0 +1,3 @@
[remap]
importer="keep"

View File

@@ -0,0 +1,450 @@
@tool
class_name XRToolsCollisionHand
extends XRToolsForceBody
## XRTools Collision Hand Container Script
##
## This script implements logic for collision hands. Specifically it tracks
## its ancestor [XRController3D], and can act as a container for hand models
## and pickup functions.
# We reached our teleport distance
signal max_distance_reached
## Modes for collision hand
enum CollisionHandMode {
## Hand is disabled and must be moved externally
DISABLED,
## Hand teleports to controller
TELEPORT,
## Hand collides with world (based on mask)
COLLIDE
}
# Default layer of 18:player-hands
const DEFAULT_LAYER := 0b0000_0000_0000_0010_0000_0000_0000_0000
# Default mask of 0xFFFF (1..16)
# - 1:static-world
# - 2:dynamic-world
# - 3:pickable-objects
# - 4:wall-walking
# - 5:grappling-target
const DEFAULT_MASK := 0b0000_0000_0000_0101_0000_0000_0001_1111
# How much displacement is required for the hand to start orienting to a surface
const ORIENT_DISPLACEMENT := 0.05
# Distance to teleport hands
const TELEPORT_DISTANCE := 1.0
## Controls the hand collision mode
@export var mode : CollisionHandMode = CollisionHandMode.COLLIDE
## Links to skeleton that adds finger digits
@export var hand_skeleton : Skeleton3D:
set(value):
if hand_skeleton == value:
return
if hand_skeleton:
if hand_skeleton.has_signal("skeleton_updated"):
# Godot 4.3+
hand_skeleton.skeleton_updated.disconnect(_on_skeleton_updated)
else:
hand_skeleton.pose_updated.disconnect(_on_skeleton_updated)
for digit in _digit_collision_shapes:
var shape : CollisionShape3D = _digit_collision_shapes[digit]
remove_child(shape)
shape.queue_free()
_digit_collision_shapes.clear()
hand_skeleton = value
if hand_skeleton and is_inside_tree():
_update_hand_skeleton()
notify_property_list_changed()
## Minimum force we can exert on a picked up object
@export_range(1.0, 1000.0, 0.1, "suffix:N") var min_pickup_force : float = 15.0
## Force we exert on a picked up object when hand is at maximum distance
## before letting go.
@export_range(1.0, 1000.0, 0.1, "suffix:N") var max_pickup_force : float = 300.0
# Controller to target (if no target overrides)
var _controller : XRController3D
# Sorted stack of TargetOverride
var _target_overrides := []
# Current target (controller or override)
var _target : Node3D
# Skeleton collisions
var _palm_collision_shape : CollisionShape3D
var _digit_collision_shapes : Dictionary
# The weight held by this hand
var _held_weight : float = 0.0
# Movement on last frame
var _last_movement : Vector3 = Vector3()
## Target-override class
class TargetOverride:
## Target of the override
var target : Node3D
## Target priority
var priority : int
## Target-override constructor
func _init(t : Node3D, p : int):
target = t
priority = p
# Update the weight attributed to this hand (updated from pickable system).
func set_held_weight(new_weight):
_held_weight = new_weight
# Add support for is_xr_class on XRTools classes
func is_xr_class(xr_name: String) -> bool:
return xr_name == "XRToolsCollisionHand"
# Return warnings related to this node
func _get_configuration_warnings() -> PackedStringArray:
var warnings := PackedStringArray()
# Check palm node
if not _palm_collision_shape:
warnings.push_back("Collision hand scenes are deprecated, use collision node script directly.")
# Check if skeleton is a child
if hand_skeleton and not is_ancestor_of(hand_skeleton):
warnings.push_back("The hand skeleton node should be within the tree of this node.")
# Return warnings
return warnings
# Called when the node enters the scene tree for the first time.
func _ready():
var palm_collision : CollisionShape3D = get_node_or_null("CollisionShape3D")
if not palm_collision:
# We create our object even in editor to supress our warning.
# This allows us to just add an XRToolsCollisionHand node without
# using our scene.
_palm_collision_shape = CollisionShape3D.new()
_palm_collision_shape.name = "Palm"
_palm_collision_shape.shape = \
preload("res://addons/godot-xr-tools/hands/scenes/collision/hand_palm.shape")
_palm_collision_shape.transform.origin = Vector3(0.0, -0.05, 0.11)
add_child(_palm_collision_shape, false, Node.INTERNAL_MODE_BACK)
elif not Engine.is_editor_hint():
# Use our existing collision shape node but only in runtime.
# In editor we can check this to provide a deprecation warning.
palm_collision.name = "Palm"
_palm_collision_shape = palm_collision
_update_hand_skeleton()
# Do not initialise if in the editor
if Engine.is_editor_hint():
return
# Disconnect from parent transform as we move to it in the physics step,
# and boost the physics priority above any grab-drivers or hands.
top_level = true
process_physics_priority = -90
sync_to_physics = false
# Connect to player body signals (if applicable)
var player_body = XRToolsPlayerBody.find_instance(self)
if player_body:
player_body.player_moved.connect(_on_player_moved)
player_body.player_teleported.connect(_on_player_teleported)
# Populate nodes
_controller = XRTools.find_xr_ancestor(self, "*", "XRController3D")
# Update the target
_update_target()
# Handle physics processing
func _physics_process(delta):
# Do not process if in the editor
if Engine.is_editor_hint():
return
var current_position = global_position
# Move to the current target
_move_to_target(delta)
_last_movement = global_position - current_position
## This function adds a target override. The collision hand will attempt to
## move to the highest priority target, or the [XRController3D] if no override
## is specified.
func add_target_override(target : Node3D, priority : int) -> void:
# Remove any existing target override from this source
var modified := _remove_target_override(target)
# Insert the target override
_insert_target_override(target, priority)
modified = true
# Update the target
if modified:
_update_target()
## This function remove a target override.
func remove_target_override(target : Node3D) -> void:
# Remove the target override
var modified := _remove_target_override(target)
# Update the pose
if modified:
_update_target()
## This function searches from the specified node for an [XRToolsCollisionHand]
## assuming the node is a sibling of the hand under an [XRController3D].
static func find_instance(node : Node) -> XRToolsCollisionHand:
return XRTools.find_xr_child(
XRHelpers.get_xr_controller(node),
"*",
"XRToolsCollisionHand") as XRToolsCollisionHand
## This function searches an [XRToolsCollisionHand] that is an ancestor
## of the given node.
static func find_ancestor(node : Node) -> XRToolsCollisionHand:
return XRTools.find_xr_ancestor(
node,
"*",
"XRToolsCollisionHand") as XRToolsCollisionHand
## This function searches from the specified node for the left controller
## [XRToolsCollisionHand] assuming the node is a sibling of the [XROrigin3D].
static func find_left(node : Node) -> XRToolsCollisionHand:
return XRTools.find_xr_child(
XRHelpers.get_left_controller(node),
"*",
"XRToolsCollisionHand") as XRToolsCollisionHand
## This function searches from the specified node for the right controller
## [XRToolsCollisionHand] assuming the node is a sibling of the [XROrigin3D].
static func find_right(node : Node) -> XRToolsCollisionHand:
return XRTools.find_xr_child(
XRHelpers.get_right_controller(node),
"*",
"XRToolsCollisionHand") as XRToolsCollisionHand
# This function moves the collision hand to the target node.
func _move_to_target(delta):
# Handle DISABLED or no target
if mode == CollisionHandMode.DISABLED or not _target:
return
# Handle TELEPORT
if mode == CollisionHandMode.TELEPORT:
global_transform = _target.global_transform
return
# Handle too far from target
if global_position.distance_to(_target.global_position) > TELEPORT_DISTANCE:
max_distance_reached.emit()
global_transform = _target.global_transform
return
# Orient the hand
rotate_and_collide(_target.global_basis)
# Adjust target position if we're holding something
var target_movement : Vector3 = _target.global_position - global_position
if _held_weight > 0.0:
var gravity_state := PhysicsServer3D.body_get_direct_state(get_rid())
var gravity = gravity_state.total_gravity * delta
# Calculate the movement of our held object if we weren't holding it
var base_movement : Vector3 = _last_movement * 0.2 + gravity
# How much movement is left until we reach our target
var remaining_movement = target_movement - base_movement
# The below is an approximation as we're not taking the logarithmic
# nature of force acceleration into account for simplicitiy.
# Distance over time gives our needed acceleration which
# gives us the force needed on the object to move it to our
# target destination.
# But dividing and then multiplying over delta and mass is wasteful.
var needed_distance = remaining_movement.length()
# Force we can exert on the object
var force = min_pickup_force + \
(target_movement.length() * (max_pickup_force-min_pickup_force) / TELEPORT_DISTANCE)
# How much can we move our object?
var possible_distance = delta * force / _held_weight
if possible_distance < needed_distance:
# We can't make our distance? adjust our movement!
remaining_movement *= (possible_distance / needed_distance)
target_movement = base_movement + remaining_movement
# And move
move_and_slide(target_movement)
force_update_transform()
# If our player moved, attempt to move our hand but ignoring weight.
func _on_player_moved(delta_transform : Transform3D):
if mode == CollisionHandMode.DISABLED:
return
if mode == CollisionHandMode.TELEPORT:
_on_player_teleported(delta_transform)
return
var target : Transform3D = delta_transform * global_transform
# Rotate
rotate_and_collide(target.basis)
# And attempt to move
move_and_slide(target.origin - global_position)
force_update_transform()
# If our player teleported, just move.
func _on_player_teleported(delta_transform : Transform3D):
if mode == CollisionHandMode.DISABLED:
return
global_transform = delta_transform * global_transform
force_update_transform()
# This function inserts a target override into the overrides list by priority
# order.
func _insert_target_override(target : Node3D, priority : int) -> void:
# Construct the target override
var override := TargetOverride.new(target, priority)
# Iterate over all target overrides in the list
for pos in _target_overrides.size():
# Get the target override
var o : TargetOverride = _target_overrides[pos]
# Insert as early as possible to not invalidate sorting
if o.priority <= priority:
_target_overrides.insert(pos, override)
return
# Insert at the end
_target_overrides.push_back(override)
# This function removes a target from the overrides list
func _remove_target_override(target : Node) -> bool:
var pos := 0
var length := _target_overrides.size()
var modified := false
# Iterate over all pose overrides in the list
while pos < length:
# Get the target override
var o : TargetOverride = _target_overrides[pos]
# Check for a match
if o.target == target:
# Remove the override
_target_overrides.remove_at(pos)
modified = true
length -= 1
else:
# Advance down the list
pos += 1
# Return the modified indicator
return modified
# This function updates the target for hand movement.
func _update_target() -> void:
# Start by assuming the controller
_target = _controller
# Use first target override if specified
if _target_overrides.size():
_target = _target_overrides[0].target
# If a skeleton is set, update.
func _update_hand_skeleton():
if hand_skeleton:
if hand_skeleton.has_signal("skeleton_updated"):
# Godot 4.3+
hand_skeleton.skeleton_updated.connect(_on_skeleton_updated)
else:
hand_skeleton.pose_updated.connect(_on_skeleton_updated)
# Run atleast once to init
_on_skeleton_updated()
# Update our finger digits when our skeleton updates
func _on_skeleton_updated():
if not hand_skeleton:
return
var bone_count = hand_skeleton.get_bone_count()
for i in bone_count:
var collision_node : CollisionShape3D
var offset : Transform3D
offset.origin = Vector3(0.0, 0.015, 0.0) # move to side of joint
var bone_name = hand_skeleton.get_bone_name(i)
if bone_name == "Palm_L":
offset.origin = Vector3(-0.02, 0.025, 0.0) # move to side of joint
collision_node = _palm_collision_shape
elif bone_name == "Palm_R":
offset.origin = Vector3(0.02, 0.025, 0.0) # move to side of joint
collision_node = _palm_collision_shape
elif bone_name.contains("Proximal") or bone_name.contains("Intermediate") or \
bone_name.contains("Distal"):
if _digit_collision_shapes.has(bone_name):
collision_node = _digit_collision_shapes[bone_name]
else:
collision_node = CollisionShape3D.new()
collision_node.name = bone_name
collision_node.shape = \
preload("res://addons/godot-xr-tools/hands/scenes/collision/hand_digit.shape")
add_child(collision_node, false, Node.INTERNAL_MODE_BACK)
_digit_collision_shapes[bone_name] = collision_node
if collision_node:
# TODO it would require a far more complex approach,
# but being able to check if our collision shapes can move to their new locations
# would be interesting.
collision_node.transform = global_transform.inverse() \
* hand_skeleton.global_transform \
* hand_skeleton.get_bone_global_pose(i) \
* offset

View File

@@ -0,0 +1 @@
uid://c8vnltn217quj

View File

@@ -0,0 +1,505 @@
@tool
@icon("res://addons/godot-xr-tools/editor/icons/hand.svg")
class_name XRToolsHand
extends XRToolsHandPalmOffset
## XR Tools Hand Script
##
## This script manages a godot-xr-tools hand. It animates the hand blending
## grip and trigger animations based on controller input.
##
## Additionally the hand script detects world-scale changes in the XRServer
## and re-scales the hand appropriately so the hand stays scaled to the
## physical hand of the user.
## Signal emitted when the hand scale changes
signal hand_scale_changed(scale)
## Blend tree to use
@export var hand_blend_tree : AnimationNodeBlendTree: set = set_hand_blend_tree
## Override the hand material
@export var hand_material_override : Material: set = set_hand_material_override
## Default hand pose
@export var default_pose : XRToolsHandPoseSettings: set = set_default_pose
## Name of the Grip action in the OpenXR Action Map.
@export var grip_action : String = "grip"
## Name of the Trigger action in the OpenXR Action Map.
@export var trigger_action : String = "trigger"
## Last world scale (for scaling hands)
var _last_world_scale : float = 1.0
## Initial hand transform (from controller) - used for scaling hands
var _initial_transform : Transform3D
## Current hand transform (from controller) - after scale
var _transform : Transform3D
## Hand mesh
var _hand_mesh : MeshInstance3D
## Hand animation player
var _animation_player : AnimationPlayer
## Hand animation tree
var _animation_tree : AnimationTree
## Animation blend tree
var _tree_root : AnimationNodeBlendTree
## Sorted stack of PoseOverride
var _pose_overrides := []
## Force grip value (< 0 for no force)
var _force_grip := -1.0
## Force trigger value (< 0 for no force)
var _force_trigger := -1.0
# Sorted stack of TargetOverride
var _target_overrides := []
# Current target (controller or override)
var _target : Node3D
## Pose-override class
class PoseOverride:
## Who requested the override
var who : Node
## Pose priority
var priority : int
## Pose settings
var settings : XRToolsHandPoseSettings
## Pose-override constructor
func _init(w : Node, p : int, s : XRToolsHandPoseSettings):
who = w
priority = p
settings = s
## Target-override class
class TargetOverride:
## Target of the override
var target : Node3D
## Target priority
var priority : int
## Target-override constructor
func _init(t : Node3D, p : int):
target = t
priority = p
# Add support for is_xr_class on XRTools classes
func is_xr_class(xr_name: String) -> bool:
return xr_name == "XRToolsHand"
## Called when the node enters the scene tree for the first time.
func _ready() -> void:
# Save the initial hand transform
_initial_transform = transform
_transform = _initial_transform
# Disconnect from parent transform as we move to it in the physics step,
# and boost the physics priority after any grab-drivers but before other
# processing.
if not Engine.is_editor_hint():
top_level = true
process_physics_priority = -70
# Find the relevant hand nodes
_hand_mesh = _find_child(self, "MeshInstance3D")
_animation_player = _find_child(self, "AnimationPlayer")
_animation_tree = _find_child(self, "AnimationTree")
# Apply all updates
_update_hand_blend_tree()
_update_hand_material_override()
_update_pose()
_update_target()
# Called when we get added to our tree
func _enter_tree():
# Assume our first child is our hand subscene, we position this
var child = get_child(0)
if child:
set_apply_to(child)
super._enter_tree()
# Offset our hand
var base_transform = Transform3D()
if _controller and _controller.tracker == "left_hand":
base_transform.origin = Vector3(-0.01, 0.0, 0.05)
else:
base_transform.origin = Vector3(0.01, 0.0, 0.05)
set_base_transform(base_transform)
## This method checks for world-scale changes and scales itself causing the
## hand mesh and skeleton to scale appropriately. It then reads the grip and
## trigger action values to animate the hand.
func _physics_process(_delta: float) -> void:
# Do not run the rest of physics if in the editor
if Engine.is_editor_hint():
return
# Scale the hand mesh with the world scale.
if XRServer.world_scale != _last_world_scale:
_last_world_scale = XRServer.world_scale
_transform = _initial_transform.scaled(Vector3.ONE * _last_world_scale)
emit_signal("hand_scale_changed", _last_world_scale)
# Animate the hand mesh with the controller inputs
if _controller:
var grip : float = _controller.get_float(grip_action)
var trigger : float = _controller.get_float(trigger_action)
# Allow overriding of grip and trigger
if _force_grip >= 0.0: grip = _force_grip
if _force_trigger >= 0.0: trigger = _force_trigger
$AnimationTree.set("parameters/Grip/blend_amount", grip)
$AnimationTree.set("parameters/Trigger/blend_amount", trigger)
# Move to target
var target_transform : Transform3D
if _target is XRToolsGrabPointHand:
target_transform = _target.get_palm_transform(true)
if hand_offset_mode != 4:
target_transform = target_transform * XRTools.get_palm_offset(hand_offset_mode, _controller).inverse()
else:
target_transform = _target.global_transform
global_transform = target_transform * _transform
force_update_transform()
# This method verifies the hand has a valid configuration.
func _get_configuration_warnings() -> PackedStringArray:
var warnings : PackedStringArray = super._get_configuration_warnings()
# Check hand for mesh instance
if not _find_child(self, "MeshInstance3D"):
warnings.append("Hand does not have a MeshInstance3D")
# Check hand for animation player
if not _find_child(self, "AnimationPlayer"):
warnings.append("Hand does not have a AnimationPlayer")
# Check hand for animation tree
var tree : AnimationTree = _find_child(self, "AnimationTree")
if not tree:
warnings.append("Hand does not have a AnimationTree")
elif not tree.tree_root:
warnings.append("Hand AnimationTree has no root")
# Return warnings
return warnings
## Find an [XRToolsHand] node.
##
## This function searches from the specified node for an [XRToolsHand] assuming
## the node is a sibling of the hand under an [XROrigin3D].
static func find_instance(node : Node) -> XRToolsHand:
return XRTools.find_xr_child(
XRHelpers.get_xr_controller(node),
"*",
"XRToolsHand") as XRToolsHand
## This function searches from the specified node for the left controller
## [XRToolsHand] assuming the node is a sibling of the [XROrigin3D].
static func find_left(node : Node) -> XRToolsHand:
return XRTools.find_xr_child(
XRHelpers.get_left_controller(node),
"*",
"XRToolsHand") as XRToolsHand
## This function searches from the specified node for the right controller
## [XRToolsHand] assuming the node is a sibling of the [XROrigin3D].
static func find_right(node : Node) -> XRToolsHand:
return XRTools.find_xr_child(
XRHelpers.get_right_controller(node),
"*",
"XRToolsHand") as XRToolsHand
# Check property config
func _validate_property(property):
if property.name == "top_level":
# Hide it!
property.usage = PROPERTY_USAGE_NONE
else:
super._validate_property(property)
## Set the blend tree
func set_hand_blend_tree(blend_tree : AnimationNodeBlendTree) -> void:
hand_blend_tree = blend_tree
if is_inside_tree():
_update_hand_blend_tree()
_update_pose()
## Set the hand material override
func set_hand_material_override(material : Material) -> void:
hand_material_override = material
if is_inside_tree():
_update_hand_material_override()
## Set the default open-hand pose
func set_default_pose(pose : XRToolsHandPoseSettings) -> void:
default_pose = pose
if is_inside_tree():
_update_pose()
## Add a pose override
func add_pose_override(who : Node, priority : int, settings : XRToolsHandPoseSettings) -> void:
# Remove any existing pose override from this source
var modified := _remove_pose_override(who)
# Insert the pose override
if settings:
_insert_pose_override(who, priority, settings)
modified = true
# Update the pose
if modified:
_update_pose()
## Remove a pose override
func remove_pose_override(who : Node) -> void:
# Remove the pose override
var modified := _remove_pose_override(who)
# Update the pose
if modified:
_update_pose()
## Force the grip and trigger values (primarily for preview)
func force_grip_trigger(grip : float = -1.0, trigger : float = -1.0) -> void:
# Save the forced values
_force_grip = grip
_force_trigger = trigger
# Update the animation if forcing to specific values
if grip >= 0.0: $AnimationTree.set("parameters/Grip/blend_amount", grip)
if trigger >= 0.0: $AnimationTree.set("parameters/Trigger/blend_amount", trigger)
## This function adds a target override. The collision hand will attempt to
## move to the highest priority target, or the [XRController3D] if no override
## is specified.
func add_target_override(target : Node3D, priority : int) -> void:
# Remove any existing target override from this source
var modified := _remove_target_override(target)
# Insert the target override
_insert_target_override(target, priority)
modified = true
# Update the target
if modified:
_update_target()
## This function remove a target override.
func remove_target_override(target : Node3D) -> void:
# Remove the target override
var modified := _remove_target_override(target)
# Update the pose
if modified:
_update_target()
func _update_hand_blend_tree() -> void:
# As we're going to make modifications to our animation tree, we need to do
# a deep copy, simply setting resource local to scene does not seem to be enough
if _animation_tree and hand_blend_tree:
_tree_root = hand_blend_tree.duplicate(true)
_animation_tree.tree_root = _tree_root
func _update_hand_material_override() -> void:
if _hand_mesh:
_hand_mesh.material_override = hand_material_override
func _update_pose() -> void:
# Skip if no blend tree
if !_tree_root:
return
# Select the pose settings
var pose_settings : XRToolsHandPoseSettings = default_pose
if _pose_overrides.size():
pose_settings = _pose_overrides[0].settings
# Get the open and closed pose animations
var open_pose : Animation = pose_settings.open_pose
var closed_pose : Animation = pose_settings.closed_pose
# Apply the open hand pose in the player and blend tree
if open_pose:
var open_name = _animation_player.find_animation(open_pose)
if open_name == "":
open_name = "open_hand"
if _animation_player.has_animation(open_name):
_animation_player.get_animation_library("").remove_animation(open_name)
_animation_player.get_animation_library("").add_animation(open_name, open_pose)
var open_hand_obj : AnimationNodeAnimation = _tree_root.get_node("OpenHand")
if open_hand_obj:
open_hand_obj.animation = open_name
# Apply the closed hand pose in the player and blend tree
if closed_pose:
var closed_name = _animation_player.find_animation(closed_pose)
if closed_name == "":
closed_name = "closed_hand"
if _animation_player.has_animation(closed_name):
_animation_player.get_animation_library("").remove_animation(closed_name)
_animation_player.get_animation_library("").add_animation(closed_name, closed_pose)
var closed_hand_obj : AnimationNodeAnimation = _tree_root.get_node("ClosedHand1")
if closed_hand_obj:
closed_hand_obj.animation = closed_name
closed_hand_obj = _tree_root.get_node("ClosedHand2")
if closed_hand_obj:
closed_hand_obj.animation = closed_name
func _insert_pose_override(who : Node, priority : int, settings : XRToolsHandPoseSettings) -> void:
# Construct the pose override
var override := PoseOverride.new(who, priority, settings)
# Iterate over all pose overrides in the list
for pos in _pose_overrides.size():
# Get the pose override
var pose : PoseOverride = _pose_overrides[pos]
# Insert as early as possible to not invalidate sorting
if pose.priority <= priority:
_pose_overrides.insert(pos, override)
return
# Insert at the end
_pose_overrides.push_back(override)
func _remove_pose_override(who : Node) -> bool:
var pos := 0
var length := _pose_overrides.size()
var modified := false
# Iterate over all pose overrides in the list
while pos < length:
# Get the pose override
var pose : PoseOverride = _pose_overrides[pos]
# Check for a match
if pose.who == who:
# Remove the override
_pose_overrides.remove_at(pos)
modified = true
length -= 1
else:
# Advance down the list
pos += 1
# Return the modified indicator
return modified
# This function inserts a target override into the overrides list by priority
# order.
func _insert_target_override(target : Node3D, priority : int) -> void:
# Construct the target override
var override := TargetOverride.new(target, priority)
# Iterate over all target overrides in the list
for pos in _target_overrides.size():
# Get the target override
var o : TargetOverride = _target_overrides[pos]
# Insert as early as possible to not invalidate sorting
if o.priority <= priority:
_target_overrides.insert(pos, override)
return
# Insert at the end
_target_overrides.push_back(override)
# This function removes a target from the overrides list
func _remove_target_override(target : Node) -> bool:
var pos := 0
var length := _target_overrides.size()
var modified := false
# Iterate over all pose overrides in the list
while pos < length:
# Get the target override
var o : TargetOverride = _target_overrides[pos]
# Check for a match
if o.target == target:
# Remove the override
_target_overrides.remove_at(pos)
modified = true
length -= 1
else:
# Advance down the list
pos += 1
# Return the modified indicator
return modified
# This function updates the target for hand movement.
func _update_target() -> void:
if _target_overrides.size():
_target = _target_overrides[0].target
else:
_target = get_parent()
static func _find_child(node : Node, type : String) -> Node:
# Iterate through all children
for child in node.get_children():
# If the child is a match then return it
if child.is_class(type):
return child
# Recurse into child
var found := _find_child(child, type)
if found:
return found
# No child found matching type
return null

View File

@@ -0,0 +1 @@
uid://cnwtovj4mqc45

View File

@@ -0,0 +1,96 @@
@tool
class_name XRToolsHandAimOffset
extends Node3D
## XRToolsHandAimOffset will automatically adjust its position,
## to the aim position (within limits).
## Hand offset to apply based on our controller pose
## You can use auto if you're using the default aim_pose or grip_pose poses.
@export_enum("auto", "aim", "grip", "palm", "disable") var hand_offset_mode : int = 0:
set(value):
hand_offset_mode = value
notify_property_list_changed()
if is_inside_tree():
_update_transform()
# Controller
var _controller : XRController3D
# Keep track of our tracker and pose
var _controller_tracker_and_pose : String = ""
# Which node are we applying our transform on?
var _apply_to : Node3D
# Additional transform to apply
var _base_transform : Transform3D = Transform3D()
## Add support for is_xr_class on XRTools classes
func is_xr_class(xr_name: String) -> bool:
return xr_name == "XRToolsHandAimOffset"
## Set the node we apply our transform to
## Must be set before _enter_tree is called for the first time.
func set_apply_to(node : Node3D) -> void:
_apply_to = node
## Set a base transform to apply
func set_base_transform(base_transform : Transform3D) -> void:
_base_transform = base_transform
if is_inside_tree():
_update_transform()
# Called when we're added to the tree
func _enter_tree():
if not _apply_to:
_apply_to = self
_controller = XRHelpers.get_xr_controller(self)
_update_transform()
# Called when we exit the tree
func _exit_tree():
if _controller:
_controller = null
# Check property config
func _validate_property(property):
if hand_offset_mode != 4 and (property.name == "position" or property.name == "rotation" or property.name == "scale" or property.name == "rotation_edit_mode" or property.name == "rotation_order"):
# We control these, don't let the user set them.
property.usage = PROPERTY_USAGE_NONE
# This method verifies the hand has a valid configuration.
func _get_configuration_warnings() -> PackedStringArray:
var warnings := PackedStringArray()
# Check for XR Controller
var controller = XRHelpers.get_xr_controller(self)
if not controller:
warnings.append("Hand should descent from an XRController3D node")
return warnings
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
# If we have a controller, make sure our hand transform is updated when needed.
if _controller:
var tracker_and_pose = _controller.tracker + "." + _controller.pose
if _controller_tracker_and_pose != tracker_and_pose:
_controller_tracker_and_pose = tracker_and_pose
if hand_offset_mode == 0:
_update_transform()
# Update our transform so we are positioned on our aim pose
func _update_transform() -> void:
if _apply_to and hand_offset_mode != 4:
_apply_to.transform = XRTools.get_aim_offset(hand_offset_mode, _controller) * _base_transform

View File

@@ -0,0 +1 @@
uid://bhjb6f4bslawl

View File

@@ -0,0 +1,100 @@
@tool
class_name XRToolsHandPalmOffset
extends Node3D
## XRToolsHandPalmOffset will automatically adjust its position,
## to locate the palm of a hand (within limits).
## Hand offset to apply based on our controller pose
## You can use auto if you're using the default aim_pose or grip_pose poses.
@export_enum("auto", "aim", "grip", "palm", "disable") var hand_offset_mode : int = 0:
set(value):
hand_offset_mode = value
notify_property_list_changed()
if is_inside_tree():
_update_transform()
# Controller
var _controller : XRController3D
# Keep track of our tracker and pose
var _controller_tracker_and_pose : String = ""
# Which node are we applying our transform on?
var _apply_to : Node3D
# Additional transform to apply
var _base_transform : Transform3D = Transform3D()
## Add support for is_xr_class on XRTools classes
func is_xr_class(xr_name: String) -> bool:
return xr_name == "XRToolsHandAimOffset"
## Set the node we apply our transform to
## Must be set before _enter_tree is called for the first time.
func set_apply_to(node : Node3D) -> void:
_apply_to = node
## Set a base transform to apply
func set_base_transform(base_transform : Transform3D) -> void:
_base_transform = base_transform
if is_inside_tree():
_update_transform()
# Called when we're added to the tree
func _enter_tree():
if not _apply_to:
_apply_to = self
_controller = XRHelpers.get_xr_controller(self)
_update_transform()
# Called when we exit the tree
func _exit_tree():
if _controller:
_controller = null
# Check property config
func _validate_property(property):
if hand_offset_mode != 4 and (property.name == "position" or property.name == "rotation" or property.name == "scale" or property.name == "rotation_edit_mode" or property.name == "rotation_order"):
# We control these, don't let the user set them.
property.usage = PROPERTY_USAGE_NONE
# This method verifies the hand has a valid configuration.
func _get_configuration_warnings() -> PackedStringArray:
var warnings := PackedStringArray()
# Check for XR Controller
var controller = XRHelpers.get_xr_controller(self)
if not controller:
warnings.append("Hand should descent from an XRController3D node")
elif controller.pose == "aim":
# FIXME we don't get a refresh when the pose changes,
# so this doesn't clear until a scene is reloaded...
warnings.append("The aim pose is no longer suitable for hand position, consider using the grip pose instead")
return warnings
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
# If we have a controller, make sure our hand transform is updated when needed.
if _controller:
var tracker_and_pose = _controller.tracker + "." + _controller.pose
if _controller_tracker_and_pose != tracker_and_pose:
_controller_tracker_and_pose = tracker_and_pose
if hand_offset_mode == 0:
_update_transform()
# Update our transform so we are positioned on our palm
func _update_transform() -> void:
if _apply_to and hand_offset_mode != 4:
_apply_to.transform = XRTools.get_palm_offset(hand_offset_mode, _controller) * _base_transform

View File

@@ -0,0 +1 @@
uid://drbnpanwky1vo

View File

@@ -0,0 +1,154 @@
@tool
class_name XRToolsHandPhysicsBone
extends BoneAttachment3D
## XR Tools Physics Hand Bone
##
## This script adds a physics-bone to a godot-xr-tools physics hand.
##
## It extends from [BoneAttachment3D] to track the position of the bone in
## the hand skeleton, and uses this position to move a [CharacterBody3D]
## physics-bone with a [CapsuleShape3D] collider.
##
## The physics-bone is manually driven with to the position and rotation of the
## skeletal-bone. The physics-bone is set as top-level to prevent the
## physics-bone from inheriting any hand scaling as this would scale the
## collider which the physics engine cannot tolerate.
##
## To handle scaling, this script subscribes to the hand_scale_changed signal
## emitted by the [XRToolsHand] script and manually adjusts the [CapsuleShape3D]
## collider of the physics-bone to keep it sized appropriately.
##
## There are additional collision and group settings for this specific
## bone, which allows per-bone collision detection.
## Length of the physics-bone
@export var length : float = 0.03
## Ratio of bone length to width
@export var width_ratio : float = 0.3
## Additional collision layer for this one bone
@export_flags_3d_physics var collision_layer : int = 0
## Additional bone group for this one bone
@export var bone_group : String = ""
# Physics-bone collider shape
var _bone_shape : CapsuleShape3D
# Physics-bone body node
var _physics_bone : CharacterBody3D
# Node attached to the skeletal-bone, and the target of the physics-bone
var _skeletal_bone : Node3D
# Add support for is_xr_class on XRTools classes
func is_xr_class(xr_name: String) -> bool:
return xr_name == "XRToolsHandPhysicsBone"
# Called when the node enters the scene tree. This constructs the physics-bone
# nodes and performs initial positioning.
func _ready():
# Skip if in the editor
if Engine.is_editor_hint():
return
# Connect the 'hand_scale_changed' signal
var physics_hand := XRToolsHand.find_instance(self) as XRToolsPhysicsHand
if physics_hand:
physics_hand.hand_scale_changed.connect(_on_hand_scale_changed)
# Construct the physics-bone shape
_bone_shape = CapsuleShape3D.new()
_bone_shape.margin = physics_hand.margin
_on_hand_scale_changed(XRServer.world_scale)
# Construct the physics-bone collision shape
var bone_collision := CollisionShape3D.new()
bone_collision.set_name("BoneCollision")
bone_collision.shape = _bone_shape
#bone_collision.transform.basis = Basis(Vector3.RIGHT, PI/2)
# Construct the physics-bone body
_physics_bone = CharacterBody3D.new()
_physics_bone.set_name("BoneBody")
_physics_bone.set_as_top_level(true)
_physics_bone.collision_layer = physics_hand.collision_layer | collision_layer
_physics_bone.collision_mask = 0
_physics_bone.add_child(bone_collision)
# Set the optional bone group for all bones in the hand
if not physics_hand.bone_group.is_empty():
_physics_bone.add_to_group(physics_hand.bone_group)
# Set the optional bone group for this one bone
if not bone_group.is_empty():
_physics_bone.add_to_group(bone_group)
# Construct the bone middle spatial
_skeletal_bone = Node3D.new()
_skeletal_bone.transform.origin = Vector3.UP * length / 2
# Add the physics-bone body to this hand bone
add_child(_physics_bone)
add_child(_skeletal_bone)
# Perform initial teleport of the phsysics-bone to the skeletal-bone
_teleport_bone()
# Called during the physics process and moves the physics-bone to follow the
# skeletal-bone.
func _physics_process(delta: float) -> void:
# Skip if in the editor
if Engine.is_editor_hint():
return
_move_bone(delta)
# This method moves the physics-bone to the skeletal-bone by first doing a
# move_and_slide as this works well for collision-interactions. It then
# rotates the physics-bone to match the skeletal-bone.
func _move_bone(delta: float) -> void:
# Get the skeletal-bone transform
var bone_xform := _skeletal_bone.global_transform
# Get the required velocity to move the physics-bone to the skeletal-bone
var bone_vel := (bone_xform.origin - _physics_bone.global_transform.origin) / delta
# Move the physics-bone into position
_physics_bone.velocity = bone_vel
_physics_bone.move_and_slide()
# Rotate the physics-bone into the correct rotation
_physics_bone.global_transform.basis = bone_xform.basis
# This method teleports the physics-bone to the skeletal-bone.
func _teleport_bone() -> void:
# Get the bone transform
var bone_xform := _skeletal_bone.global_transform
# Set the bone position
_physics_bone.global_transform = Transform3D(
Basis(bone_xform.basis.get_rotation_quaternion()),
bone_xform.origin)
# This method handles changes to the hand scale by adjusting the
# physics-bone collider shape to match.
func _on_hand_scale_changed(scale: float) -> void:
# Get the scaled length and width
var length_scaled := length * scale
var width_scaled := length_scaled * width_ratio
# Adjust the shape
_bone_shape.radius = width_scaled
_bone_shape.height = length_scaled

View File

@@ -0,0 +1 @@
uid://bfo7c10rq6xuu

View File

@@ -0,0 +1,8 @@
[gd_resource type="ORMMaterial3D" load_steps=3 format=3 uid="uid://bde1c58rcajee"]
[ext_resource type="Texture2D" uid="uid://cs7m7m0k2506g" path="res://addons/godot-xr-tools/hands/textures/african_baseColor.png" id="1_dn1i6"]
[ext_resource type="Texture2D" uid="uid://h3sjbnu6uplb" path="res://addons/godot-xr-tools/hands/textures/hands_occlusionRoughnessMetallic.png" id="2_di2ux"]
[resource]
albedo_texture = ExtResource("1_dn1i6")
orm_texture = ExtResource("2_di2ux")

View File

@@ -0,0 +1,12 @@
[gd_resource type="ORMMaterial3D" load_steps=4 format=3 uid="uid://26qy0lwlws4a"]
[ext_resource type="Texture2D" uid="uid://dk8o82rjww802" path="res://addons/godot-xr-tools/hands/textures/african_realistic_baseColor.png" id="1_seyus"]
[ext_resource type="Texture2D" uid="uid://b6tgv6ucbgv3r" path="res://addons/godot-xr-tools/hands/textures/hands_normal.png" id="2_s3yd2"]
[ext_resource type="Texture2D" uid="uid://h3sjbnu6uplb" path="res://addons/godot-xr-tools/hands/textures/hands_occlusionRoughnessMetallic.png" id="3_p3lxw"]
[resource]
albedo_texture = ExtResource("1_seyus")
orm_texture = ExtResource("3_p3lxw")
normal_enabled = true
normal_scale = 0.25
normal_texture = ExtResource("2_s3yd2")

View File

@@ -0,0 +1,8 @@
[gd_resource type="ORMMaterial3D" load_steps=3 format=3 uid="uid://dy6nhifvvmm73"]
[ext_resource type="Texture2D" uid="uid://c506svon268ms" path="res://addons/godot-xr-tools/hands/textures/caucasian_baseColor.png" id="1_si4on"]
[ext_resource type="Texture2D" uid="uid://h3sjbnu6uplb" path="res://addons/godot-xr-tools/hands/textures/hands_occlusionRoughnessMetallic.png" id="2_agvfk"]
[resource]
albedo_texture = ExtResource("1_si4on")
orm_texture = ExtResource("2_agvfk")

View File

@@ -0,0 +1,12 @@
[gd_resource type="ORMMaterial3D" load_steps=4 format=3 uid="uid://rwfrc1fdcjsj"]
[ext_resource type="Texture2D" uid="uid://n1xq4cm67o07" path="res://addons/godot-xr-tools/hands/textures/caucasian_realistic_baseColor.png" id="1_c8nm1"]
[ext_resource type="Texture2D" uid="uid://b6tgv6ucbgv3r" path="res://addons/godot-xr-tools/hands/textures/hands_normal.png" id="2_ax2k6"]
[ext_resource type="Texture2D" uid="uid://h3sjbnu6uplb" path="res://addons/godot-xr-tools/hands/textures/hands_occlusionRoughnessMetallic.png" id="3_6wg12"]
[resource]
albedo_texture = ExtResource("1_c8nm1")
orm_texture = ExtResource("3_6wg12")
normal_enabled = true
normal_scale = 0.25
normal_texture = ExtResource("2_ax2k6")

View File

@@ -0,0 +1,14 @@
[gd_resource type="ORMMaterial3D" load_steps=4 format=3 uid="uid://cdb40djkihelq"]
[ext_resource type="Texture2D" uid="uid://dv711ytktesj2" path="res://addons/godot-xr-tools/hands/textures/cleaning_glove_baseColor.png" id="1_k2x02"]
[ext_resource type="Texture2D" uid="uid://dt6k4nuvpo38o" path="res://addons/godot-xr-tools/hands/textures/glove_normal.png" id="2_uiqlw"]
[ext_resource type="Texture2D" uid="uid://yslwgnpbpeen" path="res://addons/godot-xr-tools/hands/textures/glove_occlusionRoughnessMetallic.png" id="3_8ux4v"]
[resource]
albedo_texture = ExtResource("1_k2x02")
orm_texture = ExtResource("3_8ux4v")
normal_enabled = true
normal_scale = 0.1
normal_texture = ExtResource("2_uiqlw")
clearcoat_enabled = true
ao_light_affect = 1.0

View File

@@ -0,0 +1,18 @@
[gd_resource type="StandardMaterial3D" load_steps=4 format=3 uid="uid://p0q2df2dmy62"]
[ext_resource type="Texture2D" uid="uid://dgkfppran5j1f" path="res://addons/godot-xr-tools/hands/textures/glove_caucasian_green_camo.png" id="1_rbe3r"]
[ext_resource type="Texture2D" uid="uid://d2iocwfr47kqp" path="res://addons/godot-xr-tools/hands/textures/glove_fingerless_occlusionRoughnessMetallic.png" id="2_qqman"]
[sub_resource type="ORMMaterial3D" id="ORMMaterial3D_wu6j1"]
transparency = 1
depth_draw_mode = 1
albedo_texture = ExtResource("1_rbe3r")
orm_texture = ExtResource("2_qqman")
[resource]
render_priority = -1
next_pass = SubResource("ORMMaterial3D_wu6j1")
transparency = 1
no_depth_test = true
shading_mode = 0
albedo_color = Color(0, 1, 1, 0.25098)

Some files were not shown because too many files have changed in this diff Show More