// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Input { [MixedRealityController( SupportedControllerType.ArticulatedHand, new[] { Handedness.Left, Handedness.Right })] public class SimulatedArticulatedHand : SimulatedHand { public override ControllerSimulationMode SimulationMode => ControllerSimulationMode.ArticulatedHand; private Vector3 currentPointerPosition = Vector3.zero; private Quaternion currentPointerRotation = Quaternion.identity; private MixedRealityPose lastPointerPose = MixedRealityPose.ZeroIdentity; private MixedRealityPose currentPointerPose = MixedRealityPose.ZeroIdentity; private MixedRealityPose currentIndexPose = MixedRealityPose.ZeroIdentity; private MixedRealityPose currentGripPose = MixedRealityPose.ZeroIdentity; private MixedRealityPose lastGripPose = MixedRealityPose.ZeroIdentity; /// /// Constructor. /// public SimulatedArticulatedHand( TrackingState trackingState, Handedness controllerHandedness, IMixedRealityInputSource inputSource = null, MixedRealityInteractionMapping[] interactions = null) : base(trackingState, controllerHandedness, inputSource, interactions, new ArticulatedHandDefinition(inputSource, controllerHandedness)) { handDefinition = Definition as ArticulatedHandDefinition; } private readonly ArticulatedHandDefinition handDefinition; /// protected override void UpdateHandJoints(SimulatedHandData handData) { for (int i = 0; i < ArticulatedHandPose.JointCount; i++) { TrackedHandJoint handJoint = (TrackedHandJoint)i; if (!jointPoses.ContainsKey(handJoint)) { jointPoses.Add(handJoint, handData.Joints[i]); } else { jointPoses[handJoint] = handData.Joints[i]; } } handDefinition?.UpdateHandJoints(jointPoses); } /// protected override void UpdateInteractions(SimulatedHandData handData) { lastPointerPose = currentPointerPose; lastGripPose = currentGripPose; // For convenience of simulating in Unity Editor, make the ray use the index // finger position instead of knuckle, since the index finger doesn't move when we press. Vector3 pointerPosition = jointPoses[TrackedHandJoint.IndexTip].Position; IsPositionAvailable = IsRotationAvailable = pointerPosition != Vector3.zero; if (IsPositionAvailable) { HandRay.Update(pointerPosition, GetPalmNormal(), CameraCache.Main.transform, ControllerHandedness); Ray ray = HandRay.Ray; currentPointerPose.Position = ray.origin; currentPointerPose.Rotation = Quaternion.LookRotation(ray.direction); currentGripPose = jointPoses[TrackedHandJoint.Palm]; currentIndexPose = jointPoses[TrackedHandJoint.IndexTip]; } if (lastGripPose != currentGripPose) { if (IsPositionAvailable && IsRotationAvailable) { CoreServices.InputSystem?.RaiseSourcePoseChanged(InputSource, this, currentGripPose); } else if (IsPositionAvailable && !IsRotationAvailable) { CoreServices.InputSystem?.RaiseSourcePositionChanged(InputSource, this, currentPointerPosition); } else if (!IsPositionAvailable && IsRotationAvailable) { CoreServices.InputSystem?.RaiseSourceRotationChanged(InputSource, this, currentPointerRotation); } } for (int i = 0; i < Interactions?.Length; i++) { switch (Interactions[i].InputType) { case DeviceInputType.SpatialPointer: Interactions[i].PoseData = currentPointerPose; if (Interactions[i].Changed) { CoreServices.InputSystem?.RaisePoseInputChanged(InputSource, ControllerHandedness, Interactions[i].MixedRealityInputAction, currentPointerPose); } break; case DeviceInputType.SpatialGrip: Interactions[i].PoseData = currentGripPose; if (Interactions[i].Changed) { CoreServices.InputSystem?.RaisePoseInputChanged(InputSource, ControllerHandedness, Interactions[i].MixedRealityInputAction, currentGripPose); } break; case DeviceInputType.Select: case DeviceInputType.TriggerPress: case DeviceInputType.GripPress: Interactions[i].BoolData = handData.IsPinching; if (Interactions[i].Changed) { if (Interactions[i].BoolData) { CoreServices.InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, Interactions[i].MixedRealityInputAction); } else { CoreServices.InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, Interactions[i].MixedRealityInputAction); } } break; case DeviceInputType.IndexFinger: Interactions[i].PoseData = currentIndexPose; if (Interactions[i].Changed) { CoreServices.InputSystem?.RaisePoseInputChanged(InputSource, ControllerHandedness, Interactions[i].MixedRealityInputAction, currentIndexPose); } break; case DeviceInputType.ThumbStick: handDefinition?.UpdateCurrentTeleportPose(Interactions[i]); break; } } } } }