ZeroVR/ZeroPacientVR/Assets/Octave3D World Builder/Scripts/Objects/Object Snapping/ObjectSnapping.cs

259 lines
12 KiB
C#

#if UNITY_EDITOR
using UnityEngine;
using System;
using System.Collections.Generic;
namespace O3DWB
{
[Serializable]
public class ObjectSnapping : ScriptableObject
{
#region Private Variables
[SerializeField]
private ObjectSnapSettings _settings;
[SerializeField]
private ObjectMask _objectSnapMask = new ObjectMask();
[SerializeField]
private XZGrid _xzSnapGrid;
[SerializeField]
private SnapSurface _objectSnapSurface = new SnapSurface();
[SerializeField]
private bool _wasInitialized = false;
#endregion
#region Public Properties
public ObjectMask ObjectSnapMask { get { return _objectSnapMask; } }
public ObjectSnapSettings Settings
{
get
{
if (_settings == null) _settings = Octave3DWorldBuilder.ActiveInstance.CreateScriptableObject<ObjectSnapSettings>();
return _settings;
}
}
public XZGrid XZSnapGrid
{
get
{
if (_xzSnapGrid == null) _xzSnapGrid = Octave3DWorldBuilder.ActiveInstance.CreateScriptableObject<XZGrid>();
return _xzSnapGrid;
}
}
public XZGridRenderSettings RenderSettingsForColliderSnapSurfaceGrid { get { return _objectSnapSurface.RenderSettingsForColliderSnapSurfaceGrid; } }
public Plane ObjectSnapSurfacePlane { get { return _objectSnapSurface.Plane; } }
public GameObject ObjectSnapSurfaceObject { get { return _objectSnapSurface.SurfaceObject; } }
public SnapSurfaceType SnapSurfaceType { get { return _objectSnapSurface.SurfaceType; } }
#endregion
#region Public Static Functions
public static ObjectSnapping Get()
{
return Octave3DWorldBuilder.ActiveInstance.ObjectSnapping;
}
#endregion
#region Public Methods
public void RefreshSnapSurface()
{
_objectSnapSurface.Refresh();
}
public void RenderGizmos()
{
XZSnapGrid.RenderGizmos();
if (!Settings.SnapToCursorHitPoint) _objectSnapSurface.RenderGizmos();
}
public void SnapXZGridToCursorPickPoint(bool snapToClosestTopOrBottom)
{
MouseCursor.Instance.PushObjectMask(null);
MouseCursor.Instance.PushObjectPickMaskFlags(MouseCursorObjectPickFlags.ObjectBox | MouseCursorObjectPickFlags.ObjectTerrain);
MouseCursorRayHit cursorRayHit = MouseCursor.Instance.GetRayHit();
MouseCursor.Instance.PopObjectPickMaskFlags();
MouseCursor.Instance.PopObjectMask();
if (cursorRayHit.WasAnythingHit)
{
Vector3 snapDestPoint;
if (cursorRayHit.WasAnObjectHit)
{
snapDestPoint = cursorRayHit.ClosestObjectRayHit.HitPoint;
UndoEx.RecordForToolAction(XZSnapGrid);
if(snapToClosestTopOrBottom)
{
Box objectWorldBox = cursorRayHit.ClosestObjectRayHit.HitObject.GetWorldBox();
Vector3 fromCenterToHitPoint = snapDestPoint - objectWorldBox.Center;
if (Vector3.Dot(fromCenterToHitPoint, Vector3.up) > 0.0f) snapDestPoint = objectWorldBox.Center + Vector3.up * objectWorldBox.Extents.y;
else snapDestPoint = objectWorldBox.Center - Vector3.up * objectWorldBox.Extents.y;
}
XZSnapGrid.SnapToPoint(snapDestPoint);
}
}
}
public List<XZGrid> GetAllSnapGrids()
{
var allSnapGrids = new List<XZGrid>();
allSnapGrids.Add(XZSnapGrid);
return allSnapGrids;
}
public void UpdateProjectedBoxFacePivotPoints(GameObject hierarchyRoot, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints, bool keepCurrentSnapSurface)
{
OrientedBox hierarchyWorldOrientedBox = hierarchyRoot.GetHierarchyWorldOrientedBox();
if (!hierarchyWorldOrientedBox.IsValid()) return;
if(keepCurrentSnapSurface)
{
if (!_objectSnapSurface.IsValid) return;
projectedBoxFacePivotPoints.FromOrientedBoxAndSnapSurface(hierarchyWorldOrientedBox, _objectSnapSurface);
}
else
{
_objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
projectedBoxFacePivotPoints.FromOrientedBoxAndSnapSurface(hierarchyWorldOrientedBox, _objectSnapSurface);
}
}
public void SnapObjectHierarchy(GameObject hierarchyRoot, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints, float offsetFromSnapSurface)
{
_objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
if (_objectSnapSurface.IsValid)
{
OrientedBox hierarchyWorldOrientedBox = hierarchyRoot.GetHierarchyWorldOrientedBox();
if (!hierarchyWorldOrientedBox.IsValid()) return;
Vector3 pivotPoint = projectedBoxFacePivotPoints.ActivePoint;
if (Settings.UseOriginalPivot) pivotPoint = hierarchyRoot.transform.position;
if (Settings.SnapToCursorHitPoint || Settings.EnableObjectToObjectSnap)
{
SnapObjectHierarchyPosition(hierarchyRoot, pivotPoint, _objectSnapSurface.CursorPickPoint, projectedBoxFacePivotPoints, offsetFromSnapSurface);
}
else
{
if (_objectSnapSurface.SurfaceType == SnapSurfaceType.GridCell && Settings.SnapCenterToCenterForXZGrid && !Settings.UseOriginalPivot) SnapObjectHierarchyToCenterOfSnapSurface(hierarchyRoot, projectedBoxFacePivotPoints.CenterPoint, projectedBoxFacePivotPoints, offsetFromSnapSurface);
else
if (_objectSnapSurface.SurfaceType == SnapSurfaceType.ObjectCollider && Settings.SnapCenterToCenterForObjectSurface && !Settings.UseOriginalPivot) SnapObjectHierarchyToCenterOfSnapSurface(hierarchyRoot, projectedBoxFacePivotPoints.CenterPoint, projectedBoxFacePivotPoints, offsetFromSnapSurface);
else SnapObjectHierarchyPosition(hierarchyRoot, pivotPoint, _objectSnapSurface.GetSnapDestinationPointClosestToCursorPickPoint(), projectedBoxFacePivotPoints, offsetFromSnapSurface);
if (AllShortcutCombos.Instance.KeepSnappedHierarchyInSnapSurfaceArea.IsActive()) KeepSnappedHierarchyInSnapSurfaceArea(hierarchyRoot, projectedBoxFacePivotPoints);
}
if (Settings.EnableObjectToObjectSnap) SnapHierarchyToNearbyObjects(hierarchyRoot, projectedBoxFacePivotPoints);
}
}
public void SnapHierarchyToNearbyObjects(GameObject hierarchyRoot, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints)
{
Object2ObjectSnap.SnapResult snapResult = Object2ObjectSnap.Snap(hierarchyRoot, Settings.ObjectToObjectSnapEpsilon, ObjectSnapping.Get().ObjectSnapMask.ObjectCollectionMask.GetAllMaskedGameObjects());
if (snapResult.WasSnapped) projectedBoxFacePivotPoints.MovePoints(snapResult.SnapDestination - snapResult.SnapPivot);
}
public void SnapObjectHierarchyToCenterOfSnapSurface(GameObject hierarchyRoot, Vector3 snapPivotPoint, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints, float offsetFromSnapSurface)
{
_objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
if (_objectSnapSurface.IsValid)
{
OrientedBox hierarchyWorldOrientedBox = hierarchyRoot.GetHierarchyWorldOrientedBox();
if (!hierarchyWorldOrientedBox.IsValid()) return;
SnapObjectHierarchyPosition(hierarchyRoot, snapPivotPoint, _objectSnapSurface.Center, projectedBoxFacePivotPoints, offsetFromSnapSurface);
}
}
public void SnapObjectPosition(GameObject gameObject)
{
_objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
if(_objectSnapSurface.IsValid)
{
Transform objectTransform = gameObject.transform;
objectTransform.position = _objectSnapSurface.GetSnapDestinationPointClosestToCursorPickPoint();
}
}
public void SnapObjectPositionToSnapSurfaceCenter(GameObject gameObject)
{
_objectSnapSurface.FromMouseCursorRayHit(GetCursorRayHit());
if (_objectSnapSurface.IsValid)
{
Transform objectTransform = gameObject.transform;
objectTransform.position = _objectSnapSurface.Center;
}
}
#endregion
#region Private Methods
private void OnEnable()
{
if(!_wasInitialized)
{
CoordinateSystemRenderSettings coordSystemRenderSettings = XZSnapGrid.RenderableCoordinateSystem.RenderSettings;
coordSystemRenderSettings.SetAxisRenderInfinite(CoordinateSystemAxis.PositiveRight, true);
coordSystemRenderSettings.SetAxisRenderInfinite(CoordinateSystemAxis.NegativeRight, true);
coordSystemRenderSettings.SetAxisRenderInfinite(CoordinateSystemAxis.PositiveLook, true);
coordSystemRenderSettings.SetAxisRenderInfinite(CoordinateSystemAxis.NegativeLook, true);
_wasInitialized = true;
}
}
private MouseCursorRayHit GetCursorRayHit()
{
MouseCursor.Instance.PushObjectPickMaskFlags(MouseCursorObjectPickFlags.ObjectTerrain | MouseCursorObjectPickFlags.ObjectMesh);
MouseCursor.Instance.PushObjectMask(ObjectSnapMask);
MouseCursorRayHit cursorRayHit = MouseCursor.Instance.GetRayHit();
MouseCursor.Instance.PopObjectPickMaskFlags();
MouseCursor.Instance.PopObjectMask();
return cursorRayHit;
}
private void SnapObjectHierarchyPosition(GameObject hierarchyRoot, Vector3 snapPivotPoint, Vector3 snapDestinationPoint, ProjectedBoxFacePivotPoints projectedBoxFacePivotPoints, float offsetFromSnapSurface)
{
snapDestinationPoint += _objectSnapSurface.Plane.normal * offsetFromSnapSurface;
Transform hierarchyRootTransform = hierarchyRoot.transform;
Vector3 snapVector = hierarchyRootTransform.position - snapPivotPoint;
hierarchyRootTransform.position = snapDestinationPoint + snapVector;
projectedBoxFacePivotPoints.MovePoints(snapDestinationPoint - snapPivotPoint);
}
private void KeepSnappedHierarchyInSnapSurfaceArea(GameObject hierarchyRoot, ProjectedBoxFacePivotPoints projectedHierarchyBoxFacePivotPoints)
{
OrientedBox hierarchyWorldOrientedBox = hierarchyRoot.GetHierarchyWorldOrientedBox();
List<Vector3> worldBoxPoints = hierarchyWorldOrientedBox.GetCenterAndCornerPoints();
XZOrientedQuad3D snapSurfaceQuad = _objectSnapSurface.SurfaceQuad;
List<Plane> quadSegmentPlanes = snapSurfaceQuad.GetBoundarySegmentPlanesFacingOutward();
List<Vector3> pushVectors = new List<Vector3>(quadSegmentPlanes.Count);
// All box points which are in front of the surface quad's plane are outside
// the surface so we will have to push them back.
for(int segmentPlaneIndex = 0; segmentPlaneIndex < quadSegmentPlanes.Count; ++segmentPlaneIndex)
{
Plane segmentPlane = quadSegmentPlanes[segmentPlaneIndex];
Vector3 furthestPointInFront;
if(segmentPlane.GetFurthestPointInFront(worldBoxPoints, out furthestPointInFront))
{
Vector3 projectedPoint = segmentPlane.ProjectPoint(furthestPointInFront);
pushVectors.Add(projectedPoint - furthestPointInFront);
}
}
Transform hierarchyRootTransform = hierarchyRoot.transform;
foreach(Vector3 pushVector in pushVectors)
{
hierarchyRootTransform.position += pushVector;
projectedHierarchyBoxFacePivotPoints.MovePoints(pushVector);
}
}
#endregion
}
}
#endif