Merge remote-tracking branch 'origin/master' into 1.10
This commit is contained in:
@@ -112,6 +112,12 @@ namespace FlaxEditor.Content
|
||||
throw new TargetException("Missing Visual Script asset.");
|
||||
_type.Asset.SetScriptInstanceParameterValue(_parameter.Name, (Object)obj, value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Invoke(object obj, object[] parameters)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
sealed class VisualScriptMethodInfo : IScriptMemberInfo
|
||||
@@ -240,6 +246,14 @@ namespace FlaxEditor.Content
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Invoke(object obj, object[] parameters)
|
||||
{
|
||||
if (!_type.Asset)
|
||||
throw new TargetException("Missing Visual Script asset.");
|
||||
return _type.Asset.InvokeMethod(_index, obj, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
@@ -127,12 +128,41 @@ namespace FlaxEditor.CustomEditors
|
||||
|
||||
_isSetBlocked = true;
|
||||
Initialize(layout);
|
||||
ShowButtons();
|
||||
Refresh();
|
||||
_isSetBlocked = false;
|
||||
|
||||
CurrentCustomEditor = prev;
|
||||
}
|
||||
|
||||
private void ShowButtons()
|
||||
{
|
||||
var values = Values;
|
||||
if (values == null || values.HasDifferentTypes)
|
||||
return;
|
||||
var type = TypeUtils.GetObjectType(values[0]);
|
||||
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
if (!method.HasAttribute(typeof(ButtonAttribute)) ||
|
||||
method.ParametersCount != 0)
|
||||
continue;
|
||||
var attribute = method.GetAttribute<ButtonAttribute>();
|
||||
var text = string.IsNullOrEmpty(attribute.Text) ? Utilities.Utils.GetPropertyNameUI(method.Name) : attribute.Text;
|
||||
var tooltip = string.IsNullOrEmpty(attribute.Tooltip) ? Editor.Instance.CodeDocs.GetTooltip(method) : attribute.Tooltip;
|
||||
var button = _layout.Button(text, tooltip);
|
||||
button.Button.Tag = method;
|
||||
button.Button.ButtonClicked += OnButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnButtonClicked(Button button)
|
||||
{
|
||||
var method = (ScriptMemberInfo)button.Tag;
|
||||
var obj = method.IsStatic ? null : Values[0];
|
||||
method.Invoke(obj);
|
||||
}
|
||||
|
||||
internal static CustomEditor CurrentCustomEditor;
|
||||
|
||||
internal void OnChildCreated(CustomEditor child)
|
||||
@@ -271,8 +301,16 @@ namespace FlaxEditor.CustomEditors
|
||||
_valueToSet = null;
|
||||
|
||||
// Assign value
|
||||
for (int i = 0; i < _values.Count; i++)
|
||||
_values[i] = val;
|
||||
if (val is IList l && l.Count == _values.Count)
|
||||
{
|
||||
for (int i = 0; i < _values.Count; i++)
|
||||
_values[i] = l[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < _values.Count; i++)
|
||||
_values[i] = val;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -11,7 +12,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
class BezierCurveObjectEditor<T> : CustomEditor where T : struct
|
||||
{
|
||||
private bool _isSetting;
|
||||
private int _firstTimeShow;
|
||||
private BezierCurveEditor<T> _curve;
|
||||
private Splitter _splitter;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -20,6 +23,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_curve = item.CustomControl;
|
||||
_curve.Height = 120.0f;
|
||||
_curve.Edited += OnCurveEdited;
|
||||
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
|
||||
_splitter = new Splitter
|
||||
{
|
||||
Moved = OnSplitterMoved,
|
||||
Parent = _curve,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchBottom,
|
||||
Bounds = new Rectangle(0, _curve.Height - Splitter.DefaultHeight, _curve.Width, Splitter.DefaultHeight),
|
||||
};
|
||||
}
|
||||
|
||||
private void OnCurveEdited()
|
||||
@@ -32,6 +43,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_isSetting = false;
|
||||
}
|
||||
|
||||
private void OnSplitterMoved(Float2 location)
|
||||
{
|
||||
_curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
@@ -44,12 +60,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_curve.SetKeyframes(value.Keyframes);
|
||||
_isSetting = false;
|
||||
}
|
||||
if (_firstTimeShow-- > 0)
|
||||
_curve.ShowWholeCurve();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_curve = null;
|
||||
_splitter = null;
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
@@ -111,7 +130,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
class LinearCurveObjectEditor<T> : CustomEditor where T : struct
|
||||
{
|
||||
private bool _isSetting;
|
||||
private int _firstTimeShow;
|
||||
private LinearCurveEditor<T> _curve;
|
||||
private Splitter _splitter;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -120,6 +141,14 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_curve = item.CustomControl;
|
||||
_curve.Height = 120.0f;
|
||||
_curve.Edited += OnCurveEdited;
|
||||
_firstTimeShow = 4; // For some weird reason it needs several frames of warmup (probably due to sliders smoothing)
|
||||
_splitter = new Splitter
|
||||
{
|
||||
Moved = OnSplitterMoved,
|
||||
Parent = _curve,
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchBottom,
|
||||
Bounds = new Rectangle(0, _curve.Height - Splitter.DefaultHeight, _curve.Width, Splitter.DefaultHeight),
|
||||
};
|
||||
}
|
||||
|
||||
private void OnCurveEdited()
|
||||
@@ -132,6 +161,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_isSetting = false;
|
||||
}
|
||||
|
||||
private void OnSplitterMoved(Float2 location)
|
||||
{
|
||||
_curve.Height = Mathf.Clamp(_splitter.PointToParent(location).Y, 50.0f, 1000.0f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
@@ -144,12 +178,15 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
_curve.SetKeyframes(value.Keyframes);
|
||||
_isSetting = false;
|
||||
}
|
||||
if (_firstTimeShow-- > 0)
|
||||
_curve.ShowWholeCurve();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_curve = null;
|
||||
_splitter = null;
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
|
||||
@@ -407,20 +407,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// <seealso cref="FlaxEditor.CustomEditors.Editors.GenericEditor" />
|
||||
public class UIControlControlEditor : GenericEditor
|
||||
{
|
||||
private Type _cachedType;
|
||||
private ScriptType[] _valueTypes;
|
||||
private bool _anchorDropDownClosed = true;
|
||||
private Button _pivotRelativeButton;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
_cachedType = null;
|
||||
if (HasDifferentTypes)
|
||||
{
|
||||
// TODO: support stable editing multiple different control types (via generic way or for transform-only)
|
||||
return;
|
||||
}
|
||||
|
||||
// Set control type button
|
||||
var space = layout.Space(20);
|
||||
var buttonText = "Set Type";
|
||||
@@ -445,12 +438,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
}
|
||||
|
||||
// Add control type helper label
|
||||
if (!Values.HasDifferentTypes)
|
||||
{
|
||||
var type = Values[0].GetType();
|
||||
_cachedType = type;
|
||||
var label = layout.AddPropertyItem("Type", "The type of the created control.");
|
||||
label.Label(type.FullName);
|
||||
label.Label(Values[0].GetType().FullName);
|
||||
}
|
||||
_valueTypes = Values.ValuesTypes;
|
||||
|
||||
// Show control properties
|
||||
base.Initialize(layout);
|
||||
@@ -720,22 +713,20 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
if (_cachedType != null)
|
||||
// Automatic layout rebuild if control type gets changed
|
||||
if (_valueTypes != null &&
|
||||
!Values.HasNull &&
|
||||
!Utils.ArraysEqual(_valueTypes, Values.ValuesTypes))
|
||||
{
|
||||
// Automatic layout rebuild if control type gets changed
|
||||
var type = Values.HasNull ? null : Values[0].GetType();
|
||||
if (type != _cachedType)
|
||||
{
|
||||
RebuildLayout();
|
||||
return;
|
||||
}
|
||||
RebuildLayout();
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh anchors
|
||||
GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes);
|
||||
if (xEq != _cachedXEq || yEq != _cachedYEq)
|
||||
{
|
||||
RebuildLayout();
|
||||
}
|
||||
// Refresh anchors
|
||||
GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes);
|
||||
if (xEq != _cachedXEq || yEq != _cachedYEq)
|
||||
{
|
||||
RebuildLayout();
|
||||
}
|
||||
|
||||
base.Refresh();
|
||||
|
||||
@@ -33,6 +33,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use the average for sliding different values.
|
||||
/// </summary>
|
||||
public virtual bool AllowSlidingForDifferentValues => true;
|
||||
|
||||
private ValueChanged _valueChanged;
|
||||
private float _defaultSlidingSpeed;
|
||||
private bool _slidingEnded = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -46,18 +55,31 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
XElement = grid.FloatValue();
|
||||
XElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
XElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
|
||||
|
||||
YElement = grid.FloatValue();
|
||||
YElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
YElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
ZElement = grid.FloatValue();
|
||||
ZElement.ValueBox.Category = Utils.ValueCategory.Angle;
|
||||
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
if (LinkedLabel != null)
|
||||
{
|
||||
@@ -69,33 +91,118 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
|
||||
private void OnXValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.X;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnYValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.Y;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnZValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.Z;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var useCachedAngles = isSliding && token == _cachedToken;
|
||||
|
||||
float x = (useCachedAngles && !XElement.IsSliding) ? _cachedAngles.X : XElement.ValueBox.Value;
|
||||
float y = (useCachedAngles && !YElement.IsSliding) ? _cachedAngles.Y : YElement.ValueBox.Value;
|
||||
float z = (useCachedAngles && !ZElement.IsSliding) ? _cachedAngles.Z : ZElement.ValueBox.Value;
|
||||
|
||||
x = Mathf.UnwindDegrees(x);
|
||||
y = Mathf.UnwindDegrees(y);
|
||||
z = Mathf.UnwindDegrees(z);
|
||||
|
||||
if (!useCachedAngles)
|
||||
|
||||
if (HasDifferentValues && Values.Count > 1)
|
||||
{
|
||||
_cachedAngles = new Float3(x, y, z);
|
||||
var xValue = XElement.ValueBox.Value;
|
||||
var yValue = YElement.ValueBox.Value;
|
||||
var zValue = ZElement.ValueBox.Value;
|
||||
|
||||
xValue = Mathf.UnwindDegrees(xValue);
|
||||
yValue = Mathf.UnwindDegrees(yValue);
|
||||
zValue = Mathf.UnwindDegrees(zValue);
|
||||
|
||||
var value = new Float3(xValue, yValue, zValue);
|
||||
|
||||
_cachedToken = token;
|
||||
|
||||
var newObjects = new object[Values.Count];
|
||||
// Handle Sliding
|
||||
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
|
||||
{
|
||||
Float3 average = Float3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = (Quaternion)Values[0];
|
||||
var euler = v.EulerAngles;
|
||||
|
||||
average += euler;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
var newValue = value - average;
|
||||
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var val = (Quaternion)v;
|
||||
Quaternion.Euler(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0, out Quaternion qVal);
|
||||
v = val * qVal;
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
|
||||
// Capture last sliding value
|
||||
if (_slidingEnded)
|
||||
_slidingEnded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
object v = Values[i];
|
||||
var val = (Quaternion)v;
|
||||
var euler = val.EulerAngles;
|
||||
Quaternion.Euler(_valueChanged == ValueChanged.X ? xValue : euler.X, _valueChanged == ValueChanged.Y ? yValue : euler.Y, _valueChanged == ValueChanged.Z ? zValue : euler.Z, out Quaternion qVal);
|
||||
v = val * qVal;
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newObjects, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
float x = (useCachedAngles && !XElement.IsSliding) ? _cachedAngles.X : XElement.ValueBox.Value;
|
||||
float y = (useCachedAngles && !YElement.IsSliding) ? _cachedAngles.Y : YElement.ValueBox.Value;
|
||||
float z = (useCachedAngles && !ZElement.IsSliding) ? _cachedAngles.Z : ZElement.ValueBox.Value;
|
||||
|
||||
_cachedToken = token;
|
||||
x = Mathf.UnwindDegrees(x);
|
||||
y = Mathf.UnwindDegrees(y);
|
||||
z = Mathf.UnwindDegrees(z);
|
||||
|
||||
Quaternion.Euler(x, y, z, out Quaternion value);
|
||||
SetValue(value, token);
|
||||
if (!useCachedAngles)
|
||||
{
|
||||
_cachedAngles = new Float3(x, y, z);
|
||||
}
|
||||
|
||||
_cachedToken = token;
|
||||
|
||||
Quaternion.Euler(x, y, z, out Quaternion value);
|
||||
SetValue(value, token);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -112,7 +219,73 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
// Get which values are different
|
||||
bool xDifferent = false;
|
||||
bool yDifferent = false;
|
||||
bool zDifferent = false;
|
||||
Float3 cachedFirstValue = Float3.Zero;
|
||||
Float3 average = Float3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var value = (Quaternion)Values[i];
|
||||
var euler = value.EulerAngles;
|
||||
|
||||
average += euler;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
cachedFirstValue = euler;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Mathf.NearEqual(cachedFirstValue.X, value.X))
|
||||
xDifferent = true;
|
||||
if (!Mathf.NearEqual(cachedFirstValue.Y, value.Y))
|
||||
yDifferent = true;
|
||||
if (!Mathf.NearEqual(cachedFirstValue.Z, value.Z))
|
||||
zDifferent = true;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
if (!xDifferent)
|
||||
{
|
||||
XElement.ValueBox.Value = cachedFirstValue.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
XElement.ValueBox.Value = average.X;
|
||||
else
|
||||
XElement.ValueBox.SlideSpeed = 0;
|
||||
XElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!yDifferent)
|
||||
{
|
||||
YElement.ValueBox.Value = cachedFirstValue.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
YElement.ValueBox.Value = average.Y;
|
||||
else
|
||||
YElement.ValueBox.SlideSpeed = 0;
|
||||
YElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!zDifferent)
|
||||
{
|
||||
ZElement.ValueBox.Value = cachedFirstValue.Z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
ZElement.ValueBox.Value = average.Z;
|
||||
else
|
||||
ZElement.ValueBox.SlideSpeed = 0;
|
||||
ZElement.ValueBox.Text = "---";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -121,6 +294,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement.ValueBox.Value = euler.X;
|
||||
YElement.ValueBox.Value = euler.Y;
|
||||
ZElement.ValueBox.Value = euler.Z;
|
||||
|
||||
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
Source/Editor/CustomEditors/Editors/TerrainLayerEditor.cs
Normal file
45
Source/Editor/CustomEditors/Editors/TerrainLayerEditor.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.CustomEditors.Elements;
|
||||
using FlaxEditor.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for picking terrain layers. Instead of choosing bit mask or layer index it shows a combo box with simple layer picking by name.
|
||||
/// </summary>
|
||||
public sealed class TerrainLayerEditor : CustomEditor
|
||||
{
|
||||
private ComboBoxElement element;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
element = layout.ComboBox();
|
||||
element.ComboBox.SetItems(LayersAndTagsSettings.GetCurrentTerrainLayers());
|
||||
element.ComboBox.SelectedIndex = (int)Values[0];
|
||||
element.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged;
|
||||
}
|
||||
|
||||
private void OnSelectedIndexChanged(ComboBox comboBox)
|
||||
{
|
||||
int value = comboBox.SelectedIndex;
|
||||
if (value == -1)
|
||||
value = 0;
|
||||
|
||||
SetValue(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
element.ComboBox.SelectedIndex = (int)Values[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,27 @@ using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// The value changed by custom Vector3 editors.
|
||||
/// </summary>
|
||||
public enum ValueChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// X value changed.
|
||||
/// </summary>
|
||||
X = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Y value changed.
|
||||
/// </summary>
|
||||
Y = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Z value changed.
|
||||
/// </summary>
|
||||
Z = 2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of the inspector used to edit Vector3 value type properties.
|
||||
/// </summary>
|
||||
@@ -49,14 +70,14 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
/// </summary>
|
||||
public bool LinkValues = false;
|
||||
|
||||
private enum ValueChanged
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Z = 2
|
||||
}
|
||||
/// <summary>
|
||||
/// Whether to use the average for sliding different values.
|
||||
/// </summary>
|
||||
public virtual bool AllowSlidingForDifferentValues => true;
|
||||
|
||||
private ValueChanged _valueChanged;
|
||||
private float _defaultSlidingSpeed;
|
||||
private bool _slidingEnded = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -83,19 +104,32 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement.SetLimits(limit);
|
||||
XElement.SetCategory(category);
|
||||
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||
XElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
|
||||
|
||||
YElement = grid.FloatValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.SetCategory(category);
|
||||
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||
YElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
ZElement = grid.FloatValue();
|
||||
ZElement.SetLimits(limit);
|
||||
ZElement.SetCategory(category);
|
||||
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||
ZElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
if (LinkedLabel != null)
|
||||
{
|
||||
@@ -118,8 +152,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
if (LinkValues)
|
||||
_valueChanged = ValueChanged.X;
|
||||
_valueChanged = ValueChanged.X;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
@@ -127,8 +160,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
if (LinkValues)
|
||||
_valueChanged = ValueChanged.Y;
|
||||
_valueChanged = ValueChanged.Y;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
@@ -136,8 +168,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
if (LinkValues)
|
||||
_valueChanged = ValueChanged.Z;
|
||||
_valueChanged = ValueChanged.Z;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
@@ -191,14 +222,79 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Float3(xValue, yValue, zValue);
|
||||
object v = Values[0];
|
||||
if (v is Vector3)
|
||||
v = (Vector3)value;
|
||||
else if (v is Float3)
|
||||
v = (Float3)value;
|
||||
else if (v is Double3)
|
||||
v = (Double3)value;
|
||||
SetValue(v, token);
|
||||
if (HasDifferentValues && Values.Count > 1)
|
||||
{
|
||||
var newObjects = new object[Values.Count];
|
||||
// Handle Sliding
|
||||
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
|
||||
{
|
||||
// TODO: handle linked values
|
||||
Float3 average = Float3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var castedValue = Float3.Zero;
|
||||
if (v is Vector3 asVector3)
|
||||
castedValue = asVector3;
|
||||
else if (v is Float3 asFloat3)
|
||||
castedValue = asFloat3;
|
||||
else if (v is Double3 asDouble3)
|
||||
castedValue = asDouble3;
|
||||
|
||||
average += castedValue;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
var newValue = value - average;
|
||||
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
if (v is Vector3 asVector3)
|
||||
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
else if (v is Float3 asFloat3)
|
||||
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
else if (v is Double3 asDouble3)
|
||||
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
|
||||
// Capture last sliding value
|
||||
if (_slidingEnded)
|
||||
_slidingEnded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle linked values
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
object v = Values[i];
|
||||
if (v is Vector3 asVector3)
|
||||
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
|
||||
else if (v is Float3 asFloat3)
|
||||
v = new Float3(_valueChanged == ValueChanged.X ? xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? zValue : asFloat3.Z);
|
||||
else if (v is Double3 asDouble3)
|
||||
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newObjects, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
object v = Values[0];
|
||||
if (v is Vector3)
|
||||
v = (Vector3)value;
|
||||
else if (v is Float3)
|
||||
v = (Float3)value;
|
||||
else if (v is Double3)
|
||||
v = (Double3)value;
|
||||
SetValue(v, token);
|
||||
}
|
||||
}
|
||||
|
||||
private float GetRatio(float value, float initialValue)
|
||||
@@ -218,7 +314,79 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
// Get which values are different
|
||||
bool xDifferent = false;
|
||||
bool yDifferent = false;
|
||||
bool zDifferent = false;
|
||||
Float3 cachedFirstValue = Float3.Zero;
|
||||
Float3 average = Float3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var value = Float3.Zero;
|
||||
if (v is Vector3 asVector3)
|
||||
value = asVector3;
|
||||
else if (v is Float3 asFloat3)
|
||||
value = asFloat3;
|
||||
else if (v is Double3 asDouble3)
|
||||
value = asDouble3;
|
||||
|
||||
average += value;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
cachedFirstValue = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Mathf.NearEqual(cachedFirstValue.X, value.X))
|
||||
xDifferent = true;
|
||||
if (!Mathf.NearEqual(cachedFirstValue.Y, value.Y))
|
||||
yDifferent = true;
|
||||
if (!Mathf.NearEqual(cachedFirstValue.Z, value.Z))
|
||||
zDifferent = true;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
if (!xDifferent)
|
||||
{
|
||||
XElement.ValueBox.Value = cachedFirstValue.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
XElement.ValueBox.Value = average.X;
|
||||
else
|
||||
XElement.ValueBox.SlideSpeed = 0;
|
||||
XElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!yDifferent)
|
||||
{
|
||||
YElement.ValueBox.Value = cachedFirstValue.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
YElement.ValueBox.Value = average.Y;
|
||||
else
|
||||
YElement.ValueBox.SlideSpeed = 0;
|
||||
YElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!zDifferent)
|
||||
{
|
||||
ZElement.ValueBox.Value = cachedFirstValue.Z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
ZElement.ValueBox.Value = average.Z;
|
||||
else
|
||||
ZElement.ValueBox.SlideSpeed = 0;
|
||||
ZElement.ValueBox.Text = "---";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -232,6 +400,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement.ValueBox.Value = value.X;
|
||||
YElement.ValueBox.Value = value.Y;
|
||||
ZElement.ValueBox.Value = value.Z;
|
||||
|
||||
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,6 +434,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DisplayStyle Style => DisplayStyle.Inline;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use the average for sliding different values.
|
||||
/// </summary>
|
||||
public virtual bool AllowSlidingForDifferentValues => true;
|
||||
|
||||
private ValueChanged _valueChanged;
|
||||
private float _defaultSlidingSpeed;
|
||||
private bool _slidingEnded = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -284,20 +468,33 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement = grid.DoubleValue();
|
||||
XElement.SetLimits(limit);
|
||||
XElement.SetCategory(category);
|
||||
XElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += ClearToken;
|
||||
XElement.ValueBox.ValueChanged += OnXValueChanged;
|
||||
XElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
_defaultSlidingSpeed = XElement.ValueBox.SlideSpeed;
|
||||
|
||||
YElement = grid.DoubleValue();
|
||||
YElement.SetLimits(limit);
|
||||
YElement.SetCategory(category);
|
||||
YElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += ClearToken;
|
||||
YElement.ValueBox.ValueChanged += OnYValueChanged;
|
||||
YElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
ZElement = grid.DoubleValue();
|
||||
ZElement.SetLimits(limit);
|
||||
ZElement.SetCategory(category);
|
||||
ZElement.ValueBox.ValueChanged += OnValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += ClearToken;
|
||||
ZElement.ValueBox.ValueChanged += OnZValueChanged;
|
||||
ZElement.ValueBox.SlidingEnd += () =>
|
||||
{
|
||||
_slidingEnded = true;
|
||||
ClearToken();
|
||||
};
|
||||
|
||||
if (LinkedLabel != null)
|
||||
{
|
||||
@@ -316,22 +513,115 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
}
|
||||
|
||||
private void OnXValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.X;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnYValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.Y;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnZValueChanged()
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
_valueChanged = ValueChanged.Z;
|
||||
OnValueChanged();
|
||||
}
|
||||
|
||||
private void OnValueChanged()
|
||||
{
|
||||
if (IsSetBlocked || Values == null)
|
||||
return;
|
||||
|
||||
var xValue = XElement.ValueBox.Value;
|
||||
var yValue = YElement.ValueBox.Value;
|
||||
var zValue = ZElement.ValueBox.Value;
|
||||
|
||||
var isSliding = XElement.IsSliding || YElement.IsSliding || ZElement.IsSliding;
|
||||
var token = isSliding ? this : null;
|
||||
var value = new Double3(XElement.ValueBox.Value, YElement.ValueBox.Value, ZElement.ValueBox.Value);
|
||||
object v = Values[0];
|
||||
if (v is Vector3)
|
||||
v = (Vector3)value;
|
||||
else if (v is Float3)
|
||||
v = (Float3)value;
|
||||
else if (v is Double3)
|
||||
v = (Double3)value;
|
||||
SetValue(v, token);
|
||||
var value = new Double3(xValue, yValue, zValue);
|
||||
if (HasDifferentValues && Values.Count > 1)
|
||||
{
|
||||
var newObjects = new object[Values.Count];
|
||||
// Handle Sliding
|
||||
if (AllowSlidingForDifferentValues && (isSliding || _slidingEnded))
|
||||
{
|
||||
// TODO: handle linked values
|
||||
Double3 average = Double3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var castedValue = Double3.Zero;
|
||||
if (v is Vector3 asVector3)
|
||||
castedValue = asVector3;
|
||||
else if (v is Float3 asFloat3)
|
||||
castedValue = asFloat3;
|
||||
else if (v is Double3 asDouble3)
|
||||
castedValue = asDouble3;
|
||||
|
||||
average += castedValue;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
var newValue = value - average;
|
||||
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
if (v is Vector3 asVector3)
|
||||
v = asVector3 + new Vector3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
else if (v is Float3 asFloat3)
|
||||
v = asFloat3 + new Float3(_valueChanged == ValueChanged.X ? (float)newValue.X : 0, _valueChanged == ValueChanged.Y ? (float)newValue.Y : 0, _valueChanged == ValueChanged.Z ? (float)newValue.Z : 0);
|
||||
else if (v is Double3 asDouble3)
|
||||
v = asDouble3 + new Double3(_valueChanged == ValueChanged.X ? newValue.X : 0, _valueChanged == ValueChanged.Y ? newValue.Y : 0, _valueChanged == ValueChanged.Z ? newValue.Z : 0);
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
|
||||
// Capture last sliding value
|
||||
if (_slidingEnded)
|
||||
_slidingEnded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle linked values
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
object v = Values[i];
|
||||
if (v is Vector3 asVector3)
|
||||
v = new Vector3(_valueChanged == ValueChanged.X ? xValue : asVector3.X, _valueChanged == ValueChanged.Y ? yValue : asVector3.Y, _valueChanged == ValueChanged.Z ? zValue : asVector3.Z);
|
||||
else if (v is Float3 asFloat3)
|
||||
v = new Float3(_valueChanged == ValueChanged.X ? (float)xValue : asFloat3.X, _valueChanged == ValueChanged.Y ? (float)yValue : asFloat3.Y, _valueChanged == ValueChanged.Z ? (float)zValue : asFloat3.Z);
|
||||
else if (v is Double3 asDouble3)
|
||||
v = new Double3(_valueChanged == ValueChanged.X ? xValue : asDouble3.X, _valueChanged == ValueChanged.Y ? yValue : asDouble3.Y, _valueChanged == ValueChanged.Z ? zValue : asDouble3.Z);
|
||||
|
||||
newObjects[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
SetValue(newObjects, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
object v = Values[0];
|
||||
if (v is Vector3)
|
||||
v = (Vector3)value;
|
||||
else if (v is Float3)
|
||||
v = (Float3)value;
|
||||
else if (v is Double3)
|
||||
v = (Double3)value;
|
||||
SetValue(v, token);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -341,7 +631,79 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
if (HasDifferentValues)
|
||||
{
|
||||
// TODO: support different values for ValueBox<T>
|
||||
// Get which values are different
|
||||
bool xDifferent = false;
|
||||
bool yDifferent = false;
|
||||
bool zDifferent = false;
|
||||
Double3 cachedFirstValue = Double3.Zero;
|
||||
Double3 average = Double3.Zero;
|
||||
for (int i = 0; i < Values.Count; i++)
|
||||
{
|
||||
var v = Values[i];
|
||||
var value = Double3.Zero;
|
||||
if (v is Vector3 asVector3)
|
||||
value = asVector3;
|
||||
else if (v is Float3 asFloat3)
|
||||
value = asFloat3;
|
||||
else if (v is Double3 asDouble3)
|
||||
value = asDouble3;
|
||||
|
||||
average += value;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
cachedFirstValue = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Mathd.NearEqual(cachedFirstValue.X, value.X))
|
||||
xDifferent = true;
|
||||
if (!Mathd.NearEqual(cachedFirstValue.Y, value.Y))
|
||||
yDifferent = true;
|
||||
if (!Mathd.NearEqual(cachedFirstValue.Z, value.Z))
|
||||
zDifferent = true;
|
||||
}
|
||||
|
||||
average /= Values.Count;
|
||||
|
||||
if (!xDifferent)
|
||||
{
|
||||
XElement.ValueBox.Value = cachedFirstValue.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
XElement.ValueBox.Value = average.X;
|
||||
else
|
||||
XElement.ValueBox.SlideSpeed = 0;
|
||||
XElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!yDifferent)
|
||||
{
|
||||
YElement.ValueBox.Value = cachedFirstValue.Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
YElement.ValueBox.Value = average.Y;
|
||||
else
|
||||
YElement.ValueBox.SlideSpeed = 0;
|
||||
YElement.ValueBox.Text = "---";
|
||||
}
|
||||
|
||||
if (!zDifferent)
|
||||
{
|
||||
ZElement.ValueBox.Value = cachedFirstValue.Z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllowSlidingForDifferentValues)
|
||||
ZElement.ValueBox.Value = average.Z;
|
||||
else
|
||||
ZElement.ValueBox.SlideSpeed = 0;
|
||||
ZElement.ValueBox.Text = "---";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -355,6 +717,19 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
XElement.ValueBox.Value = value.X;
|
||||
YElement.ValueBox.Value = value.Y;
|
||||
ZElement.ValueBox.Value = value.Z;
|
||||
|
||||
if (!Mathf.NearEqual(XElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
{
|
||||
XElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
if (!Mathf.NearEqual(YElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
{
|
||||
YElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
if (!Mathf.NearEqual(ZElement.ValueBox.SlideSpeed, _defaultSlidingSpeed))
|
||||
{
|
||||
ZElement.ValueBox.SlideSpeed = _defaultSlidingSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@@ -385,10 +386,21 @@ namespace FlaxEditor.CustomEditors
|
||||
if (instanceValues.Count != Count)
|
||||
throw new ArgumentException();
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
if (value is IList l && l.Count == Count)
|
||||
{
|
||||
Info.SetValue(instanceValues[i], value);
|
||||
this[i] = Info.GetValue(instanceValues[i]);
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
Info.SetValue(instanceValues[i], l[i]);
|
||||
this[i] = Info.GetValue(instanceValues[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
Info.SetValue(instanceValues[i], value);
|
||||
this[i] = Info.GetValue(instanceValues[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -673,6 +673,7 @@ bool Editor::Init()
|
||||
Managed = New<ManagedEditor>();
|
||||
|
||||
// Show splash screen
|
||||
if (!CommandLine::Options.Headless.IsTrue())
|
||||
{
|
||||
PROFILE_CPU_NAMED("Splash");
|
||||
if (EditorImpl::Splash == nullptr)
|
||||
|
||||
@@ -49,9 +49,9 @@ namespace FlaxEditor
|
||||
}
|
||||
|
||||
private readonly List<EditorModule> _modules = new List<EditorModule>(16);
|
||||
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode;
|
||||
private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode, _autoExit;
|
||||
private string _projectToOpen;
|
||||
private float _lastAutoSaveTimer;
|
||||
private float _lastAutoSaveTimer, _autoExitTimeout = 0.1f;
|
||||
private Button _saveNowButton;
|
||||
private Button _cancelSaveButton;
|
||||
private bool _autoSaveNow;
|
||||
@@ -258,10 +258,11 @@ namespace FlaxEditor
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
internal void Init(bool isHeadless, bool skipCompile, bool newProject, Guid startupScene)
|
||||
internal void Init(StartupFlags flags, Guid startupScene)
|
||||
{
|
||||
Log("Setting up C# Editor...");
|
||||
_isHeadlessMode = isHeadless;
|
||||
_isHeadlessMode = flags.HasFlag(StartupFlags.Headless);
|
||||
_autoExit = flags.HasFlag(StartupFlags.Exit);
|
||||
_startupSceneCmdLine = startupScene;
|
||||
|
||||
Profiler.BeginEvent("Projects");
|
||||
@@ -297,11 +298,11 @@ namespace FlaxEditor
|
||||
StateMachine = new EditorStateMachine(this);
|
||||
Undo = new EditorUndo(this);
|
||||
|
||||
if (newProject)
|
||||
if (flags.HasFlag(StartupFlags.NewProject))
|
||||
InitProject();
|
||||
EnsureState<LoadingState>();
|
||||
Log("Editor init");
|
||||
if (isHeadless)
|
||||
if (_isHeadlessMode)
|
||||
Log("Running in headless mode");
|
||||
|
||||
// Note: we don't sort modules before Init (optimized)
|
||||
@@ -357,7 +358,7 @@ namespace FlaxEditor
|
||||
InitializationStart?.Invoke();
|
||||
|
||||
// Start Editor initialization ending phrase (will wait for scripts compilation result)
|
||||
StateMachine.LoadingState.StartInitEnding(skipCompile);
|
||||
StateMachine.LoadingState.StartInitEnding(flags.HasFlag(StartupFlags.SkipCompile));
|
||||
}
|
||||
|
||||
internal void RegisterModule(EditorModule module)
|
||||
@@ -486,6 +487,15 @@ namespace FlaxEditor
|
||||
{
|
||||
StateMachine.Update();
|
||||
UpdateAutoSave();
|
||||
if (_autoExit && StateMachine.CurrentState == StateMachine.EditingSceneState)
|
||||
{
|
||||
_autoExitTimeout -= Time.UnscaledGameTime;
|
||||
if (_autoExitTimeout < 0.0f)
|
||||
{
|
||||
Log("Auto exit");
|
||||
Engine.RequestExit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!StateMachine.IsPlayMode)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
@@ -29,6 +28,7 @@ namespace FlaxEditor.GUI
|
||||
internal Float2 _mousePos = Float2.Minimum;
|
||||
internal bool _isMovingSelection;
|
||||
internal bool _isMovingTangent;
|
||||
internal bool _movedView;
|
||||
internal bool _movedKeyframes;
|
||||
private TangentPoint _movingTangent;
|
||||
private Float2 _movingSelectionStart;
|
||||
@@ -190,31 +190,28 @@ namespace FlaxEditor.GUI
|
||||
// Moving view
|
||||
if (_rightMouseDown)
|
||||
{
|
||||
var delta = location - _movingViewLastPos;
|
||||
var movingViewPos = Parent.PointToParent(PointToParent(location));
|
||||
var delta = movingViewPos - _movingViewLastPos;
|
||||
if (_editor.CustomViewPanning != null)
|
||||
delta = _editor.CustomViewPanning(delta);
|
||||
delta *= GetUseModeMask(_editor.EnablePanning) * _editor.ViewScale;
|
||||
delta *= GetUseModeMask(_editor.EnablePanning);
|
||||
if (delta.LengthSquared > 0.01f)
|
||||
{
|
||||
_editor._mainPanel.ViewOffset += delta;
|
||||
_movingViewLastPos = location;
|
||||
_movingViewLastPos = movingViewPos;
|
||||
_movedView = true;
|
||||
if (_editor.CustomViewPanning != null)
|
||||
{
|
||||
Cursor = CursorType.SizeAll;
|
||||
if (Cursor == CursorType.Default)
|
||||
Cursor = CursorType.SizeAll;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (_editor.EnablePanning)
|
||||
{
|
||||
case UseMode.Vertical:
|
||||
Cursor = CursorType.SizeNS;
|
||||
break;
|
||||
case UseMode.Horizontal:
|
||||
Cursor = CursorType.SizeWE;
|
||||
break;
|
||||
case UseMode.On:
|
||||
Cursor = CursorType.SizeAll;
|
||||
break;
|
||||
case UseMode.Vertical: Cursor = CursorType.SizeNS; break;
|
||||
case UseMode.Horizontal: Cursor = CursorType.SizeWE; break;
|
||||
case UseMode.On: Cursor = CursorType.SizeAll; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,11 +231,10 @@ namespace FlaxEditor.GUI
|
||||
else if (_isMovingTangent)
|
||||
{
|
||||
var viewRect = _editor._mainPanel.GetClientArea();
|
||||
var direction = _movingTangent.IsIn ? -1.0f : 1.0f;
|
||||
var k = _editor.GetKeyframe(_movingTangent.Index);
|
||||
var kv = _editor.GetKeyframeValue(k);
|
||||
var value = _editor.Accessor.GetCurveValue(ref kv, _movingTangent.Component);
|
||||
_movingTangent.TangentValue = direction * (PointToKeyframes(location, ref viewRect).Y - value);
|
||||
_movingTangent.TangentValue = PointToKeyframes(location, ref viewRect).Y - value;
|
||||
_editor.UpdateTangents();
|
||||
Cursor = CursorType.SizeNS;
|
||||
_movedKeyframes = true;
|
||||
@@ -299,7 +295,8 @@ namespace FlaxEditor.GUI
|
||||
{
|
||||
_rightMouseDown = true;
|
||||
_rightMouseDownPos = location;
|
||||
_movingViewLastPos = location;
|
||||
_movedView = false;
|
||||
_movingViewLastPos = Parent.PointToParent(PointToParent(location));
|
||||
}
|
||||
|
||||
// Check if any node is under the mouse
|
||||
@@ -444,7 +441,7 @@ namespace FlaxEditor.GUI
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
// Check if no move has been made at all
|
||||
if (Float2.Distance(ref location, ref _rightMouseDownPos) < 2.0f)
|
||||
if (!_movedView)
|
||||
{
|
||||
var selectionCount = _editor.SelectionCount;
|
||||
var point = GetChildAt(location) as KeyframePoint;
|
||||
@@ -512,6 +509,27 @@ namespace FlaxEditor.GUI
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseDoubleClick(location, button))
|
||||
return true;
|
||||
|
||||
// Add keyframe on double click
|
||||
var child = GetChildAt(location);
|
||||
if (child is not KeyframePoint &&
|
||||
child is not TangentPoint &&
|
||||
_editor.KeyframesCount < _editor.MaxKeyframes)
|
||||
{
|
||||
var viewRect = _editor._mainPanel.GetClientArea();
|
||||
var pos = PointToKeyframes(location, ref viewRect);
|
||||
_editor.AddKeyframe(pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseWheel(Float2 location, float delta)
|
||||
{
|
||||
@@ -519,10 +537,29 @@ namespace FlaxEditor.GUI
|
||||
return true;
|
||||
|
||||
// Zoom in/out
|
||||
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && RootWindow.GetKey(KeyboardKeys.Control))
|
||||
var zoom = RootWindow.GetKey(KeyboardKeys.Control);
|
||||
var zoomAlt = RootWindow.GetKey(KeyboardKeys.Shift);
|
||||
if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && (zoom || zoomAlt))
|
||||
{
|
||||
// TODO: preserve the view center point for easier zooming
|
||||
_editor.ViewScale += GetUseModeMask(_editor.EnableZoom) * (delta * 0.1f);
|
||||
// Cache mouse location in curve-space
|
||||
var viewRect = _editor._mainPanel.GetClientArea();
|
||||
var locationInKeyframes = PointToKeyframes(location, ref viewRect);
|
||||
var locationInEditorBefore = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
|
||||
|
||||
// Scale relative to the curve size
|
||||
var scale = new Float2(delta * 0.1f);
|
||||
_editor._mainPanel.GetDesireClientArea(out var mainPanelArea);
|
||||
var curveScale = mainPanelArea.Size / _editor._contents.Size;
|
||||
scale *= curveScale;
|
||||
if (zoomAlt)
|
||||
scale.X = 0; // Scale Y axis only
|
||||
scale *= GetUseModeMask(_editor.EnableZoom); // Mask scale depending on allowed usage
|
||||
_editor.ViewScale += scale;
|
||||
|
||||
// Zoom towards the mouse position
|
||||
var locationInEditorAfter = _editor.PointFromKeyframes(locationInKeyframes, ref viewRect);
|
||||
var locationInEditorDelta = locationInEditorAfter - locationInEditorBefore;
|
||||
_editor.ViewOffset -= locationInEditorDelta;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -671,8 +671,22 @@ namespace FlaxEditor.GUI
|
||||
/// <inheritdoc />
|
||||
public override void ShowWholeCurve()
|
||||
{
|
||||
ViewScale = ApplyUseModeMask(EnableZoom, _mainPanel.Size / _contents.Size, ViewScale);
|
||||
ViewOffset = ApplyUseModeMask(EnablePanning, -_mainPanel.ControlsBounds.Location, ViewOffset);
|
||||
_mainPanel.GetDesireClientArea(out var mainPanelArea);
|
||||
ViewScale = ApplyUseModeMask(EnableZoom, mainPanelArea.Size / _contents.Size, ViewScale);
|
||||
Float2 minPos = Float2.Maximum;
|
||||
foreach (var point in _points)
|
||||
{
|
||||
var pos = point.PointToParent(point.Location);
|
||||
Float2.Min(ref minPos, ref pos, out minPos);
|
||||
}
|
||||
var minPosPoint = _contents.PointToParent(ref minPos);
|
||||
var scroll = new Float2(_mainPanel.HScrollBar?.TargetValue ?? 0, _mainPanel.VScrollBar?.TargetValue ?? 0);
|
||||
scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll);
|
||||
if (_mainPanel.HScrollBar != null)
|
||||
_mainPanel.HScrollBar.TargetValue = scroll.X;
|
||||
if (_mainPanel.VScrollBar != null)
|
||||
_mainPanel.VScrollBar.TargetValue = scroll.Y;
|
||||
|
||||
UpdateKeyframes();
|
||||
}
|
||||
|
||||
@@ -923,6 +937,11 @@ namespace FlaxEditor.GUI
|
||||
KeyframesEditorUtils.Paste(this);
|
||||
return true;
|
||||
}
|
||||
else if (options.FocusSelection.Process(this))
|
||||
{
|
||||
ShowWholeCurve();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1384,9 +1403,7 @@ namespace FlaxEditor.GUI
|
||||
// Calculate bounds
|
||||
var bounds = _points[0].Bounds;
|
||||
for (var i = 1; i < _points.Count; i++)
|
||||
{
|
||||
bounds = Rectangle.Union(bounds, _points[i].Bounds);
|
||||
}
|
||||
|
||||
// Adjust contents bounds to fill the curve area
|
||||
if (EnablePanning != UseMode.Off || !ShowCollapsed)
|
||||
@@ -1632,6 +1649,7 @@ namespace FlaxEditor.GUI
|
||||
var o = _keyframes[p.Index - 1];
|
||||
var oValue = Accessor.GetCurveValue(ref o.Value, p.Component);
|
||||
var slope = (value - oValue) / (k.Time - o.Time);
|
||||
slope = -slope;
|
||||
Accessor.SetCurveValue(slope, ref k.TangentIn, p.Component);
|
||||
}
|
||||
|
||||
@@ -2116,9 +2134,7 @@ namespace FlaxEditor.GUI
|
||||
// Calculate bounds
|
||||
var bounds = _points[0].Bounds;
|
||||
for (int i = 1; i < _points.Count; i++)
|
||||
{
|
||||
bounds = Rectangle.Union(bounds, _points[i].Bounds);
|
||||
}
|
||||
|
||||
// Adjust contents bounds to fill the curve area
|
||||
if (EnablePanning != UseMode.Off || !ShowCollapsed)
|
||||
@@ -2184,12 +2200,12 @@ namespace FlaxEditor.GUI
|
||||
|
||||
var tangent = t.TangentValue;
|
||||
var direction = t.IsIn ? -1.0f : 1.0f;
|
||||
var offset = 30.0f * direction;
|
||||
var offset = 30.0f;
|
||||
var location = GetKeyframePoint(ref k, selectedComponent);
|
||||
t.Size = KeyframesSize / ViewScale;
|
||||
t.Location = new Float2
|
||||
(
|
||||
location.X * UnitsPerSecond - t.Width * 0.5f + offset,
|
||||
location.X * UnitsPerSecond - t.Width * 0.5f + offset * direction,
|
||||
location.Y * -UnitsPerSecond - t.Height * 0.5f + curveContentAreaBounds.Height - offset * tangent
|
||||
);
|
||||
|
||||
@@ -2265,14 +2281,13 @@ namespace FlaxEditor.GUI
|
||||
var startTangent = Accessor.GetCurveValue(ref startK.TangentOut, component);
|
||||
var endTangent = Accessor.GetCurveValue(ref endK.TangentIn, component);
|
||||
|
||||
var offset = (end.X - start.X) * 0.5f;
|
||||
|
||||
var tangentScale = (endK.Time - startK.Time) / 3.0f;
|
||||
var p1 = PointFromKeyframes(start, ref viewRect);
|
||||
var p2 = PointFromKeyframes(start + new Float2(offset, startTangent * offset), ref viewRect);
|
||||
var p3 = PointFromKeyframes(end - new Float2(offset, endTangent * offset), ref viewRect);
|
||||
var p2 = PointFromKeyframes(start + new Float2(0, startTangent * tangentScale), ref viewRect);
|
||||
var p3 = PointFromKeyframes(end + new Float2(0, endTangent * tangentScale), ref viewRect);
|
||||
var p4 = PointFromKeyframes(end, ref viewRect);
|
||||
|
||||
Render2D.DrawBezier(p1, p2, p3, p4, color);
|
||||
Render2D.DrawSpline(p1, p2, p3, p4, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
86
Source/Editor/GUI/Splitter.cs
Normal file
86
Source/Editor/GUI/Splitter.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI
|
||||
{
|
||||
sealed class Splitter : Control
|
||||
{
|
||||
private bool _clicked;
|
||||
|
||||
public Action<Float2> Moved;
|
||||
public const float DefaultHeight = 5.0f;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var style = Style.Current;
|
||||
if (IsMouseOver || _clicked)
|
||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
|
||||
}
|
||||
|
||||
public override void OnEndMouseCapture()
|
||||
{
|
||||
base.OnEndMouseCapture();
|
||||
|
||||
_clicked = false;
|
||||
}
|
||||
|
||||
public override void Defocus()
|
||||
{
|
||||
base.Defocus();
|
||||
|
||||
_clicked = false;
|
||||
}
|
||||
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
base.OnMouseEnter(location);
|
||||
|
||||
Cursor = CursorType.SizeNS;
|
||||
}
|
||||
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
_clicked = true;
|
||||
Focus();
|
||||
StartMouseCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
base.OnMouseMove(location);
|
||||
|
||||
if (_clicked)
|
||||
{
|
||||
Moved(location);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _clicked)
|
||||
{
|
||||
_clicked = false;
|
||||
EndMouseCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ namespace FlaxEditor.GUI.Timeline.GUI
|
||||
continue;
|
||||
|
||||
// Draw all ticks
|
||||
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 1);
|
||||
int l = Mathf.Clamp(smallestTick + level, 0, _tickSteps.Length - 2);
|
||||
var lStep = _tickSteps[l];
|
||||
var lNextStep = _tickSteps[l + 1];
|
||||
int startTick = Mathf.FloorToInt(min / lStep);
|
||||
|
||||
@@ -1446,6 +1446,17 @@ namespace FlaxEditor.GUI.Timeline
|
||||
{
|
||||
GetTracks(SelectedTracks[i], tracks);
|
||||
}
|
||||
|
||||
// Find the lowest track position for selection
|
||||
int lowestTrackLocation = Tracks.Count - 1;
|
||||
for (int i = 0; i < tracks.Count; i++)
|
||||
{
|
||||
var trackToDelete = tracks[i];
|
||||
if (trackToDelete.TrackIndex < lowestTrackLocation)
|
||||
{
|
||||
lowestTrackLocation = trackToDelete.TrackIndex;
|
||||
}
|
||||
}
|
||||
SelectedTracks.Clear();
|
||||
if (withUndo && Undo != null && Undo.Enabled)
|
||||
{
|
||||
@@ -1468,6 +1479,18 @@ namespace FlaxEditor.GUI.Timeline
|
||||
}
|
||||
OnTracksChanged();
|
||||
MarkAsEdited();
|
||||
|
||||
// Select track above deleted tracks unless track is first track
|
||||
if (Tracks.Count > 0)
|
||||
{
|
||||
if (lowestTrackLocation - 1 >= 0)
|
||||
Select(Tracks[lowestTrackLocation - 1]);
|
||||
else
|
||||
Select(Tracks[0]);
|
||||
|
||||
SelectedTracks[0].Focus();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1655,6 +1678,14 @@ namespace FlaxEditor.GUI.Timeline
|
||||
}
|
||||
OnTracksChanged();
|
||||
MarkAsEdited();
|
||||
|
||||
// Deselect and select new clones.
|
||||
Deselect();
|
||||
foreach (var clone in clones)
|
||||
{
|
||||
Select(clone, true);
|
||||
}
|
||||
|
||||
SelectedTracks[0].Focus();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,87 +19,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
/// <seealso cref="MemberTrack" />
|
||||
public abstract class CurvePropertyTrackBase : MemberTrack, IKeyframesEditorContext
|
||||
{
|
||||
private sealed class Splitter : Control
|
||||
{
|
||||
private bool _clicked;
|
||||
internal CurvePropertyTrackBase _track;
|
||||
|
||||
public override void Draw()
|
||||
{
|
||||
var style = Style.Current;
|
||||
if (IsMouseOver || _clicked)
|
||||
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), _clicked ? style.BackgroundSelected : style.BackgroundHighlighted);
|
||||
}
|
||||
|
||||
public override void OnEndMouseCapture()
|
||||
{
|
||||
base.OnEndMouseCapture();
|
||||
|
||||
_clicked = false;
|
||||
}
|
||||
|
||||
public override void Defocus()
|
||||
{
|
||||
base.Defocus();
|
||||
|
||||
_clicked = false;
|
||||
}
|
||||
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
base.OnMouseEnter(location);
|
||||
|
||||
Cursor = CursorType.SizeNS;
|
||||
}
|
||||
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
base.OnMouseLeave();
|
||||
}
|
||||
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
_clicked = true;
|
||||
Focus();
|
||||
StartMouseCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseDown(location, button);
|
||||
}
|
||||
|
||||
public override void OnMouseMove(Float2 location)
|
||||
{
|
||||
base.OnMouseMove(location);
|
||||
|
||||
if (_clicked)
|
||||
{
|
||||
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
|
||||
if (!Mathf.NearEqual(height, _track._expandedHeight))
|
||||
{
|
||||
_track.Height = _track._expandedHeight = height;
|
||||
_track.Timeline.ArrangeTracks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && _clicked)
|
||||
{
|
||||
_clicked = false;
|
||||
EndMouseCapture();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _curveEditingStartData;
|
||||
private float _expandedHeight = 120.0f;
|
||||
private Splitter _splitter;
|
||||
@@ -251,12 +170,21 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
{
|
||||
_splitter = new Splitter
|
||||
{
|
||||
_track = this,
|
||||
Moved = OnSplitterMoved,
|
||||
Parent = Curve,
|
||||
};
|
||||
}
|
||||
var splitterHeight = 5.0f;
|
||||
_splitter.Bounds = new Rectangle(0, Curve.Height - splitterHeight, Curve.Width, splitterHeight);
|
||||
_splitter.Bounds = new Rectangle(0, Curve.Height - Splitter.DefaultHeight, Curve.Width, Splitter.DefaultHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSplitterMoved(Float2 location)
|
||||
{
|
||||
var height = Mathf.Clamp(PointToParent(location).Y, 40.0f, 1000.0f);
|
||||
if (!Mathf.NearEqual(height, _expandedHeight))
|
||||
{
|
||||
Height = _expandedHeight = height;
|
||||
Timeline.ArrangeTracks();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,19 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
MaxMediaCount = 1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDuplicated(Track clone)
|
||||
{
|
||||
base.OnDuplicated(clone);
|
||||
|
||||
// Clone overriden parameters
|
||||
if (clone is ParticleEmitterTrack cloneTrack)
|
||||
{
|
||||
foreach (var e in ParametersOverrides)
|
||||
cloneTrack.ParametersOverrides.Add(e.Key, e.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -61,6 +61,9 @@ namespace FlaxEditor.GUI.Timeline.Undo
|
||||
_timeline.AddTrack(track, false);
|
||||
track.TrackIndex = _order;
|
||||
_timeline.OnTracksOrderChanged();
|
||||
_timeline.Focus();
|
||||
_timeline.Select(track);
|
||||
track.Focus();
|
||||
}
|
||||
|
||||
private void Remove()
|
||||
@@ -68,10 +71,11 @@ namespace FlaxEditor.GUI.Timeline.Undo
|
||||
var track = _timeline.FindTrack(_name);
|
||||
if (track == null)
|
||||
{
|
||||
Editor.LogWarning($"Cannot remove track {_name}. It doesn't already exists.");
|
||||
Editor.LogWarning($"Cannot remove track {_name}. It doesn't exist.");
|
||||
return;
|
||||
}
|
||||
_timeline.Delete(track, false);
|
||||
_timeline.Focus();
|
||||
}
|
||||
|
||||
public string ActionString => _isAdd ? "Add track" : "Remove track";
|
||||
|
||||
@@ -176,7 +176,7 @@ ManagedEditor::~ManagedEditor()
|
||||
void ManagedEditor::Init()
|
||||
{
|
||||
// Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time
|
||||
void* args[4];
|
||||
void* args[2];
|
||||
MClass* mclass = GetClass();
|
||||
if (mclass == nullptr)
|
||||
{
|
||||
@@ -193,18 +193,22 @@ void ManagedEditor::Init()
|
||||
LOG(Fatal, "Failed to create editor instance.");
|
||||
}
|
||||
MObject* exception = nullptr;
|
||||
bool isHeadless = CommandLine::Options.Headless.IsTrue();
|
||||
bool skipCompile = CommandLine::Options.SkipCompile.IsTrue();
|
||||
bool newProject = CommandLine::Options.NewProject.IsTrue();
|
||||
args[0] = &isHeadless;
|
||||
args[1] = &skipCompile;
|
||||
args[2] = &newProject;
|
||||
StartupFlags flags = StartupFlags::None;
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
flags |= StartupFlags::Headless;
|
||||
if (CommandLine::Options.SkipCompile.IsTrue())
|
||||
flags |= StartupFlags::SkipCompile;
|
||||
if (CommandLine::Options.NewProject.IsTrue())
|
||||
flags |= StartupFlags::NewProject;
|
||||
if (CommandLine::Options.Exit.IsTrue())
|
||||
flags |= StartupFlags::Exit;
|
||||
args[0] = &flags;
|
||||
Guid sceneId;
|
||||
if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId)))
|
||||
{
|
||||
sceneId = Guid::Empty;
|
||||
}
|
||||
args[3] = &sceneId;
|
||||
args[1] = &sceneId;
|
||||
initMethod->Invoke(instance, args, &exception);
|
||||
if (exception)
|
||||
{
|
||||
@@ -219,7 +223,7 @@ void ManagedEditor::Init()
|
||||
WasExitCalled = false;
|
||||
|
||||
// Load scripts if auto-load on startup is disabled
|
||||
if (!ManagedEditorOptions.ForceScriptCompilationOnStartup || skipCompile)
|
||||
if (!ManagedEditorOptions.ForceScriptCompilationOnStartup || EnumHasAllFlags(flags, StartupFlags::SkipCompile))
|
||||
{
|
||||
LOG(Info, "Loading managed assemblies (due to disabled compilation on startup)");
|
||||
Scripting::Load();
|
||||
|
||||
@@ -22,6 +22,15 @@ API_CLASS(Namespace="FlaxEditor", Name="Editor", NoSpawn, NoConstructor) class M
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ManagedEditor);
|
||||
static Guid ObjectID;
|
||||
|
||||
API_ENUM(Attributes="Flags", Internal) enum class StartupFlags
|
||||
{
|
||||
None = 0,
|
||||
Headless = 1,
|
||||
SkipCompile = 2,
|
||||
NewProject = 4,
|
||||
Exit = 8,
|
||||
};
|
||||
|
||||
struct InternalOptions
|
||||
{
|
||||
byte AutoReloadScriptsOnMainWindowFocus = 1;
|
||||
@@ -258,3 +267,5 @@ public:
|
||||
// [ScriptingObject]
|
||||
void DestroyManaged() override;
|
||||
};
|
||||
|
||||
DECLARE_ENUM_OPERATORS(ManagedEditor::StartupFlags);
|
||||
|
||||
@@ -274,11 +274,7 @@ namespace FlaxEditor.Modules
|
||||
private void Load()
|
||||
{
|
||||
if (!File.Exists(_cachePath))
|
||||
{
|
||||
Editor.LogWarning("Missing editor cache file.");
|
||||
return;
|
||||
}
|
||||
|
||||
_lastSaveTime = DateTime.UtcNow;
|
||||
|
||||
try
|
||||
|
||||
@@ -950,7 +950,10 @@ namespace FlaxEditor.Modules
|
||||
MainWindow = null;
|
||||
|
||||
// Capture project icon screenshot (not in play mode and if editor was used for some time)
|
||||
if (!Editor.StateMachine.IsPlayMode && Time.TimeSinceStartup >= 5.0f && GPUDevice.Instance?.RendererType != RendererType.Null)
|
||||
if (!Editor.StateMachine.IsPlayMode &&
|
||||
Time.TimeSinceStartup >= 5.0f &&
|
||||
!Editor.IsHeadlessMode &&
|
||||
GPUDevice.Instance?.RendererType != RendererType.Null)
|
||||
{
|
||||
Editor.Log("Capture project icon screenshot");
|
||||
_projectIconScreenshotTimeout = Time.TimeSinceStartup + 0.8f; // wait 800ms for a screenshot task
|
||||
|
||||
@@ -449,6 +449,13 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Visject", "Grid Snapping Size"), EditorOrder(551), Tooltip("Defines the size of the grid for nodes snapping."), VisibleIf(nameof(SurfaceGridSnapping))]
|
||||
public float SurfaceGridSnappingSize { get; set; } = 20.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates if a warning should be displayed when deleting a Visject parameter that is used in a graph.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Visject", "Warn when deleting used parameter"), EditorOrder(552)]
|
||||
public bool WarnOnDeletingUsedVisjectParameter { get; set; } = true;
|
||||
|
||||
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
||||
private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
if (Level.ScenesCount > 1)
|
||||
contextMenu.AddButton("Unload all but this scene", OnUnloadAllButSelectedScene).LinkTooltip("Unloads all of the active scenes except for the selected scene.").Enabled = Editor.Instance.StateMachine.CurrentState.CanChangeScene;
|
||||
|
||||
contextMenu.MaximumItemsInViewCount += 3;
|
||||
base.OnContextMenu(contextMenu, window);
|
||||
}
|
||||
|
||||
|
||||
@@ -293,6 +293,14 @@ namespace FlaxEditor.Scripting
|
||||
/// <param name="obj">The object whose member value will be modified.</param>
|
||||
/// <param name="value">The new member value.</param>
|
||||
void SetValue(object obj, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the method on a specific object (null if static) using the provided parameters.
|
||||
/// </summary>
|
||||
/// <param name="obj">The instance of the object to invoke its method. Use null for static methods.</param>
|
||||
/// <param name="parameters">List of parameters to provide.</param>
|
||||
/// <returns>The value returned by the method.</returns>
|
||||
object Invoke(object obj, object[] parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -691,6 +691,23 @@ namespace FlaxEditor.Scripting
|
||||
else
|
||||
_custom.SetValue(obj, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the method on a specific object (null if static) using the provided parameters.
|
||||
/// </summary>
|
||||
/// <param name="obj">The instance of the object to invoke its method. Use null for static methods.</param>
|
||||
/// <param name="parameters">List of parameters to provide.</param>
|
||||
/// <returns>The value returned by the method.</returns>
|
||||
public object Invoke(object obj = null, object[] parameters = null)
|
||||
{
|
||||
if (parameters == null)
|
||||
parameters = Array.Empty<object>();
|
||||
if (_managed is MethodInfo methodInfo)
|
||||
return methodInfo.Invoke(obj, parameters);
|
||||
if (_managed != null)
|
||||
throw new NotSupportedException();
|
||||
return _custom.Invoke(obj, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// Represents single blend point.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.Control" />
|
||||
protected class BlendPoint : Control
|
||||
internal class BlendPoint : Control
|
||||
{
|
||||
private readonly BlendPointsEditor _editor;
|
||||
private readonly int _index;
|
||||
@@ -48,6 +48,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
public int Index => _index;
|
||||
|
||||
/// <summary>
|
||||
/// Flag that indicates that user is moving this point with a mouse.
|
||||
/// </summary>
|
||||
public bool IsMouseDown => _isMouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlendPoint"/> class.
|
||||
/// </summary>
|
||||
@@ -211,6 +216,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// </summary>
|
||||
public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point
|
||||
|
||||
/// <summary>
|
||||
/// BLend points array.
|
||||
/// </summary>
|
||||
internal IReadOnlyList<BlendPoint> BlendPoints => _blendPoints;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlendPointsEditor"/> class.
|
||||
/// </summary>
|
||||
@@ -374,6 +384,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
/// <returns>The blend point control position.</returns>
|
||||
public Float2 BlendSpacePosToBlendPointPos(Float2 pos)
|
||||
{
|
||||
if (_rangeX.IsZero)
|
||||
{
|
||||
var data0 = (Float4)_node.Values[0];
|
||||
_rangeX = new Float2(data0.X, data0.Y);
|
||||
_rangeY = _is2D ? new Float2(data0.Z, data0.W) : Float2.Zero;
|
||||
}
|
||||
GetPointsArea(out var pointsArea);
|
||||
if (_is2D)
|
||||
{
|
||||
@@ -389,7 +405,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
pointsArea.Center.Y
|
||||
);
|
||||
}
|
||||
return pos - new Float2(BlendPoint.DefaultSize * 0.5f);
|
||||
return pos;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -424,7 +440,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
|
||||
// Update blend point
|
||||
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location);
|
||||
_blendPoints[i].Location = BlendSpacePosToBlendPointPos(location) - BlendPoint.DefaultSize * 0.5f;
|
||||
var asset = Editor.Instance.ContentDatabase.FindAsset(animId);
|
||||
var tooltip = asset?.ShortName ?? string.Empty;
|
||||
tooltip += "\nX: " + location.X;
|
||||
@@ -532,81 +548,18 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
SetAsset((int)b.Tag, Guid.Empty);
|
||||
}
|
||||
|
||||
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
||||
{
|
||||
// Draw line
|
||||
Render2D.DrawLine(start, end, gridColor);
|
||||
|
||||
// Draw label
|
||||
var labelWidth = 50.0f;
|
||||
var labelHeight = 10.0f;
|
||||
var labelMargin = 2.0f;
|
||||
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
var hAlign = TextAlignment.Near;
|
||||
Rectangle labelRect;
|
||||
if (vertical)
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
return; // Don't overlap with the first horizontal label
|
||||
}
|
||||
else
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
{
|
||||
labelRect.X = start.X - labelMargin - labelRect.Width;
|
||||
hAlign = TextAlignment.Far;
|
||||
}
|
||||
}
|
||||
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
var style = Style.Current;
|
||||
var rect = new Rectangle(Float2.Zero, Size);
|
||||
var containsFocus = ContainsFocus;
|
||||
GetPointsArea(out var pointsArea);
|
||||
var data0 = (Float4)_node.Values[0];
|
||||
var rangeX = new Float2(data0.X, data0.Y);
|
||||
|
||||
// Background
|
||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||
//Render2D.DrawRectangle(pointsArea, Color.Red);
|
||||
_node.DrawEditorBackground(ref rect);
|
||||
|
||||
// Grid
|
||||
int splits = 10;
|
||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||
var labelColor = style.ForegroundDisabled;
|
||||
var labelFont = style.FontSmall;
|
||||
//var blendArea = BlendAreaRect;
|
||||
var blendArea = pointsArea;
|
||||
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float x = blendArea.Left + blendArea.Width * alpha;
|
||||
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
||||
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
if (_is2D)
|
||||
{
|
||||
var rangeY = new Float2(data0.Z, data0.W);
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float y = blendArea.Top + blendArea.Height * alpha;
|
||||
float value = Mathf.Lerp(rangeY.X, rangeY.Y, alpha);
|
||||
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float y = blendArea.Center.Y;
|
||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||
}
|
||||
_node.DrawEditorGrid(ref rect);
|
||||
|
||||
base.Draw();
|
||||
|
||||
@@ -808,6 +761,87 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_editor.SetAsset(SelectedAnimationIndex, Guid.Empty);
|
||||
}
|
||||
|
||||
private void DrawAxis(bool vertical, Float2 start, Float2 end, ref Color gridColor, ref Color labelColor, Font labelFont, float value, bool isLast)
|
||||
{
|
||||
// Draw line
|
||||
Render2D.DrawLine(start, end, gridColor);
|
||||
|
||||
// Draw label
|
||||
var labelWidth = 50.0f;
|
||||
var labelHeight = 10.0f;
|
||||
var labelMargin = 2.0f;
|
||||
string label = Utils.RoundTo2DecimalPlaces(value).ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
var hAlign = TextAlignment.Near;
|
||||
Rectangle labelRect;
|
||||
if (vertical)
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin * 2, start.Y, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
return; // Don't overlap with the first horizontal label
|
||||
}
|
||||
else
|
||||
{
|
||||
labelRect = new Rectangle(start.X + labelMargin, start.Y - labelHeight - labelMargin, labelWidth, labelHeight);
|
||||
if (isLast)
|
||||
{
|
||||
labelRect.X = start.X - labelMargin - labelRect.Width;
|
||||
hAlign = TextAlignment.Far;
|
||||
}
|
||||
}
|
||||
Render2D.DrawText(labelFont, label, labelRect, labelColor, hAlign, TextAlignment.Center, TextWrapping.NoWrap, 1.0f, 0.7f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom drawing logic for blend space background.
|
||||
/// </summary>
|
||||
public virtual void DrawEditorBackground(ref Rectangle rect)
|
||||
{
|
||||
var style = Style.Current;
|
||||
Render2D.FillRectangle(rect, style.Background.AlphaMultiplied(0.5f));
|
||||
Render2D.DrawRectangle(rect, IsMouseOver ? style.TextBoxBackgroundSelected : style.TextBoxBackground);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom drawing logic for blend space grid.
|
||||
/// </summary>
|
||||
public virtual void DrawEditorGrid(ref Rectangle rect)
|
||||
{
|
||||
var style = Style.Current;
|
||||
_editor.GetPointsArea(out var pointsArea);
|
||||
var data0 = (Float4)Values[0];
|
||||
var rangeX = new Float2(data0.X, data0.Y);
|
||||
int splits = 10;
|
||||
var gridColor = style.TextBoxBackgroundSelected * 1.1f;
|
||||
var labelColor = style.ForegroundDisabled;
|
||||
var labelFont = style.FontSmall;
|
||||
//var blendArea = BlendAreaRect;
|
||||
var blendArea = pointsArea;
|
||||
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float x = blendArea.Left + blendArea.Width * alpha;
|
||||
float value = Mathf.Lerp(rangeX.X, rangeX.Y, alpha);
|
||||
DrawAxis(false, new Float2(x, rect.Height - 2), new Float2(x, 1), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
if (_editor.Is2D)
|
||||
{
|
||||
var rangeY = new Float2(data0.Z, data0.W);
|
||||
for (int i = 0; i <= splits; i++)
|
||||
{
|
||||
float alpha = (float)i / splits;
|
||||
float y = blendArea.Top + blendArea.Height * alpha;
|
||||
float value = Mathf.Lerp(rangeY.X, rangeY.Y, 1.0f - alpha);
|
||||
DrawAxis(true, new Float2(1, y), new Float2(rect.Width - 2, y), ref gridColor, ref labelColor, labelFont, value, i == splits);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float y = blendArea.Center.Y;
|
||||
Render2D.DrawLine(new Float2(1, y), new Float2(rect.Width - 2, y), gridColor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the editor UI.
|
||||
/// </summary>
|
||||
@@ -983,6 +1017,10 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
private readonly FloatValueBox _animationX;
|
||||
private readonly Label _animationYLabel;
|
||||
private readonly FloatValueBox _animationY;
|
||||
private Float2[] _triangles;
|
||||
private Color[] _triangleColors;
|
||||
private Float2[] _selectedTriangles;
|
||||
private Color[] _selectedColors;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||
@@ -1051,6 +1089,143 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearTriangles()
|
||||
{
|
||||
// Remove cache
|
||||
_triangles = null;
|
||||
_triangleColors = null;
|
||||
_selectedTriangles = null;
|
||||
_selectedColors = null;
|
||||
}
|
||||
|
||||
private void CacheTriangles()
|
||||
{
|
||||
// Get locations of blend point vertices
|
||||
int pointsCount = _editor.PointsCount;
|
||||
int count = 0, j = 0;
|
||||
for (int i = 0; i < pointsCount; i++)
|
||||
{
|
||||
var animId = (Guid)Values[5 + i * 2];
|
||||
if (animId != Guid.Empty)
|
||||
count++;
|
||||
}
|
||||
var vertices = new Float2[count];
|
||||
for (int i = 0; i < pointsCount; i++)
|
||||
{
|
||||
var animId = (Guid)Values[5 + i * 2];
|
||||
if (animId != Guid.Empty)
|
||||
{
|
||||
var dataA = (Float4)Values[4 + i * 2];
|
||||
vertices[j++] = new Float2(dataA.X, dataA.Y);
|
||||
}
|
||||
}
|
||||
|
||||
// Triangulate
|
||||
_triangles = FlaxEngine.Utilities.Delaunay2D.Triangulate(vertices);
|
||||
_triangleColors = null;
|
||||
|
||||
// Fix incorrect triangles (mirror logic in AnimGraphBase::onNodeLoaded)
|
||||
if (_triangles == null || _triangles.Length == 0)
|
||||
{
|
||||
// Insert dummy triangles to have something working (eg. blend points are on the same axis)
|
||||
var triangles = new List<Float2>();
|
||||
int verticesLeft = vertices.Length;
|
||||
while (verticesLeft >= 3)
|
||||
{
|
||||
verticesLeft -= 3;
|
||||
triangles.Add(vertices[verticesLeft + 0]);
|
||||
triangles.Add(vertices[verticesLeft + 1]);
|
||||
triangles.Add(vertices[verticesLeft + 2]);
|
||||
}
|
||||
if (verticesLeft == 1)
|
||||
{
|
||||
triangles.Add(vertices[0]);
|
||||
triangles.Add(vertices[0]);
|
||||
triangles.Add(vertices[0]);
|
||||
}
|
||||
else if (verticesLeft == 2)
|
||||
{
|
||||
triangles.Add(vertices[0]);
|
||||
triangles.Add(vertices[1]);
|
||||
triangles.Add(vertices[0]);
|
||||
}
|
||||
_triangles = triangles.ToArray();
|
||||
}
|
||||
|
||||
// Project to the blend space for drawing
|
||||
for (int i = 0; i < _triangles.Length; i++)
|
||||
_triangles[i] = _editor.BlendSpacePosToBlendPointPos(_triangles[i]);
|
||||
|
||||
// Check if anything is selected
|
||||
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||
if (selectedIndex != -1)
|
||||
{
|
||||
// Find triangles that contain selected point
|
||||
var dataA = (Float4)Values[4 + selectedIndex * 2];
|
||||
var pos = _editor.BlendSpacePosToBlendPointPos(new Float2(dataA.X, dataA.Y));
|
||||
var selectedTriangles = new List<Float2>();
|
||||
var selectedColors = new List<Color>();
|
||||
var style = Style.Current;
|
||||
var triangleColor = style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f);
|
||||
var selectedTriangleColor = style.BackgroundSelected.AlphaMultiplied(0.6f);
|
||||
_triangleColors = new Color[_triangles.Length];
|
||||
for (int i = 0; i < _triangles.Length; i += 3)
|
||||
{
|
||||
var is0 = Float2.NearEqual(ref _triangles[i + 0], ref pos);
|
||||
var is1 = Float2.NearEqual(ref _triangles[i + 1], ref pos);
|
||||
var is2 = Float2.NearEqual(ref _triangles[i + 2], ref pos);
|
||||
if (is0 || is1 || is2)
|
||||
{
|
||||
selectedTriangles.Add(_triangles[i + 0]);
|
||||
selectedTriangles.Add(_triangles[i + 1]);
|
||||
selectedTriangles.Add(_triangles[i + 2]);
|
||||
selectedColors.Add(is0 ? Color.White : Color.Transparent);
|
||||
selectedColors.Add(is1 ? Color.White : Color.Transparent);
|
||||
selectedColors.Add(is2 ? Color.White : Color.Transparent);
|
||||
}
|
||||
_triangleColors[i + 0] = is0 ? selectedTriangleColor : triangleColor;
|
||||
_triangleColors[i + 1] = is1 ? selectedTriangleColor : triangleColor;
|
||||
_triangleColors[i + 2] = is2 ? selectedTriangleColor : triangleColor;
|
||||
}
|
||||
_selectedTriangles = selectedTriangles.ToArray();
|
||||
_selectedColors = selectedColors.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DrawEditorBackground(ref Rectangle rect)
|
||||
{
|
||||
base.DrawEditorBackground(ref rect);
|
||||
|
||||
// Draw triangulated multi blend space
|
||||
var style = Style.Current;
|
||||
if (_triangles == null)
|
||||
CacheTriangles();
|
||||
if (_triangleColors != null && (ContainsFocus || IsMouseOver))
|
||||
Render2D.FillTriangles(_triangles, _triangleColors);
|
||||
else
|
||||
Render2D.FillTriangles(_triangles, style.TextBoxBackgroundSelected.AlphaMultiplied(0.6f));
|
||||
Render2D.DrawTriangles(_triangles, style.Foreground);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void DrawEditorGrid(ref Rectangle rect)
|
||||
{
|
||||
base.DrawEditorGrid(ref rect);
|
||||
|
||||
// Highlight selected blend point
|
||||
var style = Style.Current;
|
||||
var selectedIndex = _selectedAnimation.SelectedIndex;
|
||||
if (selectedIndex != -1 && (ContainsFocus || IsMouseOver))
|
||||
{
|
||||
var point = _editor.BlendPoints[selectedIndex];
|
||||
var highlightColor = point.IsMouseDown ? style.SelectionBorder : style.BackgroundSelected;
|
||||
Render2D.PushTint(ref highlightColor);
|
||||
Render2D.DrawTriangles(_selectedTriangles, _selectedColors);
|
||||
Render2D.PopTint();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1)
|
||||
{
|
||||
@@ -1075,6 +1250,23 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
_animationX.Enabled = isValid;
|
||||
_animationYLabel.Enabled = isValid;
|
||||
_animationY.Enabled = isValid;
|
||||
ClearTriangles();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnValuesChanged()
|
||||
{
|
||||
base.OnValuesChanged();
|
||||
|
||||
ClearTriangles();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLoaded(SurfaceNodeActions action)
|
||||
{
|
||||
base.OnLoaded(action);
|
||||
|
||||
ClearTriangles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Content.Settings;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Windows.Assets;
|
||||
@@ -590,7 +591,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.ComboBox(0, 0, 70.0f, 0, FlaxEditor.Tools.Terrain.PaintTerrainGizmoMode.TerrainLayerNames),
|
||||
NodeElementArchetype.Factory.ComboBox(0, 0, 70.0f, 0, LayersAndTagsSettings.GetCurrentTerrainLayers()),
|
||||
NodeElementArchetype.Factory.Output(0, "", typeof(float), 0),
|
||||
}
|
||||
},
|
||||
|
||||
@@ -425,6 +425,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
UpdateCombo();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsParamUsed(SurfaceParameter param)
|
||||
{
|
||||
return (Guid)Values[0] == param.ID;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLoaded(SurfaceNodeActions action)
|
||||
{
|
||||
@@ -937,13 +943,17 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
// Deselect if that parameter is selected
|
||||
if ((Guid)Values[0] == param.ID)
|
||||
{
|
||||
_combobox.SelectedIndex = -1;
|
||||
}
|
||||
|
||||
UpdateCombo();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsParamUsed(SurfaceParameter param)
|
||||
{
|
||||
return (Guid)Values[0] == param.ID;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnLoaded(SurfaceNodeActions action)
|
||||
{
|
||||
|
||||
@@ -33,5 +33,12 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
/// <param name="param">The parameter.</param>
|
||||
void OnParamDeleted(SurfaceParameter param);
|
||||
|
||||
/// <summary>
|
||||
/// Get if the parameter is referenced in a graph. Referenced in this case means in a graph and at least one node in-/output connected to another node.
|
||||
/// </summary>
|
||||
/// <param name="param">The parameter.</param>
|
||||
/// <returns>If the parameter is referenced.</returns>
|
||||
bool IsParamUsed(SurfaceParameter param);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace FlaxEditor.Surface
|
||||
typeof(TooltipAttribute),
|
||||
typeof(HideInEditorAttribute),
|
||||
typeof(NoAnimateAttribute),
|
||||
typeof(ButtonAttribute),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -84,5 +84,16 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
MarkAsEdited();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsParamUsed(SurfaceParameter param)
|
||||
{
|
||||
for (int i = 0; i < Nodes.Count; i++)
|
||||
{
|
||||
if (Nodes[i] is IParametersDependantNode node && node.IsParamUsed(param))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -776,6 +776,15 @@ namespace FlaxEditor.Surface
|
||||
private void DeleteParameter(int index)
|
||||
{
|
||||
var window = (IVisjectSurfaceWindow)Values[0];
|
||||
SurfaceParameter param = window.VisjectSurface.Parameters[index];
|
||||
|
||||
if (Editor.Instance.Options.Options.Interface.WarnOnDeletingUsedVisjectParameter && window.VisjectSurface.IsParamUsed(param))
|
||||
{
|
||||
string msg = $"Delete parameter {param.Name}?\nParameter is being used in a graph.\n\nYou can disable this warning in the editor settings.";
|
||||
if (MessageBox.Show(msg, "Delete parameter", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK)
|
||||
return;
|
||||
}
|
||||
|
||||
var action = new AddRemoveParamAction
|
||||
{
|
||||
Window = window,
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace FlaxEditor.Tools.Terrain.Paint
|
||||
/// <summary>
|
||||
/// The layer to paint with it.
|
||||
/// </summary>
|
||||
[EditorOrder(10), Tooltip("The layer to paint with it. Terrain material can access per-layer blend weight to perform materials or textures blending.")]
|
||||
[EditorOrder(10), Tooltip("The layer to paint on. Terrain material can access a per-layer blend weight to perform material or texture blending."), CustomEditorAlias("FlaxEditor.CustomEditors.Editors.TerrainLayerEditor")]
|
||||
public Layers Layer = Layers.Layer0;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -21,21 +21,6 @@ namespace FlaxEditor.Tools.Terrain
|
||||
[HideInEditor]
|
||||
public class PaintTerrainGizmoMode : EditorGizmoMode
|
||||
{
|
||||
/// <summary>
|
||||
/// The terrain layer names.
|
||||
/// </summary>
|
||||
public static readonly string[] TerrainLayerNames =
|
||||
{
|
||||
"Layer 0",
|
||||
"Layer 1",
|
||||
"Layer 2",
|
||||
"Layer 3",
|
||||
"Layer 4",
|
||||
"Layer 5",
|
||||
"Layer 6",
|
||||
"Layer 7",
|
||||
};
|
||||
|
||||
private struct SplatmapData
|
||||
{
|
||||
public IntPtr DataPtr;
|
||||
|
||||
@@ -250,6 +250,8 @@ namespace FlaxEditor.Utilities
|
||||
|
||||
internal static Int2 DrawCurveTicks(DrawCurveTick drawTick, float[] tickSteps, ref float[] tickStrengths, float min, float max, float pixelRange, float minDistanceBetweenTicks = 20, float maxDistanceBetweenTicks = 60)
|
||||
{
|
||||
if (pixelRange <= Mathf.Epsilon || maxDistanceBetweenTicks <= minDistanceBetweenTicks)
|
||||
return Int2.Zero;
|
||||
if (tickStrengths == null || tickStrengths.Length != tickSteps.Length)
|
||||
tickStrengths = new float[tickSteps.Length];
|
||||
|
||||
@@ -286,7 +288,7 @@ namespace FlaxEditor.Utilities
|
||||
continue;
|
||||
|
||||
// Draw all ticks
|
||||
int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 1);
|
||||
int l = Mathf.Clamp(smallestTick + level, 0, tickSteps.Length - 2);
|
||||
var lStep = tickSteps[l];
|
||||
var lNextStep = tickSteps[l + 1];
|
||||
int startTick = Mathf.FloorToInt(min / lStep);
|
||||
|
||||
@@ -79,6 +79,13 @@ namespace FlaxEditor.Viewport.Previews
|
||||
_uiControlLinked.Control.Parent = null;
|
||||
_uiControlLinked = null;
|
||||
}
|
||||
foreach (var child in _uiParentLink.Children.ToArray())
|
||||
{
|
||||
if (child is CanvasRootControl canvasRoot)
|
||||
{
|
||||
canvasRoot.Canvas.EditorOverride(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove for the preview
|
||||
Task.RemoveCustomActor(_instance);
|
||||
|
||||
@@ -54,24 +54,24 @@ namespace FlaxEditor.Windows.Assets
|
||||
[EditorOrder(10), EditorDisplay("General"), Tooltip("Material domain type.")]
|
||||
public MaterialDomain Domain;
|
||||
|
||||
[EditorOrder(20), EditorDisplay("General"), Tooltip("Defines how material inputs and properties are combined to result the final surface color.")]
|
||||
[EditorOrder(20), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Defines how material inputs and properties are combined to result the final surface color.")]
|
||||
public MaterialShadingModel ShadingModel;
|
||||
|
||||
[EditorOrder(30), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
|
||||
[EditorOrder(30), VisibleIf(nameof(IsStandard)), EditorDisplay("General"), Tooltip("Determinates how materials' color should be blended with the background colors.")]
|
||||
public MaterialBlendMode BlendMode;
|
||||
|
||||
// Rendering
|
||||
|
||||
[EditorOrder(100), DefaultValue(CullMode.Normal), EditorDisplay("Rendering"), Tooltip("Defines the primitives culling mode used during geometry rendering.")]
|
||||
[EditorOrder(100), DefaultValue(CullMode.Normal), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Defines the primitives culling mode used during geometry rendering.")]
|
||||
public CullMode CullMode;
|
||||
|
||||
[EditorOrder(110), DefaultValue(false), EditorDisplay("Rendering"), Tooltip("If checked, geometry will be rendered in wireframe mode without solid triangles fill.")]
|
||||
[EditorOrder(110), DefaultValue(false), VisibleIf(nameof(IsStandardOrGUI)), EditorDisplay("Rendering"), Tooltip("If checked, geometry will be rendered in wireframe mode without solid triangles fill.")]
|
||||
public bool Wireframe;
|
||||
|
||||
[EditorOrder(120), DefaultValue(true), EditorDisplay("Rendering"), Tooltip("Enables performing depth test during material rendering.")]
|
||||
[EditorOrder(120), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Enables performing depth test during material rendering.")]
|
||||
public bool DepthTest;
|
||||
|
||||
[EditorOrder(130), DefaultValue(true), EditorDisplay("Rendering"), Tooltip("Enable writing to the depth buffer during material rendering.")]
|
||||
[EditorOrder(130), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Rendering"), Tooltip("Enable writing to the depth buffer during material rendering.")]
|
||||
public bool DepthWrite;
|
||||
|
||||
// Transparency
|
||||
@@ -111,13 +111,13 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
// Misc
|
||||
|
||||
[EditorOrder(400), DefaultValue(false), EditorDisplay("Misc"), Tooltip("If checked, material input normal will be assumed as world-space rather than tangent-space.")]
|
||||
[EditorOrder(400), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc"), Tooltip("If checked, material input normal will be assumed as world-space rather than tangent-space.")]
|
||||
public bool InputWorldSpaceNormal;
|
||||
|
||||
[EditorOrder(410), DefaultValue(false), EditorDisplay("Misc", "Dithered LOD Transition"), Tooltip("If checked, material uses dithered model LOD transition for smoother LODs switching.")]
|
||||
[EditorOrder(410), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc", "Dithered LOD Transition"), Tooltip("If checked, material uses dithered model LOD transition for smoother LODs switching.")]
|
||||
public bool DitheredLODTransition;
|
||||
|
||||
[EditorOrder(420), DefaultValue(0.3f), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
|
||||
[EditorOrder(420), DefaultValue(0.3f), VisibleIf(nameof(IsStandard)), EditorDisplay("Misc"), Tooltip("Controls mask values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
|
||||
public float MaskThreshold;
|
||||
|
||||
[EditorOrder(430), DefaultValue(MaterialDecalBlendingMode.Translucent), VisibleIf(nameof(IsDecal)), EditorDisplay("Misc"), Tooltip("The decal material blending mode.")]
|
||||
@@ -144,7 +144,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
private bool IsPostProcess => Domain == MaterialDomain.PostProcess;
|
||||
private bool IsDecal => Domain == MaterialDomain.Decal;
|
||||
private bool IsGUI => Domain == MaterialDomain.GUI;
|
||||
private bool IsStandard => Domain == MaterialDomain.Surface || Domain == MaterialDomain.Terrain || Domain == MaterialDomain.Particle || Domain == MaterialDomain.Deformable;
|
||||
private bool IsStandardOrGUI => IsStandard || IsGUI;
|
||||
|
||||
/// <summary>
|
||||
/// Gathers parameters from the specified material.
|
||||
|
||||
@@ -52,12 +52,13 @@ namespace FlaxEditor.Windows
|
||||
contextMenu.AddSeparator();
|
||||
|
||||
// Basic editing options
|
||||
|
||||
var firstSelection = hasSthSelected ? Editor.SceneEditing.Selection[0] as ActorNode : null;
|
||||
b = contextMenu.AddButton("Rename", inputOptions.Rename, Rename);
|
||||
b = contextMenu.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate);
|
||||
b.Enabled = hasSthSelected;
|
||||
b = contextMenu.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate);
|
||||
b.Enabled = hasSthSelected && (firstSelection != null ? firstSelection.CanDuplicate : true);
|
||||
|
||||
if (isSingleActorSelected)
|
||||
if (isSingleActorSelected && firstSelection?.Actor is not Scene)
|
||||
{
|
||||
var convertMenu = contextMenu.AddChildMenu("Convert");
|
||||
convertMenu.ContextMenu.AutoSort = true;
|
||||
@@ -117,31 +118,31 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
}
|
||||
b = contextMenu.AddButton("Delete", inputOptions.Delete, Editor.SceneEditing.Delete);
|
||||
b.Enabled = hasSthSelected;
|
||||
b.Enabled = hasSthSelected && (firstSelection != null ? firstSelection.CanDelete : true);
|
||||
|
||||
contextMenu.AddSeparator();
|
||||
|
||||
b = contextMenu.AddButton("Copy", inputOptions.Copy, Editor.SceneEditing.Copy);
|
||||
b.Enabled = hasSthSelected && (firstSelection != null ? firstSelection.CanCopyPaste : true);
|
||||
|
||||
b.Enabled = hasSthSelected;
|
||||
contextMenu.AddButton("Paste", inputOptions.Paste, Editor.SceneEditing.Paste);
|
||||
|
||||
b = contextMenu.AddButton("Cut", inputOptions.Cut, Editor.SceneEditing.Cut);
|
||||
b.Enabled = canEditScene;
|
||||
b.Enabled = canEditScene && hasSthSelected && (firstSelection != null ? firstSelection.CanCopyPaste : true);
|
||||
|
||||
// Create option
|
||||
|
||||
contextMenu.AddSeparator();
|
||||
|
||||
b = contextMenu.AddButton("Parent to new Actor", inputOptions.GroupSelectedActors, Editor.SceneEditing.CreateParentForSelectedActors);
|
||||
b.Enabled = canEditScene && hasSthSelected;
|
||||
b.Enabled = canEditScene && hasSthSelected && firstSelection?.Actor is not Scene;
|
||||
|
||||
b = contextMenu.AddButton("Create Prefab", Editor.Prefabs.CreatePrefab);
|
||||
b.Enabled = isSingleActorSelected &&
|
||||
((ActorNode)Editor.SceneEditing.Selection[0]).CanCreatePrefab &&
|
||||
(firstSelection != null ? firstSelection.CanCreatePrefab : false) &&
|
||||
Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets;
|
||||
|
||||
bool hasPrefabLink = canEditScene && isSingleActorSelected && (Editor.SceneEditing.Selection[0] as ActorNode).HasPrefabLink;
|
||||
bool hasPrefabLink = canEditScene && isSingleActorSelected && (firstSelection != null ? firstSelection.HasPrefabLink : false);
|
||||
if (hasPrefabLink)
|
||||
{
|
||||
contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab);
|
||||
|
||||
Reference in New Issue
Block a user