235 lines
No EOL
6.3 KiB
C#
235 lines
No EOL
6.3 KiB
C#
namespace Oriels;
|
|
|
|
class Trackballer : dof {
|
|
public bool Active { get; set; }
|
|
|
|
// input
|
|
public Handed handed = Handed.Left;
|
|
|
|
// data
|
|
public Btn btnPull = new Btn();
|
|
public Btn btnPush = new Btn();
|
|
bool onTheBall;
|
|
public Quat ori = Quat.Identity;
|
|
|
|
Quat momentum = Quat.Identity;
|
|
Quat delta = Quat.Identity;
|
|
Matrix pad = Matrix.Identity;
|
|
Matrix oldPad = Matrix.Identity;
|
|
int lastClosestIndex;
|
|
|
|
PullRequest.Vec3PID compliance = new PullRequest.Vec3PID();
|
|
|
|
Model model = Model.FromFile("thumb_pad.glb");
|
|
Mesh mesh;
|
|
|
|
public void Init() {
|
|
mesh = model.GetMesh("Pad");
|
|
}
|
|
|
|
public void Frame() {
|
|
Hand hand = Input.Hand(handed);
|
|
if (hand.tracked.IsActive() && !hand.tracked.IsJustActive()) {
|
|
UpdateMomentum(hand);
|
|
}
|
|
|
|
Quat newOri = (momentum * ori).Normalized;
|
|
if (new Vec3(newOri.x, newOri.y, newOri.z).LengthSq > 0) {
|
|
ori = newOri;
|
|
}
|
|
}
|
|
|
|
public void UpdateMomentum(Hand hand) {
|
|
// Thumb pad
|
|
HandJoint thumbJoint = hand.Get(FingerId.Thumb, JointId.Tip);
|
|
oldPad = pad;
|
|
pad = Matrix.TRS(
|
|
thumbJoint.position,
|
|
thumbJoint.orientation,
|
|
new Vec3(handed == Handed.Left ? -1f : 1f, 1f, 1f) * 0.1666f
|
|
);
|
|
mesh.Draw(Mono.inst.matHoloframe, pad, new Color(0, 1, 1));
|
|
|
|
// Ball anchor
|
|
HandJoint ballJoint = hand.Get(FingerId.Index, JointId.KnuckleMajor);
|
|
Vec3 anchorOrigin = ballJoint.position + hand.palm.orientation * new Vec3(
|
|
aX.value * (handed == Handed.Left ? -1 : 1),
|
|
aY.value,
|
|
aZ.value
|
|
);
|
|
Vec3 anchorPos = anchorOrigin + compliance.Update(
|
|
Vec3.Zero,
|
|
onTheBall ? 1f : 10f,
|
|
onTheBall ? 0.5f : 5f // 10x less integral when on the ball?
|
|
);
|
|
// compliance;
|
|
// compliance = Vec3.Lerp(compliance, Vec3.Zero, Time.Stepf * 10f);
|
|
Matrix anchor = Matrix.TR(anchorPos, hand.palm.orientation);
|
|
Matrix anchorInv = anchor.Inverse;
|
|
|
|
// Traction delta mesh matrix
|
|
Vertex[] verts = mesh.GetVerts();
|
|
float oldClosest = (
|
|
pad.Transform(verts[lastClosestIndex].pos) - anchorPos
|
|
).LengthSq;
|
|
float closest = 100000f;
|
|
int closestIndex = lastClosestIndex;
|
|
for (int i = 0; i < verts.Length; i++) {
|
|
Vec3 v = pad.Transform(verts[i].pos);
|
|
float d = (v - anchorPos).LengthSq;
|
|
if (d < closest && d < oldClosest - 0.00002f) {
|
|
closest = d;
|
|
closestIndex = i;
|
|
}
|
|
}
|
|
lastClosestIndex = closestIndex;
|
|
|
|
Vec3 point = anchorInv.Transform(
|
|
pad.Transform(verts[closestIndex].pos)
|
|
);
|
|
Vec3 oldPoint = anchorInv.Transform(
|
|
oldPad.Transform(verts[closestIndex].pos)
|
|
);
|
|
|
|
|
|
// Pull
|
|
float pull = point.Length / pullClick.value;
|
|
btnPull.Frame(pull > 1f, pull > 0.333f); // magic sticky var
|
|
|
|
float pullScalar = btnPull.held ? MathF.Max((pull - 0.333f) / 0.666f, 0) : MathF.Max(1 - pull, 0);
|
|
Mesh.Sphere.Draw(Mono.inst.matHoloframe,
|
|
Matrix.TRS(anchorPos, thumbJoint.orientation, pullScalar * radius.value),
|
|
new Color(0, 1, 1) * (btnPull.held ? 1f : 0.0666f)
|
|
);
|
|
Lines.Add(
|
|
anchor.Transform(point), anchorPos,
|
|
new Color(0, 1, 1), 1f * U.mm
|
|
);
|
|
Mesh.Sphere.Draw(Mono.inst.matHoloframe,
|
|
Matrix.TRS(anchor.Transform(point), thumbJoint.orientation, 2f * U.mm),
|
|
new Color(0, 1, 1)
|
|
);
|
|
|
|
|
|
// Push
|
|
float push = compliance.value.Length / pushClick.value;
|
|
btnPush.Frame(push > 1f, push > 0.333f); // magic sticky var
|
|
|
|
float pushScalar = btnPush.held ? MathF.Max((MathF.Min(push, 1f) - 0.333f) / 0.666f, 0) : MathF.Max(1 - push, 0);
|
|
Mesh.Sphere.Draw(Mono.inst.matHoloframe,
|
|
Matrix.TRS(anchorPos, ori, (radius.value * 2) * pushScalar),
|
|
new Color(1, 0, 0) * (btnPush.held ? 1f : 0.2f)
|
|
);
|
|
|
|
|
|
onTheBall = point.Length < radius.value;
|
|
if (onTheBall) {
|
|
delta = Quat.Delta(
|
|
oldPoint.Normalized,
|
|
point.Normalized
|
|
).Relative(hand.palm.orientation);
|
|
|
|
momentum = Quat.Slerp(momentum, delta, Time.Stepf * 10f);
|
|
|
|
Vec3 contact = point.Normalized * radius.value;
|
|
Vec3 offset = point - contact;
|
|
|
|
// no z axis
|
|
// offset.z = 0;
|
|
offset = hand.palm.orientation * offset;
|
|
compliance.value += offset * compliant.value;
|
|
compliance.integral = Vec3.Zero;
|
|
} else {
|
|
PullRequest.ToAxisAngle(momentum, out Vec3 axis, out float angle);
|
|
if (angle < stop.value) {
|
|
momentum = Quat.Slerp(momentum, Quat.Identity, Time.Stepf * 10f);
|
|
}
|
|
}
|
|
|
|
// Draw ball result
|
|
Mesh.Sphere.Draw(
|
|
Mono.inst.matHoloframe,
|
|
Matrix.TRS(anchorPos, ori, radius.value * 2),
|
|
new Color(0.8f, 0, 0)
|
|
);
|
|
}
|
|
|
|
// design
|
|
public Design radius = new Design { str="2", term=">0cm", unit=U.cm, min=0.5f };
|
|
public Design pullClick = new Design { str="6.66", term=">0cm", unit=U.cm, min=0.1f };
|
|
public Design pushClick = new Design { str="1.5", term=">0cm", unit=U.cm, min=0.1f };
|
|
public Design aX = new Design { str=" 1.0", term="-0+cm", unit=U.cm, min=-10f, max=10f };
|
|
public Design aY = new Design { str=" 2.0", term="-0+cm", unit=U.cm, min=-10f, max=10f };
|
|
public Design aZ = new Design { str="-4.0", term="-0+cm", unit=U.cm, min=-10f, max=10f };
|
|
public Design compliant = new Design { str="0.2", term="0+1t", min=0, max=1 };
|
|
public Design stop = new Design { str="0.05", term="0+", min=0 };
|
|
|
|
|
|
Vec3 cursorPos = new Vec3(0f, 0f, 0f);
|
|
public void Demo() {
|
|
Matrix panel = Matrix.TR(
|
|
new Vec3(
|
|
1.47f,
|
|
1.145f, // - World.BoundsPose.position.y,
|
|
1.08f),
|
|
Quat.FromAngles(-3.2f, 90f, 0)
|
|
);
|
|
|
|
float width = 52 * U.cm;
|
|
float height = 29f * U.cm;
|
|
Mesh.Quad.Draw(
|
|
Mono.inst.matHoloframe,
|
|
Matrix.S(new Vec3(width, height, 1)) * panel,
|
|
new Color(1, 1, 1)
|
|
);
|
|
|
|
|
|
cursorPos.x = PullRequest.Clamp(
|
|
cursorPos.x + (momentum * Vec3.Right).z * 0.1f,
|
|
width / -2f,
|
|
width / 2f
|
|
);
|
|
cursorPos.y = PullRequest.Clamp(
|
|
cursorPos.y + (momentum * Vec3.Right).y * -0.1f,
|
|
height / -2f,
|
|
height / 2f
|
|
);
|
|
|
|
Mesh.Quad.Draw(
|
|
Material.Unlit,
|
|
Matrix.TS(cursorPos, 1 * U.cm) * panel,
|
|
new Color(1, 1, 1)
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
COMMENTS
|
|
|
|
press in is not reliable
|
|
|
|
full thumb mesh and contact point lerp
|
|
|
|
pullbtn mouse click scale anim
|
|
|
|
thumb inside instead
|
|
so the natural thumb position doesn't affect the trackballer
|
|
|
|
sens for cursor
|
|
|
|
be able to stop it easily without jittering
|
|
use another fingers flexion to be able to brake the trackballer
|
|
|
|
finger gun interaction (on the other hand) to move the trackballer
|
|
|
|
distinct interactions to account for (relative to palm orientation)
|
|
y swipe (move x)
|
|
z swipe (move y)
|
|
x spin (scroll)
|
|
|
|
how reliable is the provided palm orientation?
|
|
|
|
more boolean visual and audio feeback
|
|
|
|
*/ |