Skip to content

Commit 986110b

Browse files
author
lawwong
committed
Now grabbable can be stretched when there're more then 2 grabbers
Must have valid min/maxScaleOnStretch value (min < max) or it won't scale (stretch only) Must enable "multiple grabbers" option
1 parent a59d93e commit 986110b

File tree

4 files changed

+249
-35
lines changed

4 files changed

+249
-35
lines changed

Assets/HTC.UnityPlugin/ViveInputUtility/Examples/3.3DDrag/Scripts/Draggable.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ public override RigidPose grabberOrigin
5353
{
5454
var cam = eventData.pointerPressRaycast.module.eventCamera;
5555
var ray = cam.ScreenPointToRay(eventData.position);
56-
return new RigidPose(ray.origin, Quaternion.LookRotation(ray.direction, cam.transform.up));
56+
return new RigidPose(ray.origin, Quaternion.LookRotation(ray.direction, cam.transform.up)) * grabber2hit;
5757
}
5858
}
5959

60-
public override RigidPose grabOffset { get { return grabber2hit * hit2pivot; } set { hit2pivot = grabber2hit.GetInverse() * value; } }
60+
public override RigidPose grabOffset { get { return hit2pivot; } set { hit2pivot = value; } }
6161

6262
public RigidPose grabber2hit { get; set; }
6363

@@ -94,6 +94,10 @@ public float hitDistance
9494
[SerializeField]
9595
[FormerlySerializedAs("m_scrollDelta")]
9696
private float m_scrollingSpeed = 0.01f;
97+
[SerializeField]
98+
private float m_minStretchScale = 1f;
99+
[SerializeField]
100+
private float m_maxStretchScale = 1f;
97101
[FormerlySerializedAs("afterGrabbed")]
98102
[SerializeField]
99103
private UnityEventDraggable m_afterGrabbed = new UnityEventDraggable();
@@ -116,6 +120,10 @@ public float hitDistance
116120

117121
public bool unblockableGrab { get { return m_unblockableGrab; } set { m_unblockableGrab = value; } }
118122

123+
public override float minScaleOnStretch { get { return m_minStretchScale; } set { m_minStretchScale = value; } }
124+
125+
public override float maxScaleOnStretch { get { return m_maxStretchScale; } set { m_maxStretchScale = value; } }
126+
119127
public UnityEventDraggable afterGrabbed { get { return m_afterGrabbed; } }
120128

121129
public UnityEventDraggable beforeRelease { get { return m_beforeRelease; } }
@@ -189,15 +197,16 @@ public virtual void OnBeginDrag(PointerEventData eventData)
189197
}
190198
}
191199

200+
private static WaitForFixedUpdate waitForFixedUpdate = new WaitForFixedUpdate();
192201
private IEnumerator PhysicsGrabUpdate()
193202
{
194-
yield return new WaitForFixedUpdate();
203+
yield return waitForFixedUpdate;
195204

196205
while (isGrabbed)
197206
{
198207
OnGrabRigidbody();
199208

200-
yield return new WaitForFixedUpdate();
209+
yield return waitForFixedUpdate;
201210
}
202211

203212
yield break;
@@ -209,11 +218,14 @@ private IEnumerator DragUpdate()
209218

210219
while (isGrabbed)
211220
{
212-
var grabber = currentGrabber;
213-
var scrollDelta = grabber.eventData.scrollDelta * m_scrollingSpeed;
214-
if (scrollDelta != Vector2.zero)
221+
for (int i = allGrabbers.Count - 1; i >= 0; --i)
215222
{
216-
grabber.hitDistance = Mathf.Max(0f, grabber.hitDistance + scrollDelta.y);
223+
var grabber = allGrabbers.GetValueByIndex(i);
224+
var scrollDelta = grabber.eventData.scrollDelta * m_scrollingSpeed;
225+
if (scrollDelta != Vector2.zero)
226+
{
227+
grabber.hitDistance = Mathf.Max(0f, grabber.hitDistance + scrollDelta.y);
228+
}
217229
}
218230

219231
if (!moveByVelocity)

Assets/HTC.UnityPlugin/ViveInputUtility/Scripts/Misc/BasicGrabbable.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public static void Release(Grabber grabber)
8080
private bool m_allowMultipleGrabbers = true;
8181
[SerializeField]
8282
private bool m_grabOnLastEntered = false;
83+
[SerializeField]
84+
private float m_minStretchScale = 1f;
85+
[SerializeField]
86+
private float m_maxStretchScale = 1f;
8387
[FormerlySerializedAs("afterGrabbed")]
8488
[SerializeField]
8589
private UnityEventGrabbable m_afterGrabbed = new UnityEventGrabbable();
@@ -98,6 +102,10 @@ public static void Release(Grabber grabber)
98102

99103
public bool grabOnLastEntered { get { return m_grabOnLastEntered; } set { m_grabOnLastEntered = value; } }
100104

105+
public override float minScaleOnStretch { get { return m_minStretchScale; } set { m_minStretchScale = value; } }
106+
107+
public override float maxScaleOnStretch { get { return m_maxStretchScale; } set { m_maxStretchScale = value; } }
108+
101109
public UnityEventGrabbable afterGrabbed { get { return m_afterGrabbed; } }
102110

103111
public UnityEventGrabbable beforeRelease { get { return m_beforeRelease; } }

Assets/HTC.UnityPlugin/ViveInputUtility/Scripts/Misc/GrabbableBase.cs

Lines changed: 213 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ private struct PoseSample
4040
public abstract bool isGrabbed { get; }
4141
public abstract bool isChangingGrabber { get; }
4242
public abstract GrabberBase currentGrabberBase { get; }
43+
public abstract float minScaleOnStretch { get; set; }
44+
public abstract float maxScaleOnStretch { get; set; }
4345
public Rigidbody grabRigidbody { get; protected set; }
4446

4547
private Queue<PoseSample> m_poseSamples = new Queue<PoseSample>();
@@ -63,32 +65,6 @@ protected void RecordLatestPosesForDrop(float currentTime, float recordLength)
6365
});
6466
}
6567

