[gd_scene format=3 uid="uid://draoeoi7h6mv6"] [ext_resource type="PackedScene" uid="uid://bq86r4yll8po" path="res://addons/godot-xr-tools/hands/scenes/lowpoly/left_fullglove_low.tscn" id="1_g2els"] [ext_resource type="Script" uid="uid://ch7s411vwan10" path="res://scripts/xr_origin_3d.gd" id="1_smehm"] [ext_resource type="Script" uid="uid://csbuxf0o41r55" path="res://scripts/player.gd" id="1_ur7pv"] [ext_resource type="PackedScene" uid="uid://xqimcf20s2jp" path="res://addons/godot-xr-tools/hands/scenes/lowpoly/right_fullglove_low.tscn" id="2_qhqgy"] [ext_resource type="PackedScene" uid="uid://bl2nuu3qhlb5k" path="res://addons/godot-xr-tools/functions/movement_direct.tscn" id="3_smehm"] [ext_resource type="Script" uid="uid://clfxgaxhlvxxo" path="res://scripts/handLogic.gd" id="4_smehm"] [ext_resource type="PackedScene" uid="uid://c2q5phg8w08o" path="res://addons/godot-xr-tools/functions/movement_jump.tscn" id="5_ur7pv"] [ext_resource type="PackedScene" uid="uid://cqhw276realc" path="res://addons/godot-xr-tools/functions/function_pointer.tscn" id="6_d2wvv"] [ext_resource type="PackedScene" uid="uid://b6bk2pj8vbj28" path="res://addons/godot-xr-tools/functions/movement_turn.tscn" id="6_y4r1p"] [ext_resource type="PackedScene" uid="uid://diyu06cw06syv" path="res://addons/godot-xr-tools/player/player_body.tscn" id="7_d2wvv"] [sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_y4r1p"] properties/0/path = NodePath("XROrigin3D:position") properties/0/spawn = true properties/0/replication_mode = 1 properties/1/path = NodePath("XROrigin3D:rotation") properties/1/spawn = true properties/1/replication_mode = 1 properties/2/path = NodePath("XROrigin3D/leftHand/LeftHand:_pose_overrides") properties/2/spawn = true properties/2/replication_mode = 1 properties/3/path = NodePath("XROrigin3D/rightHand/RightHand:_pose_overrides") properties/3/spawn = true properties/3/replication_mode = 1 properties/4/path = NodePath("XROrigin3D/leftHand/leftRigid/LeftPhysics:_pose_overrides") properties/4/spawn = true properties/4/replication_mode = 1 properties/5/path = NodePath("XROrigin3D/rightHand/rightRigid/RightPhysics:_pose_overrides") properties/5/spawn = true properties/5/replication_mode = 1 properties/6/path = NodePath("XROrigin3D/leftHand:position") properties/6/spawn = true properties/6/replication_mode = 1 properties/7/path = NodePath("XROrigin3D/leftHand:rotation") properties/7/spawn = true properties/7/replication_mode = 1 properties/8/path = NodePath("XROrigin3D/leftHand/leftRigid:position") properties/8/spawn = true properties/8/replication_mode = 1 properties/9/path = NodePath("XROrigin3D/leftHand/leftRigid:rotation") properties/9/spawn = true properties/9/replication_mode = 1 properties/10/path = NodePath("XROrigin3D/rightHand:position") properties/10/spawn = true properties/10/replication_mode = 1 properties/11/path = NodePath("XROrigin3D/rightHand:rotation") properties/11/spawn = true properties/11/replication_mode = 1 properties/12/path = NodePath("XROrigin3D/rightHand/rightRigid:position") properties/12/spawn = true properties/12/replication_mode = 1 properties/13/path = NodePath("XROrigin3D/rightHand/rightRigid:rotation") properties/13/spawn = true properties/13/replication_mode = 1 properties/14/path = NodePath("XROrigin3D/leftHand/leftRigid/HingeJoint3D:node_a") properties/14/spawn = true properties/14/replication_mode = 1 properties/15/path = NodePath("XROrigin3D/leftHand/leftRigid/HingeJoint3D:node_b") properties/15/spawn = true properties/15/replication_mode = 1 properties/16/path = NodePath("XROrigin3D/rightHand/rightRigid/HingeJoint3D:node_a") properties/16/spawn = true properties/16/replication_mode = 1 properties/17/path = NodePath("XROrigin3D/rightHand/rightRigid/HingeJoint3D:node_b") properties/17/spawn = true properties/17/replication_mode = 1 properties/18/path = NodePath("XROrigin3D/MeshInstance3D:position") properties/18/spawn = true properties/18/replication_mode = 1 [sub_resource type="SphereMesh" id="SphereMesh_6500s"] radius = 0.125 height = 0.25 [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_g6ne4"] transparency = 1 albedo_color = Color(0, 0.54509807, 0.9254902, 0.19215687) [sub_resource type="GDScript" id="GDScript_fjrip"] script/source = "@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 " [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_fjrip"] [sub_resource type="BoxShape3D" id="BoxShape3D_6500s"] size = Vector3(0.051635742, 0.1160965, 0.14953613) [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_smehm"] [node name="player" type="Node3D" unique_id=388903966] script = ExtResource("1_ur7pv") [node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="." unique_id=291104021] replication_config = SubResource("SceneReplicationConfig_y4r1p") [node name="XROrigin3D" type="XROrigin3D" parent="." unique_id=2026609409] script = ExtResource("1_smehm") [node name="MeshInstance3D" type="MeshInstance3D" parent="XROrigin3D" unique_id=944746671] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.7, 0) mesh = SubResource("SphereMesh_6500s") [node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D" unique_id=661262409] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.7, 0) [node name="leftHand" type="XRController3D" parent="XROrigin3D" unique_id=972246256 node_paths=PackedStringArray("physicsHand")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 1, 0) tracker = &"left_hand" pose = &"grip" script = ExtResource("4_smehm") physicsHand = NodePath("leftRigid") [node name="LeftHand" parent="XROrigin3D/leftHand" unique_id=750051836 instance=ExtResource("1_g2els")] hand_material_override = SubResource("StandardMaterial3D_g6ne4") [node name="MovementDirect" parent="XROrigin3D/leftHand" unique_id=552284268 instance=ExtResource("3_smehm")] strafe = true [node name="FunctionPointer" parent="XROrigin3D/leftHand" unique_id=1883490498 instance=ExtResource("6_d2wvv")] transform = Transform3D(1, 0, 0, 0, 0.70710677, 0.70710677, 0, -0.70710677, 0.70710677, 0.02, -0.03535534, -0.10606602) show_laser = 2 laser_length = 1 show_target = true [node name="leftRigid" type="RigidBody3D" parent="XROrigin3D/leftHand" unique_id=1627474705] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.39838424, -0.048642874, 0) collision_layer = 4 collision_mask = 2 mass = 0.2 gravity_scale = 0.0 contact_monitor = true max_contacts_reported = 1 linear_damp = 30.0 angular_damp = 30.0 [node name="LeftPhysics" parent="XROrigin3D/leftHand/leftRigid" unique_id=1387724990 instance=ExtResource("1_g2els")] transform = Transform3D(1, 0, 0, 0, 0.9999999, 4.3711385e-08, 0, -4.3711385e-08, 0.9999999, 0, 0, 0) script = SubResource("GDScript_fjrip") hand_material_override = SubResource("StandardMaterial3D_fjrip") [node name="CollisionShape3D" type="CollisionShape3D" parent="XROrigin3D/leftHand/leftRigid" unique_id=1238387176] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.010949463, 0.012000024, -0.025) shape = SubResource("BoxShape3D_6500s") [node name="HingeJoint3D" type="HingeJoint3D" parent="XROrigin3D/leftHand/leftRigid" unique_id=1861056992] angular_limit/enable = true angular_limit/upper = 0.0 angular_limit/lower = 0.0 [node name="rightHand" type="XRController3D" parent="XROrigin3D" unique_id=73016062 node_paths=PackedStringArray("physicsHand")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 1, 0) tracker = &"right_hand" pose = &"grip" script = ExtResource("4_smehm") physicsHand = NodePath("rightRigid") [node name="RightHand" parent="XROrigin3D/rightHand" unique_id=930822948 instance=ExtResource("2_qhqgy")] hand_material_override = SubResource("StandardMaterial3D_g6ne4") [node name="MovementJump" parent="XROrigin3D/rightHand" unique_id=1008127823 instance=ExtResource("5_ur7pv")] jump_button_action = "ax_button" [node name="MovementTurn" parent="XROrigin3D/rightHand" unique_id=685646321 instance=ExtResource("6_y4r1p")] turn_mode = 2 [node name="FunctionPointer" parent="XROrigin3D/rightHand" unique_id=1536690438 instance=ExtResource("6_d2wvv")] transform = Transform3D(1, 0, 0, 0, 0.70710677, 0.70710677, 0, -0.70710677, 0.70710677, -0.02, -0.03535534, -0.10606602) show_laser = 2 laser_length = 1 show_target = true [node name="rightRigid" type="RigidBody3D" parent="XROrigin3D/rightHand" unique_id=436660202] transform = Transform3D(1, 0, 0, 0, 0.99927187, 0.03815463, 0, -0.03815463, 0.99927187, -0.41185614, -0.053098798, 0) collision_layer = 4 collision_mask = 2 mass = 0.2 gravity_scale = 0.0 contact_monitor = true max_contacts_reported = 1 linear_damp = 30.0 angular_damp = 30.0 [node name="RightPhysics" parent="XROrigin3D/rightHand/rightRigid" unique_id=1287548770 instance=ExtResource("2_qhqgy")] script = SubResource("GDScript_fjrip") hand_material_override = SubResource("StandardMaterial3D_smehm") [node name="CollisionShape3D" type="CollisionShape3D" parent="XROrigin3D/rightHand/rightRigid" unique_id=1281593956] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0119018555, 0.012240827, -0.024719207) shape = SubResource("BoxShape3D_6500s") [node name="HingeJoint3D" type="HingeJoint3D" parent="XROrigin3D/rightHand/rightRigid" unique_id=657199832] angular_limit/enable = true angular_limit/upper = 0.0 angular_limit/lower = 0.0 [node name="PlayerBody" parent="XROrigin3D" unique_id=1152944339 instance=ExtResource("7_d2wvv")] collision_mask = 1019 [connection signal="input_float_changed" from="XROrigin3D/leftHand" to="XROrigin3D/leftHand" method="_on_input_float_changed"] [connection signal="body_entered" from="XROrigin3D/leftHand/leftRigid" to="XROrigin3D/leftHand" method="_rigid_body_entered"] [connection signal="body_exited" from="XROrigin3D/leftHand/leftRigid" to="XROrigin3D/leftHand" method="_rigid_body_exited"] [connection signal="input_float_changed" from="XROrigin3D/rightHand" to="XROrigin3D/rightHand" method="_on_input_float_changed"] [connection signal="body_entered" from="XROrigin3D/rightHand/rightRigid" to="XROrigin3D/rightHand" method="_rigid_body_entered"] [connection signal="body_exited" from="XROrigin3D/rightHand/rightRigid" to="XROrigin3D/rightHand" method="_rigid_body_exited"]