namespace Oriels; public class Mono { private static readonly Lazy lazy = new Lazy(() => new Oriels.Mono()); public static Oriels.Mono inst { get { return lazy.Value; } } public PR.Noise noise = new PR.Noise(939949595); public Material matDev; public Material matHoloframe = new Material(Shader.FromFile("above.hlsl")); Material matHoloframeUnder = new Material(Shader.FromFile("below.hlsl")); public Material matHolo = new Material(Shader.FromFile("above.hlsl")); Material matHoloUnder = new Material(Shader.FromFile("below.hlsl")); public Rig rig = new Rig(); public Space space = new Space(); public Compositor compositor = new Compositor(); // ------------------------------------------------- public Interaction[] dofs; public ColorCube colorCube = new ColorCube(); public Glove rGlove = new Glove(true), lGlove = new Glove(false); public Glove Glove(bool chirality) { return chirality ? rGlove : lGlove; } // ------------------------------------------------- // public MonoNet net = new MonoNet(); public Mono() { dofs = new Interaction[] { new Chiral(new Interaction[] { new WaveCursor() { handed = Handed.Left }, new WaveCursor() { handed = Handed.Right } }), new Chiral(new Interaction[] { new Trackballer() { handed = Handed.Left }, new Trackballer() { handed = Handed.Right } }), new Chiral(new Interaction[] { new RollsCursor() { handed = Handed.Left }, new RollsCursor() { handed = Handed.Right } }), }; } public void Init() { compositor.Init(); for (int i = 0; i < dofs.Length; i++) { dofs[i].Init(); } matDev = Material.Default.Copy(); matDev.SetTexture("diffuse", Tex.DevTex); matHolo.SetColor("clearcolor", Renderer.ClearColor); matHoloUnder.SetColor("clearcolor", Renderer.ClearColor); matHoloUnder.FaceCull = Cull.None; matHolo.Chain = matHoloUnder; matHoloframe.SetColor("clearcolor", Color.Black); matHoloframe.Transparency = Transparency.Add; matHoloframe.DepthWrite = false; matHoloframe.FaceCull = Cull.None; matHoloframe.Wireframe = true; matHoloframeUnder.SetColor("clearcolor", Color.Black); matHoloframeUnder.Transparency = Transparency.Add; matHoloframeUnder.DepthWrite = false; matHoloframeUnder.FaceCull = Cull.None; matHoloframeUnder.Wireframe = true; matHoloframe.Chain = matHoloframeUnder; } Pose shape = new Pose(new Vec3(0, 1f, -3f), Quat.FromAngles(45, 0, 45)); bool shapeHeld = false; Spatial spatial = new Spatial(); Cursor cursor = new Cursor(); public class Drawer { public Pose pose; public float open; // 0 - 1 public Drawer(Pose pose) { this.pose = pose; mat.FaceCull = Cull.None; } public void Frame(Cursor cursor, float pinch) { float width = 0.4f; float height = 0.15f; Matrix matrix = pose.ToMatrix(); Vec3 localCursor = matrix.Inverse.Transform(cursor.pos); bool inBounds = localCursor.x > width / -2f && localCursor.x < width / 2f && localCursor.y > height / -2f && localCursor.y < height / 2f; if (!opening) { if (open > 0) { float delta = localCursor.z - oldZ; if (inBounds && localCursor.z < open && delta < -0.5f * Time.Stepf) open = 0; } if (open == 0 && inBounds && localCursor.z > 0 && oldZ <= 0) { opening = true; } } if (opening) { open = MathF.Max(localCursor.z, 0); if (!inBounds || pinch == 0 || open > 0.4f) { opening = false; } // Lines.Add( // pose.position, // pose.position + pose.orientation * V.XYZ(0, 0, 0.1f), // -1? // new Color(0, 1, 0), // 0.002f // ); } openSmooth.Update(open); model.FindNode("Cube").Mesh.Draw(mat, Matrix.T(V.XYZ(0, 0, 0.5f)) * Matrix.S(V.XYZ(width, height, MathF.Max(openSmooth.value, 0.01f))) * pose.ToMatrix(), new Color(0.8f, 0.8f, 0.8f, 0.5f) ); oldZ = localCursor.z; } float oldZ = 0; bool opening = false; PR.PID openSmooth = new PR.PID(10f, 0.01f); Model model = Model.FromFile("drawer.glb", Shader.Default); Material mat = Material.Default.Copy(); } Drawer drawerA = new Drawer(new Pose(new Vec3(-0.5f, 0.6f, -0.8f), Quat.Identity)); Drawer drawerB = new Drawer(new Pose(new Vec3(0, 0.6f, -0.8f), Quat.Identity)); Drawer drawerC = new Drawer(new Pose(new Vec3(0.5f, 0.6f, -0.8f), Quat.Identity)); public void Frame() { // Input.HandClearOverride(Handed.Left); // Input.HandClearOverride(Handed.Right); // store hand pre override in rig rig.Step(); // Hand hand = Input.Hand(Handed.Right); // Controller con = Input.Controller(Handed.Right); // Mesh.Cube.Draw( // Material.Default, // hand.IsJustPinched // ) // Con -> Hand // lGlove.Step(); // rGlove.Step(); compositor.Frame(); // spatial.Frame(); { float deadzone = 0.01f; float strength = 6f; Hand hand = Input.Hand(Handed.Right); Vec3 indexTip = hand.Get(FingerId.Index, JointId.Tip).position; Vec3 thumbTip = hand.Get(FingerId.Thumb, JointId.Tip).position; Vec3 delta = indexTip - thumbTip; float mag = delta.Magnitude; float pinch = MathF.Max(mag - deadzone, 0); Vec3 dir = delta.Normalized; cursor.raw = indexTip + dir * pinch * strength; Lines.Add(indexTip, thumbTip, new Color(0, 0, 1), 0.002f); Mesh.Sphere.Draw(matHolo, Matrix.TS(cursor.pos, 0.01f), new Color(0.5f, 0.5f, 0.5f)); // V.XYZ(0, 0, ); drawerA.Frame(cursor, pinch); drawerB.Frame(cursor, pinch); drawerC.Frame(cursor, pinch); } // Input.Subscribe(InputSource.Hand, BtnState.Any, Action); // ------------------------------------------------- for (int i = 0; i < dofs.Length; i++) { if (dofs[i].Active) { dofs[i].Frame(); } } // WaveCursor lwc = (WaveCursor)((Chiral)dofs[0]).dofs[0]; WaveCursor rwc = (WaveCursor)((Chiral)dofs[0]).dofs[1]; Trackballer ltb = (Trackballer)((Chiral)dofs[1]).dofs[0]; Trackballer rtb = (Trackballer)((Chiral)dofs[1]).dofs[1]; if (lwc.Active) { lwc.Demo(ltb.ori); } if (rwc.Active) { rwc.Demo(rtb.ori); } if (rtb.Active) { rtb.Demo(); } if (!shapeHeld) { shapeHeld = rtb.btnPush.frameDown; } if (shapeHeld) { shape.position = rwc.cursor.smooth; shape.orientation = rtb.ori; shapeHeld = !rtb.btnPull.frameDown; } // I'd rather have it be pose.pos & pose.ori // as it's a bit of space hog Mesh.Cube.Draw( Mono.inst.matHoloframe, shape.ToMatrix(0.5f), new Color(0.5f, 0.55f, 0.75f) * 0.3f ); // // pinch drawers // do this quick and fun // what's inside? // friction flip thumb swipe // overcome with >x force impulse // local to palm // dofchan bows on the back of the ankles that double as trackers // rBlock.Step(); lBlock.Step(); // cubicCon.Step(); // colorCube.Palm(lCon.device); // ------------------------------------------------- // net.me.Step(); // net.send = true; ShowWindowButton(); } int dofIndex = 0; Pose windowPose = new Pose(0, 1.5f, -0.5f, Quat.FromAngles(0, 0, 0)); TextStyle style = Text.MakeStyle(Font.FromFile("add/fonts/DM-Mono.ttf"), 1f * U.cm, Color.White); TextStyle style2 = Text.MakeStyle(Font.FromFile("add/fonts/DM-Mono.ttf"), 1f * U.cm, new Color(0.5f, 0.5f, 0.5f)); Vec2 fieldSize = new Vec2(6f * U.cm, 3f * U.cm); void ShowWindowButton() { UI.WindowBegin("design vars", ref windowPose); UI.SetThemeColor(UIColor.Background, new Color(0f, 0f, 0f)); UI.SetThemeColor(UIColor.Primary, new Color(0.5f, 0.5f, 0.5f)); UI.PushTextStyle(style); // if (UI.Button("Draw Oriel Axis")) { oriel.drawAxis = !oriel.drawAxis; } // if (UI.Button("Reset Oriel Quat")) { oriel.ori = Quat.Identity; } // if (UI.Button("Scale w/Height")) { oriel.scaleWithHeight = !oriel.scaleWithHeight; } // UI.HSlider("Scale", ref oriel.scale, 0.1f, 1f, 0.1f); // UI.HSlider("Multiplier", ref oriel.multiplier, 0.1f, 1f, 0.1f); // // UI.Label("Player.y"); // UI.HSlider("Player.y", ref greenyard.height, 1f, 6f, 0.2f); // UI.Label("pos.y"); // UI.HSlider("pos.y", ref playerY, -1f, 1f, 0.1f); if (UI.Button("prev") && dofIndex > 0) { dofIndex--; } UI.SameLine(); if (UI.Button("next") && dofIndex < dofs.Length - 1) { dofIndex++; } Interaction dof = dofs[dofIndex]; Type type = dof.GetType(); // active toggle Color tint = dof.Active ? new Color(0, 1, 0) : new Color(1, 0, 0); UI.PushTint(tint); if (UI.Button(dof.Active ? "on" : "off")) { dof.Active = !dof.Active; } UI.PopTint(); if (type == typeof(Chiral)) { Chiral chiral = (Chiral)dof; System.Reflection.FieldInfo[] fields = typeof(Chiral).GetFields(); foreach (System.Reflection.FieldInfo field in fields) { if (field.FieldType == typeof(Handed)) { Handed handed = (Handed)field.GetValue(chiral); if (UI.Button("<") && (int)handed > 0) { handed = (Handed)((int)handed - 1); field.SetValue(chiral, handed); } UI.SameLine(); if (UI.Button(">") && (int)handed < 2) { handed = (Handed)((int)handed + 1); field.SetValue(chiral, handed); } UI.SameLine(); UI.Label(handed.ToString()); } } RenderDof(chiral.dofs[0]); } else { RenderDof(dof); } UI.WindowEnd(); } void RenderDof(Interaction dof) { Type type = dof.GetType(); UI.Label("°" + type.Name); System.Reflection.FieldInfo[] fields = type.GetFields(); for (int j = 0; j < fields.Length; j++) { System.Reflection.FieldInfo field = fields[j]; if (field.FieldType == typeof(Design)) { Design design = (Design)field.GetValue(dof); UI.Input(field.Name, ref design.str, fieldSize, TextContext.Number); UI.SameLine(); UI.PushTextStyle(style2); UI.Label(design.term, new Vec2(4f * U.cm, 3f * U.cm)); UI.PopTextStyle(); UI.SameLine(); UI.Label(field.Name); } } } } // Chiral : handedness implies symmetry public class Chiral : Interaction { public Chiral(Interaction[] dofs) => this.dofs = dofs; private bool active; public bool Active { get { return this.active; } set { this.active = value; for (int i = 0; i < this.dofs.Length; i++) { Interaction dof = this.dofs[i]; if ((int)this.handed == 2 || i == (int)this.handed) { dof.Active = value; } else { dof.Active = false; } } } } public Interaction[] dofs = new Interaction[2]; // public Design handed = new Design { str = "2", min = 0, max = 2}; public Handed handed = Handed.Max; public void Init() { dofs[0].Init(); dofs[1].Init(); } public void Frame() { // sync the left design variables to the right System.Reflection.FieldInfo[] fields = dofs[0].GetType().GetFields(); foreach (System.Reflection.FieldInfo field in fields) { if (field.FieldType == typeof(Design)) { Design design = (Design)field.GetValue(dofs[0]); // define type? field.SetValue(dofs[1], design); } } for (int i = 0; i < dofs.Length; i++) { Interaction dof = dofs[i]; if ((int)handed == 2 || i == (int)handed) { dof.Frame(); dof.Active = true; } else { dof.Active = false; } } } } public class Design { public string str; public string term; public float min = float.NegativeInfinity; public float max = float.PositiveInfinity; public float unit = U.m; public float value { get { try { float value = PR.Clamp(float.Parse(str), min, max); // if clamped, update string if (value != float.Parse(str)) { if (Input.Key(Key.Return).IsJustActive()) { str = value.ToString(); } } return value * unit; } catch { return MathF.Max(0, min) * unit; } } } // public int integer {}; } public class Cursor { PR.OneEuroFilter xF = new PR.OneEuroFilter(0.001f, 0.1f); PR.OneEuroFilter yF = new PR.OneEuroFilter(0.001f, 0.1f); PR.OneEuroFilter zF = new PR.OneEuroFilter(0.001f, 0.1f); Vec3 _raw; public Vec3 raw { get => _raw; set { _raw = value; pos = new Vec3( (float)xF.Filter(raw.x, (double)Time.Stepf), (float)yF.Filter(raw.y, (double)Time.Stepf), (float)zF.Filter(raw.z, (double)Time.Stepf) ); smooth = Vec3.Lerp(smooth, pos, Time.Stepf * 6f); } } public Vec3 pos { get; private set; } public Vec3 smooth { get; private set; } } /* COMMENTS ranges 0+1 1-0 1-0+1 -0+ 0+&&- 0+||- units m cm mm t demo seperate the demos from the dofs, and make them rebindable (assigning input using reflection?) virtual shapes(scalable) -> that can be slotted physics boxes mirror mirroring vectors(line segments) is really easy easier than rendering.. actually just render twice with the material chain stereonick mentioned debug bool rendering the raw output particularly for hand tracking dofs (so Moses can better test them!) raw = 0.333f alpha ~ */ /* we have a whole inspector thing going on here but people are working on better alternatives: malek's foss social xr project which was part of the reason that i chopped off the networking parts of this project. as we have vrc for reaching larger audiences + malek's project to develop w/the fossxr community so running our own networking is needlessly redundant, and not my strong suit. refocusing this project on just prototyping and hosting our xr tools centrally to then be ported to wherever they can best be applied ^-^ the inspector is a crutch for the lack of a native spatial interface for prototyping as it's an incredibly limited and an awkward abstraction of what is happening spatially expose spatial functions and dofs allow the user to bind them to tracked inputs+ *don't need it all to be fully featured and extensible out of the gate just need a better foundation than a paper paradigm inspector 'world origin' needs to be adjustable~ otherwise the visualizations will be difficult to decipher in different contexts no names! as everything remains in it's original context text for math symbols is fine~ but don't use that as an excuse to abstract things back into text you can do vector math spatially by wrapping the living vectors with operators~ i.e av + bv = cv (-- + --) -> -- it's hard to represent this with text :< but just think of different points/lines(vectors) being encapsulated by underlying larger points/lines(vectors) with symbols or other identifiers with an output, managing to represent the underlying math within the spatial context side notes need to run it in a way where if it crashes, it doesn't take the whole app down (ask malek?) */ public class Spatial { // example, to build out from // just adding two vectors // with great interactivity and visual feedback float scale = 0.1f; float thickness => 0.01f * scale; Vec3 origin = new Vec3(0, 1, -1); float t = 1.0f; Vec3 aFrom, aTo; Vec3 a => Vec3.Lerp(aFrom, aTo, MathF.Min(t, 1f)); Vec3 bFrom, bTo; Vec3 b => Vec3.Lerp(bFrom, bTo, MathF.Min(t, 1f)); Vec3 c => a + b; public void Frame() { // origin axis Lines.Add(origin, World(new Vec3(1, 0, 0)), new Color(1, 0, 0), thickness); Lines.Add(origin, World(new Vec3(0, 1, 0)), new Color(0, 1, 0), thickness); Lines.Add(origin, World(new Vec3(0, 0, 1)), new Color(0, 0, 1), thickness); Mesh.Sphere.Draw(Material.Unlit, Matrix.TS(origin, thickness), new Color(0.5f, 0.5f, 0.5f)); Random rand = Random.Shared; if (t >= 1.3f) { aFrom = aTo; bFrom = bTo; if (rand.NextSingle() < 0.5f) { aTo = new Vec3(rand.NextSingle(), rand.NextSingle(), rand.NextSingle()) * 0.5f; } else { bTo = new Vec3(rand.NextSingle(), rand.NextSingle(), rand.NextSingle()) * 0.5f; } t = 0.0f; } t += Time.Stepf / 2f; Lines.Add(origin, World(a), new Color(1, 1, 0), thickness); Mesh.Sphere.Draw(Material.Unlit, Matrix.TS(World(a), thickness), new Color(1, 1, 0)); Lines.Add(origin, World(b), new Color(0, 1, 1), thickness); Mesh.Sphere.Draw(Material.Unlit, Matrix.TS(World(b), thickness), new Color(0, 1, 1)); Lines.Add(World(a), World(c), new Color(0, 1, 1), thickness); Lines.Add(World(b), World(c), new Color(1, 1, 0), thickness); // color between yellow and cyan using HSV Mesh.Sphere.Draw(Material.Unlit, Matrix.TS(World(c), thickness), new Color(0.5f, 1, 1)); } Vec3 World(Vec3 local) { return origin + local * scale; } // }