using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using XED.Interfaces; using XED.Manage; using XED.RuntimeGizmo; namespace XED.Util { public class RenderObjectHandler : MonoBehaviour, IStatusController, IInputHandler { List selectedRenderObjects = new List(); List copyRenderObjects = new List(); List selectedGameObjects = new List(); CustomAssetRenderObject prevSelectedObject; RTGController rtgController; ConvexHullCalculator convexHullCalculator = new ConvexHullCalculator(); Vector3 clickBeginPos; float clickLengthThreshold = 5.0f; int uiLayer; int selectIndex; bool lockHandler = false; public event System.Action onDragBegin; public event System.Action onDragEnd; public event System.Action onDragForceEnd; public UnityEvent> onSelectObject; public UnityEvent> onRemoveObject; public UnityEvent> onCopyObject; public UnityEvent> onOrderByHierachy; public event System.Action onDeselectAll; public event System.Action> onTransformChanged; void Awake() { myHandler = GetInputHandler(); } private void Start() { rtgController = new RTGController(); rtgController.onTransformBegin = OnTransformBegin; rtgController.onTransformChanged = OnTransformChangedFromRTG; uiLayer = LayerMask.NameToLayer("UI"); //var inputManager = FindSingle(); selectIndex = 0; } private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { } } private bool ApproximatelyEqual(Vector3 a, Vector3 b, float epsilon = 1e-5f) { return Mathf.Abs(a.x - b.x) < epsilon && Mathf.Abs(a.y - b.y) < epsilon && Mathf.Abs(a.z - b.z) < epsilon; } private void OnMousePointerDown() { clickBeginPos = Input.mousePosition; //ui¸¦ ¼±ÅÃÇßÀ» °æ¿ì¿¡´Â ¿ÀºêÁ§Æ® ¼±ÅÃÀ» ¸ØÃá´Ù. PointerEventData pointerData = new PointerEventData(EventSystem.current) { position = Input.mousePosition }; List raycastResults = new List(); EventSystem.current.RaycastAll(pointerData, raycastResults); if (raycastResults.Any(x => x.gameObject.layer == uiLayer)) { return; } onDragBegin?.Invoke(clickBeginPos); } private void OnMousePointerUp() { //rtg ÄÁÆ®·Ñ·¯°¡ Á¶ÀÛÁßÀ϶§ ¶ôÀ» °Ç´Ù. //rtg ÄÁÆ®·Ñ·¯ Á¶ÀÛ ÈÄ Æ÷Ä¿½º°¡ ¹Ù²î´Â °ÍÀ» ¹æÁöÇϱâ À§ÇÑ ¿ëµµ if (lockHandler == true) { lockHandler = false; return; } //ui¸¦ ¼±ÅÃÇßÀ» °æ¿ì¿¡´Â ¿ÀºêÁ§Æ® ¼±ÅÃÀ» ¸ØÃá´Ù. PointerEventData pointerData = new PointerEventData(EventSystem.current) { position = Input.mousePosition }; List raycastResults = new List(); EventSystem.current.RaycastAll(pointerData, raycastResults); if (raycastResults.Any(x => x.gameObject.layer == uiLayer)) { onDragForceEnd?.Invoke(); return; } //Æ÷ÀÎÅÍ ¾÷ À§Ä¡°¡ Æ÷ÀÎÅÍ ´Ù¿î À§Ä¡¿¡¼­ Å©°Ô ¹þ¾î³µÀ» °æ¿ì´Â Ŭ¸¯À» ¹«½ÃÇÑ´Ù. if ((clickBeginPos - Input.mousePosition).magnitude > clickLengthThreshold) { DeselectAll(); onDragEnd?.Invoke(Input.mousePosition); return; } List raycastedTarget = new List(); CustomAssetRenderObject renderObject = null; foreach (var result in raycastResults) { renderObject = result.gameObject.GetComponent(); if (renderObject != null) { raycastedTarget.Add(renderObject); } } if (raycastedTarget.Count > 0) { //¿ÀºêÁ§Æ®°¡ ¿©·¯°³ °ãÃÄ ÀÖÀ» °æ¿ì µÚÀÇ ¿ÀºêÁ§Æ®¸¦ ¼øÂ÷ÀûÀ¸·Î ¼±ÅÃÇØÁÖ±â À§ÇÑ ¿ëµµ if (selectIndex < raycastedTarget.Count && prevSelectedObject == raycastedTarget[selectIndex]) { selectIndex++; } if (selectIndex >= raycastedTarget.Count) { selectIndex = 0; } renderObject = raycastedTarget[selectIndex]; //ÄÁÆ®·ÑÀÌ ´­·Á ÀÖÀ¸¸é ¿ÀºêÁ§Æ® ¼±ÅÃÀ» Åä±ÛÇÑ´Ù. (À¯´ÏƼ ±â´É°ú ÀÏÄ¡) if (Input.GetKey(KeyCode.LeftControl)) { if (renderObject.ToggleSelect()) { AddSelection(renderObject); } else { RemoveSelection(renderObject); } } //½ÃÇÁÆ® ۰¡ ´­·Á ÀÖ´Â °æ¿ì ´ÙÁß ¼±Åà ±â´ÉÀ¸·Î ÀÛ¿ëÇÑ´Ù. else if (Input.GetKey(KeyCode.LeftShift)) { AddSelection(renderObject); } //±× ¿Ü´Â ¼±ÅÃµÈ ¾ÆÀÌÅÛÀ» Á¦¿ÜÇÏ°í ´Ù ºñ¼±ÅÃÇØÁØ´Ù. else { RemoveAllSelections(); AddSelection(renderObject); } prevSelectedObject = renderObject; OnSelect(); } else { ResetRTG(); DeselectAll(); } onDragForceEnd?.Invoke(); } public void DeselectAll() { if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.LeftShift)) { return; } RemoveAllSelections(); onDeselectAll?.Invoke(); } public void AddSelection(CustomAssetRenderObject item) { if (selectedRenderObjects.Any(x => x == item)) { return; } item.Select(); selectedRenderObjects.Add(item); selectedGameObjects.Add(item.gameObject); rtgController.SetGizmoTargetObjects(selectedGameObjects); } public void AddSelection(List items) { for (int i = 0; i < items.Count; i++) { CustomAssetRenderObject item = items[i]; if (selectedRenderObjects.Any(x => x == item)) { return; } item.Select(); selectedRenderObjects.Add(item); selectedGameObjects.Add(item.gameObject); } rtgController.SetGizmoTargetObjects(selectedGameObjects); } public void RemoveAllSelections() { for (int i = 0; i < selectedRenderObjects.Count; i++) { selectedRenderObjects[i].Deselect(); } selectedRenderObjects.Clear(); selectedGameObjects.Clear(); rtgController.SetGizmoTargetObjects(selectedGameObjects); } public void RemoveSelection(CustomAssetRenderObject item) { selectedRenderObjects.Remove(item); selectedGameObjects.Remove(item.gameObject); rtgController.SetGizmoTargetObjects(selectedGameObjects); } private void OnSelect() { onSelectObject?.Invoke(selectedRenderObjects); rtgController.SetGizmoTargetObjects(selectedGameObjects); } public void ResetRTG() { rtgController.SetWorkGizmoId(RTGController.GizmoId.None); } public void SetRTGMove() { rtgController.SetWorkGizmoId(RTGController.GizmoId.Move); } public void SetRTGScale() { rtgController.SetWorkGizmoId(RTGController.GizmoId.Scale); } public void SetRTGRotate() { rtgController.SetWorkGizmoId(RTGController.GizmoId.Rotate); } public void OnTransformBegin() { lockHandler = true; } public void OnTransformChangedFromRTG(List transformObjects) { OnTransformChanged(transformObjects); onTransformChanged?.Invoke(selectedGameObjects); onDragForceEnd?.Invoke(); } public void OnTransformChanged(List transformObjects) { foreach (GameObject gb in transformObjects) { CustomAssetRenderObject renderObject = gb.GetComponent(); if (renderObject != null) { renderObject.onTransformChanged?.Invoke(); } } } public void RemoveItem() { if (selectedRenderObjects.Count == 0) return; onRemoveObject?.Invoke(selectedRenderObjects); } public void CopyItem() { if (selectedRenderObjects.Count == 0) return; onCopyObject?.Invoke(selectedRenderObjects); } public void SaveItemsToCopy() { copyRenderObjects.Clear(); copyRenderObjects.AddRange(selectedRenderObjects); } public void CopySavedItems() { if (copyRenderObjects.Count == 0) return; onCopyObject?.Invoke(copyRenderObjects); } public void ResetGizmoTargetObjects() { rtgController.SetGizmoTargetObjects(selectedGameObjects); } public void AlignObjects(int row, int col, float spaceX, float spaceZ, int direction) { if (selectedRenderObjects.Count <= 1) { return; } onOrderByHierachy?.Invoke(selectedRenderObjects); List objectsToAlign = new List(selectedRenderObjects); List originalPos = objectsToAlign.Select(x => x.transform.position).ToList(); direction = ((direction % 8) + 8) % 8; int primary = direction % 2 == 0 ? col : row; Vector3 primaryDir = Vector3.zero; Vector3 secondaryDir = Vector3.zero; primaryDir = (direction == 0 || direction == 2) ? Vector3.right * spaceX : (direction == 4 || direction == 6) ? -Vector3.right * spaceX : (direction == 1 || direction == 7) ? Vector3.forward * spaceZ : -Vector3.forward * spaceZ; secondaryDir = (direction == 1 || direction == 3) ? Vector3.right * spaceX : (direction == 5 || direction == 7) ? -Vector3.right * spaceX : (direction == 0 || direction == 6) ? Vector3.forward * spaceZ : -Vector3.forward * spaceZ; ActionCommand command = new ActionCommand( () => { int countP = 0; int countS = 0; Vector3 referencePoint = objectsToAlign[0].transform.position; for (int i = 1; i < objectsToAlign.Count; i++) { CustomAssetRenderObject renderObject = objectsToAlign[i]; if (countP < primary - 1 || primary == 0) { countP++; } else { countS++; countP = 0; } renderObject.transform.position = referencePoint + primaryDir * countP + secondaryDir * countS; renderObject.onTransformChanged?.Invoke(); } onTransformChanged?.Invoke(objectsToAlign.Select(renderObject => renderObject.gameObject).ToList()); }, () => { for (int i = 1; i < objectsToAlign.Count; i++) { CustomAssetRenderObject renderObject = objectsToAlign[i]; renderObject.transform.position = originalPos[i]; renderObject.onTransformChanged?.Invoke(); } onTransformChanged?.Invoke(objectsToAlign.Select(renderObject => renderObject.gameObject).ToList()); }); CommandManager.I.ExecuteCommand(command); } public void VertexSnap() { if (selectedRenderObjects.Count != 1) { return; } //°¡Àå °¡±î¿î ¿ÀºêÁ§Æ®¿¡ ºÙ¿©ÁØ´Ù. //Transform.positionÀÌ °¡±õ´Ù°í ¿ÀºêÁ§Æ® »çÀÌ ±â¸®°¡ ²À °¡Àå °¡±î¿î °ÍÀº ¾Æ´Ï±â ¶§¹®¿¡ //¿À¹ö·¦ ¹Ú½º¿¡ Ãæµ¹µÇ´Â ¸ðµç ¿ÀºêÁ§Æ®ÀÇ ¹öÅØ½º·Î KDTree¸¦ ±¸¼ºÇØ º¼±î ÇÏ´Ù°¡ °üµÎ¾ú´Ù. float minDist = float.MaxValue; CustomAssetRenderObject fromObject = selectedRenderObjects[0]; CustomAssetRenderObject toObject = null; Bounds fromBounds = fromObject.GetBounds(); Collider[] colliders = Physics.OverlapBox(fromBounds.center, fromBounds.size * 2.0f); foreach (Collider collider in colliders) { if (collider.gameObject == fromObject.gameObject) continue; CustomAssetRenderObject otherObject = collider.gameObject.GetComponent(); if (otherObject != null) { float dist = Vector3.Magnitude(fromObject.transform.position - otherObject.transform.position); if (dist < minDist) { minDist = dist; toObject = otherObject; } } } if (toObject != null) { //¼±ÅÃµÈ ¿ÀºêÁ§Æ®ÀÇ ConvexHull ¹öÅØ½º Á¡À» °è»êÇØÁØ´Ù. //ÀÌ´Â iterationÇÏ´Â ¹öÅØ½º ¼ö¸¦ ÁÙÀ̱â À§ÇØ ÇØÁÖ¸ç, //¸¸¾à ¼±ÅÃµÈ ¿ÀºêÁ§Æ®µµ TDTree·Î Nearest Æ÷ÀÎÆ® ŽÁöÇÒ °æ¿ì toObjectÀÇ ¼¾ÅÍ¿¡ °¡Àå °¡±î¿î Æ÷ÀÎÆ®¸¸ ŽÁöµÇ±â ¶§¹®¿¡ °¡Àå °¡±î¿î ¹öÅØ½º Á¡ÀÌ °ñ¶óÁöÁö ¾Ê´Â °æ¿ì°¡ ÀÖ´Ù. List verts = new List(); List tris = new List(); List normals = new List(); convexHullCalculator.GenerateHull(fromObject.GetRenderObjectVertices(), false, ref verts, ref tris, ref normals); KDTree toTree = toObject.GetVertexPointKDTree(); Vector3 nearestPoint = Vector3.zero; Vector3 snapPoint = Vector3.zero; float minDistance = float.MaxValue; for (int i = 0; i < verts.Count; i++) { Vector3 ptNearest = verts[i]; Vector3 ptSnap = toTree.FindNearest(ptNearest); float tempDist = Vector3.Magnitude(ptSnap - ptNearest); if (tempDist < minDistance) { minDistance = tempDist; nearestPoint = ptNearest; snapPoint = ptSnap; } } //Vector3 direction = snapPoint - nearestPoint; //fromObject.transform.position += direction; List dir = new List() { snapPoint - nearestPoint }; if (dir[0].magnitude < 0.5f) { fromObject.transform.position += dir[0]; } else { ActionCommand command = new ActionCommand( () => { fromObject.transform.position += dir[0]; }, () => { fromObject.transform.position -= dir[0]; }); CommandManager.I.ExecuteCommand(command); } } } InputHandler myHandler; public void CurrentStatusEvent() { FindSingle().SetHandler(myHandler); } public void PrevStatusEvent() { FindSingle().RemoveHandler(myHandler); } public InputHandler GetInputHandler() { var getKeyActions = new Dictionary(); var downKeyActions = new Dictionary(); var upKeyActions = new Dictionary(); downKeyActions.Add(KeyCode.Mouse0, OnMousePointerDown); upKeyActions.Add(KeyCode.Mouse0, OnMousePointerUp); getKeyActions.Add(KeyCode.Q, SetRTGMove); getKeyActions.Add(KeyCode.W, SetRTGRotate); getKeyActions.Add(KeyCode.E, SetRTGScale); getKeyActions.Add(KeyCode.R, ResetRTG); getKeyActions.Add(KeyCode.V, VertexSnap); var shortcutTable = new Dictionary>(); #if UNITY_EDITOR shortcutTable.Add(KeyCode.LeftShift, new Dictionary()); shortcutTable[KeyCode.LeftShift].Add(KeyCode.C, SaveItemsToCopy); shortcutTable[KeyCode.LeftShift].Add(KeyCode.V, CopySavedItems); #else shortcutTable.Add(KeyCode.LeftControl, new Dictionary()); shortcutTable[KeyCode.LeftControl].Add(KeyCode.C, SaveItemsToCopy); shortcutTable[KeyCode.LeftControl].Add(KeyCode.V, CopySavedItems); #endif var handler = new InputHandler(getKeyActions, downKeyActions, upKeyActions, shortcutTable); return handler; } } }