TaiZhouCangChu_VRanime/Assets/HTC.UnityPlugin/ViveInputUtility/Scripts/Misc/StickyGrabbable.cs

336 lines
13 KiB
C#

//========= Copyright 2016-2023, HTC Corporation. All rights reserved. ===========
#pragma warning disable 0649
using HTC.UnityPlugin.ColliderEvent;
using HTC.UnityPlugin.LiteCoroutineSystem;
using HTC.UnityPlugin.Utility;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
using GrabberPool = HTC.UnityPlugin.Utility.ObjectPool<HTC.UnityPlugin.Vive.StickyGrabbable.Grabber>;
namespace HTC.UnityPlugin.Vive
{
[AddComponentMenu("VIU/Object Grabber/Sticky Grabbable", 1)]
public class StickyGrabbable : GrabbableBase<ColliderButtonEventData, StickyGrabbable.Grabber>
, IColliderEventPressDownHandler
{
public class Grabber : GrabberBase<ColliderButtonEventData>
{
private static GrabberPool m_pool;
private ColliderButtonEventData m_eventData;
public static Grabber Get(ColliderButtonEventData eventData)
{
if (m_pool == null)
{
m_pool = new GrabberPool(() => new Grabber());
}
var grabber = m_pool.Get();
grabber.m_eventData = eventData;
return grabber;
}
public static void Release(Grabber grabber)
{
grabber.m_eventData = null;
m_pool.Release(grabber);
}
public override ColliderButtonEventData eventData { get { return m_eventData; } }
public override RigidPose grabberOrigin { get { return new RigidPose(eventData.eventCaster.transform); } }
public override RigidPose grabOffset { get; set; }
[Obsolete("This property nolonger used")]
public int grabFrame { get; set; }
[Obsolete("This property nolonger used")]
public bool redeayForRelease { get; set; }
}
[Serializable]
public class UnityEventGrabbable : UnityEvent<StickyGrabbable> { }
private struct ButtonProcessedState
{
public bool isGrabbing;
public int processedFrame;
}
// NOTE:
// We can't make sure the excution order of OnColliderEventPressDown() and Update()
// Hence log grabFrame to avoid redundant release in Update()
// and redeayForRelease flag(remove grabber from m_eventGrabberSet one frame later) to avoid redundant grabbing in OnColliderEventPressDown()
private IndexedTable<ColliderButtonEventData, ButtonProcessedState> m_buttonProcessedFrame = new IndexedTable<ColliderButtonEventData, ButtonProcessedState>();
private LiteCoroutine m_updateCoroutine;
private LiteCoroutine m_physicsCoroutine;
public bool alignPosition;
public bool alignRotation;
public Vector3 alignPositionOffset;
public Vector3 alignRotationOffset;
[Range(MIN_FOLLOWING_DURATION, MAX_FOLLOWING_DURATION)]
[FormerlySerializedAs("followingDuration")]
[SerializeField]
private float m_followingDuration = DEFAULT_FOLLOWING_DURATION;
[FormerlySerializedAs("overrideMaxAngularVelocity")]
[SerializeField]
private bool m_overrideMaxAngularVelocity = true;
[FormerlySerializedAs("unblockableGrab")]
[SerializeField]
private bool m_unblockableGrab = true;
[SerializeField]
[FlagsFromEnum(typeof(ControllerButton))]
private ulong m_primaryGrabButton = 0ul;
[SerializeField]
[FlagsFromEnum(typeof(ColliderButtonEventData.InputButton))]
private uint m_secondaryGrabButton = 1u << (int)ColliderButtonEventData.InputButton.Trigger;
[SerializeField]
[HideInInspector]
private ColliderButtonEventData.InputButton m_grabButton = ColliderButtonEventData.InputButton.Trigger;
[SerializeField]
private bool m_toggleToRelease = true;
[FormerlySerializedAs("m_multipleGrabbers")]
[SerializeField]
private bool m_allowMultipleGrabbers = false;
[SerializeField]
private bool m_grabOnLastEntered = false;
[SerializeField]
private float m_minStretchScale = 1f;
[SerializeField]
private float m_maxStretchScale = 1f;
[FormerlySerializedAs("afterGrabbed")]
[SerializeField]
private UnityEventGrabbable m_afterGrabbed = new UnityEventGrabbable();
[FormerlySerializedAs("beforeRelease")]
[SerializeField]
private UnityEventGrabbable m_beforeRelease = new UnityEventGrabbable();
[FormerlySerializedAs("onDrop")]
[SerializeField]
private UnityEventGrabbable m_onDrop = new UnityEventGrabbable(); // change rigidbody drop velocity here
public override float followingDuration { get { return m_followingDuration; } set { m_followingDuration = Mathf.Clamp(value, MIN_FOLLOWING_DURATION, MAX_FOLLOWING_DURATION); } }
public override bool overrideMaxAngularVelocity { get { return m_overrideMaxAngularVelocity; } set { overrideMaxAngularVelocity = value; } }
public bool unblockableGrab { get { return m_unblockableGrab; } set { m_unblockableGrab = value; } }
public bool toggleToRelease { get { return m_toggleToRelease; } set { m_toggleToRelease = value; } }
public bool grabOnLastEntered { get { return m_grabOnLastEntered; } set { m_grabOnLastEntered = value; } }
public override float minScaleOnStretch { get { return m_minStretchScale; } set { m_minStretchScale = value; } }
public override float maxScaleOnStretch { get { return m_maxStretchScale; } set { m_maxStretchScale = value; } }
public UnityEventGrabbable afterGrabbed { get { return m_afterGrabbed; } }
public UnityEventGrabbable beforeRelease { get { return m_beforeRelease; } }
public UnityEventGrabbable onDrop { get { return m_onDrop; } }
public ColliderButtonEventData grabbedEvent { get { return isGrabbed ? currentGrabber.eventData : null; } }
public ulong primaryGrabButton { get { return m_primaryGrabButton; } set { m_primaryGrabButton = value; } }
public uint secondaryGrabButton { get { return m_secondaryGrabButton; } set { m_secondaryGrabButton = value; } }
public bool allowMultipleGrabbers { get { return m_allowMultipleGrabbers; } set { allowMultipleGrabbers = value; } }
[Obsolete("Use IsSecondaryGrabButtonOn and SetSecondaryGrabButton instead")]
public ColliderButtonEventData.InputButton grabButton
{
get
{
for (uint btn = 0u, btns = m_secondaryGrabButton; btns > 0u; btns >>= 1, ++btn)
{
if ((btns & 1u) > 0u) { return (ColliderButtonEventData.InputButton)btn; }
}
return ColliderButtonEventData.InputButton.None;
}
set { m_secondaryGrabButton = 1u << (int)value; }
}
private bool moveByVelocity { get { return !unblockableGrab && grabRigidbody != null && !grabRigidbody.isKinematic; } }
public bool IsPrimeryGrabButtonOn(ControllerButton btn) { return EnumUtils.GetFlag(m_primaryGrabButton, (int)btn); }
public void SetPrimeryGrabButton(ControllerButton btn, bool isOn = true) { EnumUtils.SetFlag(ref m_primaryGrabButton, (int)btn, isOn); }
public void ClearPrimeryGrabButton() { m_primaryGrabButton = 0ul; }
public bool IsSecondaryGrabButtonOn(ColliderButtonEventData.InputButton btn) { return EnumUtils.GetFlag(m_secondaryGrabButton, (int)btn); }
public void SetSecondaryGrabButton(ColliderButtonEventData.InputButton btn, bool isOn = true) { EnumUtils.SetFlag(ref m_secondaryGrabButton, (int)btn, isOn); }
public void ClearSecondaryGrabButton() { m_secondaryGrabButton = 0u; }
[Obsolete("Use grabRigidbody instead")]
public Rigidbody rigid { get { return grabRigidbody; } set { grabRigidbody = value; } }
#if UNITY_EDITOR
protected virtual void OnValidate() { RestoreObsoleteGrabButton(); }
protected virtual void Reset() { m_grabOnLastEntered = true; }
#endif
private void RestoreObsoleteGrabButton()
{
if (m_grabButton == ColliderButtonEventData.InputButton.Trigger) { return; }
ClearSecondaryGrabButton();
SetSecondaryGrabButton(m_grabButton, true);
m_grabButton = ColliderButtonEventData.InputButton.Trigger;
}
protected override void Awake()
{
base.Awake();
RestoreObsoleteGrabButton();
afterGrabberGrabbed += () => m_afterGrabbed.Invoke(this);
beforeGrabberReleased += () => m_beforeRelease.Invoke(this);
onGrabberDrop += () => m_onDrop.Invoke(this);
}
protected virtual void OnDisable() { ForceRelease(); }
protected override Grabber CreateGrabber(ColliderButtonEventData eventData)
{
var grabber = Grabber.Get(eventData);
var offset = RigidPose.FromToPose(grabber.grabberOrigin, new RigidPose(transform));
if (alignPosition) { offset.pos = alignPositionOffset; }
if (alignRotation) { offset.rot = Quaternion.Euler(alignRotationOffset); }
grabber.grabOffset = offset;
return grabber;
}
protected override void DestoryGrabber(Grabber grabber)
{
Grabber.Release(grabber);
}
protected bool IsValidGrabButton(ColliderButtonEventData eventData)
{
if (m_primaryGrabButton > 0ul)
{
ViveColliderButtonEventData viveEventData;
if (eventData.TryGetViveButtonEventData(out viveEventData) && IsPrimeryGrabButtonOn(viveEventData.viveButton)) { return true; }
}
return m_secondaryGrabButton > 0u && IsSecondaryGrabButtonOn(eventData.button);
}
public virtual void OnColliderEventPressDown(ColliderButtonEventData eventData)
{
if (!IsValidGrabButton(eventData)) { return; }
Grabber grabber;
if (TryGetExistsGrabber(eventData, out grabber)) { return; }
var currentFrame = Time.frameCount;
ButtonProcessedState pState;
if (m_buttonProcessedFrame.TryGetValue(eventData, out pState))
{
// skip if button was just processed for release
if (pState.processedFrame == currentFrame)
{
Debug.Assert(!pState.isGrabbing);
return;
}
}
if (!m_allowMultipleGrabbers) { ClearGrabbers(); }
if (m_grabOnLastEntered && !eventData.eventCaster.lastEnteredCollider.transform.IsChildOf(transform)) { return; }
if (AddGrabber(eventData))
{
m_buttonProcessedFrame[eventData] = new ButtonProcessedState()
{
isGrabbing = true,
processedFrame = currentFrame
};
if (m_updateCoroutine.IsNullOrDone())
{
LiteCoroutine.StartCoroutine(ref m_updateCoroutine, GrabUpdate(), false);
if (moveByVelocity)
{
LiteCoroutine.StartCoroutine(ref m_physicsCoroutine, PhysicsGrabUpdate(), false);
}
}
}
}
private IEnumerator PhysicsGrabUpdate()
{
yield return new WaitForFixedUpdate();
while (isGrabbed)
{
OnGrabRigidbody();
yield return new WaitForFixedUpdate();
}
yield break;
}
private IEnumerator GrabUpdate()
{
yield return null;
while (isGrabbed)
{
if (!moveByVelocity)
{
RecordLatestPosesForDrop(Time.time, 0.05f);
OnGrabTransform();
}
if (toggleToRelease && m_buttonProcessedFrame.Count > 0)
{
var currentFrame = Time.frameCount;
for (int i = m_buttonProcessedFrame.Count - 1; i >= 0; --i)
{
var pState = m_buttonProcessedFrame.GetValueByIndex(i);
// skip if button was just processed for grab
if (pState.processedFrame == currentFrame)
{
Debug.Assert(pState.isGrabbing);
continue;
}
if (!pState.isGrabbing)
{
m_buttonProcessedFrame.RemoveAt(i);
}
else
{
var eventData = m_buttonProcessedFrame.GetKeyByIndex(i);
if (!eventData.GetPressDown()) { continue; }
if (RemoveGrabber(eventData))
{
m_buttonProcessedFrame.SetValueByIndex(i, new ButtonProcessedState()
{
isGrabbing = false,
processedFrame = currentFrame,
});
}
}
}
}
yield return null;
}
}
}
}