Esempio n. 1
0
        /// :CodeDoc: Guides Using Hands
        /// ## Pointers
        ///
        /// And lastly, StereoKit also has a pointer system! This applies to
        /// more than just hands. Head, mouse, and other devices will also
        /// create pointers into the scene. You can filter pointers based on
        /// source family and device capabilities, so this is a great way to
        /// abstract a few more input sources nicely!
        public static void DrawPointers()
        {
            int hands = Input.PointerCount(InputSource.Hand);

            for (int i = 0; i < hands; i++)
            {
                Pointer pointer = Input.Pointer(i, InputSource.Hand);
                Lines.Add(pointer.ray, 0.5f, Color.White, Units.mm2m);
                Lines.AddAxis(pointer.Pose);
            }
        }
Esempio n. 2
0
 /// Now all we need to do is show the QR codes! In this case,
 /// we're just displaying an axis widget, and the contents of
 /// the QR code as text.
 ///
 /// With the text, all we're doing is squeezing the text into
 /// the bounds of the QR code, and shifting it to be a little
 /// forward, in front of the code!
 public void Update()
 {
     foreach (QRData d in poses.Values)
     {
         Lines.AddAxis(d.pose, d.size);
         Text.Add(
             d.text,
             Matrix.TRS(
                 d.pose.position + d.pose.Forward * d.size * 0.1f,
                 Quat.FromAngles(0, 0, 180) * d.pose.orientation),
             Vec2.One * d.size,
             TextFit.Squeeze,
             TextAlign.XLeft | TextAlign.YTop);
     }
 }
Esempio n. 3
0
 /// Now all we need to do is show the QR codes! In this case,
 /// we're just displaying an axis widget, and the contents of
 /// the QR code as text.
 ///
 /// With the text, all we're doing is squeezing the text into
 /// the bounds of the QR code, and shifting it to be a little
 /// forward, in front of the code!
 public void Update()
 {
     foreach (QRData d in poses.Values)
     {
         Lines.AddAxis(d.pose, d.size);
         Text.Add(
             d.text,
             d.pose.ToMatrix(),
             Vec2.One * d.size,
             TextFit.Squeeze,
             TextAlign.XLeft | TextAlign.YTop,
             TextAlign.Center,
             d.size, d.size);
     }
 }
Esempio n. 4
0
    public void Update()
    {
        Vec3 wandTip = wandModel.Bounds.center + wandModel.Bounds.dimensions.y * 0.5f * Vec3.Up;

        UI.HandleBegin("wand", ref wandPose, wandModel.Bounds);
        wandModel.Draw(Matrix.Identity);
        wandTip = Hierarchy.ToWorld(wandTip);
        UI.HandleEnd();
        Lines.AddAxis(new Pose(wandTip, Quat.Identity), 0.05f);

        UI.WindowBegin("Anchors", ref pose);
        if (anchorStore != null && UI.Button("Create New"))
        {
            CreateAnchor(wandTip);
        }

        // List options for the selected anchor
        if (selected != null)
        {
            UI.HSeparator();
            UI.Label(selected);
            if (UI.Button("Delete"))
            {
                DestroyAnchor(selected);
            }
        }
        UI.WindowEnd();

        // Show anchors, and do selection logic on them
        foreach (var p in anchorPoses)
        {
            Lines.AddAxis(p.Value, 0.1f);

            if (p.Value.position.InRadius(wandTip, 0.05f))
            {
                selected = p.Key;
            }
        }
        // Outline the selected anchor
        if (selected != null)
        {
            Pose p = anchorPoses[selected];
            Mesh.Cube.Draw(Material.UIBox, p.ToMatrix(0.1f));
        }
    }
Esempio n. 5
0
        /// :End:

        public static void DrawAxes()
        {
            for (int i = 0; i < (int)Handed.Max; i++)
            {
                Hand hand = Input.Hand((Handed)i);
                if (!hand.IsTracked)
                {
                    continue;
                }

                for (int finger = 0; finger < 5; finger++)
                {
                    for (int joint = 0; joint < 5; joint++)
                    {
                        Lines.AddAxis(hand[finger, joint].Pose);
                    }
                }
                Lines.AddAxis(hand.palm);
            }
        }
Esempio n. 6
0
        /// :End:

        public static void DrawAxes()
        {
            for (int i = 0; i < (int)Handed.Max; i++)
            {
                Hand hand = Input.Hand((Handed)i);
                if (!hand.IsTracked)
                {
                    continue;
                }

                for (int f = 0; f < 5; f++)
                {
                    for (int j = 0; j < 5; j++)
                    {
                        Lines.AddAxis(hand[f, j].Pose);
                    }
                }
                Lines.AddAxis(hand.palm);
            }
        }
