/// Once you have that information, it's simply a matter of placing a /// window off to the side of the hand! The palm pose Right direction /// points to different sides of each hand, so a different X offset /// is required for each hand. public static void DrawHandMenu(Handed handed) { if (!HandFacingHead(handed)) { return; } // Decide the size and offset of the menu Vec2 size = new Vec2(4, 16); float offset = handed == Handed.Left ? -2 - size.x : 2 + size.x; // Position the menu relative to the side of the hand Hand hand = Input.Hand(handed); Vec3 at = hand[FingerId.Little, JointId.KnuckleMajor].position; Vec3 down = hand[FingerId.Little, JointId.Root].position; Vec3 across = hand[FingerId.Index, JointId.KnuckleMajor].position; Pose menuPose = new Pose( at, Quat.LookAt(at, across, at - down) * Quat.FromAngles(0, handed == Handed.Left ? 90 : -90, 0)); menuPose.position += menuPose.Right * offset * U.cm; menuPose.position += menuPose.Up * (size.y / 2) * U.cm; // And make a menu! UI.WindowBegin("HandMenu", ref menuPose, size * U.cm, UIWin.Empty); UI.Button("Test"); UI.Button("That"); UI.Button("Hand"); UI.WindowEnd(); }
public void Step(Handed handed, Color color, float thickness) { // We'll make the whole painting the child of an affordance, so we can // move the painting around while we work with it! Affordances and // Windows both push a transform onto the Hierarchy stack, so all // subsequent locations are then relative to that transform. UI.AffordanceBegin("PaintingRoot", ref _pose, new Bounds(Vec3.One * 5 * Units.cm2m), true); UpdateInput(handed, color, thickness); Draw(); UI.AffordanceEnd(); }
/// :CodeDoc: Guides Using Hands /// ## Hand Menu /// /// Lets imagine you want to make a hand menu, you might need to know /// if the user is looking at the palm of their hand! Here's a quick /// example of using the palm's pose and the dot product to determine /// this. static bool HandFacingHead(Handed handed) { Hand hand = Input.Hand(handed); if (!hand.IsTracked) { return(false); } Vec3 palmDirection = (hand.palm.Forward).Normalized(); Vec3 directionToHead = (Input.Head.position - hand.palm.position).Normalized(); return(Vec3.Dot(palmDirection, directionToHead) > 0.5f); }
void Draw(Handed handed) { Hand hand = Input.Hand(handed); Vec3 tip = Vec3.Lerp(prevTip, hand.pinchPt, 0.3f); if (hand.IsJustPinched && !UI.IsInteracting(handed)) { if (drawPoints.Count > 0) { drawList.Add(drawPoints.ToArray()); } drawPoints.Clear(); drawPoints.Add(new LinePoint(tip, activeColor, lineSize)); drawPoints.Add(new LinePoint(tip, activeColor, lineSize)); prevTip = tip; painting = true; } if (hand.IsJustUnpinched) { painting = false; } if (painting && drawPoints.Count > 1) { Vec3 prev = drawPoints[drawPoints.Count - 2].pt; Vec3 dir = (prev - (drawPoints.Count > 2 ? drawPoints[drawPoints.Count - 3].pt : drawPoints[drawPoints.Count - 1].pt)).Normalized; float dist = Vec3.Distance(prev, tip); float speed = Vec3.Distance(tip, prevTip) * Time.Elapsedf; LinePoint here = new LinePoint(tip, activeColor, Math.Max(1 - speed / 0.0003f, 0.1f) * lineSize); if ((Vec3.Dot(dir, (tip - prev).Normalized) < 0.99f && dist > 0.01f) || dist > 0.05f) { drawPoints.Add(here); } else { drawPoints[drawPoints.Count - 1] = here; } } Lines.Add(drawPoints.ToArray()); for (int i = 0; i < drawList.Count; i++) { Lines.Add(drawList[i]); } prevTip = tip; }
void Draw(Handed handed) { Hand hand = Input.Hand(handed); Vec3 tip = hand[FingerId.Index, JointId.Tip].position; tip = prevTip + (tip - prevTip) * 0.3f; const float minDist = 2 * Units.cm2m; if (hand.IsJustPinched && !UI.IsInteracting(handed)) { if (drawPoints.Count > 0) { drawList.Add(drawPoints.ToArray()); } drawPoints.Clear(); drawPoints.Add(new LinePoint(tip, activeColor, lineSize)); drawPoints.Add(new LinePoint(tip, activeColor, lineSize)); prevTip = tip; painting = true; } if (hand.IsJustUnpinched) { painting = false; } if (painting && drawPoints.Count > 1) { Vec3 prev = drawPoints[drawPoints.Count - 2].pt; Vec3 dir = (prev - (drawPoints.Count > 2 ? drawPoints[drawPoints.Count - 3].pt : drawPoints[drawPoints.Count - 1].pt)).Normalized(); float dist = (prev - tip).Magnitude; float speed = (tip - prevTip).Magnitude * Time.Elapsedf; LinePoint here = new LinePoint(tip, activeColor, Math.Max(1 - speed / 0.0003f, 0.1f) * lineSize); drawPoints[drawPoints.Count - 1] = here; if ((Vec3.Dot(dir, (tip - prev).Normalized()) < 0.99f && dist > 0.01f) || dist > 0.05f) { drawPoints.Add(here); } } Lines.Add(drawPoints.ToArray()); for (int i = 0; i < drawList.Count; i++) { Lines.Add(drawList[i]); } prevTip = tip; }
//set new weapon, used to change the hand positions public void SetWeapon(Weapon weapon) { if (weapon == null) { return; } handed = weapon.handed; renderGun.sprite = weapon.sprite; originLHand = new Vector3(weapon.LHandOffset.x, weapon.LHandOffset.y, -0.0001f); renderLHand.transform.localPosition = originLHand; originGun = weapon.spriteOffset; originBarrel = weapon.barrelOffset; ChangeHandPosition(); }
void UpdateInput(Handed handed, Color color, float thickness) { // Get the hand's fingertip, convert it to local space, and smooth // it out to reduce any jagged noise! The hand's location data is // always provided in world space, but since we're inside of an // Affordance which uses the Hierarchy stack, we need to convert the // fingertip's coordinates into Heirarchy local coordinates before we // can work with it. Hand hand = Input.Hand(handed); Vec3 fingertip = hand[FingerId.Index, JointId.Tip].position; fingertip = Hierarchy.ToLocal(fingertip); fingertip = Vec3.Lerp(_prevFingertip, fingertip, 0.3f); // If the user just made a pinching motion, and is not interacting // with the UI, we'll begin a paint stroke! if (hand.IsJustPinched && !UI.IsInteracting(handed)) { BeginStroke(fingertip, color, thickness); _isDrawing = true; } // If we're drawing a paint stroke, then lets update it with the // current steps information! if (_isDrawing) { UpdateStroke(fingertip, color, thickness); } // And when they cease the pinching motion, we'll end whatever stroke // we started. if (_isDrawing && hand.IsJustUnpinched) { EndStroke(); _isDrawing = false; } _prevFingertip = fingertip; }
/// Once you have that information, it's simply a matter of placing a /// window off to the side of the hand! The palm pose Right direction /// points to different sides of each hand, so a different X offset /// is required for each hand. public static void DrawHandMenu(Handed handed) { if (!HandFacingHead(handed)) { return; } // Decide the size and offset of the menu Vec2 size = new Vec2(4, 16); float offset = handed == Handed.Left ? -4 - size.x : 6 + size.x; // Position the menu relative to the palm Pose menuPose = Input.Hand(handed).palm; menuPose.position += menuPose.Right * offset * U.cm; menuPose.position += menuPose.Up * (size.y / 2) * U.cm; // And make a menu! UI.WindowBegin("HandMenu", ref menuPose, size * U.cm, UIWin.Empty); UI.Button("Test"); UI.Button("That"); UI.Button("Hand"); UI.WindowEnd(); }
[DllImport(NativeLib.DllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr input_hand(Handed hand);
/// <summary>Set the Material used to render the hand! The default /// material uses an offset of 10 to ensure it gets drawn overtop of /// other elements.</summary> /// <param name="hand">>If Handed.Max, this will set the value for /// both hands.</param> /// <param name="material">The new Material!</param> public static void HandMaterial(Handed hand, Material material) => NativeAPI.input_hand_material(hand, material._inst);
/// <summary>Does StereoKit register the hand with the physics /// system? By default, this is true. Right now this is just a single /// block collider, but later will involve per-joint colliders! /// </summary> /// <param name="hand">If Handed.Max, this will set the value for /// both hands.</param> /// <param name="solid">True? Physics! False? No physics.</param> public static void HandSolid(Handed hand, bool solid) => NativeAPI.input_hand_solid(hand, solid);
/// <summary>Sets whether or not StereoKit should render the hand for /// you. Turn this to false if you're going to render your own, or /// don't need the hand itself to be visible.</summary> /// <param name="hand">If Handed.Max, this will set the value for /// both hands.</param> /// <param name="visible">True, StereoKit renders this. False, it /// doesn't.</param> public static void HandVisible(Handed hand, bool visible) => NativeAPI.input_hand_visible(hand, visible);
/// <summary>Clear out the override status from Input.HandOverride, /// and restore the user's control over it again.</summary> /// <param name="hand">Which hand are we clearing the override on? /// </param> public static void HandClearOverride(Handed hand) => NativeAPI.input_hand_override(hand, IntPtr.Zero);
/// <summary>This allows you to completely override the hand's pose /// information! It is still treated like the user's hand, so this is /// great for simulating input for testing purposes. It will remain /// overridden until you call Input.HandClearOverride.</summary> /// <param name="hand">Which hand should be overridden?</param> /// <param name="joints">A 2D array of 25 joints that should be used /// as StereoKit's hand information. See `Hand.fingers` for more /// information.</param> public static void HandOverride(Handed hand, in HandJoint[] joints)
/// <summary>Retreives all the information about the user's hand! /// StereoKit will always provide hand information, however sometimes /// that information is simulated, like in the case of a mouse, or /// controllers. /// /// Note that this is a copy of the hand information, and it's a good /// chunk of data, so it's a good idea to grab it once and keep it /// around for the frame, or at least function, rather than asking /// for it again and again each time you want to touch something. /// </summary> /// <param name="handed">Do you want the left or the right hand? /// </param> /// <returns>A copy of the entire set of hand data!</returns> public static Hand Hand(Handed handed) => Marshal.PtrToStructure <Hand>(NativeAPI.input_hand(handed));
/// <summary>Retreives all the information about the user's hand! /// StereoKit will always provide hand information, however sometimes /// that information is simulated, like in the case of a mouse, or /// controllers. /// /// Note that this is a copy of the hand information, and it's a good /// chunk of data, so it's a good idea to grab it once and keep it /// around for the frame, or at least function, rather than asking /// for it again and again each time you want to touch something. /// </summary> /// <param name="handed">Do you want the left or the right hand? /// </param> /// <returns>A copy of the entire set of hand data!</returns> public static Hand Hand(Handed handed) { Marshal.PtrToStructure(NativeAPI.input_hand(handed), hands[(int)handed]); return(hands[(int)handed]); }
/// <summary>Gets raw controller input data from the system. Note that /// not all buttons provided here are guaranteed to be present on the /// user's physical controller. Controllers are also not guaranteed to /// be available on the system, and are never simulated.</summary> /// <param name="handed">The handedness of the controller to get the /// state of.</param> /// <returns>A reference to a class that contains state information /// about the indicated controller.</returns> public static Controller Controller(Handed handed) { Marshal.PtrToStructure(NativeAPI.input_controller(handed), controllers[(int)handed]); return(controllers[(int)handed]); }
[DllImport(NativeLib.DllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern void input_hand_solid(Handed hand, int solid);
/// <summary>Tells if the user is currently interacting with a UI element! This will be true /// if the hand has an active or focused UI element.</summary> /// <param name="hand">Which hand is interacting?</param> /// <returns>True if the hand has an active or focused UI element. False otherwise.</returns> public static bool IsInteracting(Handed hand) => NativeAPI.ui_is_interacting(hand);
/// :CodeSample: Controller Input.Controller TrackState Input.ControllerMenuButton Controller.IsTracked Controller.trackedPos Controller.trackedRot Controller.IsX1Pressed Controller.IsX2Pressed Controller.IsStickClicked Controller.stick Controller.aim Controller.grip Controller.trigger Controller.pose /// ### Controller Debug Visualizer /// This function shows a debug visualization of the current state of /// the controller! It's not something you'd show to users, but it's /// nice for just seeing how the API works, or as a temporary /// visualization. void ShowController(Handed hand) { Controller c = Input.Controller(hand); if (!c.IsTracked) { return; } Hierarchy.Push(c.pose.ToMatrix()); // Pick the controller color based on trackin info state Color color = Color.Black; if (c.trackedPos == TrackState.Inferred) { color.g = 0.5f; } if (c.trackedPos == TrackState.Known) { color.g = 1; } if (c.trackedRot == TrackState.Inferred) { color.b = 0.5f; } if (c.trackedRot == TrackState.Known) { color.b = 1; } Default.MeshCube.Draw(Default.Material, Matrix.S(new Vec3(3, 3, 8) * U.cm), color); // Show button info on the back of the controller Hierarchy.Push(Matrix.TR(0, 1.6f * U.cm, 0, Quat.LookAt(Vec3.Zero, new Vec3(0, 1, 0), new Vec3(0, 0, -1)))); // Show the tracking states as text Text.Add(c.trackedPos == TrackState.Known?"(pos)":(c.trackedPos == TrackState.Inferred?"~pos~":"pos"), Matrix.TS(0, -0.03f, 0, 0.25f)); Text.Add(c.trackedRot == TrackState.Known?"(rot)":(c.trackedRot == TrackState.Inferred?"~rot~":"rot"), Matrix.TS(0, -0.02f, 0, 0.25f)); // Show the controller's buttons Text.Add(Input.ControllerMenuButton.IsActive()?"(menu)":"menu", Matrix.TS(0, -0.01f, 0, 0.25f)); Text.Add(c.IsX1Pressed?"(X1)":"X1", Matrix.TS(0, 0.00f, 0, 0.25f)); Text.Add(c.IsX2Pressed?"(X2)":"X2", Matrix.TS(0, 0.01f, 0, 0.25f)); // Show the analog stick's information Vec3 stickAt = new Vec3(0, 0.03f, 0); Lines.Add(stickAt, stickAt + c.stick.XY0 * 0.01f, Color.White, 0.001f); if (c.IsStickClicked) { Text.Add("O", Matrix.TS(stickAt, 0.25f)); } // And show the trigger and grip buttons Default.MeshCube.Draw(Default.Material, Matrix.TS(0, -0.015f, -0.005f, new Vec3(0.01f, 0.04f, 0.01f)) * Matrix.TR(new Vec3(0, 0.02f, 0.03f), Quat.FromAngles(-45 + c.trigger * 40, 0, 0))); Default.MeshCube.Draw(Default.Material, Matrix.TS(0.0149f * (hand == Handed.Right?1:-1), 0, 0.015f, new Vec3(0.01f * (1 - c.grip), 0.04f, 0.01f))); Hierarchy.Pop(); Hierarchy.Pop(); // And show the pointer Default.MeshCube.Draw(Default.Material, c.aim.ToMatrix(new Vec3(1, 1, 4) * U.cm), Color.HSV(0, 0.5f, 0.8f).ToLinear()); }
[DllImport(NativeLib.DllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern void input_hand_material(Handed hand, IntPtr material);
/// <summary>This watches a volume of space for pinch interaction /// events! If a hand is inside the space indicated by the bounds, /// this function will return that hand's pinch state, as well as /// indicate which hand did it through the out parameter. /// /// Note that since this only provides the hand's pinch state, it /// won't give you JustActive and JustInactive notifications for /// when the hand enters or leaves the volume.</summary> /// <param name="bounds">A UI hierarchy space bounding volume.</param> /// <param name="hand">This will be the last hand that provides a /// pinch state within this volume. That means that if both hands are /// pinching in this volume, it will provide the Right hand.</param> /// <returns>This will be the pinch state of the last hand that /// provides a pinch state within this volume. That means that if /// both hands are pinching in this volume, it will provide the pinch /// state of the Right hand.</returns> public static BtnState InteractVolume(Bounds bounds, out Handed hand) => NativeAPI.ui_interact_volume_at(bounds, out hand);