public static void Update() { if (nextScene != null) { activeScene?.Shutdown(); if (IsTesting) { Input.HandVisible(Handed.Max, false); } nextScene.Initialize(); activeScene = nextScene; nextScene = null; } activeScene.Update(); sceneFrame++; if (IsTesting && FinishedWithTest()) { testIndex += 1; if (testIndex >= allTests.Count) { SK.Quit(); } else { SetTestActive(allTests[testIndex].Name); } } }
static void StepMenuWindow() { // Begin the application's menu window UI.WindowBegin("Menu", ref menuPose); // When the user presses the save button, lets show a save file // dialog! When a file name and folder have been selected, it'll make // a call to SavePainting with the file's path name with the .skp // extension. if (UI.Button("Save")) { FilePicker.Show( FilePickerMode.Save, defaultFolder, SavePainting, new FileFilter("Painting", "*.skp")); } // And on that same line, we'll have a load button! This'll let the // user pick out any .skp files, and will call LoadPainting with the // selected file. UI.SameLine(); if (UI.Button("Load")) { FilePicker.Show( FilePickerMode.Open, defaultFolder, LoadPainting, new FileFilter("Painting", "*.skp")); } // Clear is easy! Just create a new Painting object! if (UI.Button("Clear")) { activePainting = new Painting(); } // And if they want to quit? Just tell StereoKit! This will let // StereoKit finish the the frame properly, and then break out of the // Step loop above. if (UI.Button("Quit")) { SK.Quit(); } // And end the window! UI.WindowEnd(); }
public static void Update() { if (IsTesting && runSeconds != 0) { Time.SetTime(Time.Total + (1 / 90.0), 1 / 90.0); } if (nextScene != null) { activeScene?.Shutdown(); GC.Collect(int.MaxValue, GCCollectionMode.Forced); if (IsTesting) { Time.SetTime(0); Input.HandVisible(Handed.Max, false); } nextScene.Initialize(); sceneTime = Time.Totalf; activeScene = nextScene; nextScene = null; } activeScene.Update(); sceneFrame++; if (IsTesting && FinishedWithTest()) { testIndex += 1; if (testIndex >= allTests.Count) { SK.Quit(); } else { SetTestActive(allTests[testIndex].Name); } } }
////////////////////// public void Step() { Tests.Update(); if (Input.Key(Key.Esc).IsJustActive()) { SK.Quit(); } // If we can't see the world, we'll draw a floor! if (SK.System.displayType == Display.Opaque) { Renderer.Add(floorMesh, World.HasBounds ? World.BoundsPose.ToMatrix() : floorTr, Color.White); } // Skip selection window if we're in test mode if (Tests.IsTesting) { return; } /// :CodeSample: World.HasBounds World.BoundsSize World.BoundsPose // Here's some quick and dirty lines for the play boundary rectangle! if (World.HasBounds) { Vec2 s = World.BoundsSize / 2; Matrix pose = World.BoundsPose.ToMatrix(); Vec3 tl = pose.Transform(new Vec3(s.x, 0, s.y)); Vec3 br = pose.Transform(new Vec3(-s.x, 0, -s.y)); Vec3 tr = pose.Transform(new Vec3(-s.x, 0, s.y)); Vec3 bl = pose.Transform(new Vec3(s.x, 0, -s.y)); Lines.Add(tl, tr, Color.White, 1.5f * U.cm); Lines.Add(bl, br, Color.White, 1.5f * U.cm); Lines.Add(tl, bl, Color.White, 1.5f * U.cm); Lines.Add(tr, br, Color.White, 1.5f * U.cm); } /// :End: // Make a window for demo selection UI.WindowBegin("Demos", ref demoSelectPose, new Vec2(50 * U.cm, 0)); for (int i = 0; i < demoNames.Count; i++) { if (UI.Button(demoNames[i])) { Tests.SetDemoActive(i); } UI.SameLine(); } UI.NextLine(); UI.HSeparator(); if (UI.Button("Exit")) { SK.Quit(); } UI.WindowEnd(); RulerWindow(); DebugToolWindow.Step(); /// :CodeSample: Log.Subscribe Log /// And in your Update loop, you can draw the window. LogWindow(); /// And that's it! /// :End: }
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)); }