ZeroVR/ZeroPacientVR/Assets/Octave3D World Builder/Scripts/Objects/Object Transform Sessions/ObjectGrabSession.cs

258 lines
13 KiB
C#

#if UNITY_EDITOR
using UnityEngine;
using System.Collections.Generic;
namespace O3DWB
{
public class ObjectGrabSession
{
private enum State
{
Inactive = 0,
Moving,
Rotating,
Scaling
}
private struct ObjectSurfaceInfo
{
public Plane SurfacePlane;
public Vector3 SitPoint;
}
private State _state;
private List<GameObject> _grabbedObjects;
private Dictionary<GameObject, Vector3> _objectToPivotDir = new Dictionary<GameObject, Vector3>();
private Dictionary<GameObject, ObjectSurfaceInfo> _objectToSurfaceInfo = new Dictionary<GameObject, ObjectSurfaceInfo>();
private Dictionary<GameObject, Vector3> _objectToWorldScale = new Dictionary<GameObject, Vector3>();
private Vector2 _cursorPosAtScaleBegin;
private ObjectMask _rayHitMask = new ObjectMask();
private MouseCursorRayHit _currentCursorRayHit;
private Vector3 _surfaceHitPoint;
private ObjectGrabSettings _grabSettings;
public bool IsActive { get { return _state != State.Inactive; } }
public ObjectGrabSettings Settings { set { if (value != null) _grabSettings = value; } }
public void Begin(List<GameObject> grabbedObjects)
{
if (_grabSettings == null || grabbedObjects == null || grabbedObjects.Count == 0 || IsActive) return;
_grabbedObjects = Octave3DWorldBuilder.ActiveInstance.GetRoots(grabbedObjects);
foreach(var grabbedObj in _grabbedObjects) _rayHitMask.ObjectCollectionMask.Mask(grabbedObj.GetAllChildrenIncludingSelf());
MouseCursorRayHit cursorRayHit = GetCursorRayHit();
if (!cursorRayHit.WasAnythingHit) return;
_surfaceHitPoint = cursorRayHit.WasAnObjectHit ? cursorRayHit.ClosestObjectRayHit.HitPoint : cursorRayHit.GridCellRayHit.HitPoint;
_state = State.Moving;
foreach(var grabbedObject in _grabbedObjects)
{
if(grabbedObject != null)
{
_objectToPivotDir.Add(grabbedObject, grabbedObject.transform.position - _surfaceHitPoint);
}
}
}
public void End()
{
_state = State.Inactive;
if (_grabbedObjects != null) _grabbedObjects.Clear();
_rayHitMask.ObjectCollectionMask.UnmaskAll();
_objectToPivotDir.Clear();
_objectToSurfaceInfo.Clear();
_objectToWorldScale.Clear();
}
public void RenderGizmos()
{
if(IsActive && _grabSettings.ShowGrabLines)
{
foreach(var grabbedObject in _grabbedObjects)
{
GizmosEx.RenderLine(grabbedObject.GetHierarchyWorldOrientedBox().Center, _surfaceHitPoint, _grabSettings.GrabLineColor);
}
}
}
public void Update()
{
if(IsActive)
{
if (AllShortcutCombos.Instance.GrabRotateSelection.IsActive()) _state = State.Rotating;
else if (AllShortcutCombos.Instance.GrabScaleSelection.IsActive())
{
if(_state != State.Scaling)
{
_objectToWorldScale.Clear();
foreach (var grabbedObject in _grabbedObjects)
{
if (grabbedObject != null)
{
_objectToWorldScale.Add(grabbedObject, grabbedObject.transform.lossyScale);
}
}
_cursorPosAtScaleBegin = MouseCursor.Instance.Position;
_state = State.Scaling;
}
}
else
{
// Need to reset the anchor relationships because the cursor was moved without
// the objects following it.
if (_state == State.Rotating || _state == State.Scaling)
{
_objectToPivotDir.Clear();
foreach (var grabbedObject in _grabbedObjects)
{
if (grabbedObject != null)
{
_objectToPivotDir.Add(grabbedObject, grabbedObject.transform.position - _surfaceHitPoint);
}
}
}
_state = State.Moving;
}
_currentCursorRayHit = GetCursorRayHit();
if (!_currentCursorRayHit.WasAnythingHit) return;
if(_currentCursorRayHit.WasAnythingHit)
{
if (_currentCursorRayHit.WasAnObjectHit) _surfaceHitPoint = _currentCursorRayHit.ClosestObjectRayHit.HitPoint;
else _surfaceHitPoint = _currentCursorRayHit.GridCellRayHit.HitPoint;
}
if (_state == State.Moving || _objectToSurfaceInfo.Count == 0)
{
if (_currentCursorRayHit.WasAnObjectHit)
{
GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
GameObjectRayHit objectRayHit = _currentCursorRayHit.ClosestObjectRayHit;
foreach (var grabbedObject in _grabbedObjects)
{
if (grabbedObject == null) continue;
Transform objectTransform = grabbedObject.transform;
objectTransform.position = objectRayHit.HitPoint + _objectToPivotDir[grabbedObject];
if (objectRayHit.WasTerrainHit)
{
Ray ray = new Ray(grabbedObject.GetHierarchyWorldOrientedBox().Center + Vector3.up, -Vector3.up);
GameObjectRayHit sitPointHit = null;
if (objectRayHit.HitObject.RaycastTerrainReverseIfFail(ray, out sitPointHit))
{
Plane surfacePlane = new Plane(sitPointHit.HitNormal, sitPointHit.HitPoint);
if (_grabSettings.AlignAxis) AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, sitPointHit.HitNormal);
grabbedObject.PlaceHierarchyOnPlane(surfacePlane);
if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis) objectTransform.position += _grabSettings.OffsetFromSurface * sitPointHit.HitNormal;
if (_grabSettings.EmbedInSurfaceWhenNoAlign && !_grabSettings.AlignAxis) grabbedObject.EmbedInSurfaceByVertex(-Vector3.up, objectRayHit.HitObject);
ObjectSurfaceInfo surfaceInfo = new ObjectSurfaceInfo();
surfaceInfo.SurfacePlane = surfacePlane;
surfaceInfo.SitPoint = sitPointHit.HitPoint;
SetObjectSurfaceInfo(grabbedObject, surfaceInfo);
}
}
else
if (objectRayHit.WasMeshHit)
{
Ray ray = new Ray(grabbedObject.GetHierarchyWorldOrientedBox().Center + objectRayHit.HitNormal * 2.0f, -objectRayHit.HitNormal);
GameObjectRayHit sitPointHit = null;
if (objectRayHit.HitObject.RaycastMeshReverseIfFail(ray, out sitPointHit))
{
Plane surfacePlane = new Plane(sitPointHit.HitNormal, sitPointHit.HitPoint);
if (_grabSettings.AlignAxis) AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, sitPointHit.HitNormal);
grabbedObject.PlaceHierarchyOnPlane(surfacePlane);
if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis) objectTransform.position += _grabSettings.OffsetFromSurface * sitPointHit.HitNormal;
if (_grabSettings.EmbedInSurfaceWhenNoAlign && !_grabSettings.AlignAxis) grabbedObject.EmbedInSurfaceByVertex(-sitPointHit.HitNormal, objectRayHit.HitObject);
ObjectSurfaceInfo surfaceInfo = new ObjectSurfaceInfo();
surfaceInfo.SurfacePlane = surfacePlane;
surfaceInfo.SitPoint = sitPointHit.HitPoint;
SetObjectSurfaceInfo(grabbedObject, surfaceInfo);
}
}
}
}
else
if (_currentCursorRayHit.WasACellHit)
{
GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
GridCellRayHit cellRayHit = _currentCursorRayHit.GridCellRayHit;
foreach (var grabbedObject in _grabbedObjects)
{
if (grabbedObject == null) continue;
Transform objectTransform = grabbedObject.transform;
objectTransform.position = cellRayHit.HitPoint + _objectToPivotDir[grabbedObject];
Plane surfacePlane = new Plane(cellRayHit.HitNormal, cellRayHit.HitPoint);
Vector3 sitPoint = surfacePlane.ProjectPoint(grabbedObject.GetWorldOrientedBox().Center);
if (_grabSettings.AlignAxis) AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, cellRayHit.HitNormal);
grabbedObject.PlaceHierarchyOnPlane(surfacePlane);
if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis) objectTransform.position += _grabSettings.OffsetFromSurface * cellRayHit.HitNormal;
ObjectSurfaceInfo surfaceInfo = new ObjectSurfaceInfo();
surfaceInfo.SurfacePlane = surfacePlane;
surfaceInfo.SitPoint = sitPoint;
SetObjectSurfaceInfo(grabbedObject, surfaceInfo);
}
}
}
else
if (_state == State.Rotating)
{
GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
float rotationSensitivity = _grabSettings.RotationSensitivity;
foreach (var grabbedObject in _grabbedObjects)
{
if (!_objectToSurfaceInfo.ContainsKey(grabbedObject)) continue;
var surfaceInfo = _objectToSurfaceInfo[grabbedObject];
OrientedBox worldOOBB = grabbedObject.GetHierarchyWorldOrientedBox();
grabbedObject.RotateHierarchyBoxAroundPoint(MouseCursor.Instance.OffsetSinceLastMouseMove.x * rotationSensitivity, _grabSettings.AlignAxis ? surfaceInfo.SurfacePlane.normal : Vector3.up, worldOOBB.Center);
}
}
else
if(_state == State.Scaling)
{
GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
Vector2 currentCursorPos = MouseCursor.Instance.Position;
Vector2 cursorOffsetFromScaleBegin = currentCursorPos - _cursorPosAtScaleBegin;
float scaleFactor = 1.0f + _grabSettings.ScaleSensitivity * cursorOffsetFromScaleBegin.x;
foreach (var grabbedObject in _grabbedObjects)
{
if (!_objectToSurfaceInfo.ContainsKey(grabbedObject) ||
!_objectToWorldScale.ContainsKey(grabbedObject)) continue;
var surfaceInfo = _objectToSurfaceInfo[grabbedObject];
grabbedObject.SetHierarchyWorldScaleByPivotPoint(_objectToWorldScale[grabbedObject] * scaleFactor, surfaceInfo.SitPoint);
}
}
}
}
private void SetObjectSurfaceInfo(GameObject gameObject, ObjectSurfaceInfo surfaceInfo)
{
if (gameObject == null) return;
if (_objectToSurfaceInfo.ContainsKey(gameObject)) _objectToSurfaceInfo[gameObject] = surfaceInfo;
else _objectToSurfaceInfo.Add(gameObject, surfaceInfo);
}
private MouseCursorRayHit GetCursorRayHit()
{
MouseCursor.Instance.PushObjectPickMaskFlags(MouseCursorObjectPickFlags.ObjectBox | MouseCursorObjectPickFlags.ObjectSprite);
MouseCursor.Instance.PushObjectMask(_rayHitMask);
MouseCursorRayHit cursorRayHit = MouseCursor.Instance.GetRayHit();
MouseCursor.Instance.PopObjectPickMaskFlags();
MouseCursor.Instance.PopObjectMask();
return cursorRayHit;
}
}
}
#endif