Esempio n. 7
0
    static void HandInBounds()
    {
        /// :CodeSample: Input.Hand Bounds.Contains Lines.AddAxis Hand Handed FingerId JointId
        /// Here's a small example of checking to see if a finger joint is inside
        /// a box, and drawing an axis gizmo when it is!
        // A volume for checking inside of! 10cm on each side, at the origin
        Bounds testArea = new Bounds(Vec3.One * 0.1f);

        // This is a decent way to show we're working with both hands
        for (int h = 0; h < (int)Handed.Max; h++)
        {
            // Get the pose for the index fingertip
            Hand hand      = Input.Hand((Handed)h);
            Pose fingertip = hand[FingerId.Index, JointId.Tip].Pose;

            // Draw the fingertip pose axis if it's inside the volume
            if (testArea.Contains(fingertip.position))
            {
                Lines.AddAxis(fingertip);
            }
        }
        /// :End:
    }
Esempio n. 8
0
    public void Update()
    {
        Tests.Screenshot("GuideUserInterface.jpg", 600, 400, new Vec3(-0.363f, 0.010f, 0.135f), new Vec3(-0.743f, -0.414f, -0.687f));
        Tests.Screenshot("GuideUserInterfaceCustom.jpg", 400, 600, new Vec3(0.225f, 0.0f, .175f), new Vec3(.4f, 0.0f, 0));

        /// :CodeDoc: Guides User Interface
        /// Then we'll move over to the application step where we'll do the
        /// rest of the UI code!
        ///
        /// We'll start with a window titled "Window" that's 20cm wide, and
        /// auto-resizes on the y-axis. The U class is pretty helpful here,
        /// as it allows us to reason more visually about the units we're
        /// using! StereoKit uses meters as its base unit, which look a
        /// little awkward as raw floats, especially in the millimeter range.
        ///
        /// We'll also use a toggle to turn the window's header on and off!
        /// The value from that toggle is passed in here via the showHeader
        /// field.
        ///
        UI.WindowBegin("Window", ref windowPose, new Vec2(20, 0) * U.cm, showHeader?UIWin.Normal:UIWin.Body);
        ///
        /// When you begin a window, all visual elements are now relative to
        /// that window! UI takes advantage of the Hierarchy class and pushes
        /// the window's pose onto the Hierarchy stack. Ending the window
        /// will pop the pose off the hierarchy stack, and return things to
        /// normal!
        ///
        /// Here's that toggle button! You'll also notice our use of 'ref'
        /// values in a lot of the UI code. UI functions typically follow the
        /// pattern of returning true/false to indicate they've been
        /// interacted with during the frame, so you can nicely wrap them in
        /// 'if' statements to react to change!
        ///
        /// Then with the 'ref' parameter, we let you pass in the current
        /// state of the UI element. The UI element will update that value
        /// for you based on user interaction, but you can also change it
        /// yourself whenever you want to!
        ///
        UI.Toggle("Show Header", ref showHeader);
        ///
        /// Here's an example slider! We start off with a label element, and
        /// tell the UI to keep the next item on the same line. The slider
        /// clamps to the range [0,1], and will step at intervals of 0.2. If
        /// you want it to slide continuously, you can just set the `step`
        /// value to 0!
        ///
        UI.Label("Slide");
        UI.SameLine();
        UI.HSlider("slider", ref slider, 0, 1, 0.2f, 72 * U.mm);
        ///
        /// Here's how you use a simple button! Just check it with an 'if'.
        /// Any UI method will return true on the frame when their value or
        /// state has changed.
        ///
        if (UI.ButtonRound("Exit", powerSprite))
        {
            SK.Quit();
        }
        ///
        /// And for every begin, there must also be an end! StereoKit will
        /// log errors when this occurs, so keep your eyes peeled for that!
        ///
        UI.WindowEnd();
        ///
        /// ## Custom Windows
        ///
        /// ![Simple UI]({{site.url}}/img/screenshots/GuideUserInterfaceCustom.jpg)
        ///
        /// Mixed Reality also provides us with the opportunity to turn
        /// objects into interfaces! Instead of using the old 'window'
        /// paradigm, we can create 3D models and apply UI elements to their
        /// surface! StereoKit uses 'handles' to accomplish this, a grabbable
        /// area that behaves much like a window, but with a few more options
        /// for customizing layout and size.
        ///
        /// We'll load up a clipboard, so we can attach an interface to that!
        ///
        /// ```csharp
        /// Model clipboard = Model.FromFile("Clipboard.glb");
        /// ```
        ///
        /// And, similar to the window previously, here's how you would turn
        /// it into a grabbable interface! This behaves the same, except
        /// we're defining where the grabbable region is specifically, and
        /// then drawing our own model instead of a plain bar. You'll also
        /// notice we're drawing using an identity matrix. This takes
        /// advantage of how HandleBegin pushes the handle's pose onto the
        /// Hierarchy transform stack!
        ///
        UI.HandleBegin("Clip", ref clipboardPose, clipboard.Bounds);
        Renderer.Add(clipboard, Matrix.Identity);
        ///
        /// Once we've done that, we also need to define the layout area of
        /// the model, where UI elements will go. This is different for each
        /// model, so you'll need to plan this around the size of your
        /// object!
        ///
        UI.LayoutArea(new Vec3(12, 15, 0) * U.cm, new Vec2(24, 30) * U.cm);
        ///
        /// Then after that? We can just add UI elements like normal!
        ///
        UI.Image(logoSprite, new Vec2(22, 0) * U.cm);

        UI.Toggle("Toggle", ref clipToggle);
        UI.HSlider("Slide", ref clipSlider, 0, 1, 0, 22 * U.cm);
        ///
        /// And while we're at it, here's a quick example of doing a radio
        /// button group! Not much 'radio' actually happening, but it's still
        /// pretty simple. Pair it with an enum, or an integer, and have fun!
        ///
        if (UI.Radio("Radio1", clipOption == 1))
        {
            clipOption = 1;
        }
        UI.SameLine();
        if (UI.Radio("Radio2", clipOption == 2))
        {
            clipOption = 2;
        }
        UI.SameLine();
        if (UI.Radio("Radio3", clipOption == 3))
        {
            clipOption = 3;
        }
        ///
        /// As with windows, Handles need an End call.
        ///
        UI.HandleEnd();
        /// :End:

        // Just moving this UI out of the way so it doesn't show in
        // screenshots or anything.
        UI.PushSurface(new Pose(0, 1000, 0, Quat.Identity));

        /// :CodeDoc: Guides User Interface
        ///
        /// ## An Important Note About IDs
        ///
        /// StereoKit does store a small amount of information about the UI's
        /// state behind the scenes, like which elements are active and for
        /// how long. This internal data is attached to the UI elements via
        /// a combination of their own ids, and the parent Window/Handle's
        /// id!
        ///
        /// This means you should be careful to NOT re-use ids within a
        /// Window/Handle, otherwise you may find ghost interactions with
        /// elements that share the same ids. If you need to have elements
        /// with the same id, or if perhaps you don't know in advance that
        /// all your elements will certainly be unique, UI.PushId and
        /// UI.PopId can be used to mitigate the issue by using the same
        /// hierarchy id mixing that the Windows use to prevent collisions
        /// with the same ids in other Windows/Handles.
        ///
        /// Here's the same set of radio options, but all of them have the
        /// same name/id!
        ///
        UI.PushId(1);
        if (UI.Radio("Radio", clipOption == 1))
        {
            clipOption = 1;
        }
        UI.PopId();

        UI.SameLine();
        UI.PushId(2);
        if (UI.Radio("Radio", clipOption == 2))
        {
            clipOption = 2;
        }
        UI.PopId();

        UI.SameLine();
        UI.PushId(3);
        if (UI.Radio("Radio", clipOption == 3))
        {
            clipOption = 3;
        }
        UI.PopId();
        /// :End:

        UI.PopSurface();

        /// :CodeDoc: Guides User Interface
        /// ## What's Next?
        ///
        /// And there you go! That's how UI works in StereoKit, pretty
        /// simple, huh? For further reference, and more UI methods, check
        /// out the [UI class documentation]({{site.url}}/Pages/Reference/UI.html).
        ///
        /// If you'd like to see the complete code for this sample,
        /// [check it out on Github](https://github.com/maluoi/StereoKit/blob/master/Examples/StereoKitTest/Demos/DemoUI.cs)!
        /// :End:


        /// :CodeSample: UI.VolumeAt
        /// This code will draw an axis at the index finger's location when
        /// the user pinches while inside a VolumeAt.
        ///
        /// ![UI.InteractVolume]({{site.screen_url}}/InteractVolume.jpg)
        ///
        // Draw a transparent volume so the user can see this space
        Vec3  volumeAt   = new Vec3(0, 0.2f, -0.4f);
        float volumeSize = 0.2f;

        Default.MeshCube.Draw(Default.MaterialUIBox, Matrix.TS(volumeAt, volumeSize));

        BtnState volumeState = UI.VolumeAt("Volume", new Bounds(volumeAt, Vec3.One * volumeSize), UIConfirm.Pinch, out Handed hand);

        if (volumeState != BtnState.Inactive)
        {
            // If it just changed interaction state, make it jump in size
            float scale = volumeState.IsChanged()
                                ? 0.1f
                                : 0.05f;
            Lines.AddAxis(Input.Hand(hand)[FingerId.Index, JointId.Tip].Pose, scale);
        }
        /// :End:

        Tests.Screenshot("InteractVolume.jpg", 1, 600, 600, 90, new Vec3(-0.102f, 0.306f, -0.240f), new Vec3(0.410f, -0.248f, -0.897f));
    }