using UnityEngine; using UnityEngine.UI; public class UIHandle : MonoBehaviour { /// Object that "owns" the handle public ObjectContainer Owner; /// Object that the handle refers to. Cast in inheriting class public Transform Target; /// Display object on the UI public GameObject Handle; /// Collider for selecting the handle on UI public SphereCollider Collider; /// Object that appears when right-clicking the handle public UIPopupPanel Popup; public LineRenderer LineRenderer; protected SerializableTransform _defaultTransform; protected float _baseScale = 1; protected bool _forceDisplayOff = false; protected ModelViewerMain Main => ModelViewerMain.GetInstance(); public static T CreateAsChild(Transform parent) where T : UIHandle { var handle = new GameObject(parent.name + "_Handle").AddComponent(); handle.transform.parent = parent.transform; handle.transform.localPosition = Vector3.zero; handle.transform.localScale = Vector3.one; handle.transform.rotation = parent.rotation; handle.Target = parent.transform; return handle; } public virtual UIHandle Init(ObjectContainer owner, Transform target) { Target = target; return Init(owner); } public virtual UIHandle Init(ObjectContainer owner) { HandleManager.RegisterHandle(this); gameObject.layer = LayerMask.NameToLayer("UIHandle"); Target = Target == null? owner.transform : Target; Owner = owner; Owner.Handles.Add(this); Handle = Instantiate(HandleManager.Instance.Pfb_HandleDisplay, HandleManager.Instance.HandlesPanel.transform); Handle.transform.SetAsFirstSibling(); Popup = Instantiate(HandleManager.Instance.Pfb_Popup, HandleManager.Instance.HandlesPanel.transform).Init(this); Popup.gameObject.SetActive(false); _defaultTransform = new SerializableTransform(Target, Space.Self); var collider = gameObject.AddComponent(); collider.center = Vector3.zero; Collider = collider; return this; } private void OnDrawGizmos() { Gizmos.DrawWireSphere(transform.position + Collider.center, transform.lossyScale.x * Collider.radius); } public virtual void UpdateManual(Camera camera, bool linesEnabled) { if (Handle != null && camera != null && transform != null) { Handle.transform.localScale = Vector3.one * Mathf.Clamp(_baseScale / Vector3.Distance(camera.transform.position, transform.position), 0.1f, 1); if (Collider.transform.localScale.x != 0 && transform.lossyScale.x != 0) { Collider.radius = Mathf.Abs(35 /* magic number */ * (1 / transform.lossyScale.x) * GetRadiusOnScreen(camera, Collider.transform.position, Handle.transform.localScale.x)); Collider.radius = Mathf.Clamp(Collider.radius, 0.001f, 2); } Handle.transform.position = camera.WorldToScreenPoint(Collider.transform.TransformPoint(Collider.center)); if (ShouldBeHidden()) { if (Handle.activeSelf == true) { ToggleActive(false); } } else { bool isOnScreen = !(Handle.transform.position.x < 0 || Handle.transform.position.y < 0 || Handle.transform.position.z < 0 || Handle.transform.position.x > Screen.width || Handle.transform.position.y > Screen.height); if (Handle.activeSelf != isOnScreen) { ToggleActive(!Handle.activeSelf); } } } if(Handle.activeSelf && LineRenderer != null) { if (LineRenderer.enabled != linesEnabled) { LineRenderer.enabled = linesEnabled; } if (linesEnabled) { LineRenderer.SetPositions(new Vector3[] { (Target).position, (Target).parent.position }); } } } protected virtual bool ShouldBeHidden() { return _forceDisplayOff; } /// For future, `offset` param will allow popups to be spaced out when selecting more than 1 handle at a time. public void TogglePopup(int offset = 0) { Popup.Offset = offset * (Popup.transform as RectTransform).sizeDelta.x * ModelViewerInterface.GetInstance().MainCanvas.scaleFactor; ModelViewerInterface.ToggleVisible(Popup.gameObject); } public UIHandle SetDefaults(Vector3 localPos, Vector3 localRot, Vector3 localScale) { _defaultTransform.Position = localPos; _defaultTransform.Rotation = localRot; _defaultTransform.Scale = localScale; return this; } public UIHandle SetColor(Color color) { color.a = Handle.GetComponent().color.a; Handle.GetComponent().color = color; return this; } public UIHandle SetScale(float scale) { this._baseScale = scale; return this; } public UIHandle SetName(string name) { Handle.gameObject.name = name; Popup.NameLabel.text = name; return this; } public UIHandle SetOffset(Vector3 offset) { Collider.center = offset; return this; } public UIHandle SetContainer(ObjectContainer container) { Owner = container; return this; } public float GetRadiusOnScreen(Camera cam, Vector3 position, float screenSize) { Vector3 a = cam.WorldToScreenPoint(position); Vector3 b = new Vector3(a.x, a.y + screenSize, a.z); Vector3 aa = cam.ScreenToWorldPoint(a); Vector3 bb = cam.ScreenToWorldPoint(b); return (aa - bb).magnitude; } public UIHandle WithLineRenderer() { LineRenderer = Handle.gameObject.AddComponent(); LineRenderer.positionCount = 2; LineRenderer.startWidth = LineRenderer.endWidth = 0.005f; LineRenderer.material = HandleManager.Instance.LineRendererMaterial; LineRenderer.startColor = LineRenderer.endColor = Handle.GetComponent().color; return this; } private void OnDestroy() { if (Popup) { Destroy(Popup.gameObject); } if (Handle) { Destroy(Handle); } HandleManager.UnregisterHandle(this); } private void TransformReset(PoseLoadOptions options = null) { if(options == null) { options = PoseLoadOptions.All(); } _defaultTransform.ApplyTo(Target, options); } public void TransformResetAll() { var HandleUndoData = new HandleUndoData(this); HandleUndoData.OldPosition = new SerializableTransform(Target, Space.World); TransformReset(); HandleUndoData.NewPosition = Target; HandleManager.RegisterRuntimeGizmoUndoAction.Invoke(HandleUndoData); Owner.SetKeyframe(); } public void TransformResetPosition() { var HandleUndoData = new HandleUndoData(this); HandleUndoData.OldPosition = new SerializableTransform(Target, Space.World); TransformReset(new PoseLoadOptions(false) { Position = true }); HandleUndoData.NewPosition = Target; HandleManager.RegisterRuntimeGizmoUndoAction.Invoke(HandleUndoData); Owner.SetKeyframe(); } public void TransformResetRotation() { var HandleUndoData = new HandleUndoData(this); HandleUndoData.OldPosition = new SerializableTransform(Target, Space.World); TransformReset(new PoseLoadOptions(false) { Rotation = true }); HandleUndoData.NewPosition = Target; HandleManager.RegisterRuntimeGizmoUndoAction.Invoke(HandleUndoData); Owner.SetKeyframe(); } public void TransformResetScale() { var HandleUndoData = new HandleUndoData(this); HandleUndoData.OldPosition = new SerializableTransform(Target, Space.World); TransformReset(new PoseLoadOptions(false) { Scale = true }); HandleUndoData.NewPosition = Target; HandleManager.RegisterRuntimeGizmoUndoAction.Invoke(HandleUndoData); Owner.SetKeyframe(); } public void ToggleActive(bool value) { Handle.SetActive(value); Collider.enabled = value; Popup.gameObject.SetActive(false); } public void ForceDisplayOff(bool value) { _forceDisplayOff = value; } }