new repo who this
This commit is contained in:
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
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.
@@ -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 = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
|
||||
@@ -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.
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.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,32 @@
|
||||
[gd_resource type="AnimationNodeBlendTree" 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"]
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
[remap]
|
||||
|
||||
importer="keep"
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
[remap]
|
||||
|
||||
importer="keep"
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
[remap]
|
||||
|
||||
importer="keep"
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
[remap]
|
||||
|
||||
importer="keep"
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://c8vnltn217quj
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://cnwtovj4mqc45
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://bhjb6f4bslawl
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://drbnpanwky1vo
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
uid://bfo7c10rq6xuu
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user