ObjectGrabSession.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #if UNITY_EDITOR
  2. using UnityEngine;
  3. using System.Collections.Generic;
  4. namespace O3DWB
  5. {
  6. public class ObjectGrabSession
  7. {
  8. private enum State
  9. {
  10. Inactive = 0,
  11. Moving,
  12. Rotating,
  13. Scaling
  14. }
  15. private struct ObjectSurfaceInfo
  16. {
  17. public Plane SurfacePlane;
  18. public Vector3 SitPoint;
  19. }
  20. private State _state;
  21. private List<GameObject> _grabbedObjects;
  22. private Dictionary<GameObject, Vector3> _objectToPivotDir = new Dictionary<GameObject, Vector3>();
  23. private Dictionary<GameObject, ObjectSurfaceInfo> _objectToSurfaceInfo = new Dictionary<GameObject, ObjectSurfaceInfo>();
  24. private Dictionary<GameObject, Vector3> _objectToWorldScale = new Dictionary<GameObject, Vector3>();
  25. private Vector2 _cursorPosAtScaleBegin;
  26. private ObjectMask _rayHitMask = new ObjectMask();
  27. private MouseCursorRayHit _currentCursorRayHit;
  28. private Vector3 _surfaceHitPoint;
  29. private ObjectGrabSettings _grabSettings;
  30. public bool IsActive { get { return _state != State.Inactive; } }
  31. public ObjectGrabSettings Settings { set { if (value != null) _grabSettings = value; } }
  32. public void Begin(List<GameObject> grabbedObjects)
  33. {
  34. if (_grabSettings == null || grabbedObjects == null || grabbedObjects.Count == 0 || IsActive) return;
  35. _grabbedObjects = Octave3DWorldBuilder.ActiveInstance.GetRoots(grabbedObjects);
  36. foreach(var grabbedObj in _grabbedObjects) _rayHitMask.ObjectCollectionMask.Mask(grabbedObj.GetAllChildrenIncludingSelf());
  37. MouseCursorRayHit cursorRayHit = GetCursorRayHit();
  38. if (!cursorRayHit.WasAnythingHit) return;
  39. _surfaceHitPoint = cursorRayHit.WasAnObjectHit ? cursorRayHit.ClosestObjectRayHit.HitPoint : cursorRayHit.GridCellRayHit.HitPoint;
  40. _state = State.Moving;
  41. foreach(var grabbedObject in _grabbedObjects)
  42. {
  43. if(grabbedObject != null)
  44. {
  45. _objectToPivotDir.Add(grabbedObject, grabbedObject.transform.position - _surfaceHitPoint);
  46. }
  47. }
  48. }
  49. public void End()
  50. {
  51. _state = State.Inactive;
  52. if (_grabbedObjects != null) _grabbedObjects.Clear();
  53. _rayHitMask.ObjectCollectionMask.UnmaskAll();
  54. _objectToPivotDir.Clear();
  55. _objectToSurfaceInfo.Clear();
  56. _objectToWorldScale.Clear();
  57. }
  58. public void RenderGizmos()
  59. {
  60. if(IsActive && _grabSettings.ShowGrabLines)
  61. {
  62. foreach(var grabbedObject in _grabbedObjects)
  63. {
  64. GizmosEx.RenderLine(grabbedObject.GetHierarchyWorldOrientedBox().Center, _surfaceHitPoint, _grabSettings.GrabLineColor);
  65. }
  66. }
  67. }
  68. public void Update()
  69. {
  70. if(IsActive)
  71. {
  72. if (AllShortcutCombos.Instance.GrabRotateSelection.IsActive()) _state = State.Rotating;
  73. else if (AllShortcutCombos.Instance.GrabScaleSelection.IsActive())
  74. {
  75. if(_state != State.Scaling)
  76. {
  77. _objectToWorldScale.Clear();
  78. foreach (var grabbedObject in _grabbedObjects)
  79. {
  80. if (grabbedObject != null)
  81. {
  82. _objectToWorldScale.Add(grabbedObject, grabbedObject.transform.lossyScale);
  83. }
  84. }
  85. _cursorPosAtScaleBegin = MouseCursor.Instance.Position;
  86. _state = State.Scaling;
  87. }
  88. }
  89. else
  90. {
  91. // Need to reset the anchor relationships because the cursor was moved without
  92. // the objects following it.
  93. if (_state == State.Rotating || _state == State.Scaling)
  94. {
  95. _objectToPivotDir.Clear();
  96. foreach (var grabbedObject in _grabbedObjects)
  97. {
  98. if (grabbedObject != null)
  99. {
  100. _objectToPivotDir.Add(grabbedObject, grabbedObject.transform.position - _surfaceHitPoint);
  101. }
  102. }
  103. }
  104. _state = State.Moving;
  105. }
  106. _currentCursorRayHit = GetCursorRayHit();
  107. if (!_currentCursorRayHit.WasAnythingHit) return;
  108. if(_currentCursorRayHit.WasAnythingHit)
  109. {
  110. if (_currentCursorRayHit.WasAnObjectHit) _surfaceHitPoint = _currentCursorRayHit.ClosestObjectRayHit.HitPoint;
  111. else _surfaceHitPoint = _currentCursorRayHit.GridCellRayHit.HitPoint;
  112. }
  113. if (_state == State.Moving || _objectToSurfaceInfo.Count == 0)
  114. {
  115. if (_currentCursorRayHit.WasAnObjectHit)
  116. {
  117. GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
  118. GameObjectRayHit objectRayHit = _currentCursorRayHit.ClosestObjectRayHit;
  119. foreach (var grabbedObject in _grabbedObjects)
  120. {
  121. if (grabbedObject == null) continue;
  122. Transform objectTransform = grabbedObject.transform;
  123. objectTransform.position = objectRayHit.HitPoint + _objectToPivotDir[grabbedObject];
  124. if (objectRayHit.WasTerrainHit)
  125. {
  126. Ray ray = new Ray(grabbedObject.GetHierarchyWorldOrientedBox().Center + Vector3.up, -Vector3.up);
  127. GameObjectRayHit sitPointHit = null;
  128. if (objectRayHit.HitObject.RaycastTerrainReverseIfFail(ray, out sitPointHit))
  129. {
  130. Plane surfacePlane = new Plane(sitPointHit.HitNormal, sitPointHit.HitPoint);
  131. if (_grabSettings.AlignAxis) AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, sitPointHit.HitNormal);
  132. grabbedObject.PlaceHierarchyOnPlane(surfacePlane);
  133. if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis) objectTransform.position += _grabSettings.OffsetFromSurface * sitPointHit.HitNormal;
  134. if (_grabSettings.EmbedInSurfaceWhenNoAlign && !_grabSettings.AlignAxis) grabbedObject.EmbedInSurfaceByVertex(-Vector3.up, objectRayHit.HitObject);
  135. ObjectSurfaceInfo surfaceInfo = new ObjectSurfaceInfo();
  136. surfaceInfo.SurfacePlane = surfacePlane;
  137. surfaceInfo.SitPoint = sitPointHit.HitPoint;
  138. SetObjectSurfaceInfo(grabbedObject, surfaceInfo);
  139. }
  140. }
  141. else
  142. if (objectRayHit.WasMeshHit)
  143. {
  144. Ray ray = new Ray(grabbedObject.GetHierarchyWorldOrientedBox().Center + objectRayHit.HitNormal * 2.0f, -objectRayHit.HitNormal);
  145. GameObjectRayHit sitPointHit = null;
  146. if (objectRayHit.HitObject.RaycastMeshReverseIfFail(ray, out sitPointHit))
  147. {
  148. Plane surfacePlane = new Plane(sitPointHit.HitNormal, sitPointHit.HitPoint);
  149. if (_grabSettings.AlignAxis) AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, sitPointHit.HitNormal);
  150. grabbedObject.PlaceHierarchyOnPlane(surfacePlane);
  151. if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis) objectTransform.position += _grabSettings.OffsetFromSurface * sitPointHit.HitNormal;
  152. if (_grabSettings.EmbedInSurfaceWhenNoAlign && !_grabSettings.AlignAxis) grabbedObject.EmbedInSurfaceByVertex(-sitPointHit.HitNormal, objectRayHit.HitObject);
  153. ObjectSurfaceInfo surfaceInfo = new ObjectSurfaceInfo();
  154. surfaceInfo.SurfacePlane = surfacePlane;
  155. surfaceInfo.SitPoint = sitPointHit.HitPoint;
  156. SetObjectSurfaceInfo(grabbedObject, surfaceInfo);
  157. }
  158. }
  159. }
  160. }
  161. else
  162. if (_currentCursorRayHit.WasACellHit)
  163. {
  164. GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
  165. GridCellRayHit cellRayHit = _currentCursorRayHit.GridCellRayHit;
  166. foreach (var grabbedObject in _grabbedObjects)
  167. {
  168. if (grabbedObject == null) continue;
  169. Transform objectTransform = grabbedObject.transform;
  170. objectTransform.position = cellRayHit.HitPoint + _objectToPivotDir[grabbedObject];
  171. Plane surfacePlane = new Plane(cellRayHit.HitNormal, cellRayHit.HitPoint);
  172. Vector3 sitPoint = surfacePlane.ProjectPoint(grabbedObject.GetWorldOrientedBox().Center);
  173. if (_grabSettings.AlignAxis) AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, cellRayHit.HitNormal);
  174. grabbedObject.PlaceHierarchyOnPlane(surfacePlane);
  175. if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis) objectTransform.position += _grabSettings.OffsetFromSurface * cellRayHit.HitNormal;
  176. ObjectSurfaceInfo surfaceInfo = new ObjectSurfaceInfo();
  177. surfaceInfo.SurfacePlane = surfacePlane;
  178. surfaceInfo.SitPoint = sitPoint;
  179. SetObjectSurfaceInfo(grabbedObject, surfaceInfo);
  180. }
  181. }
  182. }
  183. else
  184. if (_state == State.Rotating)
  185. {
  186. GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
  187. float rotationSensitivity = _grabSettings.RotationSensitivity;
  188. foreach (var grabbedObject in _grabbedObjects)
  189. {
  190. if (!_objectToSurfaceInfo.ContainsKey(grabbedObject)) continue;
  191. var surfaceInfo = _objectToSurfaceInfo[grabbedObject];
  192. OrientedBox worldOOBB = grabbedObject.GetHierarchyWorldOrientedBox();
  193. grabbedObject.RotateHierarchyBoxAroundPoint(MouseCursor.Instance.OffsetSinceLastMouseMove.x * rotationSensitivity, _grabSettings.AlignAxis ? surfaceInfo.SurfacePlane.normal : Vector3.up, worldOOBB.Center);
  194. }
  195. }
  196. else
  197. if(_state == State.Scaling)
  198. {
  199. GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
  200. Vector2 currentCursorPos = MouseCursor.Instance.Position;
  201. Vector2 cursorOffsetFromScaleBegin = currentCursorPos - _cursorPosAtScaleBegin;
  202. float scaleFactor = 1.0f + _grabSettings.ScaleSensitivity * cursorOffsetFromScaleBegin.x;
  203. foreach (var grabbedObject in _grabbedObjects)
  204. {
  205. if (!_objectToSurfaceInfo.ContainsKey(grabbedObject) ||
  206. !_objectToWorldScale.ContainsKey(grabbedObject)) continue;
  207. var surfaceInfo = _objectToSurfaceInfo[grabbedObject];
  208. grabbedObject.SetHierarchyWorldScaleByPivotPoint(_objectToWorldScale[grabbedObject] * scaleFactor, surfaceInfo.SitPoint);
  209. }
  210. }
  211. }
  212. }
  213. private void SetObjectSurfaceInfo(GameObject gameObject, ObjectSurfaceInfo surfaceInfo)
  214. {
  215. if (gameObject == null) return;
  216. if (_objectToSurfaceInfo.ContainsKey(gameObject)) _objectToSurfaceInfo[gameObject] = surfaceInfo;
  217. else _objectToSurfaceInfo.Add(gameObject, surfaceInfo);
  218. }
  219. private MouseCursorRayHit GetCursorRayHit()
  220. {
  221. MouseCursor.Instance.PushObjectPickMaskFlags(MouseCursorObjectPickFlags.ObjectBox | MouseCursorObjectPickFlags.ObjectSprite);
  222. MouseCursor.Instance.PushObjectMask(_rayHitMask);
  223. MouseCursorRayHit cursorRayHit = MouseCursor.Instance.GetRayHit();
  224. MouseCursor.Instance.PopObjectPickMaskFlags();
  225. MouseCursor.Instance.PopObjectMask();
  226. return cursorRayHit;
  227. }
  228. }
  229. }
  230. #endif