66-
protected virtual void OnGrabRigidbody()
67-
{
68-
var currentGrabber = currentGrabberBase;
69-
var targetPose = currentGrabber.grabberOrigin * currentGrabber.grabOffset;
70-
ModifyPose(ref targetPose, true);
71-
72-
RigidPose.SetRigidbodyVelocity(grabRigidbody, grabRigidbody.position, targetPose.pos, followingDuration);
73-
RigidPose.SetRigidbodyAngularVelocity(grabRigidbody, grabRigidbody.rotation, targetPose.rot, followingDuration, overrideMaxAngularVelocity);
74-
}
75-
76-
protected virtual void OnGrabTransform()
77-
{
78-
var currentGrabber = currentGrabberBase;
79-
var targetPose = currentGrabber.grabberOrigin * currentGrabber.grabOffset;
80-
ModifyPose(ref targetPose, true);
81-
82-
if (grabRigidbody != null)
83-
{
84-
grabRigidbody.velocity = Vector3.zero;
85-
grabRigidbody.angularVelocity = Vector3.zero;
86-
}
87-
88-
transform.position = targetPose.pos;
89-
transform.rotation = targetPose.rot;
90-
}
91-
9268
protected virtual void DoDrop()
9369
{
9470
if (grabRigidbody != null && !grabRigidbody.isKinematic && m_poseSamples.Count > 0)
@@ -102,6 +78,82 @@ protected virtual void DoDrop()
10278
m_poseSamples.Clear();
10379
}
10480
}
81+
82+
protected abstract void OnGrabRigidbody();
83+
84+
protected abstract void OnGrabTransform();
85+
86+
public struct StretchAnchors
87+
{
88+
// A1: first anchor
89+
// A2: second anchor
90+
// C: strethable center
91+
// P: closest point on line A1-A2 away from C
92+
private Vector3 originScale;
93+
private float A1A2Len;
94+
private float A1PLen;
95+
private float PCLen;
96+
private Quaternion originRotOffset;
97+
private Quaternion lastRot;
98+
99+
public void SetupStartingAnchors(Vector3 anchor1, Vector3 anchor2, Vector3 originPos, Quaternion originRot, Vector3 originScale)
100+
{
101+
// FIXME: what if anchor1 == anchor2?
102+
this.originScale = originScale;
103+
104+
var vectorS1S2 = anchor2 - anchor1;
105+
A1A2Len = Vector3.Magnitude(vectorS1S2);
106+
107+
var vectorS1C = originPos - anchor1;
108+
A1PLen = Vector3.Dot(vectorS1C, vectorS1S2) / A1A2Len;
109+
PCLen = Mathf.Sqrt(vectorS1C.sqrMagnitude - A1PLen * A1PLen);
110+
111+
var normal = Vector3.Cross(vectorS1S2, vectorS1C);
112+
var rot = Quaternion.LookRotation(vectorS1S2, normal);
113+
originRotOffset = Quaternion.Inverse(rot) * originRot;
114+
115+
lastRot = rot;
116+
}
117+
118+
public void UpdateAnchors(Vector3 anchor1, Vector3 anchor2, out Vector3 newPos, out Quaternion newRot, out Vector3 newScale, float minScale, float maxScale)
119+
{
120+
// determin scale ratio
121+
var vectorS1S2 = anchor2 - anchor1;
122+
var vectorS1S2Len = vectorS1S2.magnitude;
123+
var vectorS1S2Norm = vectorS1S2 / vectorS1S2Len;
124+
var posScale = vectorS1S2Len / A1A2Len;
125+
126+
lastRot = Quaternion.FromToRotation(lastRot * Vector3.forward, vectorS1S2Norm) * lastRot;
127+
var tangent = lastRot * Vector3.right;
128+
129+
var transformScale = 1f;
130+
minScale = Mathf.Abs(minScale);
131+
maxScale = Mathf.Abs(maxScale);
132+
if (minScale < maxScale)
133+
{
134+
// FIXME: what if originScale have zero value?
135+
var originScaleAbs = new Vector3(Mathf.Abs(originScale.x), Mathf.Abs(originScale.y), Mathf.Abs(originScale.z));
136+
if (originScaleAbs.x == originScaleAbs.y && originScaleAbs.y == originScaleAbs.z)
137+
{
138+
transformScale = Mathf.Clamp(posScale, minScale / originScaleAbs.x, maxScale / originScaleAbs.x);
139+
}
140+
else
141+
{
142+
// when scale is irregular, clamp the scale factor to make sure no scale value is out of min/max range
143+
var minScaleAxis = Mathf.Min(originScaleAbs.x, originScaleAbs.y, originScaleAbs.z);
144+
var maxScaleAxis = Mathf.Max(originScaleAbs.x, originScaleAbs.y, originScaleAbs.z);
145+
if (minScaleAxis / maxScaleAxis >= minScale / maxScale)
146+
{
147+
transformScale = Mathf.Clamp(posScale, minScale / minScaleAxis, maxScale / maxScaleAxis);
148+
}
149+
}
150+
}
151+
152+
newPos = anchor1 + vectorS1S2Norm * A1PLen * posScale + tangent * PCLen * transformScale;
153+
newRot = lastRot * originRotOffset;
154+
newScale = originScale * transformScale;
155+
}
156+
}
105157
}
106158

