using HurricaneVR.Framework.Core; using HurricaneVR.Framework.Core.Grabbers; using HurricaneVR.Framework.Core.ScriptableObjects; using HurricaneVR.Framework.Core.Utils; using HurricaneVR.Framework.Shared; using UnityEngine; using UnityEngine.Serialization; namespace HurricaneVR.Framework.Components { /// /// Simple component to help setup a joint constrained on one axis. With bonus rotation friction option when held. /// [RequireComponent(typeof(Rigidbody))] public class HVRPhysicsDial : MonoBehaviour { [Header("Settings")] [Tooltip("Local axis of rotation")] public HVRAxis Axis; [Tooltip("Rigidbody to connect the joint to")] public Rigidbody ConnectedBody; [Tooltip("If true the angular velocity will be zero'd out on release.")] public bool StopOnRelease = true; [Tooltip("Defaults to true to prevent rotation when not held")] public bool DisableGravity = true; [Header("Joint Limits")] public bool LimitRotation; [Tooltip("Minimum Angle about the axis of rotation")] public float MinAngle; [Tooltip("Maximum rotation about the axis of rotation")] public float MaxAngle; [Header("Joint Settings")] [Tooltip("Angular Damper when the dial is grabbed")] public float GrabbedDamper = 3; [Tooltip("Angular Damper when the dial is not grabbed")] public float Damper = 3; [Tooltip("Optional spring value of the joint to return to starting rotation if desired")] public float Spring; [Header("Editor")] [SerializeField] protected Quaternion JointStartRotation; [Header("Debugging Tools")] public float TargetAngularVelocity = 0f; public Rigidbody Rigidbody { get; private set; } public HVRGrabbable Grabbable { get; private set; } public ConfigurableJoint Joint { get; set; } protected virtual void Awake() { Rigidbody = this.GetRigidbody(); Rigidbody.useGravity = !DisableGravity; Grabbable = GetComponent(); if (Grabbable) { Grabbable.HandGrabbed.AddListener(OnDialGrabbed); Grabbable.HandReleased.AddListener(OnDialReleased); } FixAngle(ref MinAngle, ref MaxAngle); SetupJoint(); AfterJointCreated(Joint); } protected virtual void Start() { } protected virtual void Update() { if (TargetAngularVelocity > 0f || TargetAngularVelocity < 0f) Joint.targetAngularVelocity = new Vector3(TargetAngularVelocity, 0f, 0f); } protected virtual void OnDialReleased(HVRHandGrabber arg0, HVRGrabbable arg1) { Joint.SetAngularXDrive(Spring, Damper, 10000f); if (StopOnRelease) { Rigidbody.angularVelocity = Vector3.zero; } } protected virtual void OnDialGrabbed(HVRHandGrabber arg0, HVRGrabbable arg1) { Joint.SetAngularXDrive(0f, GrabbedDamper, 10000f); } protected virtual void SetupJoint() { var currentRotation = transform.localRotation; transform.localRotation = JointStartRotation; Joint = gameObject.AddComponent(); Joint.connectedBody = ConnectedBody; Joint.LockLinearMotion(); Joint.LockAngularYMotion(); Joint.LockAngularZMotion(); Joint.anchor = Vector3.zero; Joint.axis = Axis.GetVector(); if (LimitRotation) { ResetLimits(); } else { Joint.angularXMotion = ConfigurableJointMotion.Free; } Joint.secondaryAxis = Joint.axis.OrthogonalVector(); Joint.SetAngularXDrive(Spring, Damper, 10000f); Joint.projectionAngle = 1f; Joint.projectionDistance = .01f; Joint.projectionMode = JointProjectionMode.PositionAndRotation; transform.localRotation = currentRotation; } protected virtual void AfterJointCreated(ConfigurableJoint joint) { } /// /// Sets the rotation limits on the Joint's main rotation axis /// public void SetLimits(float minAngle, float maxAngle) { FixAngle(ref minAngle, ref maxAngle); Joint.LimitAngularXMotion(); Joint.SetAngularXHighLimit(minAngle); Joint.SetAngularXLowLimit(maxAngle); } /// /// Resets the limit's to the -MinAngle and MaxAngle values of this component. /// public void ResetLimits() { Joint.LimitAngularXMotion(); Joint.SetAngularXHighLimit(-MinAngle); Joint.SetAngularXLowLimit(-MaxAngle); } /// /// Frees the axis of rotation /// public void RemoveLimits() { Joint.angularXMotion = ConfigurableJointMotion.Free; } /// /// Sanity check on the angles for the joint high and low settings /// private void FixAngle(ref float min, ref float max) { if (min > 0) { min *= -1; } if (max < 0) { max *= -1; } min = Mathf.Clamp(min, min, 0); max = Mathf.Clamp(max, 0, max); } } }