ObjectSnapping.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. #if UNITY_EDITOR
  2. using UnityEngine;
  3. using System;
  4. using System.Collections.Generic;
  5. namespace O3DWB
  6. {
  7. [Serializable]
  8. public class ObjectSnapping : ScriptableObject
  9. {
  10. #region Private Variables
  11. [SerializeField]
  12. private ObjectSnapSettings _settings;
  13. [SerializeField]
  14. private ObjectMask _objectSnapMask = new ObjectMask();
  15. [SerializeField]
  16. private XZGrid _xzSnapGrid;
  17. [SerializeField]
  18. private SnapSurface _objectSnapSurface = new SnapSurface();
  19. [SerializeField]
  20. private bool _wasInitialized = false;
  21. #endregion
  22. #region Public Properties
  23. public ObjectMask ObjectSnapMask { get { return _objectSnapMask; } }
  24. public ObjectSnapSettings Settings
  25. {
  26. get
  27. {
  28. if (_settings == null) _settings = Octave3DWorldBuilder.ActiveInstance.CreateScriptableObject<ObjectSnapSettings>();
  29. return _settings;
  30. }
  31. }
  32. public XZGrid XZSnapGrid
  33. {
  34. get
  35. {
  36. if (_xzSnapGrid == null) _xzSnapGrid = Octave3DWorldBuilder.ActiveInstance.CreateScriptableObject<XZGrid>();
  37. return _xzSnapGrid;
  38. }
  39. }
  40. public XZGridRenderSettings RenderSettingsForColliderSnapSurfaceGrid { get { return _objectSnapSurface.RenderSettingsForColliderSnapSurfaceGrid; } }
  41. public Plane ObjectSnapSurfacePlane { get { return _objectSnapSurface.Plane; } }
  42. public GameObject ObjectSnapSurfaceObject { get { return _objectSnapSurface.SurfaceObject; } }
  43. public SnapSurfaceType SnapSurfaceType { get { return _objectSnapSurface.SurfaceType; } }
  44. #endregion
  45. #region Public Static Functions
  46. public static ObjectSnapping Get()
  47. {
  48. return Octave3DWorldBuilder.ActiveInstance.ObjectSnapping;
  49. }
  50. #endregion
  51. #region Public Methods
  52. public void RefreshSnapSurface()
  53. {
  54. _objectSnapSurface.Refresh();
  55. }
  56. public void RenderGizmos()
  57. {
  58. XZSnapGrid.RenderGizmos();
  59. if (!Settings.SnapToCursorHitPoint) _objectSnapSurface.RenderGizmos();
  60. }
  61. public void SnapXZGridToCursorPickPoint(bool snapToClosestTopOrBottom)
  62. {
  63. MouseCursor.Instance.PushObjectMask(null);
  64. MouseCursor.Instance.PushObjectPickMaskFlags(MouseCursorObjectPickFlags.ObjectBox | MouseCursorObjectPickFlags.ObjectTerrain);
  65. MouseCursorRayHit cursorRayHit = MouseCursor.Instance.GetRayHit();
  66. MouseCursor.Instance.PopObjectPickMaskFlags();
  67. MouseCursor.Instance.PopObjectMask();
  68. if (cursorRayHit.WasAnythingHit)
  69. {
  70. Vector3 snapDestPoint;
  71. if (cursorRayHit.WasAnObjectHit)
  72. {
  73. snapDestPoint = cursorRayHit.ClosestObjectRayHit.HitPoint;
  74. UndoEx.RecordForToolAction(XZSnapGrid);
  75. if(snapToClosestTopOrBottom)
  76. {
  77. Box objectWorldBox = cursorRayHit.ClosestObjectRayHit.HitObject.GetWorldBox();
  78. Vector3 fromCenterToHitPoint = snapDestPoint - objectWorldBox.Center;
  79. if (Vector3.Dot(fromCenterToHitPoint, Vector3.up) > 0.0f) snapDestPoint = objectWorldBox.Center + Vector3.up * objectWorldBox.Extents.y;
  80. else snapDestPoint = objectWorldBox.Center - Vector3.up * objectWorldBox.Extents.y;
  81. }
  82. XZSnapGrid.SnapToPoint(snapDestPoint);
  83. }
  84. }
  85. }
  86. public List<XZGrid> GetAllSnapGrids()
  87. {
  88. var allSnapGrids = new List<XZGrid>();
  89. allSnapGrids.Add(XZSnapGrid);
  90. return allSnapGrids;
  91. }
  92. public void UpdateProjectedBoxFacePivotPoints(GameObject hierarchyRoot, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints, bool keepCurrentSnapSurface)
  93. {
  94. OrientedBox hierarchyWorldOrientedBox = hierarchyRoot.GetHierarchyWorldOrientedBox();
  95. if (!hierarchyWorldOrientedBox.IsValid()) return;
  96. if(keepCurrentSnapSurface)
  97. {
  98. if (!_objectSnapSurface.IsValid) return;
  99. projectedBoxFacePivotPoints.FromOrientedBoxAndSnapSurface(hierarchyWorldOrientedBox, _objectSnapSurface);
  100. }
  101. else
  102. {
  103. _objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
  104. projectedBoxFacePivotPoints.FromOrientedBoxAndSnapSurface(hierarchyWorldOrientedBox, _objectSnapSurface);
  105. }
  106. }
  107. public void SnapObjectHierarchy(GameObject hierarchyRoot, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints, float offsetFromSnapSurface)
  108. {
  109. _objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
  110. if (_objectSnapSurface.IsValid)
  111. {
  112. OrientedBox hierarchyWorldOrientedBox = hierarchyRoot.GetHierarchyWorldOrientedBox();
  113. if (!hierarchyWorldOrientedBox.IsValid()) return;
  114. Vector3 pivotPoint = projectedBoxFacePivotPoints.ActivePoint;
  115. if (Settings.UseOriginalPivot) pivotPoint = hierarchyRoot.transform.position;
  116. if (Settings.SnapToCursorHitPoint || Settings.EnableObjectToObjectSnap)
  117. {
  118. SnapObjectHierarchyPosition(hierarchyRoot, pivotPoint, _objectSnapSurface.CursorPickPoint, projectedBoxFacePivotPoints, offsetFromSnapSurface);
  119. }
  120. else
  121. {
  122. if (_objectSnapSurface.SurfaceType == SnapSurfaceType.GridCell && Settings.SnapCenterToCenterForXZGrid && !Settings.UseOriginalPivot) SnapObjectHierarchyToCenterOfSnapSurface(hierarchyRoot, projectedBoxFacePivotPoints.CenterPoint, projectedBoxFacePivotPoints, offsetFromSnapSurface);
  123. else
  124. if (_objectSnapSurface.SurfaceType == SnapSurfaceType.ObjectCollider && Settings.SnapCenterToCenterForObjectSurface && !Settings.UseOriginalPivot) SnapObjectHierarchyToCenterOfSnapSurface(hierarchyRoot, projectedBoxFacePivotPoints.CenterPoint, projectedBoxFacePivotPoints, offsetFromSnapSurface);
  125. else SnapObjectHierarchyPosition(hierarchyRoot, pivotPoint, _objectSnapSurface.GetSnapDestinationPointClosestToCursorPickPoint(), projectedBoxFacePivotPoints, offsetFromSnapSurface);
  126. if (AllShortcutCombos.Instance.KeepSnappedHierarchyInSnapSurfaceArea.IsActive()) KeepSnappedHierarchyInSnapSurfaceArea(hierarchyRoot, projectedBoxFacePivotPoints);
  127. }
  128. if (Settings.EnableObjectToObjectSnap) SnapHierarchyToNearbyObjects(hierarchyRoot, projectedBoxFacePivotPoints);
  129. }
  130. }
  131. public void SnapHierarchyToNearbyObjects(GameObject hierarchyRoot, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints)
  132. {
  133. Object2ObjectSnap.SnapResult snapResult = Object2ObjectSnap.Snap(hierarchyRoot, Settings.ObjectToObjectSnapEpsilon, ObjectSnapping.Get().ObjectSnapMask.ObjectCollectionMask.GetAllMaskedGameObjects());
  134. if (snapResult.WasSnapped) projectedBoxFacePivotPoints.MovePoints(snapResult.SnapDestination - snapResult.SnapPivot);
  135. }
  136. public void SnapObjectHierarchyToCenterOfSnapSurface(GameObject hierarchyRoot, Vector3 snapPivotPoint, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints, float offsetFromSnapSurface)
  137. {
  138. _objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
  139. if (_objectSnapSurface.IsValid)
  140. {
  141. OrientedBox hierarchyWorldOrientedBox = hierarchyRoot.GetHierarchyWorldOrientedBox();
  142. if (!hierarchyWorldOrientedBox.IsValid()) return;
  143. SnapObjectHierarchyPosition(hierarchyRoot, snapPivotPoint, _objectSnapSurface.Center, projectedBoxFacePivotPoints, offsetFromSnapSurface);
  144. }
  145. }
  146. public void SnapObjectPosition(GameObject gameObject)
  147. {
  148. _objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
  149. if(_objectSnapSurface.IsValid)
  150. {
  151. Transform objectTransform = gameObject.transform;
  152. objectTransform.position = _objectSnapSurface.GetSnapDestinationPointClosestToCursorPickPoint();
  153. }
  154. }
  155. public void SnapObjectPositionToSnapSurfaceCenter(GameObject gameObject)
  156. {
  157. _objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
  158. if (_objectSnapSurface.IsValid)
  159. {
  160. Transform objectTransform = gameObject.transform;
  161. objectTransform.position = _objectSnapSurface.Center;
  162. }
  163. }
  164. #endregion
  165. #region Private Methods
  166. private void OnEnable()
  167. {
  168. if(!_wasInitialized)
  169. {
  170. CoordinateSystemRenderSettings coordSystemRenderSettings = XZSnapGrid.RenderableCoordinateSystem.RenderSettings;
  171. coordSystemRenderSettings.SetAxisRenderInfinite(CoordinateSystemAxis.PositiveRight, true);
  172. coordSystemRenderSettings.SetAxisRenderInfinite(CoordinateSystemAxis.NegativeRight, true);
  173. coordSystemRenderSettings.SetAxisRenderInfinite(CoordinateSystemAxis.PositiveLook, true);
  174. coordSystemRenderSettings.SetAxisRenderInfinite(CoordinateSystemAxis.NegativeLook, true);
  175. _wasInitialized = true;
  176. }
  177. }
  178. private MouseCursorRayHit GetCursorRayHit()
  179. {
  180. MouseCursor.Instance.PushObjectPickMaskFlags(MouseCursorObjectPickFlags.ObjectTerrain | MouseCursorObjectPickFlags.ObjectMesh);
  181. MouseCursor.Instance.PushObjectMask(ObjectSnapMask);
  182. MouseCursorRayHit cursorRayHit = MouseCursor.Instance.GetRayHit();
  183. MouseCursor.Instance.PopObjectPickMaskFlags();
  184. MouseCursor.Instance.PopObjectMask();
  185. return cursorRayHit;
  186. }
  187. private void SnapObjectHierarchyPosition(GameObject hierarchyRoot, Vector3 snapPivotPoint, Vector3 snapDestinationPoint, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints, float offsetFromSnapSurface)
  188. {
  189. snapDestinationPoint += _objectSnapSurface.Plane.normal * offsetFromSnapSurface;
  190. Transform hierarchyRootTransform = hierarchyRoot.transform;
  191. Vector3 snapVector = hierarchyRootTransform.position - snapPivotPoint;
  192. hierarchyRootTransform.position = snapDestinationPoint + snapVector;
  193. projectedBoxFacePivotPoints.MovePoints(snapDestinationPoint - snapPivotPoint);
  194. }
  195. private void KeepSnappedHierarchyInSnapSurfaceArea(GameObject hierarchyRoot, ProjectedBoxFacePivotPoints projectedHierarchyBoxFacePivotPoints)
  196. {
  197. OrientedBox hierarchyWorldOrientedBox = hierarchyRoot.GetHierarchyWorldOrientedBox();
  198. List<Vector3> worldBoxPoints = hierarchyWorldOrientedBox.GetCenterAndCornerPoints();
  199. XZOrientedQuad3D snapSurfaceQuad = _objectSnapSurface.SurfaceQuad;
  200. List<Plane> quadSegmentPlanes = snapSurfaceQuad.GetBoundarySegmentPlanesFacingOutward();
  201. List<Vector3> pushVectors = new List<Vector3>(quadSegmentPlanes.Count);
  202. // All box points which are in front of the surface quad's plane are outside
  203. // the surface so we will have to push them back.
  204. for(int segmentPlaneIndex = 0; segmentPlaneIndex < quadSegmentPlanes.Count; ++segmentPlaneIndex)
  205. {
  206. Plane segmentPlane = quadSegmentPlanes[segmentPlaneIndex];
  207. Vector3 furthestPointInFront;
  208. if(segmentPlane.GetFurthestPointInFront(worldBoxPoints, out furthestPointInFront))
  209. {
  210. Vector3 projectedPoint = segmentPlane.ProjectPoint(furthestPointInFront);
  211. pushVectors.Add(projectedPoint - furthestPointInFront);
  212. }
  213. }
  214. Transform hierarchyRootTransform = hierarchyRoot.transform;
  215. foreach(Vector3 pushVector in pushVectors)
  216. {
  217. hierarchyRootTransform.position += pushVector;
  218. projectedHierarchyBoxFacePivotPoints.MovePoints(pushVector);
  219. }
  220. }
  221. #endregion
  222. }
  223. }
  224. #endif