107159
public abstract class GrabbableBase<TEventData, TGrabber> : GrabbableBase where TGrabber : GrabberBase<TEventData> where TEventData : BaseEventData
@@ -119,6 +171,10 @@ public abstract class GrabbableBase<TEventData, TGrabber> : GrabbableBase where
119171
public event Action beforeGrabberReleased; // get grabber that about to release here
120172
public event Action onGrabberDrop; // manually change drop velocity here
121173

174+
private TGrabber anchorGabber1;
175+
private TGrabber anchorGabber2;
176+
private StretchAnchors stretchAnchors;
177+
122178
protected bool IsGrabberExists(TEventData eventData)
123179
{
124180
return m_grabbers.ContainsKey(eventData);
@@ -129,6 +185,35 @@ protected bool TryGetExistsGrabber(TEventData eventData, out TGrabber grabber)
129185
return m_grabbers.TryGetValue(eventData, out grabber);
130186
}
131187

188+
protected bool TryGetValidAnchors(out TGrabber grabber1, out TGrabber grabber2, out Vector3 pose1, out Vector3 pose2)
189+
{
190+
var i = m_grabbers.Count - 1;
191+
if (i >= 1)
192+
{
193+
var g1 = m_grabbers.GetValueByIndex(i);
194+
var p1 = g1.grabberOrigin.pos;
195+
for (--i; i >= 0; --i)
196+
{
197+
var g2 = m_grabbers.GetValueByIndex(i);
198+
var p2 = g2.grabberOrigin.pos;
199+
if (!Mathf.Approximately((p2 - p1).magnitude, 0f))
200+
{
201+
grabber1 = g1;
202+
grabber2 = g2;
203+
pose1 = p1;
204+
pose2 = p2;
205+
return true;
206+
}
207+
}
208+
}
209+
210+
grabber1 = default(TGrabber);
211+
grabber2 = default(TGrabber);
212+
pose1 = default(Vector3);
213+
pose2 = default(Vector3);
214+
return false;
215+
}
216+
132217
protected abstract TGrabber CreateGrabber(TEventData eventData);
133218

134219
protected abstract void DestoryGrabber(TGrabber grabber);
@@ -146,6 +231,18 @@ protected bool AddGrabber(TEventData eventData)
146231

