////////////////////// public void Init() { Material floorMat = new Material(Shader.FromFile("floor_shader.hlsl")); floorMat.Transparency = Transparency.Blend; floorMat.SetVector("radius", new Vec4(5, 10, 0, 0)); floorMat.QueueOffset = -11; floorMesh = Model.FromMesh(Mesh.GeneratePlane(new Vec2(40, 40), Vec3.Up, Vec3.Forward), floorMat); floorTr = Matrix.TR(new Vec3(0, -1.5f, 0), Quat.Identity); demoSelectPose.position = new Vec3(0, 0, -0.6f); demoSelectPose.orientation = Quat.LookDir(-Vec3.Forward); Tests.FindTests(); Tests.SetTestActive(startTest); Tests.Initialize(); for (int i = 0; i < Tests.DemoCount; i++) { demoNames.Add(Tests.GetDemoName(i).Substring("Demo".Length)); } if (!Tests.IsTesting) { SK.AddStepper(new RenderCamera(new Pose(0.3f, 0, .5f, Quat.FromAngles(0, -90, 0)), 1000, 1000)); } }
void DrawMenu() { UI.AffordanceBegin("PaletteMenu", ref palettePose, paletteModel.Bounds); paletteModel.Draw(Matrix.Identity); Pose p = new Pose(Vec3.Zero, Quat.FromAngles(90, 0, 0)); UI.AffordanceBegin("LineSlider", ref p, new Bounds()); UI.HSliderAt("Size", ref lineSize, 0.001f, 0.02f, 0, new Vec3(6, -1, 0) * Units.cm2m, new Vec2(8, 2) * Units.cm2m); Lines.Add(new Vec3(6, 1, -1) * Units.cm2m, new Vec3(-2, 1, -1) * Units.cm2m, activeColor, lineSize); UI.AffordanceEnd(); if (UI.VolumeAt("White", new Bounds(new Vec3(4, 0, 7) * Units.cm2m, new Vec3(4, 2, 4) * Units.cm2m))) { SetColor(Color.White); } if (UI.VolumeAt("Red", new Bounds(new Vec3(9, 0, 3) * Units.cm2m, new Vec3(4, 2, 4) * Units.cm2m))) { SetColor(new Color(1, 0, 0)); } if (UI.VolumeAt("Green", new Bounds(new Vec3(9, 0, -3) * Units.cm2m, new Vec3(4, 2, 4) * Units.cm2m))) { SetColor(new Color(0, 1, 0)); } if (UI.VolumeAt("Blue", new Bounds(new Vec3(3, 0, -6) * Units.cm2m, new Vec3(4, 2, 4) * Units.cm2m))) { SetColor(new Color(0, 0, 1)); } UI.AffordanceEnd(); }
/// 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 Render() { if (!this.IsVisible) { return; } var material = this.GetMaterialGroup(-1).GetMaterial(); if (material != null) { this.Node?.Draw( SK.Matrix.TRS(this.WorldPosition.ToSKVec3(), Quat.FromAngles( this.WorldRotation.X, this.WorldRotation.Y, this.WorldRotation.Z), this.WorldSize.ToSKVec3()), material.Diffuse.ToSKColor(material.Opacity)); } else { this.Node?.Draw( SK.Matrix.TRS(this.WorldPosition.ToSKVec3(), Quat.FromAngles( this.WorldRotation.X, this.WorldRotation.Y, this.WorldRotation.Z), this.WorldSize.ToSKVec3())); } }
public static SK.Quat ToSKQuat(this Imml.Numerics.Vector3 vector3) { //StereoKit is using a coordinate system where: // X increases to the right // Y increases up // Z increases towards the viewer return(Quat.FromAngles( vector3.X, vector3.Y, vector3.Z)); }
/// 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); } }
bool TestMatrixDecompose() { Vec3 translate = new Vec3(1, 2, 3); Quat rotate = Quat.FromAngles(0, 90, 0); Vec3 scale = new Vec3(1, 2, 1); Matrix mat = Matrix.TRS(translate, rotate, scale); Vec3 exTranslate = mat.Translation; Quat exRotate = mat.Rotation; Vec3 exScale = mat.Scale; Log.Info($"Transform matrix: {translate } {rotate } {scale }"); Log.Info($"Matrix sep decompose: {exTranslate} {exRotate} {exScale}"); if (Vec3.Distance(exTranslate, translate) > tolerance) { return(false); } if (Vec3.Distance(exScale, scale) > tolerance) { return(false); } if (!mat.Decompose(out exTranslate, out exRotate, out exScale)) { return(false); } Log.Info($"Matrix decompose: {exTranslate} {exRotate} {exScale}"); if (Vec3.Distance(exTranslate, translate) > tolerance) { return(false); } if (Vec3.Distance(exScale, scale) > tolerance) { return(false); } return(true); }
public void Update() { Tests.Screenshot(1024, 1024, "PBRBalls.jpg", new Vec3(0, 0, -0.1f), new Vec3(0, 0, -1)); Hierarchy.Push(Matrix.T(0, 0, -1)); if (!Tests.IsTesting) { UI.HandleBegin("Model", ref modelPose, pbrModel.Bounds * .2f); pbrModel.Draw(Matrix.TRS(Vec3.Zero, Quat.FromAngles(0, 180, 0), 0.2f)); UI.HandleEnd(); } for (int y = 0; y < materialGrid; y++) { for (int x = 0; x < materialGrid; x++) { Renderer.Add(sphereMesh, pbrMaterials[x + y * materialGrid], Matrix.TS(new Vec3(x - materialGrid / 2.0f + 0.5f, y - materialGrid / 2.0f + 0.5f, -1) / 4.0f, 0.2f)); } } Text.Add("Metallic -->", Matrix.TRS(new Vec3(0, materialGrid / 8.0f + 0.2f, -0.25f), Quat.FromAngles(0, 180, 0), 4)); Text.Add("Roughness -->", Matrix.TRS(new Vec3(materialGrid / -8.0f - 0.2f, 0, -0.25f), Quat.FromAngles(0, 180, -90), 4)); Hierarchy.Pop(); }
/// :End: public static void DrawHandSize() { for (int h = 0; h < (int)Handed.Max; h++) { Hand hand = Input.Hand((Handed)h); if (!hand.IsTracked) { continue; } HandJoint at = hand[FingerId.Middle, JointId.Tip]; Vec3 pos = at.position + at.Pose.Forward * at.radius; Quat rot = at.orientation * Quat.FromAngles(-90, 0, 0); if (!HandFacingHead((Handed)h)) { rot = rot * Quat.FromAngles(0, 180, 0); } Text.Add( (hand.size * 100).ToString(".0") + "cm", Matrix.TRS(pos, rot, 0.3f), TextAlign.XCenter | TextAlign.YBottom); } }
/// :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()); }
public void Update() { Hierarchy.Push(Matrix.T(0, 2, 0)); Mesh.Sphere.Draw(wireframeMaterial, Matrix.TS(-0.07f, 0, 0, 0.1f)); Mesh.Sphere.Draw(Material.Default, Matrix.TS(0.07f, 0, 0, 0.1f)); Tests.Screenshot("Drawing_MeshLooksLike.jpg", 600, 400, Hierarchy.ToWorld(V.XYZ(0, 0, 0.12f)), Hierarchy.ToWorld(Vec3.Zero)); Hierarchy.Pop(); /// :CodeDoc: Guides Drawing /// At its core, drawing things in 3D is done through a combination of /// [`Mesh`]({{site.url}}/Pages/Reference/Mesh.html)es and /// [`Material`]({{site.url}}/Pages/Reference/Material.html)s. A Mesh /// is a collection of triangles in 3D space that describe where the /// surface of that 3D object is. And a Material is then a collection /// of parameters, [`Tex`]({{site.url}}/Pages/Reference/Tex.html)tures /// (2d images), and Shader code that are combined to describe the /// visual properties of the Mesh's surface! /// /// ![Meshes are made from triangles!]({{site.screen_url}}/Drawing_MeshLooksLike.jpg) /// _Meshes are made from triangles!_ /// /// And in addition to that, you'll need to know a little bit about /// matrices, which are a math construct used to describe the location, /// orientation and scale of geometry within the 3D space! A [`Matrix`]({{site.url}}/Pages/Reference/Matrix.html) /// isn't difficult the way we're using it, so don't worry if math /// isn't your thing. /// /// And then StereoKit also has a [`Model`]({{site.url}}/Pages/Reference/Model.html), /// which is a high level combination of Meshes, Material, Matrices, /// and a few more things! Most of the time, you'll probably be drawing /// Models loaded from file, but it's important to have options. /// /// Then lastly, StereoKit has easy systems for drawing [`Line`]({{site.url}}/Pages/Reference/Lines.html)s, /// [`Text`]({{site.url}}/Pages/Reference/Text.html), [`Sprite`]({{site.url}}/Pages/Reference/Sprite.html)s /// and various other things! These are still based on Meshes and /// Materials under the hood, but have some complex features that can /// make them difficult to build from scratch. /// /// ## Meshes and Materials /// /// To simplify things here, we're going to use the built-in assets, /// [`Mesh.Sphere`]({{site.url}}/Pages/Reference/Mesh/Sphere.html) /// and [`Material.Default`]({{site.url}}/Pages/Reference/Material/Default.html). /// Mesh.Sphere is a built-in mesh generated using math when StereoKit /// starts up, and Material.Default is a high performance simple /// Material that serves as StereoKit's default Material. (For more /// built-in assets, see the [`Default`]({{site.url}}/Pages/Reference/Default.html)s) /// Mesh.Sphere.Draw(Material.Default, Matrix.Identity); /// /// ![Default sphere and material]({{site.screen_url}}/Drawing_Defaults.jpg) /// _Drawing the default sphere Mesh with the default Material._ /// /// [`Matrix.Identity`]({{site.url}}/Pages/Reference/Matrix/Identity.html) /// can be though of as a 'No transform' Matrix, so this is drawing the /// sphere at the origin of the 3D space. /// /// That's pretty straightforward! StereoKit will get this Mesh/Material /// pair onto the screen this frame. Remember that StereoKit is /// generally an immediate mode API, so this won't show up for more /// than the current frame. If you want it to draw every frame, you'll /// have to call Draw every frame! /// /// So how do you get a Mesh to begin with? In most cases you'll just /// be working with Models, but you can get a Mesh directly from a few /// places: /// - [`Mesh.Sphere`]({{site.url}}/Pages/Reference/Mesh/Sphere.html), [`Mesh.Cube`]({{site.url}}/Pages/Reference/Mesh/Cube.html), and [`Mesh.Quad`]({{site.url}}/Pages/Reference/Mesh/Quad.html) are built-in mesh assets that are handy to have around. /// - [`Mesh`]({{site.url}}/Pages/Reference/Mesh.html) has a number of static methods for generating procedural shapes, such as [`Mesh.GenerateRoundedCube`]({{site.url}}/Pages/Reference/Mesh/GenerateRoundedCube.html) or [`Mesh.GeneratePlane`]({{site.url}}/Pages/Reference/Mesh/GeneratePlane.html). /// - A Mesh can be extracted from one of a [Model's nodes]({{site.url}}/Pages/Reference/ModelNode/Mesh.html). /// - You can create a Mesh from a list of vertices and indices. This is more advanced, but [check the sample here]({{site.url}}/Pages/Reference/Mesh/SetVerts.html). /// /// And where do you get a Material? Well, /// - See built-in Materials like [`Material.PBR`]({{site.url}}/Pages/Reference/Default/MaterialPBR.html) for high-quality surface or [`Material.Unlit`]({{site.url}}/Pages/Reference/Default/MaterialUnlit.html) for fast/stylisic surfaces. /// - A Material [constructor]({{site.url}}/Pages/Reference/Material/Material.html) can be called with a Shader. Check out [the Material guide]({{site.url}}/Pages/Guides/Materials-and-Shaders.html) for in-depth usage (Materials and Shaders are a lot of fun!). /// - You can call [`Material.Copy`]({{site.url}}/Pages/Reference/Material/Copy.html) to create a duplicate of an existing Material. /// /// ## Matrix basics /// /// If you like math, this explanation is not really for you! But if /// you like results, this will get you going where you need to go. The /// important thing to know about a [`Matrix`]({{site.url}}/Pages/Reference/Matrix.html), /// is that it's a good way to represent an object's transform (Translation, /// Rotation, and Scale). /// /// StereoKit provides a number of Matrix creation methods that allow /// you to easily create Translation/Rotation/Scale matrices. // The identity matrix is the matrix equivalent of '1'. You can also // think of it as a 'no-transform' matrix. Matrix transform = Matrix.Identity; // Translates points 1 meter up the Y axis transform = Matrix.T(0, 1, 0); // Scales a point by (2,2,2), rotates it 180 on the X axis, and // then translates it up 1 meter up the Y axis. transform = Matrix.TRS( new Vec3(0, 1, 0), Quat.FromAngles(180, 0, 0), new Vec3(2, 2, 2)); // To draw a cube at (0,-10,0) that's rotated 45 degrees around its Y // axis: Mesh.Cube.Draw(Material.Default, Matrix.TR(0, -10, 0, Quat.FromAngles(0, 45, 0))); /// /// The TRS methods have a lot of permutations that can help make your /// matrix creation code a bit shorter. Like, if you don't need to add /// scale to your TRS matrix, there's the TR variant! No rotation? Try /// TS! Etc. etc. /// /// Now. Even _more_ interesting, is that many Matrices can be combined /// into a single Matrix representing multiple transforms! This is done /// via multiplication, and an important note here is that matrix /// multiplication is not commutative, that is: `A*B != B*A`, so the /// order in which you combine your matrices is important. /// /// This can let you do things like, rotate around a pivot point, or /// build a hierarchy of transforms! A parent/child position hierarchy /// can be described pretty easily this way: Matrix parentTransform = Matrix.TR(10, 0, 0, Quat.FromAngles(0, 45, 0)); Matrix childTransform = Matrix.TS(1, 1, 0, 0.2f); Mesh.Cube.Draw(Material.Default, parentTransform); Mesh.Cube.Draw(Material.Default, childTransform * parentTransform); /// /// ![Combining matrices]({{site.screen_url}}/Drawing_MatrixCombine.jpg) /// _The smaller `childTransform` is relative to `parentTransform` via multiplication._ /// /// ## Models /// /// The easiest way to draw complex content is through a Model! A Model /// is basically a small scene of Mesh/Material pairs at positions with /// hierarchical relationships to eachother. If you're creating art in /// a 3D modeling tool such as Blender, then this is basically a full /// representation of the scene you've created there. /// /// Since a model already has all its information within it, all you /// need to do is provide it with a location! model.Draw(Matrix.T(10, 10, 0)); /// ![Drawing a model]({{site.screen_url}}/Drawing_Model.jpg) /// _StereoKit's main format is the .gltf file._ /// /// So... that was also pretty simple! The only real trick with Models /// is getting one in the first place, but even that's not too hard. /// There's a lot you can do with a Model beyond just drawing it, so /// for more details on that, check out [the Model guide]({{site.url}}/Pages/Guides/Working-with-Models.html)! /// /// But here's the quick list of where you can get a Model to begin /// with: /// - [`Model.FromFile`]({{site.url}}/Pages/Reference/Model/FromFile.html) is the easiest, most common way to get a Model! /// - [`Model.FromMesh`]({{site.url}}/Pages/Reference/Model/FromMesh.html) will let you create a very simple Model with a single function call. /// - The [Model constructor]({{site.url}}/Pages/Reference/Model/Model.html) lets you create an empty Model, which you can then fill with ModelNodes via [`Model.AddNode`]({{site.url}}/Pages/Reference/Model/AddNode.html) /// - You can call [`Model.Copy`]({{site.url}}/Pages/Reference/Model/Copy.html) to create a duplicate of an existing Model. /// /// ## Lines /// /// Being able to easily draw a line is incredibly useful for /// debugging, and generally quite practical for many other purposes as /// well! StereoKit has the [`Lines`]({{site.url}}/Pages/Reference/Lines.html) /// class to assist with this, and is pretty straightforward to use. /// There's a few variations, but at it's simplest, it's a few points, /// a color, and a thickness. Lines.Add( new Vec3(2, 2, 0), new Vec3(3, 2.5f, 0), Color.Black, 1 * U.cm); /// ![Drawing a line]({{site.screen_url}}/Drawing_Lines.jpg) /// _You can also draw Rays, Poses, and multicolored lists of lines!_ /// /// ## Text /// /// Text is drawn with a collection of rectangular quads, each mapped /// to a character glyph on a texture. StereoKit supports rendering any /// unicode glyphs yout throw at it, as long as the active Font has /// that glyph defined in it! This means you can work with all sorts of /// different languages right away, without any baking or preparation. /// /// To draw text with the default Font, you can do this! Text.Add("こんにちは", Matrix.T(-10, 10, 0)); /// /// ![Drawing text]({{site.screen_url}}/Drawing_Text.jpg) /// _'Hello' in Japanese, I'm pretty sure._ /// /// You can create additional font styles and fonts to use with text /// drawing, and there are a number of overloads for [`Text.Add`]({{site.url}}/Pages/Reference/Text/Add.html) /// that allow you to change the layout or constrain to a particular /// area. Check the docs for the method for more information about that! /// /// ## Cool! /// /// So that's the highlights! There's plenty more to draw and more /// tricks to be learned, but this is a great start! There's treasures /// in the documentation, so hunt around in there for more samples. You /// may also be interested in the [Materials guide]({{site.url}}/Pages/Guides/Working-with-Materials.html) /// for advanced rendering code, or [the Model guide]({{site.url}}/Pages/Guides/Working-with-Models.html) /// for managing your visible content! /// /// If you'd like to see all the code for this document, /// [check it out here!](https://github.com/maluoi/StereoKit/blob/master/Examples/StereoKitTest/Guides/GuideDrawing.cs) /// :End: Tests.Screenshot("Drawing_Defaults.jpg", 600, 600, V.XYZ(0, 0, 1.2f), Vec3.Zero); Tests.Screenshot("Drawing_MatrixCombine.jpg", 600, 600, V.XYZ(10, 0, 2), V.XYZ(10, 0, 0)); Tests.Screenshot("Drawing_Model.jpg", 600, 600, 60, V.XYZ(10, 10, -3f), V.XYZ(10, 10, 0)); Tests.Screenshot("Drawing_Lines.jpg", 600, 600, V.XYZ(2.5f, 2.25f, 0.8f), V.XYZ(2.5f, 2.25f, 0)); Tests.Screenshot("Drawing_Text.jpg", 600, 600, V.XYZ(-10, 10, -0.1f), V.XYZ(-10, 10, 0)); }
public void Update() { Tests.Screenshot("Tests/LineRenderer.jpg", 600, 600, V.XYZ(0, 0, 0.5f), Vec3.Zero); // A ring of lines that goes out past the edge of the screen Color32 c = Color32.White; for (int i = 0; i < 16; i++) { Vec3 at = Vec3.AngleXY((i / 16.0f) * 360.0f) * 0.5f; Lines.Add(at - V.XYZ(0, 0, 0.2f), at + V.XYZ(0, 0, 0.2f), c, 0.01f); } // A Circle c = new Color(1, .5f, .5f, 1).ToLinear(); LinePoint[] points = new LinePoint[17]; for (int i = 0; i < points.Length; i++) { points[i] = new LinePoint( Vec3.AngleXY((i / (float)(points.Length - 1)) * 360.0f) * 0.4f, c, 0.01f); } Lines.Add(points); // A square, worst case for this system c = new Color(.5f, 1, .5f, 1).ToLinear(); Lines.Add(new LinePoint[] { new LinePoint(V.XY0(-0.25f, 0.25f), c, 0.01f), new LinePoint(V.XY0(0.25f, 0.25f), c, 0.01f), new LinePoint(V.XY0(0.25f, -0.25f), c, 0.01f), new LinePoint(V.XY0(-0.25f, -0.25f), c, 0.01f), new LinePoint(V.XY0(-0.25f, 0.25f), c, 0.01f), }); // A beveled square c = new Color(.5f, .5f, 1, 1).ToLinear(); Lines.Add(new LinePoint[] { new LinePoint(V.XY0(-0.2f, 0.19f), c, 0.01f), new LinePoint(V.XY0(-0.19f, 0.2f), c, 0.01f), new LinePoint(V.XY0(0.19f, 0.2f), c, 0.01f), new LinePoint(V.XY0(0.2f, 0.19f), c, 0.01f), new LinePoint(V.XY0(0.2f, -0.19f), c, 0.01f), new LinePoint(V.XY0(0.19f, -0.2f), c, 0.01f), new LinePoint(V.XY0(-0.19f, -0.2f), c, 0.01f), new LinePoint(V.XY0(-0.2f, -0.19f), c, 0.01f), new LinePoint(V.XY0(-0.2f, 0.19f), c, 0.01f), }); // an Icosohedron float t = (1.0f + SKMath.Sqrt(5.0f)) / 2.0f; float s = 0.08f; float s2 = 0.3f; int h = 0; Vec3 v; Hierarchy.Push(Matrix.R(Quat.FromAngles(10, 10, 10))); v = V.XYZ(-1, t, 0) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(1, t, 0) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(-1, -t, 0) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(1, -t, 0) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(0, -1, t) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(0, 1, t) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(0, -1, -t) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(0, 1, -t) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(t, 0, -1) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(t, 0, 1) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(-t, 0, -1) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); v = V.XYZ(-t, 0, 1) * s; Lines.Add(v, v * s2, Color.HSV((h++) / 12.0f, 0.8f, 0.8f).ToLinear(), 0.01f); Hierarchy.Pop(); }
bool TestMatrixTransform() { Vec3 translate = new Vec3(10, 0, 0); Vec3 rotate = new Vec3(0, 90, 0); Vec3 scale = new Vec3(2, 2, 2); Matrix mat = Matrix.TRS(translate, Quat.FromAngles(rotate), scale); Log.Info($"Transform matrix: {translate} {rotate} {scale}"); // Check ray transforms Ray ray1 = new Ray(Vec3.Zero, V.XYZ(0, 0, 1)); Ray ray2 = new Ray(V.XYZ(1, 0, 0), V.XYZ(0, 0, 1)); Ray tRay1 = mat.Transform(ray1); Ray tRay2 = mat * ray2; Log.Info($"Ray {ray1} -> {tRay1}"); Log.Info($"Ray {ray2} -> {tRay2}"); if (Vec3.Distance(tRay1.position, V.XYZ(10, 0, 0)) > tolerance) { return(false); } if (Vec3.Distance(tRay1.direction, V.XYZ(2, 0, 0)) > tolerance) { return(false); } if (Vec3.Distance(tRay2.position, V.XYZ(10, 0, -2)) > tolerance) { return(false); } if (Vec3.Distance(tRay2.direction, V.XYZ(2, 0, 0)) > tolerance) { return(false); } // Check pose transforms Pose pose1 = Pose.Identity; Pose pose2 = new Pose(V.XYZ(1, 0, 0), Quat.FromAngles(0, 90, 0)); Pose tPose1 = mat.Transform(pose1); Pose tPose2 = mat * pose2; Log.Info($"Pose {pose1} -> {tPose1}"); Log.Info($"Pose {pose2} -> {tPose2}"); if (Vec3.Distance(tPose1.position, V.XYZ(10, 0, 0)) > tolerance) { return(false); } if (Vec3.Distance(tPose1.Forward, V.XYZ(-1, 0, 0)) > tolerance) { return(false); } if (Vec3.Distance(tPose2.position, V.XYZ(10, 0, -2)) > tolerance) { return(false); } if (Vec3.Distance(tPose2.Forward, V.XYZ(0, 0, 1)) > tolerance) { return(false); } return(true); }
public void Update() { Hierarchy.Push(Matrix.TRS(V.XYZ(0.5f, -0.25f, -0.5f), Quat.LookDir(-1, 0, 1), 0.2f)); Tests.Screenshot("ProceduralGeometry.jpg", 600, 600, V.XYZ(0.3f, -0.25f, -0.3f), V.XYZ(0.5f, -0.25f, -0.5f)); Tests.Screenshot("ProceduralGrid.jpg", 600, 600, Hierarchy.ToWorld(V.X0Z(-2, -0.7f)), Hierarchy.ToWorld(V.X0Z(-2, 0))); // Plane! Mesh planeMesh = demoPlaneMesh; Model planeModel = demoPlaneModel; /// :CodeSample: Mesh.GeneratePlane /// Drawing both a Mesh and a Model generated this way is reasonably simple, /// here's a short example! For the Mesh, you'll need to create your own material, /// we just loaded up the default Material here. Matrix planeTransform = Matrix.T(-.5f, -1, 0); planeMesh.Draw(Default.Material, planeTransform); planeTransform = Matrix.T(.5f, -1, 0); planeModel.Draw(planeTransform); /// :End: // Cube! Mesh cubeMesh = demoCubeMesh; Model cubeModel = demoCubeModel; /// :CodeSample: Mesh.GenerateCube /// Drawing both a Mesh and a Model generated this way is reasonably simple, /// here's a short example! For the Mesh, you'll need to create your own material, /// we just loaded up the default Material here. // Call this code every Step Matrix cubeTransform = Matrix.T(-.5f, -.5f, 0); cubeMesh.Draw(Default.Material, cubeTransform); cubeTransform = Matrix.T(.5f, -.5f, 0); cubeModel.Draw(cubeTransform); /// :End: // Rounded Cube! Mesh roundedCubeMesh = demoRoundedCubeMesh; Model roundedCubeModel = demoRoundedCubeModel; /// :CodeSample: Mesh Mesh.GenerateRoundedCube Mesh.Draw /// Drawing both a Mesh and a Model generated this way is reasonably simple, /// here's a short example! For the Mesh, you'll need to create your own material, /// we just loaded up the default Material here. // Call this code every Step Matrix roundedCubeTransform = Matrix.T(-.5f, 0, 0); roundedCubeMesh.Draw(Default.Material, roundedCubeTransform); roundedCubeTransform = Matrix.T(.5f, 0, 0); roundedCubeModel.Draw(roundedCubeTransform); /// :End: // Rounded Sphere! Mesh sphereMesh = demoSphereMesh; Model sphereModel = demoSphereModel; /// :CodeSample: Mesh.GenerateSphere /// Drawing both a Mesh and a Model generated this way is reasonably simple, /// here's a short example! For the Mesh, you'll need to create your own material, /// we just loaded up the default Material here. // Call this code every Step Matrix sphereTransform = Matrix.T(-.5f, .5f, 0); sphereMesh.Draw(Default.Material, sphereTransform); sphereTransform = Matrix.T(.5f, .5f, 0); sphereModel.Draw(sphereTransform); /// :End: // Cylinder! Mesh cylinderMesh = demoCylinderMesh; Model cylinderModel = demoCylinderModel; /// :CodeSample: Mesh.GenerateCylinder /// Drawing both a Mesh and a Model generated this way is reasonably simple, /// here's a short example! For the Mesh, you'll need to create your own material, /// we just loaded up the default Material here. // Call this code every Step Matrix cylinderTransform = Matrix.T(-.5f, 1, 0); cylinderMesh.Draw(Default.Material, cylinderTransform); cylinderTransform = Matrix.T(.5f, 1, 0); cylinderModel.Draw(cylinderTransform); /// :End: demoProcMesh.Draw(Default.Material, Matrix.TR(-2, 0, 0, Quat.FromAngles(-90, 0, 0))); Hierarchy.Pop(); Text.Add(title, titlePose); Text.Add(description, descPose, V.XY(0.4f, 0), TextFit.Wrap, TextAlign.TopCenter, TextAlign.TopLeft); }
void StepMenu(Hand hand) { // Animate the menu a bit float time = (Time.Elapsedf * 24); menuPose.position = Vec3.Lerp(menuPose.position, destPose.position, time); menuPose.orientation = Quat.Slerp(menuPose.orientation, destPose.orientation, time); activation = SKMath.Lerp(activation, 1, time); // Pre-calculate some circle traversal values HandRadialLayer layer = layers[activeLayer]; int count = layer.items.Length; float step = 360 / count; float halfStep = step / 2; // Push the Menu's pose onto the stack, so we can draw, and work // in local space. Hierarchy.Push(menuPose.ToMatrix(activation)); // Calculate the status of the menu! Vec3 tipWorld = hand[FingerId.Index, JointId.Tip].position; Vec3 tipLocal = Hierarchy.ToLocal(tipWorld); float magSq = tipLocal.MagnitudeSq; bool onMenu = tipLocal.z > -0.02f && tipLocal.z < 0.02f; bool focused = onMenu && magSq > minDist * minDist; bool selected = onMenu && magSq > midDist * midDist; bool cancel = magSq > maxDist * maxDist; // Find where our finger is pointing to, and draw that float fingerAngle = (float)Math.Atan2(tipLocal.y, tipLocal.x) * Units.rad2deg - (layer.startAngle + angleOffset); while (fingerAngle < 0) { fingerAngle += 360; } int angleId = (int)(fingerAngle / step); Lines.Add(Vec3.Zero, new Vec3(tipLocal.x, tipLocal.y, 0), Color.White * 0.5f, 0.001f); // Draw the menu inner and outer circles Lines.Add(circle); Lines.Add(innerCircle); // Now draw each of the menu items! for (int i = 0; i < count; i++) { float currAngle = i * step + layer.startAngle + angleOffset; bool highlightText = focused && angleId == i; bool highlightLine = highlightText || (focused && (angleId + 1) % count == i); Vec3 dir = Vec3.AngleXY(currAngle); Lines.Add(dir * minDist, dir * maxDist, highlightLine ? Color.White : Color.White * 0.5f, highlightLine?0.002f:0.001f); Text.Add(layer.items[i].name, Matrix.TRS(Vec3.AngleXY(currAngle + halfStep) * midDist, Quat.FromAngles(0, 0, currAngle + halfStep - 90), highlightText?1.2f:1), TextAlign.XCenter | TextAlign.YBottom); } // Done with local work Hierarchy.Pop(); // Execute any actions that were discovered // But not if we're still in the process of animating, interaction values // could be pretty incorrect when we're still lerping around. if (activation < 0.95f) { return; } if (selected) { SelectItem(layer.items[angleId], tipWorld, (angleId + 0.5f) * step); } if (cancel) { Close(); } }
public void Initialize() { /// :CodeSample: Model Model.AddNode Model.FindNode ModelNode.AddChild /// ### Assembling a Model /// While normally you'll load Models from file, you can also assemble /// them yourself procedurally! This example shows assembling a simple /// hierarchy of visual and empty nodes. Model model = new Model(); model .AddNode("Root", Matrix.S(0.2f), Mesh.Cube, Material.Default) .AddChild("Sub", Matrix.TR(V.XYZ(0.5f, 0, 0), Quat.FromAngles(0, 0, 45)), Mesh.Cube, Material.Default) .AddChild("Surface", Matrix.TRS(V.XYZ(0.5f, 0, 0), Quat.LookDir(V.XYZ(1, 0, 0)), V.XYZ(1, 1, 1))); ModelNode surfaceNode = model.FindNode("Surface"); surfaceNode.AddChild("UnitX", Matrix.T(Vec3.UnitX)); surfaceNode.AddChild("UnitY", Matrix.T(Vec3.UnitY)); surfaceNode.AddChild("UnitZ", Matrix.T(Vec3.UnitZ)); /// :End: Tests.Test(() => model.FindNode("NotPresent") == null); /// :CodeSample: Model Model.Nodes /// ### Simple iteration /// Walking through the Model's list of nodes is pretty /// straightforward! This will touch every ModelNode in the Model, /// in the order they were defined, regardless of hierarchy position /// or contents. Log.Info("Iterate nodes:"); foreach (ModelNode node in model.Nodes) { Log.Info(" " + node.Name); } /// :End: /// :CodeSample: Model Model.Visuals /// ### Simple iteration of visual nodes /// This will iterate through every ModelNode in the Model with visual /// data attached to it! Log.Info("Iterate visuals:"); foreach (ModelNode node in model.Visuals) { Log.Info(" " + node.Name); } /// :End: /// :CodeSample: Model Model.Visuals Model.Nodes /// ### Tagged Nodes /// You can search through Visuals and Nodes var nodes = model.Visuals .Where(n => n.Name.StartsWith("[Wire]")); foreach (var node in nodes) { node.Material = node.Material.Copy(); node.Material.Wireframe = true; } /// :End: Log.Info("Recursive ModelNode traversal:"); RecursiveTraversal(model.RootNode); Log.Info("Depth first ModelNode traversal:"); DepthFirstTraversal(model); for (int i = 0; i < model.Nodes.Count; i++) { Log.Info($"{model.Nodes[i].Name} using shader {model.Nodes[i].Material?.Shader.Name}"); } _model = model; _surfaceNode = surfaceNode; }