304 lines
12 KiB
C#
304 lines
12 KiB
C#
#if UNITY_EDITOR
|
|
using UnityEngine;
|
|
using UnityEditor;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace GSpawn
|
|
{
|
|
public class ObjectSpawnGuide : ScriptableObject
|
|
{
|
|
[SerializeField]
|
|
private PluginPrefab _sourcePrefab;
|
|
[SerializeField]
|
|
private GameObject _guide;
|
|
[NonSerialized]
|
|
private ObjectTransformSession _transformSession;
|
|
[NonSerialized]
|
|
private List<GameObject> _transformSessionTargets = new List<GameObject>();
|
|
[NonSerialized]
|
|
private ObjectBounds.QueryConfig _boundsQConfig;
|
|
|
|
public float volumeRadius { get { return _guide != null ? calcWorldOBB().extents.magnitude : 0.0f; } }
|
|
public bool isPresentInScene { get { return _guide != null && _guide.activeSelf; } }
|
|
public bool isTransformSessionActive { get { return _transformSession != null && _transformSession.isActive; } }
|
|
public ObjectTransformSession transformSession { get { return _transformSession; } }
|
|
public TransformTRS transformTRS { get { return isPresentInScene ? _guide.transform.createTransformTRS() : new TransformTRS(); } }
|
|
public GameObject gameObject { get { return _guide; } }
|
|
public Transform transform { get { return _guide.transform; } }
|
|
public Vector3 position { get { return _guide.transform.position; } }
|
|
public Quaternion rotation { get { return _guide.transform.rotation; } }
|
|
public Vector3 lossyScale { get { return _guide.transform.lossyScale; } }
|
|
public PluginPrefab sourcePrefab { get { return _sourcePrefab; } }
|
|
public ObjectBounds.QueryConfig worldOBBQConfig { get { return _boundsQConfig; } }
|
|
|
|
public ObjectSpawnGuide()
|
|
{
|
|
_boundsQConfig = ObjectBounds.QueryConfig.defaultConfig;
|
|
_boundsQConfig.objectTypes = GameObjectType.Mesh | GameObjectType.Sprite | GameObjectType.Terrain;
|
|
}
|
|
|
|
public void syncGridCellSizeToPrefabSize()
|
|
{
|
|
if (_sourcePrefab == null) return;
|
|
|
|
OBB prefabOBB = ObjectBounds.calcHierarchyWorldOBB(_sourcePrefab.prefabAsset, _boundsQConfig);
|
|
if (prefabOBB.isValid)
|
|
{
|
|
const float eps = 1e-4f;
|
|
var grid = PluginScene.instance.grid;
|
|
|
|
Vector3 prefabSize = prefabOBB.size;
|
|
grid.activeSettings.cellSizeX = MathEx.roundCorrectError(prefabSize.x, eps);
|
|
grid.activeSettings.cellSizeZ = MathEx.roundCorrectError(prefabSize.z, eps);
|
|
}
|
|
}
|
|
|
|
public bool isObjectPartOfGuideHierarchy(GameObject gameObject)
|
|
{
|
|
return isPresentInScene && (gameObject == _guide || gameObject.transform.IsChildOf(_guide.transform));
|
|
}
|
|
|
|
public void setRotationAndScale(Quaternion rotation, Vector3 scale)
|
|
{
|
|
if (isPresentInScene)
|
|
{
|
|
_guide.transform.rotation = rotation;
|
|
_guide.transform.setWorldScale(scale);
|
|
}
|
|
}
|
|
|
|
public void setRotation(Quaternion rotation)
|
|
{
|
|
if (isPresentInScene)
|
|
_guide.transform.rotation = rotation;
|
|
}
|
|
|
|
public OBB calcWorldOBB()
|
|
{
|
|
if (_guide == null) return OBB.getInvalid();
|
|
return ObjectBounds.calcHierarchyWorldOBB(_guide, _boundsQConfig);
|
|
}
|
|
|
|
public void onSceneGUI()
|
|
{
|
|
if (_guide == null && transformSession != null)
|
|
{
|
|
transformSession.end();
|
|
}
|
|
|
|
if (isPresentInScene && (_sourcePrefab == null || _sourcePrefab.prefabAsset == null))
|
|
{
|
|
destroyGuide();
|
|
return;
|
|
}
|
|
|
|
if (FixedShortcuts.cancelAction(Event.current))
|
|
{
|
|
destroyGuide();
|
|
return;
|
|
}
|
|
|
|
setGuideObjectActive(!PluginScene.instance.snapGridToPickedObjectEnabled);
|
|
|
|
// Note: We can't begin the session immediately after double clicking on UI elements
|
|
// such as prefab previews. So we will use this delay strategy.
|
|
if (!isTransformSessionActive && isPresentInScene)
|
|
{
|
|
transformSession.begin();
|
|
if (transformSession.sessionType == ObjectTransformSessionType.ModularSnap)
|
|
{
|
|
ObjectModularSnapSession modularSnapSession = transformSession as ObjectModularSnapSession;
|
|
modularSnapSession.setVerticalStep(_guide, _sourcePrefab.modularSnapVerticalStep);
|
|
}
|
|
else
|
|
if (transformSession.sessionType == ObjectTransformSessionType.SurfaceSnap)
|
|
{
|
|
ObjectSurfaceSnapSession surfaceSnapSession = transformSession as ObjectSurfaceSnapSession;
|
|
surfaceSnapSession.setAppliedOffsetFromSurface(_sourcePrefab.surfaceSnapAppliedOffsetFromSurface);
|
|
}
|
|
}
|
|
|
|
if (isTransformSessionActive)
|
|
{
|
|
// Note: Check if the transform session has modified the scale or rotation of the spawn guide.
|
|
TransformTRS spawnGuideTRS = transformTRS;
|
|
transformSession.onSceneGUI();
|
|
if (spawnGuideTRS.rotationOrScaleDiffers(transformTRS)) storeTRSInSourcePrefab();
|
|
if (transformSession.sessionType == ObjectTransformSessionType.ModularSnap)
|
|
{
|
|
ObjectModularSnapSession modularSnapSession = transformSession as ObjectModularSnapSession;
|
|
_sourcePrefab.modularSnapVerticalStep = modularSnapSession.getVerticalStep(_guide);
|
|
}
|
|
else
|
|
if (transformSession.sessionType == ObjectTransformSessionType.SurfaceSnap)
|
|
{
|
|
ObjectSurfaceSnapSession surfaceSnapSession = transformSession as ObjectSurfaceSnapSession;
|
|
_sourcePrefab.surfaceSnapAppliedOffsetFromSurface = surfaceSnapSession.appliedOffsetFromSurface;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void usePrefab(PluginPrefab prefab, ObjectTransformSession transformSession)
|
|
{
|
|
_sourcePrefab = prefab;
|
|
|
|
UndoEx.saveEnabledState();
|
|
UndoEx.enabled = false;
|
|
if (_guide != null)
|
|
{
|
|
ObjectEvents.onObjectWillBeDestroyed(_guide);
|
|
DestroyImmediate(_guide);
|
|
}
|
|
_guide = _sourcePrefab.spawnDisconnected();
|
|
_guide.makeEditorOnly();
|
|
|
|
string removeStr = "(Clone)";
|
|
int substrIndex = _guide.name.IndexOf(removeStr);
|
|
_guide.name = _guide.name.Remove(substrIndex, removeStr.Length);
|
|
_guide.transform.parent = null; // Note: Don't attach to object group.
|
|
prefab.spawnGuideTransformTRS.applyRotationAndScale(_guide.transform);
|
|
_guide.AddComponent<ObjectSpawnGuideMono>();
|
|
UndoEx.restoreEnabledState();
|
|
|
|
_transformSessionTargets.Clear();
|
|
_transformSessionTargets.Add(_guide);
|
|
|
|
if (_transformSession != null) _transformSession.end();
|
|
_transformSession = transformSession;
|
|
_transformSession.end();
|
|
_transformSession.bindTargetObjects(_transformSessionTargets);
|
|
}
|
|
|
|
public void resetRotationToOriginal()
|
|
{
|
|
if (!isPresentInScene) return;
|
|
if (!_transformSession.clientCanUpdateTargetTransforms) return;
|
|
|
|
UndoEx.recordTransform(_guide.transform);
|
|
_guide.resetRotationToOriginal();
|
|
storeTRSInSourcePrefab();
|
|
}
|
|
|
|
public void resetScaleToOriginal()
|
|
{
|
|
if (!isPresentInScene) return;
|
|
if (!_transformSession.clientCanUpdateTargetTransforms) return;
|
|
|
|
UndoEx.recordTransform(_guide.transform);
|
|
_guide.transform.localScale = _sourcePrefab.prefabAsset.transform.localScale;
|
|
storeTRSInSourcePrefab();
|
|
}
|
|
|
|
public void rotate(Vector3 axis, float degrees)
|
|
{
|
|
if (!isPresentInScene) return;
|
|
if (!_transformSession.clientCanUpdateTargetTransforms) return;
|
|
|
|
UndoEx.recordTransform(_guide.transform);
|
|
_guide.transform.Rotate(axis, degrees, Space.World);
|
|
|
|
_transformSession.onTargetTransformsChanged();
|
|
storeTRSInSourcePrefab();
|
|
ObjectEvents.onObjectsTransformed();
|
|
}
|
|
|
|
public void rotate(Vector3 point, Vector3 axis, float degrees)
|
|
{
|
|
if (!isPresentInScene) return;
|
|
if (!_transformSession.clientCanUpdateTargetTransforms) return;
|
|
|
|
UndoEx.recordTransform(_guide.transform);
|
|
_guide.transform.RotateAround(point, axis, degrees);
|
|
|
|
_transformSession.onTargetTransformsChanged();
|
|
storeTRSInSourcePrefab();
|
|
ObjectEvents.onObjectsTransformed();
|
|
}
|
|
|
|
public void setGuideObjectActive(bool active)
|
|
{
|
|
if (_guide != null && _guide.activeSelf != active)
|
|
{
|
|
_guide.SetActive(active);
|
|
if (!active) _transformSession.end();
|
|
else _transformSession.begin();
|
|
}
|
|
}
|
|
|
|
public GameObject spawn()
|
|
{
|
|
if (_guide != null && _sourcePrefab != null && _sourcePrefab.prefabAsset != null)
|
|
return _sourcePrefab.spawn(_guide.transform.position, _guide.transform.rotation, _guide.transform.lossyScale);
|
|
|
|
return null;
|
|
}
|
|
|
|
public GameObject spawn(Vector3 position, Quaternion rotation, Vector3 scale)
|
|
{
|
|
if (_guide != null && _sourcePrefab != null && _sourcePrefab.prefabAsset != null)
|
|
return _sourcePrefab.spawn(position, rotation, scale);
|
|
|
|
return null;
|
|
}
|
|
|
|
public void randomizeTransformIfNecessary(TransformRandomizationSettings randomizationSettings, Vector3 surfaceNormal)
|
|
{
|
|
if (isPresentInScene) randomizationSettings.randomizeTransform(_guide.transform, surfaceNormal);
|
|
}
|
|
|
|
public void destroyGuide()
|
|
{
|
|
if (_guide == null) return;
|
|
|
|
if (_transformSession != null) _transformSession.end();
|
|
_transformSessionTargets.Clear();
|
|
|
|
if (_guide != null)
|
|
{
|
|
ObjectEvents.onObjectWillBeDestroyed(_guide);
|
|
DestroyImmediate(_guide);
|
|
}
|
|
}
|
|
|
|
public void storeTRSInSourcePrefab()
|
|
{
|
|
if (isPresentInScene)
|
|
{
|
|
_sourcePrefab.spawnGuideTransformTRS = _guide.transform.createTransformTRS();
|
|
}
|
|
}
|
|
|
|
private void onPlayModeStateChanged(PlayModeStateChange stateChange)
|
|
{
|
|
destroyGuide();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
EditorApplication.playModeStateChanged += onPlayModeStateChanged;
|
|
Selection.selectionChanged += onEditorSelectionChanged;
|
|
Undo.undoRedoPerformed += onUndoRedo;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
destroyGuide();
|
|
|
|
EditorApplication.playModeStateChanged -= onPlayModeStateChanged;
|
|
Selection.selectionChanged -= onEditorSelectionChanged;
|
|
Undo.undoRedoPerformed -= onUndoRedo;
|
|
}
|
|
|
|
private void onEditorSelectionChanged()
|
|
{
|
|
destroyGuide();
|
|
}
|
|
|
|
private void onUndoRedo()
|
|
{
|
|
if (_guide == null && _transformSession != null) _transformSession.end();
|
|
}
|
|
}
|
|
}
|
|
#endif |