147232
if (isGrabbed) { beforeGrabberReleased(); }
148233
m_grabbers.Add(eventData, newGrabber);
234+
235+
Vector3 p1, p2;
236+
if (TryGetValidAnchors(out anchorGabber1, out anchorGabber2, out p1, out p2))
237+
{
238+
stretchAnchors.SetupStartingAnchors(
239+
p1,
240+
p2,
241+
transform.position,
242+
transform.rotation,
243+
transform.localScale);
244+
}
245+
149246
afterGrabberGrabbed();
150247
return true;
151248
}
@@ -177,6 +274,21 @@ protected bool RemoveGrabber(TEventData eventData)
177274
finally { ExitGrabberChangingLock(); }
178275
}
179276

277+
Vector3 p1, p2;
278+
if (TryGetValidAnchors(out anchorGabber1, out anchorGabber2, out p1, out p2))
279+
{
280+
stretchAnchors.SetupStartingAnchors(
281+
p1,
282+
p2,
283+
transform.position,
284+
transform.rotation,
285+
transform.localScale);
286+
}
287+
else if (m_grabbers.Count > 0)
288+
{
289+
currentGrabber.grabOffset = currentGrabber.grabberOrigin.GetInverse() * new RigidPose(transform);
290+
}
291+
180292
return true;
181293
}
182294

@@ -216,9 +328,83 @@ private void ExitGrabberChangingLock()
216328
{
217329
m_grabberChangingLock = false;
218330
}
331+
332+
protected override void OnGrabRigidbody()
333+
{
334+
if (anchorGabber1 != null && anchorGabber2 != null)
335+
{
336+
Vector3 pos;
337+
Quaternion rot;
338+
Vector3 scale;
339+
stretchAnchors.UpdateAnchors(
340+
anchorGabber1.grabberOrigin.pos,
341+
anchorGabber2.grabberOrigin.pos,
342+
out pos,
343+
out rot,
344+
out scale,
345+
minScaleOnStretch,
346+
maxScaleOnStretch);
347+
348+
GrabRigidbodyToPose(new RigidPose(pos, rot));
349+
transform.localScale = scale;
350+
}
351+
else
352+
{
353+
var currentGrabber = currentGrabberBase;
354+
GrabRigidbodyToPose(currentGrabber.grabberOrigin * currentGrabber.grabOffset);
355+
}
356+
}
357+
358+
protected override void OnGrabTransform()
359+
{
360+
if (anchorGabber1 != null && anchorGabber2 != null)
361+
{
362+
Vector3 pos;
363+
Quaternion rot;
364+
Vector3 scale;
365+
stretchAnchors.UpdateAnchors(
366+
anchorGabber1.grabberOrigin.pos,
367+
anchorGabber2.grabberOrigin.pos,
368+
out pos,
369+
out rot,
370+
out scale,
371+
minScaleOnStretch,
372+
maxScaleOnStretch);
373+
374+
GrabTransformToPose(new RigidPose(pos, rot));
375+
transform.localScale = scale;
376+
}
377+
else
378+
{
379+
var currentGrabber = currentGrabberBase;
380+
GrabTransformToPose(currentGrabber.grabberOrigin * currentGrabber.grabOffset);
381+
}
382+
}
383+
384+
protected void GrabRigidbodyToPose(RigidPose targetPose)
385+
{
386+
ModifyPose(ref targetPose, true);
387+
388+
RigidPose.SetRigidbodyVelocity(grabRigidbody, grabRigidbody.position, targetPose.pos, followingDuration);
389+
RigidPose.SetRigidbodyAngularVelocity(grabRigidbody, grabRigidbody.rotation, targetPose.rot, followingDuration, overrideMaxAngularVelocity);
390+
}
391+
392+
protected void GrabTransformToPose(RigidPose targetPose)
393+
{
394+
ModifyPose(ref targetPose, true);
395+
396+
if (grabRigidbody != null)
397+
{
398+
grabRigidbody.velocity = Vector3.zero;
399+
grabRigidbody.angularVelocity = Vector3.zero;
400+
}
401+
402+
transform.position = targetPose.pos;
403+
transform.rotation = targetPose.rot;
404+
}
219405
}
220406

221-
[Obsolete("Use GrabbableBase.Generic<TGrabber> instead")]
407+
[Obsolete("Use GrabbableBase<TEventData, TGrabber> instead")]
222408
public abstract class GrabbableBase<TGrabber> : BasePoseTracker where TGrabber : class, GrabbableBase<TGrabber>.IGrabber
223409
{
224410
public const float MIN_FOLLOWING_DURATION = 0.02f;

0 commit comments

Comments
 (0)