BITFALL/Assets/GSpawn - Level Designer/Scripts/UI/Objects/ObjectTransformUI.cs

423 lines
18 KiB
C#

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace GSpawn
{
public class ObjectTransformUI : ScriptableObject
{
private IEnumerable<GameObject> _targetObjects;
private List<Transform> _targetTransforms = new List<Transform>();
//private List<Transform> _targetParentTransforms = new List<Transform>();
private TransformDiffCheck.DiffInfo _transformDiffInfo;
private ObjectLayerDiff _objectLayerDiff;
private bool[] _positionDiff = new bool[3];
private bool[] _rotationDiff = new bool[3];
private bool[] _scaleDiff = new bool[3];
private IMGUIContainer _settingsContainer;
private VisualElement _positionContainer;
private VisualElement _rotationContainer;
private VisualElement _scaleContainer;
private VisualElement _snapButtonsContainer;
private VisualElement _alignButtonsContainer;
private VisualElement _noObjectsAvailable;
private Button _snapAllAxesBtn;
public Func<bool> visibilityCondition { get; set; }
public void onTargetObjectsChanged()
{
TransformEx.getTransforms(_targetObjects, _targetTransforms);
_transformDiffInfo = TransformDiffCheck.checkLocalDiff(_targetTransforms);
_objectLayerDiff = ObjectLayerDiffCheck.checkDiff(_targetTransforms);
}
public void removeObjects(List<GameObject> gameObjects)
{
_targetTransforms.RemoveAll(item => gameObjects.Contains(item.gameObject));
_transformDiffInfo = TransformDiffCheck.checkLocalDiff(_targetTransforms);
_objectLayerDiff = ObjectLayerDiffCheck.checkDiff(_targetTransforms);
}
public void removeNullObjects()
{
_targetTransforms.RemoveAll(item => item == null);
_transformDiffInfo = TransformDiffCheck.checkLocalDiff(_targetTransforms);
_objectLayerDiff = ObjectLayerDiffCheck.checkDiff(_targetTransforms);
}
public void refresh()
{
onTargetObjectsChanged();
refreshTooltips();
}
public void refreshTooltips()
{
if (_snapAllAxesBtn != null)
_snapAllAxesBtn.tooltip = ShortcutProfileDb.instance.activeProfile.getShortcutUITooltip(ObjectSelectionShortcutNames.snapAllAxes, "Snap objects to the closest grid point along all 3 axes.");
}
public void build(IEnumerable<GameObject> targetObjects, VisualElement parent)
{
_targetObjects = targetObjects;
onTargetObjectsChanged();
createNoObjectsAvailableLabel(parent);
var dummyContainer = new IMGUIContainer();
dummyContainer.style.height = 0.0f;
parent.Add(dummyContainer);
dummyContainer.onGUIHandler += () =>
{
if (visibilityCondition != null && !visibilityCondition()) return;
if (_targetTransforms.Count == 0)
{
_settingsContainer.setDisplayVisible(false);
_positionContainer.setDisplayVisible(false);
_rotationContainer.setDisplayVisible(false);
_scaleContainer.setDisplayVisible(false);
_snapButtonsContainer.setDisplayVisible(false);
_alignButtonsContainer.setDisplayVisible(false);
_noObjectsAvailable.setDisplayVisible(true);
}
else
{
_settingsContainer.setDisplayVisible(true);
_positionContainer.setDisplayVisible(true);
_rotationContainer.setDisplayVisible(true);
_scaleContainer.setDisplayVisible(true);
_snapButtonsContainer.setDisplayVisible(true);
_alignButtonsContainer.setDisplayVisible(true);
_noObjectsAvailable.setDisplayVisible(false);
}
};
_settingsContainer = UI.createIMGUIContainer(parent);
_settingsContainer.style.alignSelf = Align.FlexStart;
_settingsContainer.style.height = 20.0f;
_settingsContainer.style.width = 200.0f;
_settingsContainer.style.marginTop = 0.0f;
_settingsContainer.style.marginLeft = UIValues.smallIconSize + 6.0f;
_settingsContainer.onGUIHandler += () =>
{
if (visibilityCondition != null && !visibilityCondition()) return;
if (_targetTransforms.Count != 0)
{
const float labelWidth = 40.0f;
EditorUIEx.saveLabelWidth();
EditorUIEx.saveShowMixedValue();
EditorGUIUtility.labelWidth = labelWidth;
// Layers
EditorGUI.showMixedValue = _objectLayerDiff.layer;
int layer = _objectLayerDiff.layer ? LayerEx.getMaxLayer() + 1 : _targetTransforms[0].gameObject.layer;
if (!_objectLayerDiff.layer)
{
const float top = 3.0f;
float left = -UIValues.smallIconSize - 2.0f;
if (PluginObjectLayerDb.instance.isLayerTerrainMesh(layer))
{
Rect rect = new Rect(left, top - 1.0f, UIValues.smallIconSize, UIValues.smallIconSize);
GUI.DrawTexture(rect, TexturePool.instance.terrain);
}
else if (PluginObjectLayerDb.instance.isLayerSphericalMesh(layer))
{
Rect rect = new Rect(left, top - 1.0f, UIValues.smallIconSize, UIValues.smallIconSize);
GUI.DrawTexture(rect, TexturePool.instance.greenSphere);
}
}
EditorGUI.BeginChangeCheck();
int newLayer = EditorGUILayout.LayerField("Layer ", layer, GUILayout.Width(150.0f));
if (EditorGUI.EndChangeCheck())
{
foreach (var t in _targetTransforms)
{
UndoEx.record(t.gameObject);
t.gameObject.layer = newLayer;
}
// Note: Needs to be updated after layers are changed.
_objectLayerDiff = ObjectLayerDiffCheck.checkDiff(_targetTransforms);
}
EditorUIEx.restoreShowMixedValue();
EditorUIEx.restoreLabelWidth();
}
};
const float floatFieldWidth = 80.0f;
_positionContainer = createChannelRow(TransformChannel.Position,
() =>
{
// Note: When objects are deleted from the scene this needs to be here.
// It seems that putting this in the 'dummyContainer.onGUIHandler'
// doesn't work.
_targetTransforms.RemoveAll(item => item == null);
if (_targetTransforms.Count == 0) return;
_transformDiffInfo.getPositionDiff(_positionDiff);
var result = EditorUIEx.vector3FieldEx(_targetTransforms[0].localPosition, _positionDiff, floatFieldWidth);
if (result.hasChanged)
{
UndoEx.recordTransforms(_targetTransforms);
foreach (var transform in _targetTransforms)
{
Vector3 newVal = transform.localPosition;
newVal[result.changedAxisIndex] = result.newAxisValue;
transform.localPosition = newVal;
}
ObjectEvents.onObjectsTransformedByUI();
}
}, parent);
_rotationContainer = createChannelRow(TransformChannel.Rotation,
() =>
{
if (_targetTransforms.Count == 0) return;
_transformDiffInfo.getRotationDiff(_rotationDiff);
var result = EditorUIEx.vector3FieldEx(_targetTransforms[0].localEulerAngles, _rotationDiff, floatFieldWidth);
if (result.hasChanged)
{
UndoEx.recordTransforms(_targetTransforms);
foreach (var transform in _targetTransforms)
{
Vector3 newVal = transform.localEulerAngles;
newVal[result.changedAxisIndex] = result.newAxisValue;
transform.localEulerAngles = newVal;
}
ObjectEvents.onObjectsTransformedByUI();
}
}, parent);
_scaleContainer = createChannelRow(TransformChannel.Scale,
() =>
{
if (_targetTransforms.Count == 0) return;
_transformDiffInfo.getScaleDiff(_scaleDiff);
var result = EditorUIEx.vector3FieldEx(_targetTransforms[0].localScale, _scaleDiff, floatFieldWidth);
if (result.hasChanged)
{
UndoEx.recordTransforms(_targetTransforms);
foreach (var transform in _targetTransforms)
{
Vector3 newVal = transform.localScale;
newVal[result.changedAxisIndex] = result.newAxisValue;
transform.localScale = newVal;
}
ObjectEvents.onObjectsTransformedByUI();
}
}, parent);
_snapButtonsContainer = createSnapButtons(parent);
_alignButtonsContainer = createAlignButtons(parent);
refreshTooltips();
}
private void createNoObjectsAvailableLabel(VisualElement parent)
{
_noObjectsAvailable = new Label("No objects available.");
_noObjectsAvailable.style.unityFontStyleAndWeight = FontStyle.Bold;
_noObjectsAvailable.style.marginLeft = 5.0f;
_noObjectsAvailable.style.color = UIValues.infoLabelColor;
parent.Add(_noObjectsAvailable);
}
private Button createResetButton(string tooltip, VisualElement parent)
{
var button = UI.createButton(TexturePool.instance.refresh, UI.ButtonStyle.Push, parent);
button.style.setBackgroundImage(TexturePool.instance.refresh, true);
button.style.unityBackgroundImageTintColor = Color.white;
button.tooltip = tooltip;
parent.Add(button);
return button;
}
private Label createTransformChannelLabel(string text, VisualElement parent)
{
var label = new Label(text);
label.style.marginTop = 1.0f;
label.style.width = 50.0f;
label.style.flexGrow = 1.0f;
parent.Add(label);
return label;
}
private VisualElement createChannelRow(TransformChannel channel, Action fieldIMGUIHandler, VisualElement parent)
{
VisualElement row = new VisualElement();
row.style.flexDirection = FlexDirection.Row;
parent.Add(row);
string channelName = "position";
if (channel == TransformChannel.Rotation) channelName = "rotation";
else if (channel == TransformChannel.Scale) channelName = "scale";
Button resetBtn = createResetButton("Reset " + channelName + (channel != TransformChannel.Scale ? " to 0." : " to 1."), row);
if (channel == TransformChannel.Position)
{
resetBtn.clicked += () =>
{
UndoEx.recordTransforms(_targetTransforms);
foreach (var transform in _targetTransforms)
transform.localPosition = Vector3.zero;
ObjectEvents.onObjectsTransformedByUI();
};
}
else
if (channel == TransformChannel.Rotation)
{
resetBtn.clicked += () =>
{
UndoEx.recordTransforms(_targetTransforms);
foreach (var transform in _targetTransforms)
transform.localRotation = Quaternion.identity;
ObjectEvents.onObjectsTransformedByUI();
};
}
else
if (channel == TransformChannel.Scale)
{
resetBtn.clicked += () =>
{
UndoEx.recordTransforms(_targetTransforms);
foreach (var transform in _targetTransforms)
transform.localScale = Vector3.one;
ObjectEvents.onObjectsTransformedByUI();
};
}
createTransformChannelLabel(channelName.replaceAt(0, Char.ToUpper(channelName[0])), row);
var container = new IMGUIContainer();
row.Add(container);
container.style.overflow = Overflow.Hidden;
container.onGUIHandler += fieldIMGUIHandler;
return row;
}
private VisualElement createSnapButtons(VisualElement parent)
{
var snapRow = new VisualElement();
parent.Add(snapRow);
snapRow.style.flexDirection = FlexDirection.Row;
const float buttonWidth = 90.0f;
var snapBtn = new Button();
snapRow.Add(snapBtn);
snapBtn.text = "Snap X";
snapBtn.tooltip = "Snap objects to the closest grid point along the X axis.";
snapBtn.style.width = buttonWidth * 0.6f;
snapBtn.clicked += () =>
{
UndoEx.recordTransforms(_targetTransforms);
PluginScene.instance.grid.snapTransformsAxis(_targetTransforms, 0);
ObjectEvents.onObjectsTransformedByUI();
};
snapBtn = new Button();
snapRow.Add(snapBtn);
snapBtn.text = "Snap Y";
snapBtn.tooltip = "Snap objects to the closest grid point along the Y axis.";
snapBtn.style.width = buttonWidth;
snapBtn.style.width = buttonWidth * 0.6f;
snapBtn.style.marginLeft = UIValues.actionButtonLeftMargin;
snapBtn.clicked += () =>
{
UndoEx.recordTransforms(_targetTransforms);
PluginScene.instance.grid.snapTransformsAxis(_targetTransforms, 1);
ObjectEvents.onObjectsTransformedByUI();
};
snapBtn = new Button();
snapRow.Add(snapBtn);
snapBtn.text = "Snap Z";
snapBtn.tooltip = "Snap objects to the closest grid point along the Z axis.";
snapBtn.style.width = buttonWidth;
snapBtn.style.width = buttonWidth * 0.6f;
snapBtn.style.marginLeft = UIValues.actionButtonLeftMargin;
snapBtn.clicked += () =>
{
UndoEx.recordTransforms(_targetTransforms);
PluginScene.instance.grid.snapTransformsAxis(_targetTransforms, 2);
ObjectEvents.onObjectsTransformedByUI();
};
_snapAllAxesBtn = new Button();
snapRow.Add(_snapAllAxesBtn);
_snapAllAxesBtn.text = "Snap all axes";
_snapAllAxesBtn.style.width = buttonWidth;
_snapAllAxesBtn.style.marginLeft = UIValues.actionButtonLeftMargin;
_snapAllAxesBtn.clicked += () =>
{
UndoEx.recordTransforms(_targetTransforms);
PluginScene.instance.grid.snapTransformsAllAxes(_targetTransforms);
ObjectEvents.onObjectsTransformedByUI();
};
return snapRow;
}
private VisualElement createAlignButtons(VisualElement parent)
{
var alignRow = new VisualElement();
parent.Add(alignRow);
alignRow.style.flexDirection = FlexDirection.Row;
const float buttonWidth = 90.0f;
var alignButton = new Button();
alignRow.Add(alignButton);
alignButton.text = "Align X";
alignButton.tooltip = "Align the object positions along the X axis.";
alignButton.style.width = buttonWidth * 0.6f;
alignButton.clicked += () =>
{
ObjectAlignment.alignObjects(_targetObjects, 0, true);
ObjectEvents.onObjectsTransformedByUI();
};
alignButton = new Button();
alignRow.Add(alignButton);
alignButton.text = "Align Y";
alignButton.tooltip = "Align the object positions along the Y axis.";
alignButton.style.width = buttonWidth * 0.6f;
alignButton.style.marginLeft = UIValues.actionButtonLeftMargin;
alignButton.clicked += () =>
{
ObjectAlignment.alignObjects(_targetObjects, 1, true);
ObjectEvents.onObjectsTransformedByUI();
};
alignButton = new Button();
alignRow.Add(alignButton);
alignButton.text = "Align Z";
alignButton.tooltip = "Align the object positions along the Z axis.";
alignButton.style.width = buttonWidth * 0.6f;
alignButton.style.marginLeft = UIValues.actionButtonLeftMargin;
alignButton.clicked += () =>
{
ObjectAlignment.alignObjects(_targetObjects, 2, true);
ObjectEvents.onObjectsTransformedByUI();
};
return alignRow;
}
}
}
#endif