/////////////////////////////////////////// static Vec3 CalcPedestalUIDir() { // Get the angle from the center of the pedestal to the user's head, // flatten it on the Y axis, and normalize it for angle calculations. Vec3 dir = Input.Head.position - terrainPose.position; dir = dir.XZ.Normalized.X0Y; // Use a 'sticky' algorithm for updating the angle of the UI. We snap // to increments of 60 degrees, but only do it after we've traveled // 20 degrees into the next increment. This prevents the UI from // moving back and forth when the user is wiggling around at the edge // of a snap increment. const float snapAngle = 60; const float stickyAmount = 20; float angle = dir.XZ.Angle(); if (SKMath.AngleDist(angle, uiAngle) > snapAngle / 2 + stickyAmount) { uiAngle = (int)(angle / snapAngle) * snapAngle + snapAngle / 2; } // Turn the angle back into a direction we can use to position the // pedestal return(Vec3.AngleXZ(uiAngle)); }
public void Initialize() { /// :CodeSample: Sound Sound.FromFile Sound.Play /// ### Basic usage Sound sound = Sound.FromFile("BlipNoise.wav"); sound.Play(Vec3.Zero); /// :End: /// :CodeSample: Sound Sound.Generate /// ### Generating a sound /// Making a procedural sound is pretty straightforward! Here's /// an example of building a sound from two frequencies of sin /// wave. Sound genSound = Sound.Generate((t) => { float band1 = SKMath.Sin(t * 523.25f * SKMath.Tau); // a 'C' tone float band2 = SKMath.Sin(t * 659.25f * SKMath.Tau); // an 'E' tone const float volume = 0.1f; return((band1 * 0.6f + band2 * 0.4f) * volume); }, 0.5f); genSound.Play(Vec3.Zero); /// :End: }
void StepWand() { if (wandStream == null) { wandStream = Sound.CreateStream(5f); wandStreamInst = wandStream.Play(wandTipPrev); } if (wandModel == null) { wandModel = Model.FromFile("Wand.glb", Shader.UI); } if (wandFollow == null) { wandFollow = new LinePoint[10]; for (int i = 0; i < wandFollow.Length; i += 1) { wandFollow[i] = new LinePoint(Vec3.Zero, new Color(1, 1, 1, i / (float)wandFollow.Length), (i / (float)wandFollow.Length) * 0.01f + 0.001f); } } UI.HandleBegin("wand", ref wandPose, wandModel.Bounds); wandModel.Draw(Matrix.Identity); UI.HandleEnd(); Vec3 wandTip = wandPose.ToMatrix() * (wandModel.Bounds.center + wandModel.Bounds.dimensions.y * 0.5f * Vec3.Up); Vec3 wandVel = (wandTip - wandTipPrev) * Time.Elapsedf; float wandSpeed = wandVel.Magnitude * 100; int count = Math.Max(0, (int)(0.1f * 48000) - (wandStream.TotalSamples - wandStream.CursorSamples)); if (wandSamples.Length < count) { wandSamples = new float[count]; } for (int i = 0; i < count; i++) { wandIntensity = Math.Min(1, SKMath.Lerp(wandIntensity, wandSpeed, 0.001f)); wandTime += (1 / 48000.0) * (30000 * wandIntensity + 2000); wandSamples[i] = (float)Math.Sin(wandTime) * wandIntensity; } wandStreamInst.Position = wandTip; wandStream.WriteSamples(wandSamples, count); for (int i = 0; i < wandFollow.Length - 1; i++) { wandFollow[i].pt = wandFollow[i + 1].pt; } wandFollow[wandFollow.Length - 1].pt = wandTip; Lines.Add(wandFollow); wandTipPrev = wandTip; }
public void Initialize() { /// :CodeSample: Sound Sound.FromFile Sound.Play /// ### Basic usage Sound sound = Sound.FromFile("BlipNoise.wav"); sound.Play(Vec3.Zero); /// :End: /// :CodeSample: Sound Sound.Generate /// ### Generating a sound via generator /// Making a procedural sound is pretty straightforward! Here's /// an example of building a 500ms sound from two frequencies of /// sin wave. Sound genSound = Sound.Generate((t) => { float band1 = SKMath.Sin(t * 523.25f * SKMath.Tau); // a 'C' tone float band2 = SKMath.Sin(t * 659.25f * SKMath.Tau); // an 'E' tone const float volume = 0.1f; return((band1 * 0.6f + band2 * 0.4f) * volume); }, 0.5f); genSound.Play(Vec3.Zero); /// :End: /// :CodeSample: Sound Sound.FromSamples /// ### Generating a sound via samples /// Making a procedural sound is pretty straightforward! Here's /// an example of building a 500ms sound from two frequencies of /// sin wave. float[] samples = new float[(int)(48000 * 0.5f)]; for (int i = 0; i < samples.Length; i++) { float t = i / 48000.0f; float band1 = SKMath.Sin(t * 523.25f * SKMath.Tau); // a 'C' tone float band2 = SKMath.Sin(t * 659.25f * SKMath.Tau); // an 'E' tone const float volume = 0.1f; samples[i] = (band1 * 0.6f + band2 * 0.4f) * volume; } Sound sampleSound = Sound.FromSamples(samples); sampleSound.Play(Vec3.Zero); /// :End: }
public void Update() { UI.PushSurface(_modelPose); _model.Draw(Matrix.Identity, new Color(0.5f, 0.5f, 0.5f)); UI.PushSurface( _surfaceNode.ModelTransform.Pose, _surfaceNode.ModelTransform.Scale.XY0 / 2, _surfaceNode.ModelTransform.Scale.XY); Vec3 center = Hierarchy.ToWorld(Vec3.Zero); UI.Label("UI on a Model node!"); UI.HSeparator(); UI.Button("Do Thing"); UI.PopSurface(); UI.PopSurface(); if (_model.Intersect(new Ray(V.XYZ(SKMath.Cos(Time.Totalf) * 0.15f, 1, 0), -Vec3.Up), out Ray at)) { Lines.Add(at, 0.3f, new Color32(0, 255, 0, 255), 0.005f); } Tests.Screenshot("Tests/NodeUI.jpg", 400, 400, center + Vec3.One * 0.15f, center); }
bool TestAngleDist() { float angleDistA = SKMath.AngleDist(0, 359); float angleDistB = SKMath.AngleDist(359, 0); float angleDistC = SKMath.AngleDist(359, 720); float angleDistD = SKMath.AngleDist(-60, 70); float angleDistE = SKMath.AngleDist(-60, 140); Log.Info($"AngleDist 0 to 359: {angleDistA}"); Log.Info($"AngleDist 359 to 0: {angleDistB}"); Log.Info($"AngleDist 359 to 720: {angleDistC}"); Log.Info($"AngleDist -60 to 70: {angleDistD}"); Log.Info($"AngleDist -60 to 140: {angleDistE}"); if (MathF.Abs(angleDistA - angleDistB) > tolerance) { return(false); } if (MathF.Abs(angleDistA - angleDistC) > tolerance) { return(false); } if (MathF.Abs(angleDistA - 1) > tolerance) { return(false); } if (MathF.Abs(angleDistD - 130) > tolerance) { return(false); } if (MathF.Abs(angleDistE - 160) > tolerance) { return(false); } return(true); }
public void Initialize() { /// :CodeSample: Mesh.GenerateCube /// ![Procedural Geometry Demo]({{site.url}}/img/screenshots/ProceduralGeometry.jpg) /// Here's a quick example of generating a mesh! You can store it in just a /// Mesh, or you can attach it to a Model for easier rendering later on. Mesh cubeMesh = Mesh.GenerateCube(Vec3.One * 0.4f); Model cubeModel = Model.FromMesh(cubeMesh, Default.Material); /// :End: demoCubeMesh = cubeMesh; demoCubeModel = cubeModel; /// :CodeSample: Mesh.GenerateRoundedCube /// ![Procedural Geometry Demo]({{site.url}}/img/screenshots/ProceduralGeometry.jpg) /// Here's a quick example of generating a mesh! You can store it in just a /// Mesh, or you can attach it to a Model for easier rendering later on. Mesh roundedCubeMesh = Mesh.GenerateRoundedCube(Vec3.One * 0.4f, 0.05f); Model roundedCubeModel = Model.FromMesh(roundedCubeMesh, Default.Material); /// :End: demoRoundedCubeMesh = roundedCubeMesh; demoRoundedCubeModel = roundedCubeModel; /// :CodeSample: Mesh.GenerateSphere /// ![Procedural Geometry Demo]({{site.url}}/img/screenshots/ProceduralGeometry.jpg) /// Here's a quick example of generating a mesh! You can store it in just a /// Mesh, or you can attach it to a Model for easier rendering later on. Mesh sphereMesh = Mesh.GenerateSphere(0.4f); Model sphereModel = Model.FromMesh(sphereMesh, Default.Material); /// :End: demoSphereMesh = sphereMesh; demoSphereModel = sphereModel; /// :CodeSample: Mesh.GenerateCylinder /// ![Procedural Geometry Demo]({{site.url}}/img/screenshots/ProceduralGeometry.jpg) /// Here's a quick example of generating a mesh! You can store it in just a /// Mesh, or you can attach it to a Model for easier rendering later on. Mesh cylinderMesh = Mesh.GenerateCylinder(0.4f, 0.4f, Vec3.Up); Model cylinderModel = Model.FromMesh(cylinderMesh, Default.Material); /// :End: demoCylinderMesh = cylinderMesh; demoCylinderModel = cylinderModel; /// :CodeSample: Mesh.GeneratePlane /// ![Procedural Geometry Demo]({{site.url}}/img/screenshots/ProceduralGeometry.jpg) /// Here's a quick example of generating a mesh! You can store it in just a /// Mesh, or you can attach it to a Model for easier rendering later on. Mesh planeMesh = Mesh.GeneratePlane(Vec2.One * 0.4f); Model planeModel = Model.FromMesh(planeMesh, Default.Material); /// :End: demoPlaneMesh = planeMesh; demoPlaneModel = planeModel; /// :CodeSample: Mesh.SetVerts Mesh.SetInds /// ## Procedurally generating a wavy grid /// ![Wavy Grid]({{site.url}}/img/screenshots/ProceduralGrid.jpg) /// Here, we'll generate a grid mesh using Mesh.SetVerts and Mesh.SetInds! This /// is a common example of creating a grid using code, we're using a sin wave /// to make it more visually interesting, but you could also substitute this for /// something like sampling a heightmap, or a more interesting mathematical /// formula! /// /// Note: x+y*gridSize is the formula for 2D (x,y) access of a 1D array that represents /// a grid. const int gridSize = 8; Vertex[] verts = new Vertex[gridSize * gridSize]; uint [] inds = new uint [gridSize * gridSize * 6]; for (int y = 0; y < gridSize; y++) { for (int x = 0; x < gridSize; x++) { // Create a vertex on a grid, centered about the origin. The dimensions extends // from -0.5 to +0.5 on the X and Z axes. The Y value is then sampled from // a sin wave using the x and y values. // // The normal of the vertex is then calculated from the derivative of the Y // value! verts[x + y * gridSize] = new Vertex( new Vec3( x / (float)gridSize - 0.5f, SKMath.Sin((x + y) * 0.7f) * 0.1f, y / (float)gridSize - 0.5f), new Vec3( -SKMath.Cos((x + y) * 0.7f), 1, -SKMath.Cos((x + y) * 0.7f)).Normalized); // Create triangle face indices from the current vertex, and the vertices // on the next row and column! Since there is no 'next' row and column on // the last row and column, we guard this with a check for size-1. if (x < gridSize - 1 && y < gridSize - 1) { int ind = (x + y * gridSize) * 6; inds[ind] = (uint)((x + 1) + (y + 1) * gridSize); inds[ind + 1] = (uint)((x + 1) + (y) * gridSize); inds[ind + 2] = (uint)((x) + (y + 1) * gridSize); inds[ind + 3] = (uint)((x) + (y + 1) * gridSize); inds[ind + 4] = (uint)((x + 1) + (y) * gridSize); inds[ind + 5] = (uint)((x) + (y) * gridSize); } } } demoProcMesh = new Mesh(); demoProcMesh.SetVerts(verts); demoProcMesh.SetInds(inds); /// :End: }
public void Update() { Hierarchy.Push(Matrix.T(0, 0, -0.3f)); UI.WindowBegin("Alignment", ref alignWindow, new Vec2(20, 0) * Units.cm2m); Vec2 size = new Vec2(5 * Units.cm2m, UI.LineHeight); if (UI.Radio("Left", alignX == TextAlign.XLeft, size)) { alignX = TextAlign.XLeft; } UI.SameLine(); if (UI.Radio("CenterX", alignX == TextAlign.XCenter, size)) { alignX = TextAlign.XCenter; } UI.SameLine(); if (UI.Radio("Right", alignX == TextAlign.XRight, size)) { alignX = TextAlign.XRight; } if (UI.Radio("Top", alignY == TextAlign.YTop, size)) { alignY = TextAlign.YTop; } UI.SameLine(); if (UI.Radio("CenterY", alignY == TextAlign.YCenter, size)) { alignY = TextAlign.YCenter; } UI.SameLine(); if (UI.Radio("Bottom", alignY == TextAlign.YBottom, size)) { alignY = TextAlign.YBottom; } UI.WindowEnd(); Hierarchy.Push(Matrix.T(0.1f, 0, 0)); Text.Add("X Center", Matrix.TRS(new Vec3(0, .1f, 0), Quat.LookDir(0, 0, 1)), TextAlign.XCenter | TextAlign.YCenter, alignX | alignY); Text.Add("X Left", Matrix.TRS(new Vec3(0, .15f, 0), Quat.LookDir(0, 0, 1)), TextAlign.XLeft | TextAlign.YCenter, alignX | alignY); Text.Add("X Right", Matrix.TRS(new Vec3(0, .2f, 0), Quat.LookDir(0, 0, 1)), TextAlign.XRight | TextAlign.YCenter, alignX | alignY); Lines.Add(new Vec3(0, .05f, 0), new Vec3(0, .25f, 0), Color32.White, 0.001f); Hierarchy.Pop(); Hierarchy.Push(Matrix.T(-0.1f, 0, 0)); Text.Add("Y Center", Matrix.TRS(new Vec3(0, .1f, 0), Quat.LookDir(0, 0, 1)), TextAlign.YCenter | TextAlign.XCenter, alignX | alignY); Lines.Add(new Vec3(-0.05f, .1f, 0), new Vec3(.05f, .1f, 0), Color32.White, 0.001f); Text.Add("Y Top", Matrix.TRS(new Vec3(0, .15f, 0), Quat.LookDir(0, 0, 1)), TextAlign.YTop | TextAlign.XCenter, alignX | alignY); Lines.Add(new Vec3(-0.05f, .15f, 0), new Vec3(.05f, .15f, 0), Color32.White, 0.001f); Text.Add("Y Bottom", Matrix.TRS(new Vec3(0, .2f, 0), Quat.LookDir(0, 0, 1)), TextAlign.YBottom | TextAlign.XCenter, alignX | alignY); Lines.Add(new Vec3(-0.05f, .2f, 0), new Vec3(.05f, .2f, 0), Color32.White, 0.001f); Hierarchy.Pop(); Hierarchy.Push(Matrix.T(0, -0.1f, 0)); if (Tests.IsTesting) { Renderer.Screenshot(Hierarchy.ToWorld(new Vec3(0, 0, 0.09f)), Hierarchy.ToWorld(Vec3.Zero), 600, 300, "../../../docs/img/screenshots/BasicText.jpg"); } /// :CodeSample: Text.MakeStyle Font.FromFile Text.Add /// Then it's pretty trivial to just draw some text on the screen! Just call /// Text.Add on update. If you don't have a TextStyle available, calling it /// without one will just fall back on the default style. // Text with an explicit text style Text.Add( "Here's\nSome\nMulti-line\nText!!", Matrix.TRS(new Vec3(0.1f, 0, 0), Quat.LookDir(0, 0, 1)), style); // Text using the default text style Text.Add( "Here's\nSome\nMulti-line\nText!!", Matrix.TRS(new Vec3(-0.1f, 0, 0), Quat.LookDir(0, 0, 1))); /// :End: Hierarchy.Push(Matrix.T(0, -0.2f, 0)); Text.Add( "Here's Some Multi-line Text!!", Matrix.TRS(new Vec3(0, 0.0f, 0), Quat.LookDir(0, 0, 1)), new Vec2(SKMath.Cos(Time.Totalf) * 10 + 11, 20) * Units.cm2m, TextFit.Clip, style, TextAlign.Center, alignX | alignY); Hierarchy.Pop(); Hierarchy.Pop(); Hierarchy.Pop(); }
public void Update() { Color colIntersect = Color.HSV(0, 0.8f, 1); Color colTest = Color.HSV(0, 0.6f, 1); Color colObj = Color.HSV(0.05f, 0.7f, 1); Color active = new Color(1, 1, 1, 0.7f); Color notActive = new Color(1, 1, 1, 1); // Plane and Ray bool planeRayActive = UI.AffordanceBegin("PlaneRay", ref posePlaneRay, new Bounds(Vec3.One * 0.4f)); boundsMesh.Draw(boundsMat, Matrix.TS(Vec3.Zero, 0.4f), planeRayActive ? active:notActive); Plane ground = new Plane(Vec3.Zero, new Vec3(1, 2, 0)); Ray groundRay = new Ray(Vec3.Zero + new Vec3(0, 0.2f, 0), Vec3.AngleXZ(Time.Totalf * 90, -2).Normalized()); Lines.Add(groundRay.position, groundRay.position + groundRay.direction * 0.1f, new Color32(255, 0, 0, 255), 2 * Units.mm2m); planeMesh.Draw(material, Matrix.TRS(Vec3.Zero, Quat.LookDir(ground.normal), 0.25f), colObj); if (groundRay.Intersect(ground, out Vec3 groundAt)) { sphereMesh.Draw(material, Matrix.TS(groundAt, 0.02f), colIntersect); } UI.AffordanceEnd(); if (Demos.TestMode) { Renderer.Screenshot(posePlaneRay.position + new Vec3(0.0f, 0.3f, 0.15f), posePlaneRay.position + Vec3.Up * 0.1f, 400, 400, "../../../docs/img/screenshots/RayIntersectPlane.jpg"); } // Line and Plane bool linePlaneActive = UI.AffordanceBegin("LinePlane", ref poseLinePlane, new Bounds(Vec3.One * 0.4f)); boundsMesh.Draw(boundsMat, Matrix.TS(Vec3.Zero, 0.4f), linePlaneActive ? active : notActive); Plane groundLinePlane = new Plane(Vec3.Zero, new Vec3(1, 2, 0)); Ray groundLineRay = new Ray(Vec3.Zero + new Vec3(0, 0.25f, 0), Vec3.AngleXZ(Time.Totalf * 90, -2).Normalized()); Vec3 groundLineP1 = groundLineRay.position + groundLineRay.direction * (SKMath.Cos(Time.Totalf * 3) + 1) * 0.2f; Vec3 groundLineP2 = groundLineRay.position + groundLineRay.direction * ((SKMath.Cos(Time.Totalf * 3) + 1) * 0.2f + 0.1f); Lines.Add(groundLineP1, groundLineP2, colTest, 2 * Units.mm2m); sphereMesh.Draw(material, Matrix.TS(groundLineP1, 0.01f), colTest); sphereMesh.Draw(material, Matrix.TS(groundLineP2, 0.01f), colTest); bool groundLineIntersects = groundLinePlane.Intersect(groundLineP1, groundLineP2, out Vec3 groundLineAt); planeMesh.Draw(material, Matrix.TRS(Vec3.Zero, Quat.LookDir(groundLinePlane.normal), 0.25f), groundLineIntersects? colIntersect : colObj); if (groundLineIntersects) { sphereMesh.Draw(material, Matrix.TS(groundLineAt, 0.02f), colIntersect); } UI.AffordanceEnd(); if (Demos.TestMode) { Renderer.Screenshot(poseLinePlane.position + new Vec3(0.0f, 0.3f, 0.15f), poseLinePlane.position + Vec3.Up * 0.1f, 400, 400, "../../../docs/img/screenshots/LineIntersectPlane.jpg"); } // Sphere and Ray bool sphereRayActive = UI.AffordanceBegin("SphereRay", ref poseSphereRay, new Bounds(Vec3.One * 0.4f)); boundsMesh.Draw(boundsMat, Matrix.TS(Vec3.Zero, 0.4f), sphereRayActive ? active : notActive); Sphere sphere = new Sphere(Vec3.Zero, 0.25f); Vec3 sphereDir = Vec3.AngleXZ(Time.Totalf * 90, SKMath.Cos(Time.Totalf * 3) * 1.5f + 0.1f).Normalized(); Ray sphereRay = new Ray(sphere.center - sphereDir * 0.35f, sphereDir); Lines.Add(sphereRay.position, sphereRay.position + sphereRay.direction * 0.1f, colTest, 2 * Units.mm2m); if (sphereRay.Intersect(sphere, out Vec3 sphereAt)) { sphereMesh.Draw(material, Matrix.TS(sphereAt, 0.02f), colIntersect); } sphereMesh.Draw(material, Matrix.TS(sphere.center, 0.25f), colObj); UI.AffordanceEnd(); if (Demos.TestMode) { Renderer.Screenshot(poseSphereRay.position + new Vec3(0.0f, 0.3f, 0.15f), poseSphereRay.position, 400, 400, "../../../docs/img/screenshots/RayIntersectSphere.jpg"); } // Bounds and Ray bool boundsRayActive = UI.AffordanceBegin("BoundsRay", ref poseBoundsRay, new Bounds(Vec3.One * 0.4f)); boundsMesh.Draw(boundsMat, Matrix.TS(Vec3.Zero, 0.4f), boundsRayActive ? active : notActive); Bounds bounds = new Bounds(Vec3.Zero, Vec3.One * 0.25f); Vec3 boundsDir = Vec3.AngleXZ(Time.Totalf * 90, SKMath.Cos(Time.Totalf * 3) * 1.5f).Normalized(); Ray boundsRay = new Ray(bounds.center - boundsDir * 0.35f, boundsDir); Lines.Add(boundsRay.position, boundsRay.position + boundsRay.direction * 0.1f, colTest, 2 * Units.mm2m); if (boundsRay.Intersect(bounds, out Vec3 boundsAt)) { sphereMesh.Draw(material, Matrix.TS(boundsAt, 0.02f), colIntersect); } cubeMesh.Draw(material, Matrix.TS(bounds.center, 0.25f), colObj); UI.AffordanceEnd(); if (Demos.TestMode) { Renderer.Screenshot(poseBoundsRay.position + new Vec3(0.0f, 0.3f, 0.15f), poseBoundsRay.position, 400, 400, "../../../docs/img/screenshots/RayIntersectBounds.jpg"); } // Bounds and Line bool boundsLineActive = UI.AffordanceBegin("BoundsLine", ref poseBoundsLine, new Bounds(Vec3.One * 0.4f)); boundsMesh.Draw(boundsMat, Matrix.TS(Vec3.Zero, 0.4f), boundsLineActive ? active : notActive); Bounds boundsLine = new Bounds(Vec3.Zero, Vec3.One * 0.25f); Vec3 boundsLineP1 = boundsLine.center + Vec3.AngleXZ(Time.Totalf * 45, SKMath.Cos(Time.Totalf * 3)) * 0.35f; Vec3 boundsLineP2 = boundsLine.center + Vec3.AngleXZ(Time.Totalf * 90, SKMath.Cos(Time.Totalf * 6)) * SKMath.Cos(Time.Totalf) * 0.35f; Lines.Add(boundsLineP1, boundsLineP2, colTest, 2 * Units.mm2m); sphereMesh.Draw(material, Matrix.TS(boundsLineP1, 0.01f), colTest); sphereMesh.Draw(material, Matrix.TS(boundsLineP2, 0.01f), colTest); cubeMesh.Draw(material, Matrix.TS(boundsLine.center, 0.25f), boundsLine.Contains(boundsLineP1, boundsLineP2) ? colIntersect : colObj); UI.AffordanceEnd(); if (Demos.TestMode) { Renderer.Screenshot(poseBoundsLine.position + new Vec3(0.0f, 0.3f, 0.15f), poseBoundsLine.position, 400, 400, "../../../docs/img/screenshots/LineIntersectBounds.jpg"); } // Cross product bool crossActive = UI.AffordanceBegin("Cross", ref poseCross, new Bounds(Vec3.One * 0.4f)); boundsMesh.Draw(boundsMat, Matrix.TS(Vec3.Zero, 0.4f), crossActive ? active : notActive); Vec3 crossStart = Vec3.Zero; //Vec3 right = Vec3.Cross(Vec3.Forward, Vec3.Up); // These are the same! Vec3 right = Vec3.PerpendicularRight(Vec3.Forward, Vec3.Up); Lines.Add(crossStart, crossStart + Vec3.Up * 0.1f, new Color32(255, 255, 255, 255), 2 * Units.mm2m); Lines.Add(crossStart, crossStart + Vec3.Forward * 0.1f, new Color32(255, 255, 255, 255), 2 * Units.mm2m); Lines.Add(crossStart, crossStart + right * 0.1f, new Color32(0, 255, 0, 255), 2 * Units.mm2m); Text.Add("Up", Matrix.TRS(crossStart + Vec3.Up * 0.1f, Quat.LookDir(-Vec3.Forward), 1), TextAlign.XCenter | TextAlign.YBottom); Text.Add("Fwd", Matrix.TRS(crossStart + Vec3.Forward * 0.1f, Quat.LookDir(-Vec3.Forward), 1), TextAlign.XCenter | TextAlign.YBottom); Text.Add("Vec3.Cross(Fwd,Up)", Matrix.TRS(crossStart + right * 0.1f, Quat.LookDir(-Vec3.Forward), 1), TextAlign.XCenter | TextAlign.YBottom); UI.AffordanceEnd(); if (Demos.TestMode) { Renderer.Screenshot(poseCross.position + new Vec3(0.075f, 0.1f, 0.15f), poseCross.position + new Vec3(0.075f, 0, 0), 400, 400, "../../../docs/img/screenshots/CrossProduct.jpg"); } }
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(); }
public void Step() { const float shoulderWidth = 0.16f; // half the total shoulder width const float forearm = 0.22f; // length of the forearm const float uarm = 0.25f; // length of the upper arm const float headLength = 0.1f; // length from head point to neck const float neckLength = 0.02f; // length from neck to center shoulders const float shoulderDrop = 0.05f; // shoulder drop height from shoulder center const float elbowFlare = 0.45f; // Elbows point at a location 1m down, and elbowFlare out from the shoulder Pose head = Input.Head; // Head, neck, and shoulder center headLine[0].pt = head.position; headLine[1].pt = headLine[0].pt + head.orientation * V.XYZ(0, -headLength, 0); headLine[2].pt = headLine[1].pt - V.XYZ(0, neckLength, 0); headLine[0].pt = Vec3.Lerp(headLine[0].pt, headLine[1].pt, 0.1f); Lines.Add(headLine); // Shoulder forward facing direction is head direction weighted // equally with the direction of both hands. Vec3 forward = head.Forward.X0Z.Normalized * 2 + (Input.Hand(Handed.Right).wrist.position - headLine[2].pt.X0Z).Normalized + (Input.Hand(Handed.Left).wrist.position - headLine[2].pt.X0Z).Normalized; forward = forward * 0.25f; Vec3 right = Vec3.PerpendicularRight(forward, Vec3.Up).Normalized; // Now for each arm for (int h = 0; h < 2; h++) { float handed = h == 0 ? -1 : 1; Hand hand = Input.Hand((Handed)h); Vec3 handPos = hand.wrist.position; if (!hand.IsTracked) { continue; } armLine[0].pt = headLine[2].pt; armLine[1].pt = armLine[0].pt + right * handed * shoulderWidth - Vec3.Up * shoulderDrop; armLine[3].pt = handPos; // Triangle represented by 3 edges, forearm, uarm, and armDist float armDist = Math.Min(forearm + uarm, Vec3.Distance(armLine[1].pt, handPos)); // Heron's formula to find area float s = (forearm + uarm + armDist) / 2; float area = SKMath.Sqrt(s * (s - forearm) * (s - uarm) * (s - armDist)); // Height of triangle based on area float offsetH = (2 * area) / armDist; // Height can now be used to calculate how far off the elbow is float offsetD = SKMath.Sqrt(Math.Abs(offsetH * offsetH - uarm * uarm)); // Elbow calculation begins somewhere along the line between the // shoulder and the wrist. Vec3 dir = (handPos - armLine[1].pt).Normalized; Vec3 at = armLine[1].pt + dir * offsetD; // The elbow naturally flares out to the side, rather than // dropping straight down. Here, we find a point to flare out // towards. Vec3 flarePoint = headLine[2].pt + right * handed * elbowFlare - Vec3.Up; Plane flarePlane = new Plane(flarePoint, Ray.FromTo(headLine[2].pt, handPos).Closest(flarePoint) - flarePoint); Vec3 dirDown = (flarePlane.Closest(at) - at).Normalized; armLine[2].pt = at + dirDown * offsetH; Lines.Add(armLine); for (int i = 1; i < 5; i++) { Lines.Add(handPos, hand[(FingerId)i, JointId.KnuckleMajor].position, Color32.White, new Color32(255, 255, 255, 0), 0.01f); } } }
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(); } }