290 lines
20 KiB
C#
290 lines
20 KiB
C#
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
#if UNITY_2021
|
|
using UnityEditor.UIElements;
|
|
#endif
|
|
|
|
namespace GSpawn
|
|
{
|
|
public enum ObjectSelectionGrowRotationConstraintMode
|
|
{
|
|
None = 0,
|
|
Exact,
|
|
Flexible
|
|
}
|
|
|
|
public class ObjectSelectionGrowSettings : PluginSettings<ObjectSelectionGrowSettings>
|
|
{
|
|
private const float _minDistanceThreshold = 0.0005f;
|
|
private const float _minAngleThreshold = 1e-5f;
|
|
private const float _maxAngleThreshold = 90.0f;
|
|
private const float _minPositionThreshold = 1e-5f;
|
|
|
|
[SerializeField]
|
|
private float _distanceThreshold = defaultDistanceThreshold;
|
|
|
|
[SerializeField]
|
|
private bool _xPositionConstraint = defaultXPositionConstraint;
|
|
[SerializeField]
|
|
private bool _useXPositionThreshold = defaultUseXPositionThreshold;
|
|
[SerializeField]
|
|
private float _xPositionThreshold = defaultXPositionThreshold;
|
|
[SerializeField]
|
|
private bool _growRight = defaultGrowRight;
|
|
[SerializeField]
|
|
private bool _growLeft = defaultGrowLeft;
|
|
|
|
[SerializeField]
|
|
private bool _yPositionConstraint = defaultYPositionConstraint;
|
|
[SerializeField]
|
|
private bool _useYPositionThreshold = defaultUseYPositionThreshold;
|
|
[SerializeField]
|
|
private float _yPositionThreshold = defaultYPositionThreshold;
|
|
[SerializeField]
|
|
private bool _growUp = defaultGrowUp;
|
|
[SerializeField]
|
|
private bool _growDown = defaultGrowDown;
|
|
|
|
[SerializeField]
|
|
private bool _zPositionConstraint = defaultZPositionConstraint;
|
|
[SerializeField]
|
|
private bool _useZPositionThreshold = defaultUseZPositionThreshold;
|
|
[SerializeField]
|
|
private float _zPositionThreshold = defaultZPositionThreshold;
|
|
[SerializeField]
|
|
private bool _growForward = defaultGrowForward;
|
|
[SerializeField]
|
|
private bool _growBackward = defaultGrowBackward;
|
|
|
|
[SerializeField]
|
|
private bool _prefabConstraint = defaultPrefabConstraint;
|
|
[SerializeField]
|
|
private ObjectSelectionGrowRotationConstraintMode _rotationConstraintMode = defaultRotationConstraintMode;
|
|
[SerializeField]
|
|
private float _angleThreshold = defaultAngleThreshold;
|
|
[SerializeField]
|
|
private bool _useMaxCountConstraint = defaultUseMaxCountConstraint;
|
|
[SerializeField]
|
|
private int _maxCount = defaultMaxCount;
|
|
[SerializeField]
|
|
private bool _ignoreOutOfView = defaultIgnoreOutOfView;
|
|
|
|
public float distanceThreshold { get { return _distanceThreshold; } set { UndoEx.record(this); _distanceThreshold = Mathf.Max(value, _minDistanceThreshold); EditorUtility.SetDirty(this); } }
|
|
|
|
public bool xPositionConstraint { get { return _xPositionConstraint; } set { UndoEx.record(this); _xPositionConstraint = value; EditorUtility.SetDirty(this); } }
|
|
public float xPositionThreshold { get { return _xPositionThreshold; } set { UndoEx.record(this); _xPositionThreshold = Mathf.Max(_minPositionThreshold, value); EditorUtility.SetDirty(this); } }
|
|
public bool useXPositionThreshold { get { return _useXPositionThreshold; } set { UndoEx.record(this); _useXPositionThreshold = value; EditorUtility.SetDirty(this); } }
|
|
public bool growRight { get { return _growRight; } set { UndoEx.record(this); _growRight = value; EditorUtility.SetDirty(this); } }
|
|
public bool growLeft { get { return _growLeft; } set { UndoEx.record(this); _growLeft = value; EditorUtility.SetDirty(this); } }
|
|
|
|
public bool yPositionConstraint { get { return _yPositionConstraint; } set { UndoEx.record(this); _yPositionConstraint = value; EditorUtility.SetDirty(this); } }
|
|
public bool useYPositionThreshold { get { return _useYPositionThreshold; } set { UndoEx.record(this); _useYPositionThreshold = value; EditorUtility.SetDirty(this); } }
|
|
public float yPositionThreshold { get { return _yPositionThreshold; } set { UndoEx.record(this); _yPositionThreshold = Mathf.Max(_minPositionThreshold, value); EditorUtility.SetDirty(this); } }
|
|
public bool growUp { get { return _growUp; } set { UndoEx.record(this); _growUp = value; EditorUtility.SetDirty(this); } }
|
|
public bool growDown { get { return _growDown; } set { UndoEx.record(this); _growDown = value; EditorUtility.SetDirty(this); } }
|
|
|
|
public bool zPositionConstraint { get { return _zPositionConstraint; } set { UndoEx.record(this); _zPositionConstraint = value; EditorUtility.SetDirty(this); } }
|
|
public bool useZPositionThreshold { get { return _useZPositionThreshold; } set { UndoEx.record(this); _useZPositionThreshold = value; EditorUtility.SetDirty(this); } }
|
|
public float zPositionThreshold { get { return _zPositionThreshold; } set { UndoEx.record(this); _zPositionThreshold = Mathf.Max(_minPositionThreshold, value); EditorUtility.SetDirty(this); } }
|
|
public bool growForward { get { return _growForward; } set { UndoEx.record(this); _growForward = value; EditorUtility.SetDirty(this); } }
|
|
public bool growBackward { get { return _growBackward; } set { UndoEx.record(this); _growBackward = value; EditorUtility.SetDirty(this); } }
|
|
|
|
public bool usePrefabConstraint { get { return _prefabConstraint; } set { UndoEx.record(this); _prefabConstraint = value; EditorUtility.SetDirty(this); } }
|
|
public ObjectSelectionGrowRotationConstraintMode rotationConstraintMode { get { return _rotationConstraintMode; } set { UndoEx.record(this); _rotationConstraintMode = value; EditorUtility.SetDirty(this); } }
|
|
public float angleThreshold { get { return _angleThreshold; } set { UndoEx.record(this); _angleThreshold = Mathf.Clamp(value, _minAngleThreshold, _maxAngleThreshold); } }
|
|
public bool useMaxCountConstraint { get { return _useMaxCountConstraint; } set { UndoEx.record(this); _useMaxCountConstraint = value; EditorUtility.SetDirty(this); } }
|
|
public int maxCount { get { return _maxCount; } set { UndoEx.record(this); _maxCount = Mathf.Max(value, 1); EditorUtility.SetDirty(this); } }
|
|
public bool ignoreOutOfView { get { return _ignoreOutOfView; } set { UndoEx.record(this); _ignoreOutOfView = value; EditorUtility.SetDirty(this); } }
|
|
|
|
public static float defaultDistanceThreshold { get { return _minDistanceThreshold; } }
|
|
|
|
public static bool defaultXPositionConstraint { get { return false; } }
|
|
public static bool defaultUseXPositionThreshold { get { return false; } }
|
|
public static float defaultXPositionThreshold { get { return 0.05f; } }
|
|
public static bool defaultGrowRight { get { return true; } }
|
|
public static bool defaultGrowLeft { get { return true; } }
|
|
|
|
public static bool defaultYPositionConstraint { get { return false; } }
|
|
public static bool defaultUseYPositionThreshold { get { return false; } }
|
|
public static float defaultYPositionThreshold { get { return 0.05f; } }
|
|
public static bool defaultGrowUp { get { return true; } }
|
|
public static bool defaultGrowDown { get { return true; } }
|
|
|
|
public static bool defaultZPositionConstraint { get { return false; } }
|
|
public static bool defaultUseZPositionThreshold { get { return false; } }
|
|
public static float defaultZPositionThreshold { get { return 0.05f; } }
|
|
public static bool defaultGrowForward { get { return true; } }
|
|
public static bool defaultGrowBackward { get { return true; } }
|
|
|
|
public static bool defaultPrefabConstraint { get { return false; } }
|
|
public static ObjectSelectionGrowRotationConstraintMode defaultRotationConstraintMode { get { return ObjectSelectionGrowRotationConstraintMode.None; } }
|
|
public static float defaultAngleThreshold { get { return _minAngleThreshold; } }
|
|
public static bool defaultUseMaxCountConstraint { get { return false; } }
|
|
public static int defaultMaxCount { get { return 50; } }
|
|
public static bool defaultIgnoreOutOfView { get { return false; } }
|
|
|
|
public void buildUI(VisualElement parent)
|
|
{
|
|
const float labelSize = 155.0f;
|
|
|
|
var floatField = UI.createFloatField("_distanceThreshold", serializedObject, "Distance threshold",
|
|
"The maximum allowed distance between objects.", _minDistanceThreshold, parent);
|
|
floatField.setChildLabelWidth(labelSize);
|
|
|
|
var toggle = UI.createToggle("_prefabConstraint", serializedObject, "Prefab constraint",
|
|
"If checked, the grow operation will only include objects that belong to prefabs that reside in the original selection.", parent);
|
|
toggle.setChildLabelWidth(labelSize);
|
|
|
|
// X/Y/Z position constraints
|
|
UI.createRowSeparator(parent);
|
|
createPositionConstraintControls(0, labelSize, parent);
|
|
createPositionConstraintControls(1, labelSize, parent);
|
|
createPositionConstraintControls(2, labelSize, parent);
|
|
|
|
UI.createRowSeparator(parent);
|
|
FloatField angleThresholdField = null;
|
|
var rotationConstraintModeField = UI.createEnumField(typeof(ObjectSelectionGrowRotationConstraintMode), "_rotationConstraintMode", serializedObject, "Rotation constraint mode",
|
|
"The type of rotation constraint to be applied.", parent);
|
|
rotationConstraintModeField.setChildLabelWidth(labelSize);
|
|
rotationConstraintModeField.RegisterValueChangedCallback(p =>
|
|
{
|
|
angleThresholdField.setDisplayVisible(rotationConstraintMode == ObjectSelectionGrowRotationConstraintMode.Flexible);
|
|
});
|
|
|
|
angleThresholdField = UI.createFloatField("_angleThreshold", serializedObject, "Angle threshold",
|
|
"Allows you to specify an angle threshold value in degrees used when checking alignment.", _minAngleThreshold, _maxAngleThreshold, parent);
|
|
angleThresholdField.setChildLabelWidth(labelSize);
|
|
angleThresholdField.setDisplayVisible(rotationConstraintMode == ObjectSelectionGrowRotationConstraintMode.Flexible);
|
|
|
|
IntegerField maxCountField = null;
|
|
toggle = UI.createToggle("_useMaxCountConstraint", serializedObject, "Max count constraint",
|
|
"If checked, the grow operation will stop when a maximum number of objects have been selected.", parent);
|
|
toggle.setChildLabelWidth(labelSize);
|
|
toggle.RegisterValueChangedCallback(p =>
|
|
{
|
|
maxCountField.setDisplayVisible(useMaxCountConstraint);
|
|
});
|
|
|
|
maxCountField = UI.createIntegerField("_maxCount", serializedObject, "Max count",
|
|
"The maximum number of objects that can be selected when the max count constraint is enabled.", 1, parent);
|
|
maxCountField.setChildLabelWidth(labelSize);
|
|
maxCountField.setDisplayVisible(useMaxCountConstraint);
|
|
|
|
toggle = UI.createToggle("_ignoreOutOfView", serializedObject, "Ignore out of view",
|
|
"If checked, the grow operation will not include objects that are culled from the perspective of the scene camera.", parent);
|
|
toggle.setChildLabelWidth(labelSize);
|
|
|
|
UI.createUseDefaultsButton(() => { useDefaults(); }, parent);
|
|
}
|
|
|
|
public override void useDefaults()
|
|
{
|
|
distanceThreshold = defaultDistanceThreshold;
|
|
|
|
xPositionConstraint = defaultXPositionConstraint;
|
|
useXPositionThreshold = defaultUseXPositionThreshold;
|
|
xPositionThreshold = defaultXPositionThreshold;
|
|
growRight = defaultGrowRight;
|
|
growLeft = defaultGrowLeft;
|
|
|
|
yPositionConstraint = defaultYPositionConstraint;
|
|
useYPositionThreshold = defaultUseYPositionThreshold;
|
|
yPositionThreshold = defaultYPositionThreshold;
|
|
growUp = defaultGrowUp;
|
|
growDown = defaultGrowDown;
|
|
|
|
zPositionConstraint = defaultZPositionConstraint;
|
|
useZPositionThreshold = defaultUseZPositionThreshold;
|
|
zPositionThreshold = defaultZPositionThreshold;
|
|
growForward = defaultGrowForward;
|
|
growBackward = defaultGrowBackward;
|
|
|
|
usePrefabConstraint = defaultPrefabConstraint;
|
|
rotationConstraintMode = defaultRotationConstraintMode;
|
|
useMaxCountConstraint = defaultUseMaxCountConstraint;
|
|
maxCount = defaultMaxCount;
|
|
ignoreOutOfView = defaultIgnoreOutOfView;
|
|
|
|
EditorUtility.SetDirty(this);
|
|
}
|
|
|
|
private static string[] _posConstr_PropertyNames = new string[] { "_xPositionConstraint", "_yPositionConstraint", "_zPositionConstraint" };
|
|
private static string[] _usePosThreshold_PropertyNames = new string[] { "_useXPositionThreshold", "_useYPositionThreshold", "_useZPositionThreshold" };
|
|
private static string[] _posThreshold_PropertyNames = new string[] { "_xPositionThreshold", "_yPositionThreshold", "_zPositionThreshold" };
|
|
private void createPositionConstraintControls(int axis, float labelSize, VisualElement parent)
|
|
{
|
|
const float indent = 20.0f;
|
|
string axisName = StringEx.axisIndexToAxisName(axis);
|
|
string label = axisName + " constraint";
|
|
string tooltip = string.Format("If checked, the grow operation will filter objects based on their {0} coordinates using different criteria.", axisName);
|
|
var posConstrToggle = UI.createToggle(_posConstr_PropertyNames[axis], serializedObject, label, tooltip, parent);
|
|
posConstrToggle.setChildLabelWidth(labelSize);
|
|
|
|
var ctrlContainer = new VisualElement();
|
|
parent.Add(ctrlContainer);
|
|
|
|
var thresholdContainer = new VisualElement();
|
|
ctrlContainer.Add(thresholdContainer);
|
|
thresholdContainer.style.flexDirection = FlexDirection.Row;
|
|
thresholdContainer.style.marginLeft = indent;
|
|
var thresholdToggle = UI.createToggle(_usePosThreshold_PropertyNames[axis], serializedObject, "", string.Format("Toggle {0} position threshold.", axisName), thresholdContainer);
|
|
thresholdToggle.style.marginRight = 0.0f;
|
|
thresholdToggle.style.flexGrow = 0.0f;
|
|
tooltip = string.Format("Specifies how much the object {0} coordinates can differ along the {0} axis relative to the original selection.", axisName);
|
|
var thresholdField = UI.createFloatField(_posThreshold_PropertyNames[axis], serializedObject,
|
|
string.Format("{0} threshold", axisName), tooltip, _minPositionThreshold, thresholdContainer);
|
|
thresholdField.setChildLabelWidth(labelSize - indent);
|
|
|
|
string growPstv_PropertyName = string.Empty;
|
|
string growNgtv_PropertyName = string.Empty;
|
|
string growPstv_Label = string.Empty;
|
|
string growNgtv_Label = string.Empty;
|
|
string growPstv_Dir = string.Empty;
|
|
string growNgtv_Dir = string.Empty;
|
|
if (axis == 0)
|
|
{
|
|
growPstv_PropertyName = "_growRight";
|
|
growNgtv_PropertyName = "_growLeft";
|
|
growPstv_Label = "Grow right";
|
|
growNgtv_Label = "Grow left";
|
|
growPstv_Dir = "rightward";
|
|
growNgtv_Dir = "leftward";
|
|
}
|
|
else
|
|
if (axis == 1)
|
|
{
|
|
growPstv_PropertyName = "_growUp";
|
|
growNgtv_PropertyName = "_growDown";
|
|
growPstv_Label = "Grow up";
|
|
growNgtv_Label = "Grow down";
|
|
growPstv_Dir = "upward";
|
|
growNgtv_Dir = "downward";
|
|
}
|
|
else
|
|
if (axis == 2)
|
|
{
|
|
growPstv_PropertyName = "_growForward";
|
|
growNgtv_PropertyName = "_growBackward";
|
|
growPstv_Label = "Grow forward";
|
|
growNgtv_Label = "Grow back";
|
|
growPstv_Dir = "forward";
|
|
growNgtv_Dir = "backward";
|
|
}
|
|
|
|
tooltip = string.Format("");
|
|
var growPstvField = UI.createToggle(growPstv_PropertyName, serializedObject, growPstv_Label,
|
|
string.Format("If checked, the grow operation can extend {0} relative to the original selection.", growPstv_Dir), ctrlContainer);
|
|
growPstvField.setChildLabelWidth(labelSize);
|
|
growPstvField.style.marginLeft = indent;
|
|
var growNgtvField = UI.createToggle(growNgtv_PropertyName, serializedObject, growNgtv_Label,
|
|
string.Format("If checked, the grow operation can extend {0} relative to the original selection.", growNgtv_Dir), ctrlContainer);
|
|
growNgtvField.setChildLabelWidth(labelSize);
|
|
growNgtvField.style.marginLeft = indent;
|
|
posConstrToggle.RegisterValueChangedCallback(p => { ctrlContainer.setDisplayVisible(p.newValue);});
|
|
ctrlContainer.setDisplayVisible(posConstrToggle.value);
|
|
}
|
|
}
|
|
}
|
|
#endif |