From 43952fdc31634b4feb2dd02f072a150c74e5063b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 20 Aug 2024 20:33:36 -0500 Subject: [PATCH 01/84] Add support for Vector3, Float3, Double3, and Quaternion mutliselect value changing. --- Source/Editor/CustomEditors/CustomEditor.cs | 15 +- .../CustomEditors/Editors/QuaternionEditor.cs | 262 ++++++++- .../CustomEditors/Editors/Vector3Editor.cs | 547 ++++++++++++++++-- .../CustomEditors/Values/ValueContainer.cs | 18 +- 4 files changed, 772 insertions(+), 70 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 2bc9aaac8..5b86c9b13 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -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; @@ -269,10 +270,18 @@ namespace FlaxEditor.CustomEditors object val = _valueToSet; _hasValueDirty = false; _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 { diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs index 96f9d0646..704e1c803 100644 --- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs @@ -33,6 +33,15 @@ namespace FlaxEditor.CustomEditors.Editors /// public override DisplayStyle Style => DisplayStyle.Inline; + + /// + /// Whether to use the average for sliding different values. + /// + public virtual bool AllowSlidingForDifferentValues => true; + + private ValueChanged _valueChanged; + private float _defaultSlidingSpeed; + private bool _slidingEnded = false; /// public override void Initialize(LayoutElementsContainer layout) @@ -46,18 +55,30 @@ 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(); + }; 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 +90,162 @@ 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]; + switch (_valueChanged) + { + case ValueChanged.X: + { + var val = (Quaternion)v; + Quaternion.Euler(newValue.X, 0, 0, out Quaternion qVal); + v = val * qVal; + break; + } + case ValueChanged.Y: + { + var val = (Quaternion)v; + Quaternion.Euler(0, newValue.Y, 0, out Quaternion qVal); + v = val * qVal; + break; + } + case ValueChanged.Z: + { + var val = (Quaternion)v; + Quaternion.Euler(0, 0, newValue.Z, out Quaternion qVal); + v = val * qVal; + break; + } + } + + newObjects[i] = v; + } + + // Capture last sliding value + if (_slidingEnded) + _slidingEnded = false; + } + else + { + for (int i = 0; i < Values.Count; i++) + { + object v = Values[i]; + switch (_valueChanged) + { + case ValueChanged.X: + { + var val = (Quaternion)v; + var euler = val.EulerAngles; + Quaternion.Euler(xValue, euler.Y, euler.Z, out Quaternion qVal); + v = qVal; + break; + } + case ValueChanged.Y: + { + var val = (Quaternion)v; + var euler = val.EulerAngles; + Quaternion.Euler(euler.X, yValue, euler.Z, out Quaternion qVal); + v = qVal; + break; + } + case ValueChanged.Z: + { + var val = (Quaternion)v; + var euler = val.EulerAngles; + Quaternion.Euler(euler.X, euler.Y, zValue, out Quaternion qVal); + v = qVal; + break; + } + } + + 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); + } } /// @@ -112,7 +262,73 @@ namespace FlaxEditor.CustomEditors.Editors if (HasDifferentValues) { - // TODO: support different values for ValueBox + // 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[0]; + 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 { diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 719fbf05d..0e8104fa6 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -7,6 +7,27 @@ using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Editors { + /// + /// The value changed by custom Vector3 editors. + /// + public enum ValueChanged + { + /// + /// X value changed. + /// + X = 0, + + /// + /// Y value changed. + /// + Y = 1, + + /// + /// Z value changed. + /// + Z = 2 + } + /// /// Default implementation of the inspector used to edit Vector3 value type properties. /// @@ -49,14 +70,14 @@ namespace FlaxEditor.CustomEditors.Editors /// public bool LinkValues = false; - private enum ValueChanged - { - X = 0, - Y = 1, - Z = 2 - } + /// + /// Whether to use the average for sliding different values. + /// + public virtual bool AllowSlidingForDifferentValues => true; private ValueChanged _valueChanged; + private float _defaultSlidingSpeed; + private bool _slidingEnded = false; /// 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,121 @@ 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]; + switch (_valueChanged) + { + case ValueChanged.X: + if (v is Vector3 asVector3) + v = asVector3 + new Vector3(newValue.X, 0, 0); + else if (v is Float3 asFloat3) + v = asFloat3 + new Float3(newValue.X, 0, 0); + else if (v is Double3 asDouble3) + v = asDouble3 + new Double3(newValue.X, 0, 0); + break; + case ValueChanged.Y: + if (v is Vector3 asVector3y) + v = asVector3y + new Vector3(0, newValue.Y, 0); + else if (v is Float3 asFloat3y) + v = asFloat3y + new Float3(0, newValue.Y, 0); + else if (v is Double3 asDouble3y) + v = asDouble3y + new Double3(0, newValue.Y, 0); + break; + case ValueChanged.Z: + if (v is Vector3 asVector3z) + v = asVector3z + new Vector3(0, 0, newValue.Z); + else if (v is Float3 asFloat3z) + v = asFloat3z + new Float3(0, 0, newValue.Z); + else if (v is Double3 asDouble3z) + v = asDouble3z + new Double3(0, 0, newValue.Z); + break; + } + + 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]; + switch (_valueChanged) + { + case ValueChanged.X: + if (v is Vector3 vx3) + v = new Vector3(xValue, vx3.Y, vx3.Z); + else if (v is Float3 fx3) + v = new Float3(xValue, fx3.Y, fx3.Z); + else if (v is Double3 dx3) + v = new Double3(xValue, dx3.Y, dx3.Z); + break; + case ValueChanged.Y: + if (v is Vector3 vy3) + v = new Vector3(vy3.X, yValue, vy3.Z); + else if (v is Float3 fy3) + v = new Float3(fy3.X, yValue, fy3.Z); + else if (v is Double3 dy3) + v = new Double3(dy3.X, yValue, dy3.Z); + break; + case ValueChanged.Z: + if (v is Vector3 vz3) + v = new Vector3(vz3.X, vz3.Y, zValue); + else if (v is Float3 fz3) + v = new Float3(fz3.X, fz3.Y, zValue); + else if (v is Double3 dz3) + v = new Double3(dz3.X, dz3.Y, zValue); + break; + } + + 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 +356,79 @@ namespace FlaxEditor.CustomEditors.Editors if (HasDifferentValues) { - // TODO: support different values for ValueBox + // 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 +442,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; + } } } } @@ -259,6 +482,15 @@ namespace FlaxEditor.CustomEditors.Editors /// public override DisplayStyle Style => DisplayStyle.Inline; + + /// + /// Whether to use the average for sliding different values. + /// + public virtual bool AllowSlidingForDifferentValues => true; + + private ValueChanged _valueChanged; + private float _defaultSlidingSpeed; + private bool _slidingEnded = false; /// public override void Initialize(LayoutElementsContainer layout) @@ -284,20 +516,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,32 +561,239 @@ 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) 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]; + switch (_valueChanged) + { + case ValueChanged.X: + if (v is Vector3 asVector3) + v = asVector3 + new Vector3(newValue.X, 0, 0); + else if (v is Float3 asFloat3) + v = asFloat3 + new Float3((float)newValue.X, 0, 0); + else if (v is Double3 asDouble3) + v = asDouble3 + new Double3(newValue.X, 0, 0); + break; + case ValueChanged.Y: + if (v is Vector3 asVector3y) + v = asVector3y + new Vector3(0, newValue.Y, 0); + else if (v is Float3 asFloat3y) + v = asFloat3y + new Float3(0, (float)newValue.Y, 0); + else if (v is Double3 asDouble3y) + v = asDouble3y + new Double3(0, newValue.Y, 0); + break; + case ValueChanged.Z: + if (v is Vector3 asVector3z) + v = asVector3z + new Vector3(0, 0, newValue.Z); + else if (v is Float3 asFloat3z) + v = asFloat3z + new Float3(0, 0, (float)newValue.Z); + else if (v is Double3 asDouble3z) + v = asDouble3z + new Double3(0, 0, newValue.Z); + break; + } + + 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]; + switch (_valueChanged) + { + case ValueChanged.X: + if (v is Vector3 vx3) + v = new Vector3(xValue, vx3.Y, vx3.Z); + else if (v is Float3 fx3) + v = new Float3((float)xValue, fx3.Y, fx3.Z); + else if (v is Double3 dx3) + v = new Double3(xValue, dx3.Y, dx3.Z); + break; + case ValueChanged.Y: + if (v is Vector3 vy3) + v = new Vector3(vy3.X, yValue, vy3.Z); + else if (v is Float3 fy3) + v = new Float3(fy3.X, (float)yValue, fy3.Z); + else if (v is Double3 dy3) + v = new Double3(dy3.X, yValue, dy3.Z); + break; + case ValueChanged.Z: + if (v is Vector3 vz3) + v = new Vector3(vz3.X, vz3.Y, zValue); + else if (v is Float3 fz3) + v = new Float3(fz3.X, fz3.Y, (float)zValue); + else if (v is Double3 dz3) + v = new Double3(dz3.X, dz3.Y, zValue); + break; + } + + 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); + } } - /// + /// public override void Refresh() { base.Refresh(); if (HasDifferentValues) { - // TODO: support different values for ValueBox + // 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 +807,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; + } } } } diff --git a/Source/Editor/CustomEditors/Values/ValueContainer.cs b/Source/Editor/CustomEditors/Values/ValueContainer.cs index 5dc6adcc2..2defd573b 100644 --- a/Source/Editor/CustomEditors/Values/ValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/ValueContainer.cs @@ -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; @@ -379,10 +380,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]); + } } } From 252de16c135cd2785bd91e8e1ce4b5cbed67f0df Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 20 Aug 2024 20:40:50 -0500 Subject: [PATCH 02/84] Fix bugs with quaternion editor. --- .../Editor/CustomEditors/Editors/QuaternionEditor.cs | 10 +++++++++- Source/Editor/CustomEditors/Editors/Vector3Editor.cs | 6 ------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs index 704e1c803..1edf18c03 100644 --- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs @@ -61,6 +61,7 @@ namespace FlaxEditor.CustomEditors.Editors _slidingEnded = true; ClearToken(); }; + _defaultSlidingSpeed = XElement.ValueBox.SlideSpeed; YElement = grid.FloatValue(); YElement.ValueBox.Category = Utils.ValueCategory.Angle; @@ -270,7 +271,7 @@ namespace FlaxEditor.CustomEditors.Editors Float3 average = Float3.Zero; for (int i = 0; i < Values.Count; i++) { - var value = (Quaternion)Values[0]; + var value = (Quaternion)Values[i]; var euler = value.EulerAngles; average += euler; @@ -337,6 +338,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; } } } diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 0e8104fa6..8f002a2d4 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -444,17 +444,11 @@ namespace FlaxEditor.CustomEditors.Editors 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; - } } } } From ce06809970c5f6324da2ed722838f5ac760dfb26 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 20 Aug 2024 20:43:07 -0500 Subject: [PATCH 03/84] Small code style fix. --- Source/Editor/CustomEditors/CustomEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 5b86c9b13..dd8b715ff 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -270,7 +270,7 @@ namespace FlaxEditor.CustomEditors object val = _valueToSet; _hasValueDirty = false; _valueToSet = null; - + // Assign value if (val is IList l && l.Count == _values.Count) { From 8802cfa32a469fa80d6f55eed99fefa7473d010f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 20 Aug 2024 20:58:07 -0500 Subject: [PATCH 04/84] Simplify code. --- .../CustomEditors/Editors/QuaternionEditor.cs | 62 ++------ .../CustomEditors/Editors/Vector3Editor.cs | 136 ++++-------------- 2 files changed, 35 insertions(+), 163 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs index 1edf18c03..0da35aa4f 100644 --- a/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/QuaternionEditor.cs @@ -156,31 +156,10 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < Values.Count; i++) { var v = Values[i]; - switch (_valueChanged) - { - case ValueChanged.X: - { - var val = (Quaternion)v; - Quaternion.Euler(newValue.X, 0, 0, out Quaternion qVal); - v = val * qVal; - break; - } - case ValueChanged.Y: - { - var val = (Quaternion)v; - Quaternion.Euler(0, newValue.Y, 0, out Quaternion qVal); - v = val * qVal; - break; - } - case ValueChanged.Z: - { - var val = (Quaternion)v; - Quaternion.Euler(0, 0, newValue.Z, out Quaternion qVal); - v = val * qVal; - break; - } - } - + 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; } @@ -193,34 +172,11 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < Values.Count; i++) { object v = Values[i]; - switch (_valueChanged) - { - case ValueChanged.X: - { - var val = (Quaternion)v; - var euler = val.EulerAngles; - Quaternion.Euler(xValue, euler.Y, euler.Z, out Quaternion qVal); - v = qVal; - break; - } - case ValueChanged.Y: - { - var val = (Quaternion)v; - var euler = val.EulerAngles; - Quaternion.Euler(euler.X, yValue, euler.Z, out Quaternion qVal); - v = qVal; - break; - } - case ValueChanged.Z: - { - var val = (Quaternion)v; - var euler = val.EulerAngles; - Quaternion.Euler(euler.X, euler.Y, zValue, out Quaternion qVal); - v = qVal; - break; - } - } - + 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; } } diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index 8f002a2d4..d1807ea83 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -251,34 +251,13 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < Values.Count; i++) { var v = Values[i]; - switch (_valueChanged) - { - case ValueChanged.X: - if (v is Vector3 asVector3) - v = asVector3 + new Vector3(newValue.X, 0, 0); - else if (v is Float3 asFloat3) - v = asFloat3 + new Float3(newValue.X, 0, 0); - else if (v is Double3 asDouble3) - v = asDouble3 + new Double3(newValue.X, 0, 0); - break; - case ValueChanged.Y: - if (v is Vector3 asVector3y) - v = asVector3y + new Vector3(0, newValue.Y, 0); - else if (v is Float3 asFloat3y) - v = asFloat3y + new Float3(0, newValue.Y, 0); - else if (v is Double3 asDouble3y) - v = asDouble3y + new Double3(0, newValue.Y, 0); - break; - case ValueChanged.Z: - if (v is Vector3 asVector3z) - v = asVector3z + new Vector3(0, 0, newValue.Z); - else if (v is Float3 asFloat3z) - v = asFloat3z + new Float3(0, 0, newValue.Z); - else if (v is Double3 asDouble3z) - v = asDouble3z + new Double3(0, 0, newValue.Z); - break; - } - + 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; } @@ -292,34 +271,13 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < Values.Count; i++) { object v = Values[i]; - switch (_valueChanged) - { - case ValueChanged.X: - if (v is Vector3 vx3) - v = new Vector3(xValue, vx3.Y, vx3.Z); - else if (v is Float3 fx3) - v = new Float3(xValue, fx3.Y, fx3.Z); - else if (v is Double3 dx3) - v = new Double3(xValue, dx3.Y, dx3.Z); - break; - case ValueChanged.Y: - if (v is Vector3 vy3) - v = new Vector3(vy3.X, yValue, vy3.Z); - else if (v is Float3 fy3) - v = new Float3(fy3.X, yValue, fy3.Z); - else if (v is Double3 dy3) - v = new Double3(dy3.X, yValue, dy3.Z); - break; - case ValueChanged.Z: - if (v is Vector3 vz3) - v = new Vector3(vz3.X, vz3.Y, zValue); - else if (v is Float3 fz3) - v = new Float3(fz3.X, fz3.Y, zValue); - else if (v is Double3 dz3) - v = new Double3(dz3.X, dz3.Y, zValue); - break; - } - + 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; } } @@ -620,33 +578,12 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < Values.Count; i++) { var v = Values[i]; - switch (_valueChanged) - { - case ValueChanged.X: - if (v is Vector3 asVector3) - v = asVector3 + new Vector3(newValue.X, 0, 0); - else if (v is Float3 asFloat3) - v = asFloat3 + new Float3((float)newValue.X, 0, 0); - else if (v is Double3 asDouble3) - v = asDouble3 + new Double3(newValue.X, 0, 0); - break; - case ValueChanged.Y: - if (v is Vector3 asVector3y) - v = asVector3y + new Vector3(0, newValue.Y, 0); - else if (v is Float3 asFloat3y) - v = asFloat3y + new Float3(0, (float)newValue.Y, 0); - else if (v is Double3 asDouble3y) - v = asDouble3y + new Double3(0, newValue.Y, 0); - break; - case ValueChanged.Z: - if (v is Vector3 asVector3z) - v = asVector3z + new Vector3(0, 0, newValue.Z); - else if (v is Float3 asFloat3z) - v = asFloat3z + new Float3(0, 0, (float)newValue.Z); - else if (v is Double3 asDouble3z) - v = asDouble3z + new Double3(0, 0, newValue.Z); - break; - } + 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; } @@ -661,33 +598,12 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < Values.Count; i++) { object v = Values[i]; - switch (_valueChanged) - { - case ValueChanged.X: - if (v is Vector3 vx3) - v = new Vector3(xValue, vx3.Y, vx3.Z); - else if (v is Float3 fx3) - v = new Float3((float)xValue, fx3.Y, fx3.Z); - else if (v is Double3 dx3) - v = new Double3(xValue, dx3.Y, dx3.Z); - break; - case ValueChanged.Y: - if (v is Vector3 vy3) - v = new Vector3(vy3.X, yValue, vy3.Z); - else if (v is Float3 fy3) - v = new Float3(fy3.X, (float)yValue, fy3.Z); - else if (v is Double3 dy3) - v = new Double3(dy3.X, yValue, dy3.Z); - break; - case ValueChanged.Z: - if (v is Vector3 vz3) - v = new Vector3(vz3.X, vz3.Y, zValue); - else if (v is Float3 fz3) - v = new Float3(fz3.X, fz3.Y, (float)zValue); - else if (v is Double3 dz3) - v = new Double3(dz3.X, dz3.Y, zValue); - break; - } + 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; } From 0cc3026b07da6a31081fa4a47d63f3851e719b06 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Wed, 21 Aug 2024 08:53:19 -0500 Subject: [PATCH 05/84] Small code style fix. --- Source/Editor/CustomEditors/Editors/Vector3Editor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs index d1807ea83..c8557f8ae 100644 --- a/Source/Editor/CustomEditors/Editors/Vector3Editor.cs +++ b/Source/Editor/CustomEditors/Editors/Vector3Editor.cs @@ -624,7 +624,7 @@ namespace FlaxEditor.CustomEditors.Editors } } - /// + /// public override void Refresh() { base.Refresh(); From 667eb739111648b2439cfd928a175bc8eaf79b3e Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Tue, 22 Oct 2024 13:10:21 +0200 Subject: [PATCH 06/84] add warning when used param is being deleted "Used" means in graph and connected --- Source/Editor/Surface/VisjectSurfaceWindow.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index 16e4f303b..39c1ce8b2 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -776,6 +776,30 @@ namespace FlaxEditor.Surface private void DeleteParameter(int index) { var window = (IVisjectSurfaceWindow)Values[0]; + SurfaceParameter param = window.VisjectSurface.Parameters[index]; + + int connectedParameterNodeCount = 0; + + List nodes = window.VisjectSurface.Nodes; + + for (int i = 0; i < window.VisjectSurface.Nodes.Count; i++) + { + if (nodes[i] is IParametersDependantNode node) + { + if ((Guid)nodes[i].Values[0] == param.ID && nodes[i].GetBoxes().Any(b => b.Connections.Count > 0)) + connectedParameterNodeCount++; + } + } + + string singularPlural = connectedParameterNodeCount > 1 ? "s" : ""; + + string msg = $"Delete parameter {param.Name}?\nParameter is being used in a graph {connectedParameterNodeCount} time{singularPlural}.\n\nYou can disable this warning in the editor settings."; + string caption = "Delete parameter" + singularPlural; + + if (connectedParameterNodeCount > 0) + if (MessageBox.Show(msg, caption, MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) + return; + var action = new AddRemoveParamAction { Window = window, From aee5382ff64ada6abd0ee12cdb08c13b7e217b92 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Tue, 22 Oct 2024 13:38:37 +0200 Subject: [PATCH 07/84] Remove plural from caption --- Source/Editor/Surface/VisjectSurfaceWindow.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index 39c1ce8b2..fdb9fa9bc 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -794,10 +794,9 @@ namespace FlaxEditor.Surface string singularPlural = connectedParameterNodeCount > 1 ? "s" : ""; string msg = $"Delete parameter {param.Name}?\nParameter is being used in a graph {connectedParameterNodeCount} time{singularPlural}.\n\nYou can disable this warning in the editor settings."; - string caption = "Delete parameter" + singularPlural; if (connectedParameterNodeCount > 0) - if (MessageBox.Show(msg, caption, MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) + if (MessageBox.Show(msg, "Delete parameter", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) return; var action = new AddRemoveParamAction From b880edc889646e2db59f2eb4a00670f0d65520d6 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Tue, 22 Oct 2024 13:49:26 +0200 Subject: [PATCH 08/84] add editor option to toggle warning --- Source/Editor/Options/InterfaceOptions.cs | 7 +++++++ Source/Editor/Surface/VisjectSurfaceWindow.cs | 18 +++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index d8493a70a..976f47d1b 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -396,6 +396,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; + /// + /// Gets or sets a value that indicates if a warning should be displayed when deleting a Visject parameter that is used in a graph. + /// + [DefaultValue(true)] + [EditorDisplay("Visject", "Warn when deleting used parameter"), EditorOrder(552)] + public bool WarnOnDeletingUsedVisjectParameter { get; set; } = true; + private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.PrimaryFont); private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal(EditorAssets.InconsolataRegularFont); diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index fdb9fa9bc..57ec0d2c9 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -775,6 +775,8 @@ namespace FlaxEditor.Surface private void DeleteParameter(int index) { + bool displayWarning = Editor.Instance.Options.Options.Interface.WarnOnDeletingUsedVisjectParameter; + var window = (IVisjectSurfaceWindow)Values[0]; SurfaceParameter param = window.VisjectSurface.Parameters[index]; @@ -786,18 +788,20 @@ namespace FlaxEditor.Surface { if (nodes[i] is IParametersDependantNode node) { - if ((Guid)nodes[i].Values[0] == param.ID && nodes[i].GetBoxes().Any(b => b.Connections.Count > 0)) + if (displayWarning && (Guid)nodes[i].Values[0] == param.ID && nodes[i].GetBoxes().Any(b => b.Connections.Count > 0)) connectedParameterNodeCount++; } } - string singularPlural = connectedParameterNodeCount > 1 ? "s" : ""; + if (displayWarning) + { + string singularPlural = connectedParameterNodeCount > 1 ? "s" : ""; + string msg = $"Delete parameter {param.Name}?\nParameter is being used in a graph {connectedParameterNodeCount} time{singularPlural}.\n\nYou can disable this warning in the editor settings."; - string msg = $"Delete parameter {param.Name}?\nParameter is being used in a graph {connectedParameterNodeCount} time{singularPlural}.\n\nYou can disable this warning in the editor settings."; - - if (connectedParameterNodeCount > 0) - if (MessageBox.Show(msg, "Delete parameter", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) - return; + if (connectedParameterNodeCount > 0) + if (MessageBox.Show(msg, "Delete parameter", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) + return; + } var action = new AddRemoveParamAction { From 894741eaa7d9da92aa91f247757f99b1e5f63775 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Tue, 22 Oct 2024 13:59:48 +0200 Subject: [PATCH 09/84] fix typo in filename --- ...{VisjectSurface.Paramaters.cs => VisjectSurface.Parameters.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Source/Editor/Surface/{VisjectSurface.Paramaters.cs => VisjectSurface.Parameters.cs} (100%) diff --git a/Source/Editor/Surface/VisjectSurface.Paramaters.cs b/Source/Editor/Surface/VisjectSurface.Parameters.cs similarity index 100% rename from Source/Editor/Surface/VisjectSurface.Paramaters.cs rename to Source/Editor/Surface/VisjectSurface.Parameters.cs From 6b4b85b11393bd3c6bf1be67e7fb91fe08fda1d1 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 24 Oct 2024 20:48:53 +0200 Subject: [PATCH 10/84] fix code style --- Source/Editor/Surface/Archetypes/Parameters.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index a75be6bb5..5401ba840 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -937,9 +937,7 @@ namespace FlaxEditor.Surface.Archetypes { // Deselect if that parameter is selected if ((Guid)Values[0] == param.ID) - { _combobox.SelectedIndex = -1; - } UpdateCombo(); } From 57b4e9f2955b21d93239d718bc6e8fcaaf811906 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 24 Oct 2024 23:24:28 +0200 Subject: [PATCH 11/84] move used checks to interface --- .../Editor/Surface/Archetypes/Parameters.cs | 29 +++++++++++++++++++ .../Surface/IParametersDependantNode.cs | 8 +++++ Source/Editor/Surface/VisjectSurfaceWindow.cs | 2 +- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index 5401ba840..f30a33090 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -13,6 +13,7 @@ using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; using FlaxEngine; using FlaxEngine.Utilities; +using System.Linq; namespace FlaxEditor.Surface.Archetypes { @@ -425,6 +426,20 @@ namespace FlaxEditor.Surface.Archetypes UpdateCombo(); } + /// + public bool IsParameterReferenced(SurfaceParameter param, VisjectSurface surface) + { + for (int i = 0; i < surface.Nodes.Count; i++) + { + if (surface.Nodes[i] is IParametersDependantNode node) + { + return (Guid)surface.Nodes[i].Values[0] == param.ID && surface.Nodes[i].GetBoxes().Any(b => b.Connections.Count > 0); + } + } + + return false; + } + /// public override void OnLoaded(SurfaceNodeActions action) { @@ -942,6 +957,20 @@ namespace FlaxEditor.Surface.Archetypes UpdateCombo(); } + /// + public bool IsParameterReferenced(SurfaceParameter param, VisjectSurface surface) + { + for (int i = 0; i < surface.Nodes.Count; i++) + { + if (surface.Nodes[i] is IParametersDependantNode node) + { + return (Guid)surface.Nodes[i].Values[0] == param.ID && surface.Nodes[i].GetBoxes().Any(b => b.Connections.Count > 0); + } + } + + return false; + } + /// public override void OnLoaded(SurfaceNodeActions action) { diff --git a/Source/Editor/Surface/IParametersDependantNode.cs b/Source/Editor/Surface/IParametersDependantNode.cs index 9683abd70..c424454d5 100644 --- a/Source/Editor/Surface/IParametersDependantNode.cs +++ b/Source/Editor/Surface/IParametersDependantNode.cs @@ -33,5 +33,13 @@ namespace FlaxEditor.Surface /// /// The parameter. void OnParamDeleted(SurfaceParameter param); + + /// + /// 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. + /// + /// The parameter. + /// /// The visject surface. + /// How often the parameter is referenced. + bool IsParameterReferenced(SurfaceParameter param, VisjectSurface surface); } } diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index 57ec0d2c9..7c653a823 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -788,7 +788,7 @@ namespace FlaxEditor.Surface { if (nodes[i] is IParametersDependantNode node) { - if (displayWarning && (Guid)nodes[i].Values[0] == param.ID && nodes[i].GetBoxes().Any(b => b.Connections.Count > 0)) + if (displayWarning && node.IsParameterReferenced(param, window.VisjectSurface)) connectedParameterNodeCount++; } } From 2952bdca66003f85763ef0f1e36465f88db3b814 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 24 Oct 2024 23:28:48 +0200 Subject: [PATCH 12/84] fix docs commend and function name --- Source/Editor/Surface/Archetypes/Parameters.cs | 4 ++-- Source/Editor/Surface/IParametersDependantNode.cs | 6 +++--- Source/Editor/Surface/VisjectSurfaceWindow.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index f30a33090..a06ef769e 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -427,7 +427,7 @@ namespace FlaxEditor.Surface.Archetypes } /// - public bool IsParameterReferenced(SurfaceParameter param, VisjectSurface surface) + public bool IsParamreferenced(SurfaceParameter param, VisjectSurface surface) { for (int i = 0; i < surface.Nodes.Count; i++) { @@ -958,7 +958,7 @@ namespace FlaxEditor.Surface.Archetypes } /// - public bool IsParameterReferenced(SurfaceParameter param, VisjectSurface surface) + public bool IsParamreferenced(SurfaceParameter param, VisjectSurface surface) { for (int i = 0; i < surface.Nodes.Count; i++) { diff --git a/Source/Editor/Surface/IParametersDependantNode.cs b/Source/Editor/Surface/IParametersDependantNode.cs index c424454d5..650dff545 100644 --- a/Source/Editor/Surface/IParametersDependantNode.cs +++ b/Source/Editor/Surface/IParametersDependantNode.cs @@ -38,8 +38,8 @@ namespace FlaxEditor.Surface /// 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. /// /// The parameter. - /// /// The visject surface. - /// How often the parameter is referenced. - bool IsParameterReferenced(SurfaceParameter param, VisjectSurface surface); + /// The visject surface. + /// If the parameter is referenced. + bool IsParamreferenced(SurfaceParameter param, VisjectSurface surface); } } diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index 7c653a823..5f6e95a0a 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -788,7 +788,7 @@ namespace FlaxEditor.Surface { if (nodes[i] is IParametersDependantNode node) { - if (displayWarning && node.IsParameterReferenced(param, window.VisjectSurface)) + if (displayWarning && node.IsParamreferenced(param, window.VisjectSurface)) connectedParameterNodeCount++; } } From a0aee15267b3fe19d11b65b0640088f297fc3eb4 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Fri, 25 Oct 2024 15:30:54 +0200 Subject: [PATCH 13/84] add named terrain layers --- .../Editors/TerrainLayerEditor.cs | 45 +++++++++++++++++++ Source/Editor/Surface/Archetypes/Material.cs | 3 +- .../Tools/Terrain/Paint/SingleLayerMode.cs | 2 +- .../Core/Config/LayersAndTagsSettings.cs | 24 ++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 Source/Editor/CustomEditors/Editors/TerrainLayerEditor.cs diff --git a/Source/Editor/CustomEditors/Editors/TerrainLayerEditor.cs b/Source/Editor/CustomEditors/Editors/TerrainLayerEditor.cs new file mode 100644 index 000000000..6e6f876b0 --- /dev/null +++ b/Source/Editor/CustomEditors/Editors/TerrainLayerEditor.cs @@ -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 +{ + /// + /// 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. + /// + public sealed class TerrainLayerEditor : CustomEditor + { + private ComboBoxElement element; + + /// + public override DisplayStyle Style => DisplayStyle.Inline; + + /// + 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); + } + + /// + public override void Refresh() + { + base.Refresh(); + + element.ComboBox.SelectedIndex = (int)Values[0]; + } + } +} diff --git a/Source/Editor/Surface/Archetypes/Material.cs b/Source/Editor/Surface/Archetypes/Material.cs index f1da861c1..9da0ec067 100644 --- a/Source/Editor/Surface/Archetypes/Material.cs +++ b/Source/Editor/Surface/Archetypes/Material.cs @@ -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), } }, diff --git a/Source/Editor/Tools/Terrain/Paint/SingleLayerMode.cs b/Source/Editor/Tools/Terrain/Paint/SingleLayerMode.cs index 5581eeb66..9cf550f5c 100644 --- a/Source/Editor/Tools/Terrain/Paint/SingleLayerMode.cs +++ b/Source/Editor/Tools/Terrain/Paint/SingleLayerMode.cs @@ -60,7 +60,7 @@ namespace FlaxEditor.Tools.Terrain.Paint /// /// The layer to paint with it. /// - [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; /// diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index 9346efee7..ddee29e88 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System.Collections.Generic; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using FlaxEngine; @@ -30,6 +31,29 @@ namespace FlaxEditor.Content.Settings return GetCurrentLayers(out int _); } + /// + /// The layers names. + /// + [EditorOrder(10), EditorDisplay("Terrain Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)] + public string[] TerrainLayers = new string[8]; + + /// + /// Gets the current terrain layer names. Returns "Layer" + index for layers without a name. + /// + /// The layer names. + public static string[] GetCurrentTerrainLayers() + { + string[] layerNames = GameSettings.Load().TerrainLayers.ToArray(); + + for (int i = 0; i < layerNames.Length; i++) + { + if (string.IsNullOrEmpty(layerNames[i])) + layerNames[i] = $"Terrain Layer {i}"; + } + + return layerNames; + } + [LibraryImport("FlaxEngine", EntryPoint = "LayersAndTagsSettingsInternal_GetCurrentLayers", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))] [return: MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "layerCount")] internal static partial string[] GetCurrentLayers(out int layerCount); From 68e531aebbd99570a8ad42e5fad1e1e7ad0652a7 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Fri, 25 Oct 2024 16:09:30 +0200 Subject: [PATCH 14/84] change how layers with no name get displayed Makes the name shorter, fits into comboboxes more easily --- Source/Engine/Core/Config/LayersAndTagsSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index ddee29e88..30f84f493 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -48,7 +48,7 @@ namespace FlaxEditor.Content.Settings for (int i = 0; i < layerNames.Length; i++) { if (string.IsNullOrEmpty(layerNames[i])) - layerNames[i] = $"Terrain Layer {i}"; + layerNames[i] = $"Layer {i}"; } return layerNames; From 1d7054265d1e5702cc1d2007eb6647925f2ca838 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Fri, 25 Oct 2024 19:58:49 +0200 Subject: [PATCH 15/84] avoid null string issues --- Source/Engine/Core/Config/LayersAndTagsSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index 30f84f493..840332efb 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -35,7 +35,7 @@ namespace FlaxEditor.Content.Settings /// The layers names. /// [EditorOrder(10), EditorDisplay("Terrain Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)] - public string[] TerrainLayers = new string[8]; + public string[] TerrainLayers = Enumerable.Repeat(string.Empty, 8).ToArray(); /// /// Gets the current terrain layer names. Returns "Layer" + index for layers without a name. From c8146a8e99d02f5c1e0bda2023d62189f6c05f07 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 16 Nov 2024 17:05:44 +0100 Subject: [PATCH 16/84] remove unused TerrainLayerNames --- .../Editor/Tools/Terrain/PaintTerrainGizmoMode.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Source/Editor/Tools/Terrain/PaintTerrainGizmoMode.cs b/Source/Editor/Tools/Terrain/PaintTerrainGizmoMode.cs index 0be0fe41c..f9cf12fc1 100644 --- a/Source/Editor/Tools/Terrain/PaintTerrainGizmoMode.cs +++ b/Source/Editor/Tools/Terrain/PaintTerrainGizmoMode.cs @@ -21,21 +21,6 @@ namespace FlaxEditor.Tools.Terrain [HideInEditor] public class PaintTerrainGizmoMode : EditorGizmoMode { - /// - /// The terrain layer names. - /// - 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; From 75d4a110de45b5494fd7ee3c1f49597797dd5605 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 16 Nov 2024 17:08:10 +0100 Subject: [PATCH 17/84] remove usage of System.Linq --- Source/Engine/Core/Config/LayersAndTagsSettings.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index 840332efb..911a131a7 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using FlaxEngine; @@ -35,7 +34,7 @@ namespace FlaxEditor.Content.Settings /// The layers names. /// [EditorOrder(10), EditorDisplay("Terrain Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)] - public string[] TerrainLayers = Enumerable.Repeat(string.Empty, 8).ToArray(); + public string[] TerrainLayers = new string[8]; /// /// Gets the current terrain layer names. Returns "Layer" + index for layers without a name. @@ -43,7 +42,7 @@ namespace FlaxEditor.Content.Settings /// The layer names. public static string[] GetCurrentTerrainLayers() { - string[] layerNames = GameSettings.Load().TerrainLayers.ToArray(); + string[] layerNames = GameSettings.Load().TerrainLayers; for (int i = 0; i < layerNames.Length; i++) { From 498e94548ba11017f459fe34b2555f573d62a7f6 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 16 Nov 2024 17:08:34 +0100 Subject: [PATCH 18/84] fix tests not passing --- Source/Engine/Core/Config/LayersAndTagsSettings.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index 911a131a7..92063b27d 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -42,6 +42,9 @@ namespace FlaxEditor.Content.Settings /// The layer names. public static string[] GetCurrentTerrainLayers() { + #if FLAX_TESTS + return Array.Empty(); + #else string[] layerNames = GameSettings.Load().TerrainLayers; for (int i = 0; i < layerNames.Length; i++) @@ -51,6 +54,7 @@ namespace FlaxEditor.Content.Settings } return layerNames; + #endif } [LibraryImport("FlaxEngine", EntryPoint = "LayersAndTagsSettingsInternal_GetCurrentLayers", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))] From b055117144d42df15435a98207e41299161185c6 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 16 Nov 2024 18:37:36 +0100 Subject: [PATCH 19/84] simplify parameter deleted code --- Source/Editor/Surface/Archetypes/Parameters.cs | 16 ++++++++-------- .../Editor/Surface/IParametersDependantNode.cs | 3 +-- Source/Editor/Surface/VisjectSurfaceWindow.cs | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index a06ef769e..10d4337bd 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -427,13 +427,13 @@ namespace FlaxEditor.Surface.Archetypes } /// - public bool IsParamreferenced(SurfaceParameter param, VisjectSurface surface) + public bool IsParamreferenced(SurfaceParameter param) { - for (int i = 0; i < surface.Nodes.Count; i++) + for (int i = 0; i < Surface.Nodes.Count; i++) { - if (surface.Nodes[i] is IParametersDependantNode node) + if (Surface.Nodes[i] is IParametersDependantNode node) { - return (Guid)surface.Nodes[i].Values[0] == param.ID && surface.Nodes[i].GetBoxes().Any(b => b.Connections.Count > 0); + return (Guid)Values[0] == param.ID; } } @@ -958,13 +958,13 @@ namespace FlaxEditor.Surface.Archetypes } /// - public bool IsParamreferenced(SurfaceParameter param, VisjectSurface surface) + public bool IsParamreferenced(SurfaceParameter param) { - for (int i = 0; i < surface.Nodes.Count; i++) + for (int i = 0; i < Surface.Nodes.Count; i++) { - if (surface.Nodes[i] is IParametersDependantNode node) + if (Surface.Nodes[i] is IParametersDependantNode node) { - return (Guid)surface.Nodes[i].Values[0] == param.ID && surface.Nodes[i].GetBoxes().Any(b => b.Connections.Count > 0); + return (Guid)Values[0] == param.ID; } } diff --git a/Source/Editor/Surface/IParametersDependantNode.cs b/Source/Editor/Surface/IParametersDependantNode.cs index 650dff545..0148682f1 100644 --- a/Source/Editor/Surface/IParametersDependantNode.cs +++ b/Source/Editor/Surface/IParametersDependantNode.cs @@ -38,8 +38,7 @@ namespace FlaxEditor.Surface /// 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. /// /// The parameter. - /// The visject surface. /// If the parameter is referenced. - bool IsParamreferenced(SurfaceParameter param, VisjectSurface surface); + bool IsParamreferenced(SurfaceParameter param); } } diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index 5f6e95a0a..5d6a050a6 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -788,7 +788,7 @@ namespace FlaxEditor.Surface { if (nodes[i] is IParametersDependantNode node) { - if (displayWarning && node.IsParamreferenced(param, window.VisjectSurface)) + if (displayWarning && node.IsParamreferenced(param)) connectedParameterNodeCount++; } } From 38d1e25604cc260fb2f60227a0bae90b43cc30ca Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 21 Nov 2024 20:20:42 +0100 Subject: [PATCH 20/84] replace for loop --- .../Editor/Surface/Archetypes/Parameters.cs | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index 10d4337bd..4509ce753 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -429,15 +429,7 @@ namespace FlaxEditor.Surface.Archetypes /// public bool IsParamreferenced(SurfaceParameter param) { - for (int i = 0; i < Surface.Nodes.Count; i++) - { - if (Surface.Nodes[i] is IParametersDependantNode node) - { - return (Guid)Values[0] == param.ID; - } - } - - return false; + return (Guid)Values[0] == param.ID; } /// @@ -960,15 +952,7 @@ namespace FlaxEditor.Surface.Archetypes /// public bool IsParamreferenced(SurfaceParameter param) { - for (int i = 0; i < Surface.Nodes.Count; i++) - { - if (Surface.Nodes[i] is IParametersDependantNode node) - { - return (Guid)Values[0] == param.ID; - } - } - - return false; + return (Guid)Values[0] == param.ID; } /// From c8b97b8a385196130adbfa6e6a45ec7e3d2d22ba Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 21 Nov 2024 20:00:26 -0600 Subject: [PATCH 21/84] Small UX improvements to tracks --- Source/Editor/GUI/Timeline/Timeline.cs | 31 +++++++++++++++++++ .../GUI/Timeline/Undo/AddRemoveTrackAction.cs | 6 +++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs index 556c4dcdb..8dabba251 100644 --- a/Source/Editor/GUI/Timeline/Timeline.cs +++ b/Source/Editor/GUI/Timeline/Timeline.cs @@ -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(); + } + } /// @@ -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(); } diff --git a/Source/Editor/GUI/Timeline/Undo/AddRemoveTrackAction.cs b/Source/Editor/GUI/Timeline/Undo/AddRemoveTrackAction.cs index dfdb911b3..51a832eeb 100644 --- a/Source/Editor/GUI/Timeline/Undo/AddRemoveTrackAction.cs +++ b/Source/Editor/GUI/Timeline/Undo/AddRemoveTrackAction.cs @@ -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"; From 2a7dcff5c46bdcd18804611b6d913ce328f3c822 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 21 Nov 2024 20:24:16 -0600 Subject: [PATCH 22/84] Fix scene tree menu options being enabled when not supposed to be --- Source/Editor/SceneGraph/Actors/SceneNode.cs | 1 + Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Source/Editor/SceneGraph/Actors/SceneNode.cs b/Source/Editor/SceneGraph/Actors/SceneNode.cs index f0938492f..bb5998ef4 100644 --- a/Source/Editor/SceneGraph/Actors/SceneNode.cs +++ b/Source/Editor/SceneGraph/Actors/SceneNode.cs @@ -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); } diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index 2cbe56a27..75dab7828 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -55,9 +55,9 @@ namespace FlaxEditor.Windows b = contextMenu.AddButton("Rename", inputOptions.Rename, Rename); b = contextMenu.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate); - b.Enabled = hasSthSelected; + b.Enabled = hasSthSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).CanDuplicate; - if (isSingleActorSelected) + if (isSingleActorSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).Actor is not Scene) { var convertMenu = contextMenu.AddChildMenu("Convert"); convertMenu.ContextMenu.AutoSort = true; @@ -117,24 +117,24 @@ namespace FlaxEditor.Windows } } b = contextMenu.AddButton("Delete", inputOptions.Delete, Editor.SceneEditing.Delete); - b.Enabled = hasSthSelected; + b.Enabled = hasSthSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).CanDelete; contextMenu.AddSeparator(); b = contextMenu.AddButton("Copy", inputOptions.Copy, Editor.SceneEditing.Copy); + b.Enabled = hasSthSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).CanCopyPaste; - 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 && ((ActorNode)Editor.SceneEditing.Selection[0]).CanCopyPaste; // Create option contextMenu.AddSeparator(); b = contextMenu.AddButton("Parent to new Actor", inputOptions.GroupSelectedActors, Editor.SceneEditing.CreateParentForSelectedActors); - b.Enabled = canEditScene && hasSthSelected; + b.Enabled = canEditScene && hasSthSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).Actor is not Scene; b = contextMenu.AddButton("Create Prefab", Editor.Prefabs.CreatePrefab); b.Enabled = isSingleActorSelected && From 27044da099669d393ba29f7a662f07bda1f1c342 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 21 Nov 2024 20:29:38 -0600 Subject: [PATCH 23/84] Small optimization. --- .../Windows/SceneTreeWindow.ContextMenu.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index 75dab7828..b4a52d394 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -52,12 +52,12 @@ namespace FlaxEditor.Windows contextMenu.AddSeparator(); // Basic editing options - + var firstSelection = (ActorNode)Editor.SceneEditing.Selection[0]; b = contextMenu.AddButton("Rename", inputOptions.Rename, Rename); b = contextMenu.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate); - b.Enabled = hasSthSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).CanDuplicate; + b.Enabled = hasSthSelected && firstSelection.CanDuplicate; - if (isSingleActorSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).Actor is not Scene) + if (isSingleActorSelected && firstSelection.Actor is not Scene) { var convertMenu = contextMenu.AddChildMenu("Convert"); convertMenu.ContextMenu.AutoSort = true; @@ -117,31 +117,31 @@ namespace FlaxEditor.Windows } } b = contextMenu.AddButton("Delete", inputOptions.Delete, Editor.SceneEditing.Delete); - b.Enabled = hasSthSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).CanDelete; + b.Enabled = hasSthSelected && firstSelection.CanDelete; contextMenu.AddSeparator(); b = contextMenu.AddButton("Copy", inputOptions.Copy, Editor.SceneEditing.Copy); - b.Enabled = hasSthSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).CanCopyPaste; + b.Enabled = hasSthSelected && firstSelection.CanCopyPaste; contextMenu.AddButton("Paste", inputOptions.Paste, Editor.SceneEditing.Paste); b = contextMenu.AddButton("Cut", inputOptions.Cut, Editor.SceneEditing.Cut); - b.Enabled = canEditScene && ((ActorNode)Editor.SceneEditing.Selection[0]).CanCopyPaste; + b.Enabled = canEditScene && firstSelection.CanCopyPaste; // Create option contextMenu.AddSeparator(); b = contextMenu.AddButton("Parent to new Actor", inputOptions.GroupSelectedActors, Editor.SceneEditing.CreateParentForSelectedActors); - b.Enabled = canEditScene && hasSthSelected && ((ActorNode)Editor.SceneEditing.Selection[0]).Actor is not Scene; + 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.CanCreatePrefab && Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets; - bool hasPrefabLink = canEditScene && isSingleActorSelected && (Editor.SceneEditing.Selection[0] as ActorNode).HasPrefabLink; + bool hasPrefabLink = canEditScene && isSingleActorSelected && firstSelection.HasPrefabLink; if (hasPrefabLink) { contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab); From 7330101206005ab2a8f6706bb42b1eddc501404b Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 21 Nov 2024 20:52:34 -0600 Subject: [PATCH 24/84] Fix errors --- .../Windows/SceneTreeWindow.ContextMenu.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs index b4a52d394..a107a72f5 100644 --- a/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs +++ b/Source/Editor/Windows/SceneTreeWindow.ContextMenu.cs @@ -52,12 +52,13 @@ namespace FlaxEditor.Windows contextMenu.AddSeparator(); // Basic editing options - var firstSelection = (ActorNode)Editor.SceneEditing.Selection[0]; + var firstSelection = hasSthSelected ? Editor.SceneEditing.Selection[0] as ActorNode : null; b = contextMenu.AddButton("Rename", inputOptions.Rename, Rename); + b.Enabled = hasSthSelected; b = contextMenu.AddButton("Duplicate", inputOptions.Duplicate, Editor.SceneEditing.Duplicate); - b.Enabled = hasSthSelected && firstSelection.CanDuplicate; + b.Enabled = hasSthSelected && (firstSelection != null ? firstSelection.CanDuplicate : true); - if (isSingleActorSelected && firstSelection.Actor is not Scene) + 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 && firstSelection.CanDelete; + b.Enabled = hasSthSelected && (firstSelection != null ? firstSelection.CanDelete : true); contextMenu.AddSeparator(); b = contextMenu.AddButton("Copy", inputOptions.Copy, Editor.SceneEditing.Copy); - b.Enabled = hasSthSelected && firstSelection.CanCopyPaste; + b.Enabled = hasSthSelected && (firstSelection != null ? firstSelection.CanCopyPaste : true); contextMenu.AddButton("Paste", inputOptions.Paste, Editor.SceneEditing.Paste); b = contextMenu.AddButton("Cut", inputOptions.Cut, Editor.SceneEditing.Cut); - b.Enabled = canEditScene && firstSelection.CanCopyPaste; + 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 && firstSelection.Actor is not Scene; + b.Enabled = canEditScene && hasSthSelected && firstSelection?.Actor is not Scene; b = contextMenu.AddButton("Create Prefab", Editor.Prefabs.CreatePrefab); b.Enabled = isSingleActorSelected && - firstSelection.CanCreatePrefab && + (firstSelection != null ? firstSelection.CanCreatePrefab : false) && Editor.Windows.ContentWin.CurrentViewFolder.CanHaveAssets; - bool hasPrefabLink = canEditScene && isSingleActorSelected && firstSelection.HasPrefabLink; + bool hasPrefabLink = canEditScene && isSingleActorSelected && (firstSelection != null ? firstSelection.HasPrefabLink : false); if (hasPrefabLink) { contextMenu.AddButton("Select Prefab", Editor.Prefabs.SelectPrefab); From 573b57ed4a512246bcfe2a47fa45511b7e0442ff Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 23 Nov 2024 17:16:02 +0100 Subject: [PATCH 25/84] add missing `System` --- Source/Engine/Core/Config/LayersAndTagsSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index 92063b27d..886c34b8a 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -43,7 +43,7 @@ namespace FlaxEditor.Content.Settings public static string[] GetCurrentTerrainLayers() { #if FLAX_TESTS - return Array.Empty(); + return System.Array.Empty(); #else string[] layerNames = GameSettings.Load().TerrainLayers; From 07aafea5afec90bdc957bf5757de1183dd003aa6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Nov 2024 14:54:11 +0100 Subject: [PATCH 26/84] Fix `Forward` and `Backward` in `Matrix` and `Matrix3x3` #3078 --- Source/Engine/Core/Math/Matrix.cs | 30 +++++++++++++------------- Source/Engine/Core/Math/Matrix.h | 30 +++++++++++++------------- Source/Engine/Core/Math/Matrix3x3.cs | 14 +++++------- Source/Engine/Core/Math/Matrix3x3.h | 32 ++++++++++++++-------------- 4 files changed, 51 insertions(+), 55 deletions(-) diff --git a/Source/Engine/Core/Math/Matrix.cs b/Source/Engine/Core/Math/Matrix.cs index c065379d3..71edef415 100644 --- a/Source/Engine/Core/Math/Matrix.cs +++ b/Source/Engine/Core/Math/Matrix.cs @@ -215,23 +215,9 @@ namespace FlaxEngine } /// - /// Gets or sets the forward of the matrix; that is -M31, -M32, and -M33. + /// Gets or sets the forward of the matrix; that is M31, M32, and M33. /// public Float3 Forward - { - get => new Float3(-M31, -M32, -M33); - set - { - M31 = -value.X; - M32 = -value.Y; - M33 = -value.Z; - } - } - - /// - /// Gets or sets the backward of the matrix; that is M31, M32, and M33. - /// - public Float3 Backward { get => new Float3(M31, M32, M33); set @@ -242,6 +228,20 @@ namespace FlaxEngine } } + /// + /// Gets or sets the backward of the matrix; that is -M31, -M32, and -M33. + /// + public Float3 Backward + { + get => new Float3(-M31, -M32, -M33); + set + { + M31 = -value.X; + M32 = -value.Y; + M33 = -value.Z; + } + } + /// /// Initializes a new instance of the struct. /// diff --git a/Source/Engine/Core/Math/Matrix.h b/Source/Engine/Core/Math/Matrix.h index 0eddc173b..e71da993a 100644 --- a/Source/Engine/Core/Math/Matrix.h +++ b/Source/Engine/Core/Math/Matrix.h @@ -210,31 +210,31 @@ public: // Gets the forward Float3 of the matrix; that is -M31, -M32, and -M33. Float3 GetForward() const { - return -Float3(M31, M32, M33); + return Float3(M31, M32, M33); } // Sets the forward Float3 of the matrix; that is -M31, -M32, and -M33. void SetForward(const Float3& value) - { - M31 = -value.X; - M32 = -value.Y; - M33 = -value.Z; - } - - // Gets the backward Float3 of the matrix; that is M31, M32, and M33. - Float3 GetBackward() const - { - return Float3(M31, M32, M33); - } - - // Sets the backward Float3 of the matrix; that is M31, M32, and M33. - void SetBackward(const Float3& value) { M31 = value.X; M32 = value.Y; M33 = value.Z; } + // Gets the backward Float3 of the matrix; that is -M31, -M32, and -M33. + Float3 GetBackward() const + { + return Float3(-M31, -M32, -M33); + } + + // Sets the backward Float3 of the matrix; that is -M31, -M32, and -M33. + void SetBackward(const Float3& value) + { + M31 = -value.X; + M32 = -value.Y; + M33 = -value.Z; + } + // Gets the first row in the matrix; that is M11, M12, M13, and M14. Float4 GetRow1() const { diff --git a/Source/Engine/Core/Math/Matrix3x3.cs b/Source/Engine/Core/Math/Matrix3x3.cs index b19d80490..970ec6cec 100644 --- a/Source/Engine/Core/Math/Matrix3x3.cs +++ b/Source/Engine/Core/Math/Matrix3x3.cs @@ -303,9 +303,6 @@ namespace FlaxEngine /// /// Gets a value indicating whether this instance is an identity Matrix3x3. /// - /// - /// true if this instance is an identity Matrix3x3; otherwise, false. - /// public bool IsIdentity => Equals(Identity); /// @@ -566,19 +563,19 @@ namespace FlaxEngine /// public bool DecomposeUniformScale(out float scale, out Quaternion rotation) { - //Scaling is the length of the rows. ( just take one row since this is a uniform matrix) + // Scaling is the length of the rows. ( just take one row since this is a uniform matrix) scale = (float)Math.Sqrt((M11 * M11) + (M12 * M12) + (M13 * M13)); var invScale = 1f / scale; - //If any of the scaling factors are zero, then the rotation matrix can not exist. + // If any of the scaling factors are zero, then the rotation matrix can not exist if (Math.Abs(scale) < Mathf.Epsilon) { rotation = Quaternion.Identity; return false; } - //The rotation is the left over matrix after dividing out the scaling. - Matrix3x3 rotationmatrix = new Matrix3x3 + // The rotation is the leftover matrix after dividing out the scaling + var rotationMatrix = new Matrix3x3 { M11 = M11 * invScale, M12 = M12 * invScale, @@ -590,8 +587,7 @@ namespace FlaxEngine M32 = M32 * invScale, M33 = M33 * invScale }; - - Quaternion.RotationMatrix(ref rotationmatrix, out rotation); + Quaternion.RotationMatrix(ref rotationMatrix, out rotation); return true; } diff --git a/Source/Engine/Core/Math/Matrix3x3.h b/Source/Engine/Core/Math/Matrix3x3.h index f5f8cd998..cb5ae04a2 100644 --- a/Source/Engine/Core/Math/Matrix3x3.h +++ b/Source/Engine/Core/Math/Matrix3x3.h @@ -175,34 +175,34 @@ public: M13 = -value.Z; } - // Gets the forward Float3 of the matrix; that is -M31, -M32, and -M33. + // Gets the forward Float3 of the matrix; that is M31, M32, and M33. Float3 GetForward() const { return -Float3(M31, M32, M33); } - // Sets the forward Float3 of the matrix; that is -M31, -M32, and -M33. + // Sets the forward Float3 of the matrix; that is M31, M32, and M33. void SetForward(const Float3& value) - { - M31 = -value.X; - M32 = -value.Y; - M33 = -value.Z; - } - - // Gets the backward Float3 of the matrix; that is M31, M32, and M33. - Float3 GetBackward() const - { - return Float3(M31, M32, M33); - } - - // Sets the backward Float3 of the matrix; that is M31, M32, and M33. - void SetBackward(const Float3& value) { M31 = value.X; M32 = value.Y; M33 = value.Z; } + // Gets the backward Float3 of the matrix; that is -M31, -M32, and -M33. + Float3 GetBackward() const + { + return Float3(-M31, -M32, -M33); + } + + // Sets the backward Float3 of the matrix; that is -M31, -M32, and -M33. + void SetBackward(const Float3& value) + { + M31 = -value.X; + M32 = -value.Y; + M33 = -value.Z; + } + // Gets the first row in the matrix; that is M11, M12 and M13. Float3 GetRow1() const { From e3c8d19a9e8dbea3ad9aa4e534550444114ee09e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Nov 2024 15:56:08 +0100 Subject: [PATCH 27/84] Fix missing D3D11 stencil ref value --- Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index 029012fa8..0ff1392ec 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -285,7 +285,10 @@ void GPUContextDX11::SetBlendFactor(const Float4& value) void GPUContextDX11::SetStencilRef(uint32 value) { if (CurrentStencilRef != value) + { + CurrentStencilRef = value; _context->OMSetDepthStencilState(CurrentDepthStencilState, CurrentStencilRef); + } } void GPUContextDX11::ResetSR() From 3bd0858acf354d08c28ce711c128a7d708eb6aa4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Nov 2024 16:03:16 +0100 Subject: [PATCH 28/84] Fix typo and indent --- Source/Engine/Core/Config/LayersAndTagsSettings.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs index 886c34b8a..4086ec657 100644 --- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs +++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs @@ -16,7 +16,7 @@ namespace FlaxEditor.Content.Settings public List Tags = new List(); /// - /// The layers names. + /// The layer names. /// [EditorOrder(10), EditorDisplay("Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)] public string[] Layers = new string[32]; @@ -31,7 +31,7 @@ namespace FlaxEditor.Content.Settings } /// - /// The layers names. + /// The layer names. /// [EditorOrder(10), EditorDisplay("Terrain Layers", EditorDisplayAttribute.InlineStyle), Collection(CanResize = false, Display = CollectionAttribute.DisplayType.Inline)] public string[] TerrainLayers = new string[8]; @@ -42,19 +42,17 @@ namespace FlaxEditor.Content.Settings /// The layer names. public static string[] GetCurrentTerrainLayers() { - #if FLAX_TESTS +#if FLAX_TESTS return System.Array.Empty(); - #else +#else string[] layerNames = GameSettings.Load().TerrainLayers; - for (int i = 0; i < layerNames.Length; i++) { if (string.IsNullOrEmpty(layerNames[i])) layerNames[i] = $"Layer {i}"; } - return layerNames; - #endif +#endif } [LibraryImport("FlaxEngine", EntryPoint = "LayersAndTagsSettingsInternal_GetCurrentLayers", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.Interop.StringMarshaller))] From 4ffb614c014e4332ed8f0c6497dc03023af2b6b8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Nov 2024 16:11:18 +0100 Subject: [PATCH 29/84] Fix compilation and simplify #3010 --- .../Editor/Surface/Archetypes/Parameters.cs | 5 ++-- .../Surface/IParametersDependantNode.cs | 2 +- .../Surface/VisjectSurface.Parameters.cs | 11 ++++++++ Source/Editor/Surface/VisjectSurfaceWindow.cs | 26 +++---------------- 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs index 4509ce753..b6b80e222 100644 --- a/Source/Editor/Surface/Archetypes/Parameters.cs +++ b/Source/Editor/Surface/Archetypes/Parameters.cs @@ -13,7 +13,6 @@ using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; using FlaxEngine; using FlaxEngine.Utilities; -using System.Linq; namespace FlaxEditor.Surface.Archetypes { @@ -427,7 +426,7 @@ namespace FlaxEditor.Surface.Archetypes } /// - public bool IsParamreferenced(SurfaceParameter param) + public bool IsParamUsed(SurfaceParameter param) { return (Guid)Values[0] == param.ID; } @@ -950,7 +949,7 @@ namespace FlaxEditor.Surface.Archetypes } /// - public bool IsParamreferenced(SurfaceParameter param) + public bool IsParamUsed(SurfaceParameter param) { return (Guid)Values[0] == param.ID; } diff --git a/Source/Editor/Surface/IParametersDependantNode.cs b/Source/Editor/Surface/IParametersDependantNode.cs index 0148682f1..4b9507032 100644 --- a/Source/Editor/Surface/IParametersDependantNode.cs +++ b/Source/Editor/Surface/IParametersDependantNode.cs @@ -39,6 +39,6 @@ namespace FlaxEditor.Surface /// /// The parameter. /// If the parameter is referenced. - bool IsParamreferenced(SurfaceParameter param); + bool IsParamUsed(SurfaceParameter param); } } diff --git a/Source/Editor/Surface/VisjectSurface.Parameters.cs b/Source/Editor/Surface/VisjectSurface.Parameters.cs index d6cf6ca0b..b31404ec5 100644 --- a/Source/Editor/Surface/VisjectSurface.Parameters.cs +++ b/Source/Editor/Surface/VisjectSurface.Parameters.cs @@ -84,5 +84,16 @@ namespace FlaxEditor.Surface } MarkAsEdited(); } + + /// + 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; + } } } diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index 5d6a050a6..6d7633b73 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -775,32 +775,14 @@ namespace FlaxEditor.Surface private void DeleteParameter(int index) { - bool displayWarning = Editor.Instance.Options.Options.Interface.WarnOnDeletingUsedVisjectParameter; - var window = (IVisjectSurfaceWindow)Values[0]; SurfaceParameter param = window.VisjectSurface.Parameters[index]; - int connectedParameterNodeCount = 0; - - List nodes = window.VisjectSurface.Nodes; - - for (int i = 0; i < window.VisjectSurface.Nodes.Count; i++) + if (Editor.Instance.Options.Options.Interface.WarnOnDeletingUsedVisjectParameter && window.VisjectSurface.IsParamUsed(param)) { - if (nodes[i] is IParametersDependantNode node) - { - if (displayWarning && node.IsParamreferenced(param)) - connectedParameterNodeCount++; - } - } - - if (displayWarning) - { - string singularPlural = connectedParameterNodeCount > 1 ? "s" : ""; - string msg = $"Delete parameter {param.Name}?\nParameter is being used in a graph {connectedParameterNodeCount} time{singularPlural}.\n\nYou can disable this warning in the editor settings."; - - if (connectedParameterNodeCount > 0) - if (MessageBox.Show(msg, "Delete parameter", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) - return; + 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 From 631dbda23b8410432106060c02eaa4bd4e6574fe Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Nov 2024 16:16:41 +0100 Subject: [PATCH 30/84] Add missing particle parameters overrides when duplicating emitter track #3064 --- .../GUI/Timeline/Tracks/ParticleEmitterTrack.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Source/Editor/GUI/Timeline/Tracks/ParticleEmitterTrack.cs b/Source/Editor/GUI/Timeline/Tracks/ParticleEmitterTrack.cs index 82a33f57b..1c25a08c2 100644 --- a/Source/Editor/GUI/Timeline/Tracks/ParticleEmitterTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/ParticleEmitterTrack.cs @@ -109,6 +109,19 @@ namespace FlaxEditor.GUI.Timeline.Tracks MaxMediaCount = 1; } + /// + 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); + } + } + /// public override void OnDestroy() { From e7051824b77f14c032cb3b369ca04e9c12465ecf Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 26 Nov 2024 23:13:43 +0100 Subject: [PATCH 31/84] Fix crash on leftover UI Canvas linked in Prefab window after reload #3079 --- Source/Editor/Viewport/Previews/PrefabPreview.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Editor/Viewport/Previews/PrefabPreview.cs b/Source/Editor/Viewport/Previews/PrefabPreview.cs index 1673c1723..258be7645 100644 --- a/Source/Editor/Viewport/Previews/PrefabPreview.cs +++ b/Source/Editor/Viewport/Previews/PrefabPreview.cs @@ -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); From 47919bd434fa4c883a92fbf974abaab3581ed8f0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Nov 2024 13:54:56 +0100 Subject: [PATCH 32/84] Add shift zoom to curve for a Y-axis only --- Source/Editor/GUI/CurveEditor.Contents.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs index bdb1c650b..022cc595d 100644 --- a/Source/Editor/GUI/CurveEditor.Contents.cs +++ b/Source/Editor/GUI/CurveEditor.Contents.cs @@ -519,10 +519,15 @@ 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); + var scale = new Float2(delta * 0.1f); + if (zoomAlt) + scale.X = 0; + _editor.ViewScale += GetUseModeMask(_editor.EnableZoom) * scale; return true; } From dc91e55cec3a36b53872d21dbafeaa99e6c5e543 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Nov 2024 20:22:38 +0100 Subject: [PATCH 33/84] Fix scroll bars when using negative content area (eg. curve editor) --- Source/Editor/GUI/CurveEditor.cs | 4 --- Source/Engine/UI/GUI/Panels/Panel.cs | 38 ++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 3499ef5d3..1ad94a9d8 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -1384,9 +1384,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) @@ -2116,9 +2114,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) diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index 6549f94da..3923e7108 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -553,7 +553,12 @@ namespace FlaxEngine.GUI if (vScrollEnabled) { - VScrollBar.SetScrollRange(scrollBounds.Top, Mathf.Max(Mathf.Max(0, scrollBounds.Top), scrollBounds.Height - height)); + float max; + if (scrollBounds.Top < 0) + max = Mathf.Max(scrollBounds.Bottom, scrollBounds.Top + scrollBounds.Height - height); + else + max = Mathf.Max(scrollBounds.Top, scrollBounds.Height - height); + VScrollBar.SetScrollRange(scrollBounds.Top, max); } VScrollBar.Bounds = new Rectangle(Width - _scrollBarsSize, 0, _scrollBarsSize, Height); } @@ -580,7 +585,12 @@ namespace FlaxEngine.GUI if (hScrollEnabled) { - HScrollBar.SetScrollRange(scrollBounds.Left, Mathf.Max(Mathf.Max(0, scrollBounds.Left), scrollBounds.Width - width)); + float max; + if (scrollBounds.Left < 0) + max = Mathf.Max(scrollBounds.Right, scrollBounds.Left + scrollBounds.Width - width); + else + max = Mathf.Max(scrollBounds.Left, scrollBounds.Width - width); + HScrollBar.SetScrollRange(scrollBounds.Left, max); } HScrollBar.Bounds = new Rectangle(0, Height - _scrollBarsSize, Width - (VScrollBar != null && VScrollBar.Visible ? VScrollBar.Width : 0), _scrollBarsSize); } @@ -596,17 +606,29 @@ namespace FlaxEngine.GUI // Calculate scroll area bounds var totalMin = Float2.Zero; var totalMax = Float2.Zero; + var hasTotal = false; for (int i = 0; i < _children.Count; i++) { var c = _children[i]; if (c.Visible && c.IsScrollable) { - var min = Float2.Zero; - var max = c.Size; - Matrix3x3.Transform2D(ref min, ref c._cachedTransform, out min); - Matrix3x3.Transform2D(ref max, ref c._cachedTransform, out max); - Float2.Min(ref min, ref totalMin, out totalMin); - Float2.Max(ref max, ref totalMax, out totalMax); + var upperLeft = Float2.Zero; + var bottomRight = c.Size; + Matrix3x3.Transform2D(ref upperLeft, ref c._cachedTransform, out upperLeft); + Matrix3x3.Transform2D(ref bottomRight, ref c._cachedTransform, out bottomRight); + Float2.Min(ref upperLeft, ref bottomRight, out var min); + Float2.Max(ref upperLeft, ref bottomRight, out var max); + if (hasTotal) + { + Float2.Min(ref min, ref totalMin, out totalMin); + Float2.Max(ref max, ref totalMax, out totalMax); + } + else + { + totalMin = min; + totalMax = max; + hasTotal = true; + } } } From e2ed6180566b2441233ec82469277d2860d6eb1e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 27 Nov 2024 23:28:22 +0100 Subject: [PATCH 34/84] Fix regression from 07aafea5afec90bdc957bf5757de1183dd003aa6 --- Source/Engine/Core/Math/BoundingBox.cpp | 12 ++++++------ Source/Engine/Core/Math/BoundingBox.cs | 12 ++++++------ Source/Engine/Core/Math/Matrix.cpp | 4 ++-- Source/Engine/Core/Math/Matrix3x3.cpp | 2 +- Source/Engine/Renderer/PostProcessingPass.cpp | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Source/Engine/Core/Math/BoundingBox.cpp b/Source/Engine/Core/Math/BoundingBox.cpp index 915014be8..6486d0188 100644 --- a/Source/Engine/Core/Math/BoundingBox.cpp +++ b/Source/Engine/Core/Math/BoundingBox.cpp @@ -124,9 +124,9 @@ void BoundingBox::Transform(const BoundingBox& box, const Matrix& matrix, Boundi const auto ya = up * box.Minimum.Y; const auto yb = up * box.Maximum.Y; - const auto backward = matrix.GetBackward(); - const auto za = backward * box.Minimum.Z; - const auto zb = backward * box.Maximum.Z; + const auto forward = matrix.GetForward(); + const auto za = forward * box.Minimum.Z; + const auto zb = forward * box.Maximum.Z; const auto translation = matrix.GetTranslation(); const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + translation; @@ -146,9 +146,9 @@ void BoundingBox::Transform(const BoundingBox& box, const ::Transform& transform const auto ya = up * box.Minimum.Y; const auto yb = up * box.Maximum.Y; - const auto backward = Float3::Transform(Float3::Backward, transform.Orientation); - const auto za = backward * box.Minimum.Z; - const auto zb = backward * box.Maximum.Z; + const auto forward = Float3::Transform(Float3::Forward, transform.Orientation); + const auto za = forward * box.Minimum.Z; + const auto zb = forward * box.Maximum.Z; const auto min = Vector3::Min(xa, xb) + Vector3::Min(ya, yb) + Vector3::Min(za, zb) + transform.Translation; const auto max = Vector3::Max(xa, xb) + Vector3::Max(ya, yb) + Vector3::Max(za, zb) + transform.Translation; diff --git a/Source/Engine/Core/Math/BoundingBox.cs b/Source/Engine/Core/Math/BoundingBox.cs index 7f871a8a1..e161ed300 100644 --- a/Source/Engine/Core/Math/BoundingBox.cs +++ b/Source/Engine/Core/Math/BoundingBox.cs @@ -474,9 +474,9 @@ namespace FlaxEngine var ya = up * box.Minimum.Y; var yb = up * box.Maximum.Y; - Double3 backward = transform.Backward; - var za = backward * box.Minimum.Z; - var zb = backward * box.Maximum.Z; + Double3 forward = transform.Forward; + var za = forward * box.Minimum.Z; + var zb = forward * box.Maximum.Z; var translation = transform.TranslationVector; var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + translation; @@ -514,9 +514,9 @@ namespace FlaxEngine var ya = up * box.Minimum.Y; var yb = up * box.Maximum.Y; - Double3 backward = transform.Backward; - var za = backward * box.Minimum.Z; - var zb = backward * box.Maximum.Z; + Double3 forward = transform.Forward; + var za = forward * box.Minimum.Z; + var zb = forward * box.Maximum.Z; var min = Vector3.Min(xa, xb) + Vector3.Min(ya, yb) + Vector3.Min(za, zb) + transform.Translation; var max = Vector3.Max(xa, xb) + Vector3.Max(ya, yb) + Vector3.Max(za, zb) + transform.Translation; diff --git a/Source/Engine/Core/Math/Matrix.cpp b/Source/Engine/Core/Math/Matrix.cpp index 5d7bab6b6..872b554a6 100644 --- a/Source/Engine/Core/Math/Matrix.cpp +++ b/Source/Engine/Core/Math/Matrix.cpp @@ -136,12 +136,12 @@ void Matrix::Decompose(Float3& scale, Matrix3x3& rotation, Float3& translation) const auto right = Float3::Cross(up, at); rotation.SetRight(right); rotation.SetUp(up); - rotation.SetBackward(at); + rotation.SetForward(at); // In case of reflexions scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X; scale.Y = Float3::Dot(up, GetUp()) > 0.0f ? scale.Y : -scale.Y; - scale.Z = Float3::Dot(at, GetBackward()) > 0.0f ? scale.Z : -scale.Z; + scale.Z = Float3::Dot(at, GetForward()) > 0.0f ? scale.Z : -scale.Z; } void Matrix::Decompose(Float3& scale, Matrix& rotation, Float3& translation) const diff --git a/Source/Engine/Core/Math/Matrix3x3.cpp b/Source/Engine/Core/Math/Matrix3x3.cpp index 0f0f520e7..b319a274a 100644 --- a/Source/Engine/Core/Math/Matrix3x3.cpp +++ b/Source/Engine/Core/Math/Matrix3x3.cpp @@ -225,7 +225,7 @@ void Matrix3x3::Decompose(Float3& scale, Matrix3x3& rotation) const const auto right = Float3::Cross(up, at); rotation.SetRight(right); rotation.SetUp(up); - rotation.SetBackward(at); + rotation.SetForward(at); // In case of reflexions scale.X = Float3::Dot(right, GetRight()) > 0.0f ? scale.X : -scale.X; diff --git a/Source/Engine/Renderer/PostProcessingPass.cpp b/Source/Engine/Renderer/PostProcessingPass.cpp index 8ff789223..99831b430 100644 --- a/Source/Engine/Renderer/PostProcessingPass.cpp +++ b/Source/Engine/Renderer/PostProcessingPass.cpp @@ -271,7 +271,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input, // Calculate star texture rotation matrix Float3 camX = renderContext.View.View.GetRight(); - Float3 camZ = renderContext.View.View.GetForward(); + Float3 camZ = renderContext.View.View.GetBackward(); float camRot = Float3::Dot(camX, Float3::Forward) + Float3::Dot(camZ, Float3::Up); float camRotCos = Math::Cos(camRot) * 0.8f; float camRotSin = Math::Sin(camRot) * 0.8f; From 6f0a2c028865794a6c6f0995fbbc3099c3d84eef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Nov 2024 18:27:09 +0100 Subject: [PATCH 35/84] Fix curve editor showing whole curve in a view --- Source/Editor/GUI/CurveEditor.cs | 16 ++++++++++++++-- Source/Engine/UI/GUI/Panels/Panel.cs | 3 +-- Source/Engine/UI/GUI/Panels/ScrollBar.cs | 6 ++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 1ad94a9d8..abf68d39e 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -671,8 +671,20 @@ namespace FlaxEditor.GUI /// 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, _mainPanel.VScrollBar.TargetValue); + scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll); + _mainPanel.HScrollBar.TargetValue = scroll.X; + _mainPanel.VScrollBar.TargetValue = scroll.Y; + UpdateKeyframes(); } diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index 3923e7108..0f802bd45 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -265,13 +265,12 @@ namespace FlaxEngine.GUI { bool wasLocked = _isLayoutLocked; _isLayoutLocked = true; - if (HScrollBar != null) HScrollBar.Value = -value.X; if (VScrollBar != null) VScrollBar.Value = -value.Y; - _isLayoutLocked = wasLocked; + base.SetViewOffset(ref value); } diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index 712c3e606..8f3ad5f38 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -330,7 +330,7 @@ namespace FlaxEngine.GUI bool needUpdate = Mathf.Abs(_thumbOpacity - targetOpacity) > 0.001f; // Ensure scroll bar is visible and smoothing is required - if (Visible && Mathf.Abs(_targetValue - _value) > 0.01f) + if (Visible && (Mathf.Abs(_targetValue - _value) > 0.0001f || _scrollAnimationProgress < 1.0f)) { // Interpolate or not if running slow float value; @@ -348,6 +348,8 @@ namespace FlaxEngine.GUI // https://easings.net/#easeOutSine var easedProgress = Mathf.Sin((progress * Mathf.Pi) / 2); + if (progress >= 1.0f) + easedProgress = 1.0f; value = Mathf.Lerp(_startValue, _targetValue, easedProgress); _scrollAnimationProgress = progress; @@ -356,7 +358,7 @@ namespace FlaxEngine.GUI { value = _targetValue; _startValue = _targetValue; - _scrollAnimationProgress = 0f; + _scrollAnimationProgress = 1f; } _value = value; From 0509fe10be481f0bf28b4b8c2c1e0e816c7aa1bf Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Nov 2024 18:27:45 +0100 Subject: [PATCH 36/84] Add displaying whole curve in properties panel upon show --- .../Editor/CustomEditors/Dedicated/CurveObjectEditor.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs index 019148b07..065e78bf0 100644 --- a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs @@ -11,6 +11,7 @@ namespace FlaxEditor.CustomEditors.Dedicated class BezierCurveObjectEditor : CustomEditor where T : struct { private bool _isSetting; + private int _firstTimeShow; private BezierCurveEditor _curve; /// @@ -20,6 +21,7 @@ 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) } private void OnCurveEdited() @@ -44,6 +46,8 @@ namespace FlaxEditor.CustomEditors.Dedicated _curve.SetKeyframes(value.Keyframes); _isSetting = false; } + if (_firstTimeShow-- > 0) + _curve.ShowWholeCurve(); } /// @@ -111,6 +115,7 @@ namespace FlaxEditor.CustomEditors.Dedicated class LinearCurveObjectEditor : CustomEditor where T : struct { private bool _isSetting; + private int _firstTimeShow; private LinearCurveEditor _curve; /// @@ -120,6 +125,7 @@ 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) } private void OnCurveEdited() @@ -144,6 +150,8 @@ namespace FlaxEditor.CustomEditors.Dedicated _curve.SetKeyframes(value.Keyframes); _isSetting = false; } + if (_firstTimeShow-- > 0) + _curve.ShowWholeCurve(); } /// From 8ee011c7f56b7d9e71702e4a83c2a1015c021721 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 28 Nov 2024 18:28:16 +0100 Subject: [PATCH 37/84] Fix curve editor zoom to be relative to the curve size --- Source/Editor/GUI/CurveEditor.Contents.cs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs index 022cc595d..bd055f590 100644 --- a/Source/Editor/GUI/CurveEditor.Contents.cs +++ b/Source/Editor/GUI/CurveEditor.Contents.cs @@ -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; @@ -206,15 +205,9 @@ namespace FlaxEditor.GUI { 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; } } } @@ -525,8 +518,14 @@ namespace FlaxEditor.GUI { // TODO: preserve the view center point for easier zooming var scale = new Float2(delta * 0.1f); + + // Scale relative to the curve size + _editor._mainPanel.GetDesireClientArea(out var mainPanelArea); + var curveScale = mainPanelArea.Size / _editor._contents.Size; + scale *= curveScale; + if (zoomAlt) - scale.X = 0; + scale.X = 0; // Scale Y axis only _editor.ViewScale += GetUseModeMask(_editor.EnableZoom) * scale; return true; } From 9b43f2f03aef92e55f233101495d31dda605f07f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Nov 2024 14:33:46 +0100 Subject: [PATCH 38/84] Add zooming in curve editor relative to the mouse cursor and adapt to curve size --- Source/Editor/GUI/CurveEditor.Contents.cs | 18 +++++++++++++----- Source/Engine/UI/GUI/Panels/Panel.cs | 4 ++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs index bd055f590..ea40a4573 100644 --- a/Source/Editor/GUI/CurveEditor.Contents.cs +++ b/Source/Editor/GUI/CurveEditor.Contents.cs @@ -516,17 +516,25 @@ namespace FlaxEditor.GUI var zoomAlt = RootWindow.GetKey(KeyboardKeys.Shift); if (_editor.EnableZoom != UseMode.Off && IsMouseOver && !_leftMouseDown && (zoom || zoomAlt)) { - // TODO: preserve the view center point for easier zooming - var scale = new Float2(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 - _editor.ViewScale += GetUseModeMask(_editor.EnableZoom) * scale; + 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; } diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index 0f802bd45..9e0391cd4 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -266,9 +266,9 @@ namespace FlaxEngine.GUI bool wasLocked = _isLayoutLocked; _isLayoutLocked = true; if (HScrollBar != null) - HScrollBar.Value = -value.X; + HScrollBar.TargetValue = -value.X; if (VScrollBar != null) - VScrollBar.Value = -value.Y; + VScrollBar.TargetValue = -value.Y; _isLayoutLocked = wasLocked; base.SetViewOffset(ref value); From 77184c7b52af7d1cabbb89de53b3dec0b3290288 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 29 Nov 2024 14:34:07 +0100 Subject: [PATCH 39/84] Fix crash when curve zoom was too high --- Source/Editor/Utilities/Utils.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index 7afe9ad0c..8d73abf4d 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -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); From 44d96ad100184b59ee9a9061d2825bbd2fb63bf2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 30 Nov 2024 23:20:57 +0100 Subject: [PATCH 40/84] Fix curve editor panning to be stable --- Source/Editor/GUI/CurveEditor.Contents.cs | 17 +++++++++++------ Source/Engine/UI/GUI/Panels/Panel.cs | 6 ++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs index ea40a4573..09e30d372 100644 --- a/Source/Editor/GUI/CurveEditor.Contents.cs +++ b/Source/Editor/GUI/CurveEditor.Contents.cs @@ -28,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; @@ -189,17 +190,20 @@ 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 { @@ -292,7 +296,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 @@ -437,7 +442,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; diff --git a/Source/Engine/UI/GUI/Panels/Panel.cs b/Source/Engine/UI/GUI/Panels/Panel.cs index 9e0391cd4..8923ff6df 100644 --- a/Source/Engine/UI/GUI/Panels/Panel.cs +++ b/Source/Engine/UI/GUI/Panels/Panel.cs @@ -263,15 +263,21 @@ namespace FlaxEngine.GUI /// protected override void SetViewOffset(ref Float2 value) { + // Update scroll bars but with locked layout bool wasLocked = _isLayoutLocked; + int layoutUpdateLock = _layoutUpdateLock; _isLayoutLocked = true; + _layoutUpdateLock = 999; if (HScrollBar != null) HScrollBar.TargetValue = -value.X; if (VScrollBar != null) VScrollBar.TargetValue = -value.Y; + _layoutUpdateLock = layoutUpdateLock; _isLayoutLocked = wasLocked; base.SetViewOffset(ref value); + + PerformLayout(); } /// From 8ef7f7cb1a0c39222dd21e929c8b63f81ecde486 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 30 Nov 2024 23:21:23 +0100 Subject: [PATCH 41/84] Add `F` key shortcut to show whole curve in editor view --- Source/Editor/GUI/CurveEditor.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index abf68d39e..1de56b993 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -935,6 +935,11 @@ namespace FlaxEditor.GUI KeyframesEditorUtils.Paste(this); return true; } + else if (options.FocusSelection.Process(this)) + { + ShowWholeCurve(); + return true; + } return false; } From 0295e1ca909191de0b2a6d051ebf993624f21b9b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 30 Nov 2024 23:28:07 +0100 Subject: [PATCH 42/84] Add adding keyframes to curve on double-click --- Source/Editor/GUI/CurveEditor.Contents.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs index 09e30d372..fd51be3fd 100644 --- a/Source/Editor/GUI/CurveEditor.Contents.cs +++ b/Source/Editor/GUI/CurveEditor.Contents.cs @@ -510,6 +510,27 @@ namespace FlaxEditor.GUI return true; } + /// + 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; + } + /// public override bool OnMouseWheel(Float2 location, float delta) { From f0f631a48b4accf0a736e237eaec6ed94d31ae1a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 30 Nov 2024 23:44:23 +0100 Subject: [PATCH 43/84] Add splitter bar for resizing curve editor inside properties panel --- .../Dedicated/CurveObjectEditor.cs | 29 ++++++ Source/Editor/GUI/Splitter.cs | 86 +++++++++++++++++ .../GUI/Timeline/Tracks/CurvePropertyTrack.cs | 96 +++---------------- 3 files changed, 127 insertions(+), 84 deletions(-) create mode 100644 Source/Editor/GUI/Splitter.cs diff --git a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs index 065e78bf0..81d826a04 100644 --- a/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/CurveObjectEditor.cs @@ -2,6 +2,7 @@ using FlaxEditor.GUI; using FlaxEngine; +using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Dedicated { @@ -13,6 +14,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private bool _isSetting; private int _firstTimeShow; private BezierCurveEditor _curve; + private Splitter _splitter; /// public override void Initialize(LayoutElementsContainer layout) @@ -22,6 +24,13 @@ namespace FlaxEditor.CustomEditors.Dedicated _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() @@ -34,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); + } + /// public override void Refresh() { @@ -54,6 +68,7 @@ namespace FlaxEditor.CustomEditors.Dedicated protected override void Deinitialize() { _curve = null; + _splitter = null; base.Deinitialize(); } @@ -117,6 +132,7 @@ namespace FlaxEditor.CustomEditors.Dedicated private bool _isSetting; private int _firstTimeShow; private LinearCurveEditor _curve; + private Splitter _splitter; /// public override void Initialize(LayoutElementsContainer layout) @@ -126,6 +142,13 @@ namespace FlaxEditor.CustomEditors.Dedicated _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() @@ -138,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); + } + /// public override void Refresh() { @@ -158,6 +186,7 @@ namespace FlaxEditor.CustomEditors.Dedicated protected override void Deinitialize() { _curve = null; + _splitter = null; base.Deinitialize(); } diff --git a/Source/Editor/GUI/Splitter.cs b/Source/Editor/GUI/Splitter.cs new file mode 100644 index 000000000..10a67706e --- /dev/null +++ b/Source/Editor/GUI/Splitter.cs @@ -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 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); + } + } +} diff --git a/Source/Editor/GUI/Timeline/Tracks/CurvePropertyTrack.cs b/Source/Editor/GUI/Timeline/Tracks/CurvePropertyTrack.cs index 3196879a1..db5fe90e5 100644 --- a/Source/Editor/GUI/Timeline/Tracks/CurvePropertyTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/CurvePropertyTrack.cs @@ -19,87 +19,6 @@ namespace FlaxEditor.GUI.Timeline.Tracks /// 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(); } } From 7e2d45012e2a1fa4f42133503386fac7b769fa23 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 1 Dec 2024 11:31:44 +0100 Subject: [PATCH 44/84] Fix scroll panel regression from 6f0a2c028865794a6c6f0995fbbc3099c3d84eef --- Source/Engine/UI/GUI/Panels/ScrollBar.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/UI/GUI/Panels/ScrollBar.cs b/Source/Engine/UI/GUI/Panels/ScrollBar.cs index 8f3ad5f38..a0d961fc4 100644 --- a/Source/Engine/UI/GUI/Panels/ScrollBar.cs +++ b/Source/Engine/UI/GUI/Panels/ScrollBar.cs @@ -330,7 +330,7 @@ namespace FlaxEngine.GUI bool needUpdate = Mathf.Abs(_thumbOpacity - targetOpacity) > 0.001f; // Ensure scroll bar is visible and smoothing is required - if (Visible && (Mathf.Abs(_targetValue - _value) > 0.0001f || _scrollAnimationProgress < 1.0f)) + if (Visible && Mathf.Abs(_targetValue - _value) > 0.01f) { // Interpolate or not if running slow float value; @@ -358,7 +358,7 @@ namespace FlaxEngine.GUI { value = _targetValue; _startValue = _targetValue; - _scrollAnimationProgress = 1f; + _scrollAnimationProgress = 0f; } _value = value; From 0a4a431f7436503b95cad0135deb44aff3e1b15c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 1 Dec 2024 21:55:19 +0100 Subject: [PATCH 45/84] Fix exception regression --- Source/Editor/GUI/CurveEditor.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 1de56b993..837106c6f 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -680,10 +680,12 @@ namespace FlaxEditor.GUI Float2.Min(ref minPos, ref pos, out minPos); } var minPosPoint = _contents.PointToParent(ref minPos); - var scroll = new Float2(_mainPanel.HScrollBar.TargetValue, _mainPanel.VScrollBar.TargetValue); + var scroll = new Float2(_mainPanel.HScrollBar?.TargetValue ?? 0, _mainPanel.VScrollBar?.TargetValue ?? 0); scroll = ApplyUseModeMask(EnablePanning, minPosPoint, scroll); - _mainPanel.HScrollBar.TargetValue = scroll.X; - _mainPanel.VScrollBar.TargetValue = scroll.Y; + if (_mainPanel.HScrollBar != null && _mainPanel.HScrollBar.Enabled) + _mainPanel.HScrollBar.TargetValue = scroll.X; + if (_mainPanel.VScrollBar != null && _mainPanel.VScrollBar.Enabled) + _mainPanel.VScrollBar.TargetValue = scroll.Y; UpdateKeyframes(); } From 57628c3d5f744fb1578fb340ef9f6d12367457b3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Dec 2024 19:10:28 +0100 Subject: [PATCH 46/84] Refactor Bezier splines drawing and editing to property evaluate value and match curve evaluation #3051 --- Flax.flaxproj | 2 +- Source/Editor/GUI/CurveEditor.Contents.cs | 3 +- Source/Editor/GUI/CurveEditor.cs | 18 ++--- Source/Editor/GUI/Timeline/GUI/Background.cs | 2 +- Source/Engine/Animations/AnimationUtils.h | 20 ++--- Source/Engine/Animations/Curve.cs | 76 +++++++++---------- Source/Engine/Animations/Curve.h | 15 ++-- Source/Engine/Level/Actors/Spline.cpp | 12 +-- Source/Engine/Level/Actors/SplineModel.cpp | 18 ++--- .../Physics/Colliders/SplineCollider.cpp | 6 +- Source/Engine/Render2D/Render2D.cpp | 39 ++++++++-- Source/Engine/Render2D/Render2D.h | 11 +++ 12 files changed, 129 insertions(+), 93 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 33dc6e45b..15b925c0d 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 9, "Revision": 0, - "Build": 6605 + "Build": 6606 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.", diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs index fd51be3fd..25954abfa 100644 --- a/Source/Editor/GUI/CurveEditor.Contents.cs +++ b/Source/Editor/GUI/CurveEditor.Contents.cs @@ -231,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; diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index 837106c6f..ed0885afd 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -682,9 +682,9 @@ namespace FlaxEditor.GUI 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.Enabled) + if (_mainPanel.HScrollBar != null) _mainPanel.HScrollBar.TargetValue = scroll.X; - if (_mainPanel.VScrollBar != null && _mainPanel.VScrollBar.Enabled) + if (_mainPanel.VScrollBar != null) _mainPanel.VScrollBar.TargetValue = scroll.Y; UpdateKeyframes(); @@ -1649,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); } @@ -2199,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 ); @@ -2280,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); } } } diff --git a/Source/Editor/GUI/Timeline/GUI/Background.cs b/Source/Editor/GUI/Timeline/GUI/Background.cs index 7bda8f4c0..57b09324e 100644 --- a/Source/Editor/GUI/Timeline/GUI/Background.cs +++ b/Source/Editor/GUI/Timeline/GUI/Background.cs @@ -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); diff --git a/Source/Engine/Animations/AnimationUtils.h b/Source/Engine/Animations/AnimationUtils.h index d801443ab..4f697a2d0 100644 --- a/Source/Engine/Animations/AnimationUtils.h +++ b/Source/Engine/Animations/AnimationUtils.h @@ -66,27 +66,23 @@ namespace AnimationUtils } template - FORCE_INLINE static void GetTangent(const T& a, const T& b, float length, T& result) + FORCE_INLINE static void GetTangent(const T& value, const T& tangent, float tangentScale, T& result) { - const float oneThird = 1.0f / 3.0f; - result = a + b * (length * oneThird); + result = value + tangent * tangentScale; } template<> - FORCE_INLINE void GetTangent(const Quaternion& a, const Quaternion& b, float length, Quaternion& result) + FORCE_INLINE void GetTangent(const Quaternion& value, const Quaternion& tangent, float tangentScale, Quaternion& result) { - const float oneThird = 1.0f / 3.0f; - Quaternion::Slerp(a, b, oneThird, result); + Quaternion::Slerp(value, tangent, 1.0f / 3.0f, result); } template<> - FORCE_INLINE void GetTangent(const Transform& a, const Transform& b, float length, Transform& result) + FORCE_INLINE void GetTangent(const Transform& value, const Transform& tangent, float tangentScale, Transform& result) { - const float oneThird = 1.0f / 3.0f; - const float oneThirdLength = length * oneThird; - result.Translation = a.Translation + b.Translation * oneThirdLength; - Quaternion::Slerp(a.Orientation, b.Orientation, oneThird, result.Orientation); - result.Scale = a.Scale + (b.Scale - a.Scale) * oneThirdLength; + GetTangent(value.Translation, tangent.Translation, tangentScale, result.Translation); + GetTangent(value.Orientation, tangent.Orientation, tangentScale, result.Orientation); + GetTangent(value.Scale, tangent.Scale, tangentScale, result.Scale); } template diff --git a/Source/Engine/Animations/Curve.cs b/Source/Engine/Animations/Curve.cs index a0d911141..7632653bc 100644 --- a/Source/Engine/Animations/Curve.cs +++ b/Source/Engine/Animations/Curve.cs @@ -24,9 +24,9 @@ namespace FlaxEngine /// /// The value. /// The tangent. - /// The length divided by 3. + /// The tangent scale factor. /// The result. - void GetTangent(ref U value, ref U tangent, float lengthThird, out U result); + void GetTangent(ref U value, ref U tangent, float tangentScale, out U result); /// /// Calculates the linear interpolation at the specified alpha. @@ -67,7 +67,7 @@ namespace FlaxEngine IKeyframeAccess, IKeyframeAccess { - public void GetTangent(ref bool value, ref bool tangent, float lengthThird, out bool result) + public void GetTangent(ref bool value, ref bool tangent, float tangentScale, out bool result) { result = value; } @@ -82,9 +82,9 @@ namespace FlaxEngine result = p0; } - public void GetTangent(ref int value, ref int tangent, float lengthThird, out int result) + public void GetTangent(ref int value, ref int tangent, float tangentScale, out int result) { - result = value + (int)(tangent * lengthThird); + result = value + (int)(tangent * tangentScale); } public void Linear(ref int a, ref int b, float alpha, out int result) @@ -102,9 +102,9 @@ namespace FlaxEngine result = Mathf.Lerp(p012, p123, alpha); } - public void GetTangent(ref double value, ref double tangent, float lengthThird, out double result) + public void GetTangent(ref double value, ref double tangent, float tangentScale, out double result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref double a, ref double b, float alpha, out double result) @@ -122,9 +122,9 @@ namespace FlaxEngine result = Mathf.Lerp(p012, p123, alpha); } - public void GetTangent(ref float value, ref float tangent, float lengthThird, out float result) + public void GetTangent(ref float value, ref float tangent, float tangentScale, out float result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref float a, ref float b, float alpha, out float result) @@ -142,9 +142,9 @@ namespace FlaxEngine result = Mathf.Lerp(p012, p123, alpha); } - public void GetTangent(ref Vector2 value, ref Vector2 tangent, float lengthThird, out Vector2 result) + public void GetTangent(ref Vector2 value, ref Vector2 tangent, float tangentScale, out Vector2 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Vector2 a, ref Vector2 b, float alpha, out Vector2 result) @@ -162,9 +162,9 @@ namespace FlaxEngine Vector2.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Vector3 value, ref Vector3 tangent, float lengthThird, out Vector3 result) + public void GetTangent(ref Vector3 value, ref Vector3 tangent, float tangentScale, out Vector3 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Vector3 a, ref Vector3 b, float alpha, out Vector3 result) @@ -182,9 +182,9 @@ namespace FlaxEngine Vector3.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Vector4 value, ref Vector4 tangent, float lengthThird, out Vector4 result) + public void GetTangent(ref Vector4 value, ref Vector4 tangent, float tangentScale, out Vector4 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Vector4 a, ref Vector4 b, float alpha, out Vector4 result) @@ -202,9 +202,9 @@ namespace FlaxEngine Vector4.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Float2 value, ref Float2 tangent, float lengthThird, out Float2 result) + public void GetTangent(ref Float2 value, ref Float2 tangent, float tangentScale, out Float2 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Float2 a, ref Float2 b, float alpha, out Float2 result) @@ -222,9 +222,9 @@ namespace FlaxEngine Float2.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Float3 value, ref Float3 tangent, float lengthThird, out Float3 result) + public void GetTangent(ref Float3 value, ref Float3 tangent, float tangentScale, out Float3 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Float3 a, ref Float3 b, float alpha, out Float3 result) @@ -242,9 +242,9 @@ namespace FlaxEngine Float3.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Float4 value, ref Float4 tangent, float lengthThird, out Float4 result) + public void GetTangent(ref Float4 value, ref Float4 tangent, float tangentScale, out Float4 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Float4 a, ref Float4 b, float alpha, out Float4 result) @@ -262,9 +262,9 @@ namespace FlaxEngine Float4.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Double2 value, ref Double2 tangent, float lengthThird, out Double2 result) + public void GetTangent(ref Double2 value, ref Double2 tangent, float tangentScale, out Double2 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Double2 a, ref Double2 b, float alpha, out Double2 result) @@ -282,9 +282,9 @@ namespace FlaxEngine Double2.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Double3 value, ref Double3 tangent, float lengthThird, out Double3 result) + public void GetTangent(ref Double3 value, ref Double3 tangent, float tangentScale, out Double3 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Double3 a, ref Double3 b, float alpha, out Double3 result) @@ -302,9 +302,9 @@ namespace FlaxEngine Double3.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Double4 value, ref Double4 tangent, float lengthThird, out Double4 result) + public void GetTangent(ref Double4 value, ref Double4 tangent, float tangentScale, out Double4 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Double4 a, ref Double4 b, float alpha, out Double4 result) @@ -322,7 +322,7 @@ namespace FlaxEngine Double4.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Quaternion value, ref Quaternion tangent, float lengthThird, out Quaternion result) + public void GetTangent(ref Quaternion value, ref Quaternion tangent, float tangentScale, out Quaternion result) { Quaternion.Slerp(ref value, ref tangent, 1.0f / 3.0f, out result); } @@ -342,9 +342,9 @@ namespace FlaxEngine Quaternion.Slerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Color32 value, ref Color32 tangent, float lengthThird, out Color32 result) + public void GetTangent(ref Color32 value, ref Color32 tangent, float tangentScale, out Color32 result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Color32 a, ref Color32 b, float alpha, out Color32 result) @@ -362,9 +362,9 @@ namespace FlaxEngine Color32.Lerp(ref p012, ref p123, alpha, out result); } - public void GetTangent(ref Color value, ref Color tangent, float lengthThird, out Color result) + public void GetTangent(ref Color value, ref Color tangent, float tangentScale, out Color result) { - result = value + tangent * lengthThird; + result = value + tangent * tangentScale; } public void Linear(ref Color a, ref Color b, float alpha, out Color result) @@ -860,9 +860,9 @@ namespace FlaxEngine // Evaluate the key at the curve result.Time = leftKey.Time + length * t; - float lengthThird = length / 3.0f; - _accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent); - _accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent); + float tangentScale = length / 3.0f; + _accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent); + _accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent); _accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result.Value); result.TangentIn = leftKey.TangentOut; result.TangentOut = rightKey.TangentIn; @@ -895,9 +895,9 @@ namespace FlaxEngine float t = Mathf.NearEqual(length, 0.0f) ? 0.0f : (time - leftKey.Time) / length; // Evaluate the value at the curve - float lengthThird = length / 3.0f; - _accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, lengthThird, out var leftTangent); - _accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, lengthThird, out var rightTangent); + float tangentScale = length / 3.0f; + _accessor.GetTangent(ref leftKey.Value, ref leftKey.TangentOut, tangentScale, out var leftTangent); + _accessor.GetTangent(ref rightKey.Value, ref rightKey.TangentIn, tangentScale, out var rightTangent); _accessor.Bezier(ref leftKey.Value, ref leftTangent, ref rightTangent, ref rightKey.Value, t, out result); } diff --git a/Source/Engine/Animations/Curve.h b/Source/Engine/Animations/Curve.h index 40dfc8add..b259dae66 100644 --- a/Source/Engine/Animations/Curve.h +++ b/Source/Engine/Animations/Curve.h @@ -247,16 +247,18 @@ public: static void Interpolate(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result) { T leftTangent, rightTangent; - AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent); - AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent); + const float tangentScale = length / 3.0f; + AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent); + AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent); AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result); } static void InterpolateFirstDerivative(const BezierCurveKeyframe& a, const BezierCurveKeyframe& b, float alpha, float length, T& result) { T leftTangent, rightTangent; - AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent); - AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent); + const float tangentScale = length / 3.0f; + AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent); + AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent); AnimationUtils::BezierFirstDerivative(a.Value, leftTangent, rightTangent, b.Value, alpha, result); } @@ -264,8 +266,9 @@ public: { result.Time = a.Time + length * alpha; T leftTangent, rightTangent; - AnimationUtils::GetTangent(a.Value, a.TangentOut, length, leftTangent); - AnimationUtils::GetTangent(b.Value, b.TangentIn, length, rightTangent); + const float tangentScale = length / 3.0f; + AnimationUtils::GetTangent(a.Value, a.TangentOut, tangentScale, leftTangent); + AnimationUtils::GetTangent(b.Value, b.TangentIn, tangentScale, rightTangent); AnimationUtils::Bezier(a.Value, leftTangent, rightTangent, b.Value, alpha, result.Value); result.TangentIn = a.TangentOut; result.TangentOut = b.TangentIn; diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 01b4248ec..8dd79c8ae 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -158,10 +158,10 @@ float Spline::GetSplineLength() const const auto& b = Curve[i]; Vector3 prevPoint = a.Value.Translation * scale; - const float length = Math::Abs(b.Time - a.Time); + const float tangentScale = Math::Abs(b.Time - a.Time) / 3.0f; Vector3 leftTangent, rightTangent; - AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent); - AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent); + AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, tangentScale, leftTangent); + AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, tangentScale, rightTangent); for (int32 slice = 1; slice < slices; slice++) { @@ -189,10 +189,10 @@ float Spline::GetSplineSegmentLength(int32 index) const const Vector3 scale = _transform.Scale; Vector3 prevPoint = a.Value.Translation * scale; { - const float length = Math::Abs(b.Time - a.Time); + const float tangentScale = Math::Abs(b.Time - a.Time) / 3.0f; Vector3 leftTangent, rightTangent; - AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent); - AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent); + AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, tangentScale, leftTangent); + AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, tangentScale, rightTangent); for (int32 slice = 1; slice < slices; slice++) { diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 29c14f035..71b618384 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -184,9 +184,9 @@ void SplineModel::OnSplineUpdated() auto& instance = _instances[segment]; const auto& start = keyframes[segment]; const auto& end = keyframes[segment + 1]; - const float length = end.Time - start.Time; - AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent); - AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent); + const float tangentScale = (end.Time - start.Time) / 3.0f; + AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent); + AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent); // Find maximum scale over the segment spline and collect the segment positions for bounds segmentPoints.Clear(); @@ -256,9 +256,9 @@ void SplineModel::UpdateDeformationBuffer() auto& instance = _instances[segment]; const auto& start = keyframes[segment]; const auto& end = keyframes[segment + 1]; - const float length = end.Time - start.Time; - AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent); - AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent); + const float tangentScale = (end.Time - start.Time) / 3.0f; + AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent); + AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent); for (int32 chunk = 0; chunk < chunksPerSegment; chunk++) { const float alpha = (chunk == chunksPerSegment - 1) ? 1.0f : ((float)chunk * chunksPerSegmentInv); @@ -291,10 +291,10 @@ void SplineModel::UpdateDeformationBuffer() { const auto& start = keyframes[segments - 1]; const auto& end = keyframes[segments]; - const float length = end.Time - start.Time; + const float tangentScale = (end.Time - start.Time) / 3.0f; const float alpha = 1.0f - ZeroTolerance; // Offset to prevent zero derivative at the end of the curve - AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent); - AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent); + AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent); + AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent); AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, transform); Vector3 direction; AnimationUtils::BezierFirstDerivative(start.Value.Translation, leftTangent.Translation, rightTangent.Translation, end.Value.Translation, alpha, direction); diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index 94e35aae5..226b6fa27 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -214,9 +214,9 @@ void SplineCollider::GetGeometry(CollisionShape& collision) auto offsetIndices = segment * collisionIndices.Count(); const auto& start = keyframes[segment]; const auto& end = keyframes[segment + 1]; - const float length = end.Time - start.Time; - AnimationUtils::GetTangent(start.Value, start.TangentOut, length, leftTangent); - AnimationUtils::GetTangent(end.Value, end.TangentIn, length, rightTangent); + const float tangentScale = (end.Time - start.Time) / 3.0f; + AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent); + AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent); // Vertex buffer is deformed along the spline auto srcVertices = collisionVertices.Get(); diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 877b6bb16..6f7c7043e 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -1882,19 +1882,46 @@ void Render2D::DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, const Float2 d3 = p4 - p3; const float len = d1.Length() + d2.Length() + d3.Length(); const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); - const float segmentCountInv = 1.0f / segmentCount; + const float segmentCountInv = 1.0f / (float)segmentCount; // Draw segmented curve - Float2 p; - AnimationUtils::Bezier(p1, p2, p3, p4, 0, p); Lines2.Clear(); - Lines2.Add(p); - for (int32 i = 1; i <= segmentCount; i++) + Lines2.Add(p1); + for (int32 i = 1; i < segmentCount; i++) { - const float t = i * segmentCountInv; + const float t = (float)i * segmentCountInv; + Float2 p; AnimationUtils::Bezier(p1, p2, p3, p4, t, p); Lines2.Add(p); } + Lines2.Add(p4); + DrawLines(Lines2.Get(), Lines2.Count(), color, color, thickness); +} + +void Render2D::DrawSpline(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness) +{ + RENDER2D_CHECK_RENDERING_STATE; + + // Find amount of segments to use + const Float2 d1 = p2 - p1; + const Float2 d2 = p3 - p2; + const Float2 d3 = p4 - p3; + const float len = d1.Length() + d2.Length() + d3.Length(); + const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); + const float segmentCountInv = 1.0f / (float)segmentCount; + + // Draw segmented curve + Lines2.Clear(); + Lines2.Add(p1); + for (int32 i = 1; i < segmentCount; i++) + { + const float t = (float)i * segmentCountInv; + Float2 p; + p.X = Math::Lerp(p1.X, p4.X, t); + AnimationUtils::Bezier(p1.Y, p2.Y, p3.Y, p4.Y, t, p.Y); + Lines2.Add(p); + } + Lines2.Add(p4); DrawLines(Lines2.Get(), Lines2.Count(), color, color, thickness); } diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 5eda59e7e..5905fa434 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -389,6 +389,17 @@ public: /// The line thickness. API_FUNCTION() static void DrawBezier(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness = 1.0f); + /// + /// Draws a spline curve (Bezier but X axis represents uniform time). + /// + /// The start point. + /// The first control point. + /// The second control point. + /// The end point. + /// The line color + /// The line thickness. + API_FUNCTION() static void DrawSpline(const Float2& p1, const Float2& p2, const Float2& p3, const Float2& p4, const Color& color, float thickness = 1.0f); + /// /// Draws the GUI material. /// From 81737083a04002d14487870b690fc4a2de8ce411 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Dec 2024 23:35:39 +0100 Subject: [PATCH 47/84] Add support for using `API_TYPEDEF` macro on `using` typedefs --- .../Bindings/BindingsGenerator.Parsing.cs | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index d85f1f342..2aff6cd49 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -273,7 +273,6 @@ namespace Flax.Build.Bindings type.GenericArgs.Add(argType); token = context.Tokenizer.NextToken(); } while (token.Type != TokenType.RightAngleBracket); - token = context.Tokenizer.NextToken(); } @@ -405,7 +404,7 @@ namespace Flax.Build.Bindings currentParam.Attributes += ", Optional"; currentParam.Name = $"namelessArg{parameters.Count}"; } - + if (currentParam.IsOut && (currentParam.Type.IsPtr || currentParam.Type.IsRef) && currentParam.Type.Type.EndsWith("*")) { // Pointer to value passed as output pointer @@ -1590,16 +1589,30 @@ namespace Flax.Build.Bindings // Read parameters from the tag var tagParams = ParseTagParameters(ref context); - // Read 'typedef' keyword + // Read 'typedef' or 'using' keyword var token = context.Tokenizer.NextToken(); - if (token.Value != "typedef") - throw new ParseException(ref context, $"Invalid {ApiTokens.Typedef} usage (expected 'typedef' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); + var isUsing = token.Value == "using"; + if (token.Value != "typedef" && !isUsing) + throw new ParseException(ref context, $"Invalid {ApiTokens.Typedef} usage (expected 'typedef' or 'using' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}')."); - // Read type definition - desc.Type = ParseType(ref context); + if (isUsing) + { + // Read name + desc.Name = ParseName(ref context); - // Read name - desc.Name = ParseName(ref context); + context.Tokenizer.ExpectToken(TokenType.Equal); + + // Read type definition + desc.Type = ParseType(ref context); + } + else + { + // Read type definition + desc.Type = ParseType(ref context); + + // Read name + desc.Name = ParseName(ref context); + } // Read ';' context.Tokenizer.ExpectToken(TokenType.SemiColon); From 10caaf4fe9e895df041b167bead10aa2958e433f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 2 Dec 2024 23:36:11 +0100 Subject: [PATCH 48/84] Fix parsing scripting type inheritance with generics --- .../Bindings/BindingsGenerator.Parsing.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 2aff6cd49..1fd29dfcb 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -544,15 +544,19 @@ namespace Flax.Build.Bindings } if (token.Type == TokenType.LeftAngleBracket) { - var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier); - token = context.Tokenizer.ExpectToken(TokenType.RightAngleBracket); - inheritType.GenericArgs = new List + inheritType.GenericArgs = new List(); + while (true) { - new TypeInfo - { - Type = genericType.Value, - } - }; + token = context.Tokenizer.NextToken(); + if (token.Type == TokenType.RightAngleBracket) + break; + if (token.Type == TokenType.Comma) + continue; + if (token.Type == TokenType.Identifier) + inheritType.GenericArgs.Add(new TypeInfo { Type = token.Value }); + else + throw new ParseException(ref context, "Incorrect inheritance"); + } // TODO: find better way to resolve this (custom base type attribute?) if (inheritType.Type == "ShaderAssetTypeBase") From c01824cd0967842537b6f6bfaf74d5564d8e34cd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 3 Dec 2024 13:33:16 +0100 Subject: [PATCH 49/84] Add support for using template type inside `MarshalAs` tag value --- .../Bindings/BindingsGenerator.Parsing.cs | 7 ++++ Source/Tools/Flax.Build/Bindings/TypeInfo.cs | 33 +++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 1fd29dfcb..a6ffe9a98 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -188,6 +188,13 @@ namespace Flax.Build.Bindings token = context.Tokenizer.NextToken(); if (token.Type == TokenType.Multiply) tag.Value += token.Value; + else if (token.Type == TokenType.LeftAngleBracket) + { + context.Tokenizer.SkipUntil(TokenType.RightAngleBracket, out var s); + tag.Value += '<'; + tag.Value += s; + tag.Value += '>'; + } else context.Tokenizer.PreviousToken(); parameters.Add(tag); diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs index 9258e7827..e1d6a0281 100644 --- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs @@ -182,11 +182,40 @@ namespace Flax.Build.Bindings public static TypeInfo FromString(string text) { - var result = new TypeInfo(text); + var result = new TypeInfo(text.Trim()); + if (result.Type.StartsWith("const")) + { + // Const + result.IsConst = true; + result.Type = result.Type.Substring(5).Trim(); + } if (result.Type.EndsWith('*')) { + // Pointer result.IsPtr = true; - result.Type = result.Type.Substring(0, result.Type.Length - 1); + result.Type = result.Type.Substring(0, result.Type.Length - 1).Trim(); + } + if (result.Type.EndsWith('&')) + { + // Reference + result.IsRef = true; + result.Type = result.Type.Substring(0, result.Type.Length - 1).Trim(); + if (result.Type.EndsWith('&')) + { + // Move reference + result.IsMoveRef = true; + result.Type = result.Type.Substring(0, result.Type.Length - 1).Trim(); + } + } + var idx = result.Type.IndexOf('<'); + if (idx != -1) + { + // Generic + result.GenericArgs = new List(); + var generics = result.Type.Substring(idx + 1, result.Type.Length - idx - 2); + foreach (var generic in generics.Split(',')) + result.GenericArgs.Add(FromString(generic)); + result.Type = result.Type.Substring(0, idx); } return result; } From 12d9d94138d0e987a00ff5956279f1dcdeec84a2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Dec 2024 13:30:56 +0100 Subject: [PATCH 50/84] Add support for curves in C++ scripting api #2546 --- Source/Engine/Animations/Curve.cs | 84 +++++++++++++++++++ Source/Engine/Animations/Curve.h | 24 ++++-- Source/Engine/Level/Actors/Spline.cpp | 4 +- .../Bindings/BindingsGenerator.CSharp.cs | 20 +++-- .../Bindings/BindingsGenerator.Cpp.cs | 22 +++-- 5 files changed, 131 insertions(+), 23 deletions(-) diff --git a/Source/Engine/Animations/Curve.cs b/Source/Engine/Animations/Curve.cs index 7632653bc..4c47dbb6e 100644 --- a/Source/Engine/Animations/Curve.cs +++ b/Source/Engine/Animations/Curve.cs @@ -1,8 +1,10 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +using FlaxEngine.Interop; using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace FlaxEngine @@ -454,6 +456,40 @@ namespace FlaxEngine time = end; } } + + /// + /// Raw memory copy (used by scripting bindings - see MarshalAs tag). + /// + /// The keyframes array. + /// The raw keyframes data. + protected static unsafe byte[] MarshalKeyframes(Keyframe[] keyframes) + { + if (keyframes == null || keyframes.Length == 0) + return null; + var keyframeSize = Unsafe.SizeOf(); + var result = new byte[keyframes.Length * keyframeSize]; + fixed (byte* resultPtr = result) + { + var keyframesHandle = ManagedHandle.Alloc(keyframes, GCHandleType.Pinned); + var keyframesPtr = Marshal.UnsafeAddrOfPinnedArrayElement(keyframes, 0); + Buffer.MemoryCopy((void*)keyframesPtr, resultPtr, (uint)result.Length, (uint)result.Length); + keyframesHandle.Free(); + } + return result; + } + + /// + /// Raw memory copy (used by scripting bindings - see MarshalAs tag). + /// + /// The raw keyframes data. + /// The keyframes array. + protected static unsafe Keyframe[] MarshalKeyframes(byte[] raw) where Keyframe : struct + { + if (raw == null || raw.Length == 0) + return null; + fixed (byte* rawPtr = raw) + return MemoryMarshal.Cast(new Span(rawPtr, raw.Length)).ToArray(); + } } /// @@ -709,6 +745,30 @@ namespace FlaxEngine leftKey = Mathf.Max(0, start - 1); rightKey = Mathf.Min(start, Keyframes.Length - 1); } + + /// + /// Raw memory copy (used by scripting bindings - see MarshalAs tag). + /// + /// The curve to copy. + /// The raw keyframes data. + public static unsafe implicit operator byte[](LinearCurve curve) + { + if (curve == null) + return null; + return MarshalKeyframes(curve.Keyframes); + } + + /// + /// Raw memory copy (used by scripting bindings - see MarshalAs tag). + /// + /// The raw keyframes data. + /// The curve. + public static unsafe implicit operator LinearCurve(byte[] raw) + { + if (raw == null || raw.Length == 0) + return null; + return new LinearCurve(MarshalKeyframes(raw)); + } } /// @@ -1000,5 +1060,29 @@ namespace FlaxEngine leftKey = Mathf.Max(0, start - 1); rightKey = Mathf.Min(start, Keyframes.Length - 1); } + + /// + /// Raw memory copy (used by scripting bindings - see MarshalAs tag). + /// + /// The curve to copy. + /// The raw keyframes data. + public static unsafe implicit operator byte[](BezierCurve curve) + { + if (curve == null) + return null; + return MarshalKeyframes(curve.Keyframes); + } + + /// + /// Raw memory copy (used by scripting bindings - see MarshalAs tag). + /// + /// The raw keyframes data. + /// The curve. + public static unsafe implicit operator BezierCurve(byte[] raw) + { + if (raw == null || raw.Length == 0) + return null; + return new BezierCurve(MarshalKeyframes(raw)); + } } } diff --git a/Source/Engine/Animations/Curve.h b/Source/Engine/Animations/Curve.h index b259dae66..e3e0be33e 100644 --- a/Source/Engine/Animations/Curve.h +++ b/Source/Engine/Animations/Curve.h @@ -501,7 +501,7 @@ protected: /// An animation spline represented by a set of keyframes, each representing an endpoint of a curve. /// template> -class Curve : public CurveBase +API_CLASS(InBuild, Template, MarshalAs=Span) class Curve : public CurveBase { public: typedef CurveBase Base; @@ -763,28 +763,42 @@ public: } return true; } + + // Raw memory copy (used by scripting bindings - see MarshalAs tag). + Curve& operator=(const Span& raw) + { + ASSERT((raw.Length() % sizeof(KeyFrame)) == 0); + const int32 count = raw.Length() / sizeof(KeyFrame); + _keyframes.Resize(count, false); + Platform::MemoryCopy(_keyframes.Get(), raw.Get(), sizeof(KeyFrame) * count); + return *this; + } + operator Span() + { + return Span((const byte*)_keyframes.Get(), _keyframes.Count() * sizeof(KeyFrame)); + } }; /// /// An animation spline represented by a set of keyframes, each representing a value point. /// template -using StepCurve = Curve>; +API_TYPEDEF() using StepCurve = Curve>; /// /// An animation spline represented by a set of keyframes, each representing an endpoint of a linear curve. /// template -using LinearCurve = Curve>; +API_TYPEDEF() using LinearCurve = Curve>; /// /// An animation spline represented by a set of keyframes, each representing an endpoint of a cubic hermite curve. /// template -using HermiteCurve = Curve>; +API_TYPEDEF() using HermiteCurve = Curve>; /// /// An animation spline represented by a set of keyframes, each representing an endpoint of Bezier curve. /// template -using BezierCurve = Curve>; +API_TYPEDEF() using BezierCurve = Curve>; diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 8dd79c8ae..bc46c3da1 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -478,9 +478,7 @@ void Spline::GetKeyframes(MArray* data) void Spline::SetKeyframes(MArray* data) { - const int32 count = MCore::Array::GetLength(data); - Curve.GetKeyframes().Resize(count, false); - Platform::MemoryCopy(Curve.GetKeyframes().Get(), MCore::Array::GetAddress(data), sizeof(Keyframe) * count); + Curve = Span((const byte*)MCore::Array::GetAddress(data), MCore::Array::GetLength(data)); UpdateSpline(); } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 551417ebf..5d03d0489 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -20,12 +20,12 @@ namespace Flax.Build.Bindings private static readonly Dictionary CSharpAdditionalCodeCache = new Dictionary(); #if USE_NETCORE private static readonly TypeInfo CSharpEventBindReturn = new TypeInfo("void"); - private static readonly List CSharpEventBindParams = new List() { new FunctionInfo.ParameterInfo() { Name = "bind", Type = new TypeInfo("bool") } }; + private static readonly List CSharpEventBindParams = new List { new FunctionInfo.ParameterInfo { Name = "bind", Type = new TypeInfo("bool") } }; #endif public static event Action GenerateCSharpTypeInternals; - internal static readonly Dictionary CSharpNativeToManagedBasicTypes = new Dictionary() + internal static readonly Dictionary CSharpNativeToManagedBasicTypes = new Dictionary { // Language types { "bool", "bool" }, @@ -46,7 +46,7 @@ namespace Flax.Build.Bindings { "double", "double" }, }; - internal static readonly Dictionary CSharpNativeToManagedDefault = new Dictionary() + internal static readonly Dictionary CSharpNativeToManagedDefault = new Dictionary { // Engine types { "String", "string" }, @@ -632,11 +632,11 @@ namespace Flax.Build.Bindings else if (returnValueType == "object[]") returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))"; else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BytesContainer" || returnNativeType == "Array") - returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = nameof(__returnCount))"; + returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = nameof(__returnCount))"; else if (functionInfo.ReturnType.Type == "Dictionary") - returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)"; + returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)"; else if (returnValueType == "byte[]") - returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__returnCount\")"; + returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__returnCount\")"; else if (returnValueType == "bool[]") { // Boolean arrays does not support custom marshalling for some unknown reason @@ -691,11 +691,15 @@ namespace Flax.Build.Bindings parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters. } else if (parameterInfo.Type.Type == "Dictionary") - parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)"; + parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)"; else if (nativeType == "bool") parameterMarshalType = "MarshalAs(UnmanagedType.U1)"; else if (nativeType == "char") parameterMarshalType = "MarshalAs(UnmanagedType.I2)"; + else if (nativeType.EndsWith("[]")) + { + parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>))"; + } if (!string.IsNullOrEmpty(parameterMarshalType)) contents.Append($"[{parameterMarshalType}] "); @@ -730,7 +734,7 @@ namespace Flax.Build.Bindings if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer") parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")"; else if (parameterInfo.Type.Type == "Dictionary") - parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)"; + parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)"; } if (nativeType == "System.Type") parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemTypeMarshaller))"; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 138291177..a189c363a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -980,6 +980,10 @@ namespace Flax.Build.Bindings UseReferenceForResult = UsePassByReference(buildData, functionInfo.ReturnType, caller), CustomParameters = new List(), }; + var returnType = functionInfo.ReturnType; + var returnApiType = FindApiTypeInfo(buildData, returnType, caller); + if (returnApiType != null && returnApiType.MarshalAs != null) + returnType = returnApiType.MarshalAs; bool returnTypeIsContainer = false; var returnValueConvert = GenerateCppWrapperNativeToManaged(buildData, functionInfo.ReturnType, caller, out var returnValueType, functionInfo); @@ -999,7 +1003,7 @@ namespace Flax.Build.Bindings }); } #if USE_NETCORE - else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BitArray" || functionInfo.ReturnType.Type == "BytesContainer") + else if (returnType.Type == "Array" || returnType.Type == "Span" || returnType.Type == "DataContainer" || returnType.Type == "BitArray" || returnType.Type == "BytesContainer") { returnTypeIsContainer = true; functionInfo.Glue.CustomParameters.Add(new FunctionInfo.ParameterInfo @@ -1173,7 +1177,7 @@ namespace Flax.Build.Bindings { callBegin += "*__resultAsRef = "; } - else if (!functionInfo.ReturnType.IsVoid) + else if (!returnType.IsVoid) { if (useInlinedReturn) callBegin += "return "; @@ -1186,7 +1190,7 @@ namespace Flax.Build.Bindings if (returnTypeIsContainer) { callReturnCount = indent; - if (functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "BytesContainer") + if (returnType.Type == "Span" || returnType.Type == "BytesContainer") callReturnCount += "*__returnCount = {0}.Length();"; else callReturnCount += "*__returnCount = {0}.Count();"; @@ -1278,7 +1282,8 @@ namespace Flax.Build.Bindings #if USE_NETCORE if (!string.IsNullOrEmpty(callReturnCount)) { - contents.Append(indent).Append("const auto& __callTemp = ").Append(string.Format(callFormat, call, callParams)).Append(";").AppendLine(); + var tempVar = returnTypeIsContainer && returnType != functionInfo.ReturnType ? $"{returnType} __callTemp = " : "const auto& __callTemp = "; + contents.Append(indent).Append(tempVar).Append(string.Format(callFormat, call, callParams)).Append(";").AppendLine(); call = "__callTemp"; contents.Append(string.Format(callReturnCount, call)); contents.AppendLine(); @@ -1357,7 +1362,7 @@ namespace Flax.Build.Bindings } } - if (!useInlinedReturn && !functionInfo.Glue.UseReferenceForResult && !functionInfo.ReturnType.IsVoid) + if (!useInlinedReturn && !functionInfo.Glue.UseReferenceForResult && !returnType.IsVoid) { contents.Append(indent).Append("return __result;").AppendLine(); } @@ -1817,6 +1822,10 @@ namespace Flax.Build.Bindings // Add includes to properly compile bindings (eg. SoftObjectReference) GenerateCppAddFileReference(buildData, caller, typeInfo, apiTypeInfo); + // TODO: find a better way to reference other include files for types that have separate serialization header + if (typeInfo.Type.EndsWith("Curve") && typeInfo.GenericArgs != null) + CppIncludeFilesList.Add("Engine/Animations/CurveSerialization.h"); + return false; } @@ -3126,13 +3135,12 @@ namespace Flax.Build.Bindings // Includes header.Clear(); CppReferencesFiles.Remove(null); - CppIncludeFilesList.Clear(); foreach (var fileInfo in CppReferencesFiles) CppIncludeFilesList.Add(fileInfo.Name); CppIncludeFilesList.AddRange(CppIncludeFiles); CppIncludeFilesList.Sort(); if (CppIncludeFilesList.Remove("Engine/Serialization/Serialization.h")) - CppIncludeFilesList.Add("Engine/Serialization/Serialization.h"); + CppIncludeFilesList.Add("Engine/Serialization/Serialization.h"); // Include serialization header as the last one to properly handle specialization of custom types serialization foreach (var path in CppIncludeFilesList) header.AppendFormat("#include \"{0}\"", path).AppendLine(); contents.Insert(headerPos, header.ToString()); From 2f16694529f241828d207c4e6b1a90c6125714f8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Dec 2024 18:40:52 +0100 Subject: [PATCH 51/84] Fix some not important warning to be less verbose in some cases --- Source/Editor/Modules/ProjectCacheModule.cs | 4 ---- Source/Engine/Graphics/Async/GPUTasksContext.cpp | 7 +++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Modules/ProjectCacheModule.cs b/Source/Editor/Modules/ProjectCacheModule.cs index b0ebb6981..3e2cd6806 100644 --- a/Source/Editor/Modules/ProjectCacheModule.cs +++ b/Source/Editor/Modules/ProjectCacheModule.cs @@ -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 diff --git a/Source/Engine/Graphics/Async/GPUTasksContext.cpp b/Source/Engine/Graphics/Async/GPUTasksContext.cpp index 11582fe78..e0ee8cae1 100644 --- a/Source/Engine/Graphics/Async/GPUTasksContext.cpp +++ b/Source/Engine/Graphics/Async/GPUTasksContext.cpp @@ -5,6 +5,7 @@ #include "Engine/Core/Log.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Threading/Threading.h" +#include "Engine/Engine/Globals.h" #define GPU_TASKS_USE_DEDICATED_CONTEXT 0 @@ -36,7 +37,8 @@ GPUTasksContext::~GPUTasksContext() auto task = tasks[i]; if (task->GetSyncPoint() <= _currentSyncPoint && task->GetState() != TaskState::Finished) { - LOG(Warning, "{0} has been canceled before a sync", task->ToString()); + if (!Globals::IsRequestingExit) + LOG(Warning, "{0} has been canceled before a sync", task->ToString()); task->CancelSync(); } } @@ -60,7 +62,8 @@ void GPUTasksContext::OnCancelSync(GPUTask* task) _tasksDone.Remove(task); - LOG(Warning, "{0} has been canceled before a sync", task->ToString()); + if (!Globals::IsRequestingExit) + LOG(Warning, "{0} has been canceled before a sync", task->ToString()); } void GPUTasksContext::OnFrameBegin() From 848dbdf5325fdea014623d3f5d3b1b69c9ec7b9c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Dec 2024 18:41:36 +0100 Subject: [PATCH 52/84] Add auto-exit command line to editor --- Source/Editor/Editor.cs | 24 +++++++++++++++++------- Source/Editor/Managed/ManagedEditor.cpp | 22 +++++++++++++--------- Source/Editor/Managed/ManagedEditor.h | 11 +++++++++++ Source/Editor/Modules/WindowsModule.cs | 5 ++++- Source/Engine/Engine/CommandLine.cpp | 1 + Source/Engine/Engine/CommandLine.h | 5 +++++ 6 files changed, 51 insertions(+), 17 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index d99a55a08..795f89d55 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -49,9 +49,9 @@ namespace FlaxEditor } private readonly List _modules = new List(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(); 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) { diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index d0b060ffe..9442aa244 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -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(); diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h index eb1cceef8..4722db997 100644 --- a/Source/Editor/Managed/ManagedEditor.h +++ b/Source/Editor/Managed/ManagedEditor.h @@ -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); diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index b9f3e246b..fa7a04d9b 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -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 diff --git a/Source/Engine/Engine/CommandLine.cpp b/Source/Engine/Engine/CommandLine.cpp index e2de230ef..efcd73524 100644 --- a/Source/Engine/Engine/CommandLine.cpp +++ b/Source/Engine/Engine/CommandLine.cpp @@ -154,6 +154,7 @@ bool CommandLine::Parse(const Char* cmdLine) PARSE_ARG_SWITCH("-build ", Build); PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile); PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug); + PARSE_BOOL_SWITCH("-exit ", Exit); PARSE_ARG_OPT_SWITCH("-play ", Play); #endif #if USE_EDITOR || !BUILD_RELEASE diff --git a/Source/Engine/Engine/CommandLine.h b/Source/Engine/Engine/CommandLine.h index 347e9bebd..4a3bab4c0 100644 --- a/Source/Engine/Engine/CommandLine.h +++ b/Source/Engine/Engine/CommandLine.h @@ -168,6 +168,11 @@ public: /// Nullable ShaderDebug; + /// + /// -exit (exits the editor after startup and performing all queued actions). Usefull when invoking editor from CL/CD. + /// + Nullable Exit; + /// /// -play !guid! ( Scene to play, can be empty to use default ) /// From e27d18ef87f81bf8e83cee791390857ca74954fc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Dec 2024 18:44:09 +0100 Subject: [PATCH 53/84] Fix default build target preset --- Source/Engine/Core/Config/BuildSettings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Core/Config/BuildSettings.cs b/Source/Engine/Core/Config/BuildSettings.cs index 8526690b1..e5838087c 100644 --- a/Source/Engine/Core/Config/BuildSettings.cs +++ b/Source/Engine/Core/Config/BuildSettings.cs @@ -21,8 +21,8 @@ namespace FlaxEditor.Content.Settings { new BuildTarget { - Name = "Windows 64bit", - Output = "Output\\Win64", + Name = "Windows", + Output = "Output\\Windows", Platform = BuildPlatform.Windows64, Mode = BuildConfiguration.Development, }, @@ -35,8 +35,8 @@ namespace FlaxEditor.Content.Settings { new BuildTarget { - Name = "Windows 64bit", - Output = "Output\\Win64", + Name = "Windows", + Output = "Output\\Windows", Platform = BuildPlatform.Windows64, Mode = BuildConfiguration.Release, }, From 93fefc9af36b43ece8403246e67cc2a4064d0d58 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Dec 2024 18:45:41 +0100 Subject: [PATCH 54/84] Fix showing splash screen window when running Editor in headless mode --- Source/Editor/Editor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index d0bfe2765..8e4090c40 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -673,6 +673,7 @@ bool Editor::Init() Managed = New(); // Show splash screen + if (!CommandLine::Options.Headless.IsTrue()) { PROFILE_CPU_NAMED("Splash"); if (EditorImpl::Splash == nullptr) From 992d907b9cea56c0c928154e5f0ea07801bf2716 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Dec 2024 18:46:31 +0100 Subject: [PATCH 55/84] Add initial test for game cooking (use one of the Flax Samples project) --- .github/data/ExitOnEsc.cs | 11 +++++++++ .github/workflows/cooking.yml | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 .github/data/ExitOnEsc.cs create mode 100644 .github/workflows/cooking.yml diff --git a/.github/data/ExitOnEsc.cs b/.github/data/ExitOnEsc.cs new file mode 100644 index 000000000..a2abcdfe0 --- /dev/null +++ b/.github/data/ExitOnEsc.cs @@ -0,0 +1,11 @@ +using FlaxEngine; + +public class ExitOnEsc : Script +{ + /// + public override void OnUpdate() + { + // Exit as soon as game starts update loaded level + Engine.RequestExit(); + } +} diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml new file mode 100644 index 000000000..558c620b5 --- /dev/null +++ b/.github/workflows/cooking.yml @@ -0,0 +1,44 @@ +name: Cooking +on: [push, pull_request] + +env: + DOTNET_NOLOGO: true + DOTNET_CLI_TELEMETRY_OPTOUT: false + +jobs: + + # Cooking on Windows + tests-windows: + name: Cooking (Windows) + runs-on: "windows-2022" + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + - name: Print .NET info + run: | + dotnet --info + dotnet workload --info + - name: Checkout LFS + run: | + git lfs version + git lfs pull + - name: Get Flax Samples + uses: actions/checkout@v3 + with: + fetch-depth: 1 + repository: FlaxEngine/FlaxSamples + path: FlaxSamples + - name: Build Editor + run: | + .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor + - name: Cook Game + run: | + cp -f -v .github\data\ExitOnEsc.cs FlaxSamples\MaterialsFeaturesTour\Source\Game + .\Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "${GITHUB_WORKSPACE}\FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" + - name: Test Game + run: | + .\FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe" -std -headless -mute -null From dcce8581c50ed488bdf65dfb909587b0f31cab76 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Dec 2024 23:39:00 +0100 Subject: [PATCH 56/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 558c620b5..403d4dd57 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -1,4 +1,4 @@ -name: Cooking +name: Cook on: [push, pull_request] env: @@ -7,9 +7,9 @@ env: jobs: - # Cooking on Windows + # Cook on Windows tests-windows: - name: Cooking (Windows) + name: Cook (Windows) runs-on: "windows-2022" steps: - name: Checkout repo @@ -31,14 +31,14 @@ jobs: with: fetch-depth: 1 repository: FlaxEngine/FlaxSamples - path: FlaxSamples + path: ..\FlaxSamples - name: Build Editor run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor - name: Cook Game run: | - cp -f -v .github\data\ExitOnEsc.cs FlaxSamples\MaterialsFeaturesTour\Source\Game - .\Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "${GITHUB_WORKSPACE}\FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" + cp .github\data\ExitOnEsc.cs ..\FlaxSamples\MaterialsFeaturesTour\Source\Game + .\Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "${GITHUB_WORKSPACE}\..\FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" - name: Test Game run: | - .\FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe" -std -headless -mute -null + ..\FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe -std -headless -mute -null From 2328096200c88887264ed62b491071e37fe08331 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Dec 2024 23:45:52 +0100 Subject: [PATCH 57/84] Fix path to be inside root folder --- .github/workflows/cooking.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 403d4dd57..4ec35da69 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -1,4 +1,4 @@ -name: Cook +name: Cooker on: [push, pull_request] env: @@ -31,14 +31,14 @@ jobs: with: fetch-depth: 1 repository: FlaxEngine/FlaxSamples - path: ..\FlaxSamples + path: FlaxSamples - name: Build Editor run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor - name: Cook Game run: | - cp .github\data\ExitOnEsc.cs ..\FlaxSamples\MaterialsFeaturesTour\Source\Game - .\Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "${GITHUB_WORKSPACE}\..\FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" + cp .github\data\ExitOnEsc.cs FlaxSamples\MaterialsFeaturesTour\Source\Game + .\Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" - name: Test Game run: | - ..\FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe -std -headless -mute -null + .\FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe -std -headless -mute -null From 24a9ec5dd5554309c53d0b5c88a52457605b1be7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 4 Dec 2024 23:52:44 +0100 Subject: [PATCH 58/84] Fix Github macos image version to be explicit --- .github/workflows/build_ios.yml | 2 +- .github/workflows/build_mac.yml | 4 ++-- .github/workflows/cd.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_ios.yml b/.github/workflows/build_ios.yml index 5a0d285fe..0f27619f7 100644 --- a/.github/workflows/build_ios.yml +++ b/.github/workflows/build_ios.yml @@ -10,7 +10,7 @@ jobs: # Game game-windows: name: Game (iOS, Release ARM64) - runs-on: "macos-latest" + runs-on: "macos-14" steps: - name: Checkout repo uses: actions/checkout@v3 diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml index a8ebb4c62..ca595ed32 100644 --- a/.github/workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -10,7 +10,7 @@ jobs: # Editor editor-mac: name: Editor (Mac, Development ARM64) - runs-on: "macos-latest" + runs-on: "macos-14" steps: - name: Checkout repo uses: actions/checkout@v3 @@ -35,7 +35,7 @@ jobs: # Game game-mac: name: Game (Mac, Release ARM64) - runs-on: "macos-latest" + runs-on: "macos-14" steps: - name: Checkout repo uses: actions/checkout@v3 diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 4b4f9a827..428dea933 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -140,7 +140,7 @@ jobs: # Mac package-mac-editor: name: Editor (Mac) - runs-on: "macos-latest" + runs-on: "macos-14" steps: - name: Checkout repo uses: actions/checkout@v3 @@ -168,7 +168,7 @@ jobs: path: Output/FlaxEditorMac.zip package-mac-game: name: Game (Mac) - runs-on: "macos-latest" + runs-on: "macos-14" steps: - name: Checkout repo uses: actions/checkout@v3 From 6fabd0c26dfcb7438f28c6355ba89cc4ea4a41c6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Dec 2024 10:33:14 +0100 Subject: [PATCH 59/84] Add iOS cooking test on Mac --- .github/workflows/cooking.yml | 39 ++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 4ec35da69..46b8deea2 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -8,7 +8,7 @@ env: jobs: # Cook on Windows - tests-windows: + cook-windows: name: Cook (Windows) runs-on: "windows-2022" steps: @@ -35,10 +35,43 @@ jobs: - name: Build Editor run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor - - name: Cook Game + - name: Cook Game (Windows) run: | cp .github\data\ExitOnEsc.cs FlaxSamples\MaterialsFeaturesTour\Source\Game .\Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" - - name: Test Game + - name: Test Game (Windows) run: | .\FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe -std -headless -mute -null + + # Cook on Mac + cook-mac: + name: Cook (Mac) + runs-on: "macos-14" + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + - name: Print .NET info + run: | + dotnet --info + dotnet workload --info + - name: Checkout LFS + run: | + git lfs version + git lfs pull + - name: Get Flax Samples + uses: actions/checkout@v3 + with: + fetch-depth: 1 + repository: FlaxEngine/FlaxSamples + path: FlaxSamples + - name: Build Editor + run: | + ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor + - name: Cook Game (iOS) + run: | + cp .github/data/ExitOnEsc.cs FlaxSamples/MaterialsFeaturesTour/Source/Game + ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS" From 2230f907fd0c1946468562b92f8a1da594674470 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Dec 2024 10:33:30 +0100 Subject: [PATCH 60/84] Fix duplicated newlines on Github Action output --- Source/Engine/Core/Log.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/Engine/Core/Log.cpp b/Source/Engine/Core/Log.cpp index c8cf4419e..9395af05f 100644 --- a/Source/Engine/Core/Log.cpp +++ b/Source/Engine/Core/Log.cpp @@ -16,10 +16,14 @@ #include #define LOG_ENABLE_FILE (!PLATFORM_SWITCH) +#define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE)) namespace { bool LogAfterInit = false, IsDuringLog = false; +#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR + bool IsWindowsSingleNewLineChar = false; +#endif int LogTotalErrorsCnt = 0; FileWriteStream* LogFile = nullptr; CriticalSection LogLocker; @@ -86,6 +90,11 @@ bool Log::Logger::Init() } LogTotalErrorsCnt = 0; LogAfterInit = true; +#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR + String envVar; + Platform::GetEnvironmentVariable(TEXT("GITHUB_ACTION"), envVar); + IsWindowsSingleNewLineChar = envVar.HasChars(); +#endif // Write BOM (UTF-16 (LE); BOM: FF FE) byte bom[] = { 0xFF, 0xFE }; @@ -127,6 +136,11 @@ void Log::Logger::Write(const StringView& msg) printf("%s", ansi.Get()); #else std::wcout.write(ptr, length); +#if LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR + if (IsWindowsSingleNewLineChar) + std::wcout.write(TEXT("\n"), 1); // Github Actions show logs with duplicated new-line characters so skip \r + else +#endif std::wcout.write(TEXT(PLATFORM_LINE_TERMINATOR), ARRAY_COUNT(PLATFORM_LINE_TERMINATOR) - 1); #endif } From 4ac334acaced26620f1b09084ade810cf0de32fd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Dec 2024 11:21:51 +0100 Subject: [PATCH 61/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 13 ++++++++++--- Source/Engine/Core/Log.cpp | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 46b8deea2..c46c2221e 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -34,14 +34,16 @@ jobs: path: FlaxSamples - name: Build Editor run: | + cp .github\data\ExitOnEsc.cs FlaxSamples\MaterialsFeaturesTour\Source\Game .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor - name: Cook Game (Windows) + shell: cmd run: | - cp .github\data\ExitOnEsc.cs FlaxSamples\MaterialsFeaturesTour\Source\Game - .\Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" + start /w Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" - name: Test Game (Windows) + shell: cmd run: | - .\FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe -std -headless -mute -null + start /w FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe -std -headless -mute -null # Cook on Mac cook-mac: @@ -50,10 +52,15 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v3 + - name: Setup Vulkan + uses: ./.github/actions/vulkan - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x + - name: Setup .NET Workload + run: | + dotnet workload install ios - name: Print .NET info run: | dotnet --info diff --git a/Source/Engine/Core/Log.cpp b/Source/Engine/Core/Log.cpp index 9395af05f..2b6d0f300 100644 --- a/Source/Engine/Core/Log.cpp +++ b/Source/Engine/Core/Log.cpp @@ -16,7 +16,7 @@ #include #define LOG_ENABLE_FILE (!PLATFORM_SWITCH) -#define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE)) +#define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_WINDOWS && PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE)) namespace { From 987916cc1c705b08a281addcae169a94db8a24f9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Dec 2024 12:23:32 +0100 Subject: [PATCH 62/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 4 ++-- Source/Engine/Platform/Mac/MacPlatform.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index c46c2221e..f8d5afe94 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -39,11 +39,11 @@ jobs: - name: Cook Game (Windows) shell: cmd run: | - start /w Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" + Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" - name: Test Game (Windows) shell: cmd run: | - start /w FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe -std -headless -mute -null + FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe -std -headless -mute -null # Cook on Mac cook-mac: diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp index 2f054f7a6..c14195d0b 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.cpp +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -473,6 +473,7 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings) { NSPipe *stdoutPipe = [NSPipe pipe]; [task setStandardOutput:stdoutPipe]; + [task setStandardError:stdoutPipe]; outputObserver = [[NSNotificationCenter defaultCenter] addObserverForName: NSFileHandleDataAvailableNotification From 0ef1220846910eb4351c4ad66d4a6a137fa37d1b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Dec 2024 15:29:24 +0100 Subject: [PATCH 63/84] Another iteration on cooking job --- .github/data/Build Settings.json | 78 ++++++++++++++++++++++ .github/workflows/cooking.yml | 18 +++-- Source/Engine/Platform/Mac/MacPlatform.cpp | 33 +++++++-- 3 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 .github/data/Build Settings.json diff --git a/.github/data/Build Settings.json b/.github/data/Build Settings.json new file mode 100644 index 000000000..955468fa6 --- /dev/null +++ b/.github/data/Build Settings.json @@ -0,0 +1,78 @@ +{ + "ID": "2364031e4e327637c1ad88b415fa756e", + "TypeName": "FlaxEditor.Content.Settings.BuildSettings", + "EngineBuild": 6605, + "Data": { + "OutputName": "${PROJECT_NAME}", + "MaxAssetsPerPackage": 4096, + "MaxPackageSizeMB": 1024, + "ContentKey": 0, + "ForDistribution": false, + "SkipPackaging": true, + "AdditionalAssets": null, + "AdditionalScenes": null, + "AdditionalAssetFolders": null, + "ShadersNoOptimize": false, + "ShadersGenerateDebugData": false, + "SkipDefaultFonts": false, + "SkipDotnetPackaging": false, + "SkipUnusedDotnetLibsPackaging": true, + "Presets": [ + { + "Name": "Development", + "Targets": [ + { + "Name": "Windows", + "Output": "Output\\Windows", + "Platform": 2, + "Mode": 1, + "CustomDefines": null, + "PreBuildAction": null, + "PostBuildAction": null + }, + { + "Name": "Linux", + "Output": "Output\\LInux", + "Platform": 6, + "Mode": 1, + "CustomDefines": null, + "PreBuildAction": null, + "PostBuildAction": null + }, + { + "Name": "Android", + "Output": "Output\\Android", + "Platform": 9, + "Mode": 1, + "CustomDefines": null, + "PreBuildAction": null, + "PostBuildAction": null + }, + { + "Name": "iOS", + "Output": "Output\\iOS", + "Platform": 14, + "Mode": 1, + "CustomDefines": null, + "PreBuildAction": null, + "PostBuildAction": null + } + ] + }, + { + "Name": "Release", + "Targets": [ + { + "Name": "Windows", + "Output": "Output\\Windows", + "Platform": 2, + "Mode": 2, + "CustomDefines": null, + "PreBuildAction": null, + "PostBuildAction": null + } + ] + } + ] +} +} \ No newline at end of file diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index f8d5afe94..b5cfb1fd0 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -32,18 +32,21 @@ jobs: fetch-depth: 1 repository: FlaxEngine/FlaxSamples path: FlaxSamples - - name: Build Editor + - name: Path Files run: | cp .github\data\ExitOnEsc.cs FlaxSamples\MaterialsFeaturesTour\Source\Game + cp ".github\data\Build Settings.json" "FlaxSamples\MaterialsFeaturesTour\Content\Settings" + - name: Build Editor + run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor - name: Cook Game (Windows) - shell: cmd + shell: bash run: | - Binaries\Editor\Win64\Development\FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples\MaterialsFeaturesTour" -build "Development.Windows" + ./Binaries/Editor/Win64/Development/FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows" - name: Test Game (Windows) - shell: cmd + shell: bash run: | - FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe -std -headless -mute -null + ./FlaxSamples/MaterialsFeaturesTour/Output/Windows/MaterialsFeaturesTour.exe -std -headless -mute -null # Cook on Mac cook-mac: @@ -75,10 +78,13 @@ jobs: fetch-depth: 1 repository: FlaxEngine/FlaxSamples path: FlaxSamples + - name: Path Files + run: | + cp .github/data/ExitOnEsc.cs FlaxSamples/MaterialsFeaturesTour/Source/Game + cp ".github/data/Build Settings.json" "FlaxSamples/MaterialsFeaturesTour/Content/Settings" - name: Build Editor run: | ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor - name: Cook Game (iOS) run: | - cp .github/data/ExitOnEsc.cs FlaxSamples/MaterialsFeaturesTour/Source/Game ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS" diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp index c14195d0b..6962949e2 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.cpp +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -468,13 +468,12 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings) if (settings.WaitForEnd) { id outputObserver = nil; + id outputObserverError = nil; if (captureStdOut) { - NSPipe *stdoutPipe = [NSPipe pipe]; + NSPipe* stdoutPipe = [NSPipe pipe]; [task setStandardOutput:stdoutPipe]; - [task setStandardError:stdoutPipe]; - outputObserver = [[NSNotificationCenter defaultCenter] addObserverForName: NSFileHandleDataAvailableNotification object: [stdoutPipe fileHandleForReading] @@ -498,8 +497,34 @@ int32 MacPlatform::CreateProcess(CreateProcessSettings& settings) } } ]; - [[stdoutPipe fileHandleForReading] waitForDataInBackgroundAndNotify]; + + NSPipe *stderrPipe = [NSPipe pipe]; + [task setStandardError:stderrPipe]; + outputObserverError = [[NSNotificationCenter defaultCenter] + addObserverForName: NSFileHandleDataAvailableNotification + object: [stderrPipe fileHandleForReading] + queue: nil + usingBlock:^(NSNotification* notification) + { + NSData* data = [stderrPipe fileHandleForReading].availableData; + if (data.length) + { + String line((const char*)data.bytes, data.length); + if (settings.SaveOutput) + settings.Output.Add(line.Get(), line.Length()); + if (settings.LogOutput) + { + StringView lineView(line); + if (line[line.Length() - 1] == '\n') + lineView = StringView(line.Get(), line.Length() - 1); + Log::Logger::Write(LogType::Error, lineView); + } + [[stderrPipe fileHandleForReading] waitForDataInBackgroundAndNotify]; + } + } + ]; + [[stderrPipe fileHandleForReading] waitForDataInBackgroundAndNotify]; } String exception; From 3aed3f19540374f4f259d8cbd01a13465a1e4640 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Dec 2024 16:59:02 +0100 Subject: [PATCH 64/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index b5cfb1fd0..109b0f19f 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -32,7 +32,7 @@ jobs: fetch-depth: 1 repository: FlaxEngine/FlaxSamples path: FlaxSamples - - name: Path Files + - name: Patch Files run: | cp .github\data\ExitOnEsc.cs FlaxSamples\MaterialsFeaturesTour\Source\Game cp ".github\data\Build Settings.json" "FlaxSamples\MaterialsFeaturesTour\Content\Settings" @@ -40,13 +40,13 @@ jobs: run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor - name: Cook Game (Windows) - shell: bash + shell: pwsh run: | - ./Binaries/Editor/Win64/Development/FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows" + & "./Binaries/Editor/Win64/Development/FlaxEditor.exe" -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows" - name: Test Game (Windows) - shell: bash + shell: pwsh run: | - ./FlaxSamples/MaterialsFeaturesTour/Output/Windows/MaterialsFeaturesTour.exe -std -headless -mute -null + & "./FlaxSamples/MaterialsFeaturesTour/Output/Windows/MaterialsFeaturesTour.exe" -std -headless -mute -null # Cook on Mac cook-mac: @@ -78,7 +78,7 @@ jobs: fetch-depth: 1 repository: FlaxEngine/FlaxSamples path: FlaxSamples - - name: Path Files + - name: Patch Files run: | cp .github/data/ExitOnEsc.cs FlaxSamples/MaterialsFeaturesTour/Source/Game cp ".github/data/Build Settings.json" "FlaxSamples/MaterialsFeaturesTour/Content/Settings" From 76518ac0515262e54837ed7e4cb9a6f00852adc6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Dec 2024 17:24:08 +0100 Subject: [PATCH 65/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 109b0f19f..35e83d131 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -42,11 +42,11 @@ jobs: - name: Cook Game (Windows) shell: pwsh run: | - & "./Binaries/Editor/Win64/Development/FlaxEditor.exe" -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows" + ./Binaries/Editor/Win64/Development/FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows" - name: Test Game (Windows) shell: pwsh run: | - & "./FlaxSamples/MaterialsFeaturesTour/Output/Windows/MaterialsFeaturesTour.exe" -std -headless -mute -null + ./FlaxSamples/MaterialsFeaturesTour/Output/Windows/MaterialsFeaturesTour.exe -std -headless -mute -null # Cook on Mac cook-mac: From 2441a35611572b78e43411f0578e0e609029860c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 5 Dec 2024 23:23:19 +0100 Subject: [PATCH 66/84] Another iteration on cooking job --- .github/data/Cook.ps1 | 2 ++ .github/workflows/cooking.yml | 8 ++------ 2 files changed, 4 insertions(+), 6 deletions(-) create mode 100644 .github/data/Cook.ps1 diff --git a/.github/data/Cook.ps1 b/.github/data/Cook.ps1 new file mode 100644 index 000000000..b32037fe7 --- /dev/null +++ b/.github/data/Cook.ps1 @@ -0,0 +1,2 @@ +Start-Process -filepath "Binaries\Editor\Win64\Development\FlaxEditor.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows"' +Start-Process -filepath "FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null' diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 35e83d131..eaea8ad9e 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -39,14 +39,10 @@ jobs: - name: Build Editor run: | .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor - - name: Cook Game (Windows) + - name: Cook and Test Game (Windows) shell: pwsh run: | - ./Binaries/Editor/Win64/Development/FlaxEditor.exe -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows" - - name: Test Game (Windows) - shell: pwsh - run: | - ./FlaxSamples/MaterialsFeaturesTour/Output/Windows/MaterialsFeaturesTour.exe -std -headless -mute -null + .\.github\data\Cook.ps1 # Cook on Mac cook-mac: From 6f00d664bbb36b04750c99c515174587fab26bd9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 09:37:21 +0100 Subject: [PATCH 67/84] Another iteration on cooking job --- .github/data/Build Settings.json | 11 ++++++++++- .github/data/Cook.ps1 | 3 +++ .github/workflows/cooking.yml | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/data/Build Settings.json b/.github/data/Build Settings.json index 955468fa6..84ee94f0c 100644 --- a/.github/data/Build Settings.json +++ b/.github/data/Build Settings.json @@ -32,13 +32,22 @@ }, { "Name": "Linux", - "Output": "Output\\LInux", + "Output": "Output\\Linux", "Platform": 6, "Mode": 1, "CustomDefines": null, "PreBuildAction": null, "PostBuildAction": null }, + { + "Name": "Mac", + "Output": "Output\\Mac", + "Platform": 13, + "Mode": 1, + "CustomDefines": null, + "PreBuildAction": null, + "PostBuildAction": null + }, { "Name": "Android", "Output": "Output\\Android", diff --git a/.github/data/Cook.ps1 b/.github/data/Cook.ps1 index b32037fe7..394e6d031 100644 --- a/.github/data/Cook.ps1 +++ b/.github/data/Cook.ps1 @@ -1,2 +1,5 @@ +Write-Output "Cooking Game" Start-Process -filepath "Binaries\Editor\Win64\Development\FlaxEditor.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Windows"' + +Write-Output "Testing Game" Start-Process -filepath "FlaxSamples\MaterialsFeaturesTour\Output\Windows\MaterialsFeaturesTour.exe" -Wait -NoNewWindow -PassThru -ArgumentList '-std -headless -mute -null' diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index eaea8ad9e..1045b36e1 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -11,6 +11,7 @@ jobs: cook-windows: name: Cook (Windows) runs-on: "windows-2022" + if: false steps: - name: Checkout repo uses: actions/checkout@v3 @@ -81,6 +82,12 @@ jobs: - name: Build Editor run: | ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor + - name: Cook Game (Mac) + run: | + ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Mac" + - name: Test Game (Mac) + run: | + ./FlaxSamples/MaterialsFeaturesTour/Outpu/Mac/MaterialsFeaturesTour" -std -headless -mute -null - name: Cook Game (iOS) run: | ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS" From 6af46bb764324d02ada2a42aad7cff0d6f71be43 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 09:57:33 +0100 Subject: [PATCH 68/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 1045b36e1..b16a8fa95 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -87,7 +87,7 @@ jobs: ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Mac" - name: Test Game (Mac) run: | - ./FlaxSamples/MaterialsFeaturesTour/Outpu/Mac/MaterialsFeaturesTour" -std -headless -mute -null + ./FlaxSamples/MaterialsFeaturesTour/Output/Mac/MaterialsFeaturesTour.app/Contents/MacOS/MaterialsFeaturesTour" -std -headless -mute -null - name: Cook Game (iOS) run: | ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS" From 56e6176e9dca6077d374505aef30ed5615654484 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 10:25:36 +0100 Subject: [PATCH 69/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index b16a8fa95..851621442 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -87,7 +87,7 @@ jobs: ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Mac" - name: Test Game (Mac) run: | - ./FlaxSamples/MaterialsFeaturesTour/Output/Mac/MaterialsFeaturesTour.app/Contents/MacOS/MaterialsFeaturesTour" -std -headless -mute -null + ./FlaxSamples/MaterialsFeaturesTour/Output/Mac/MaterialsFeaturesTour.app/Contents/MacOS/FlaxGame" -std -headless -mute -null - name: Cook Game (iOS) run: | ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS" From 47959ac901fb7cd9f9df783c21e62d7dd567fbfe Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 10:41:40 +0100 Subject: [PATCH 70/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 851621442..95f4e2652 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -87,7 +87,7 @@ jobs: ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Mac" - name: Test Game (Mac) run: | - ./FlaxSamples/MaterialsFeaturesTour/Output/Mac/MaterialsFeaturesTour.app/Contents/MacOS/FlaxGame" -std -headless -mute -null + ./FlaxSamples/MaterialsFeaturesTour/Output/Mac/MaterialsFeaturesTour.app/Contents/MacOS/FlaxGame -std -headless -mute -null - name: Cook Game (iOS) run: | ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS" From ca15318ade90f10fb53bcf779d27d6b2fb335a27 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 11:27:04 +0100 Subject: [PATCH 71/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 95f4e2652..755c4a65a 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -87,7 +87,7 @@ jobs: ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Mac" - name: Test Game (Mac) run: | - ./FlaxSamples/MaterialsFeaturesTour/Output/Mac/MaterialsFeaturesTour.app/Contents/MacOS/FlaxGame -std -headless -mute -null + open -n ./FlaxSamples/MaterialsFeaturesTour/Output/Mac/MaterialsFeaturesTour.app -W --args -std -headless -mute -null - name: Cook Game (iOS) run: | ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS" From a2087297e0e8803dec2d230ebe267fcf75364f82 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 11:37:01 +0100 Subject: [PATCH 72/84] Add deploying game debug symbols for Windows builds --- Source/Tools/Flax.Build/Deploy/Deployer.cs | 16 ++++++++++++++++ .../Flax.Build/Deploy/Deployment.Platforms.cs | 12 ++++++++++++ 2 files changed, 28 insertions(+) diff --git a/Source/Tools/Flax.Build/Deploy/Deployer.cs b/Source/Tools/Flax.Build/Deploy/Deployer.cs index aed145a26..c9809a97d 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployer.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployer.cs @@ -93,6 +93,22 @@ namespace Flax.Deploy BuildPlatform(platform, architectures); } } + + if (Platform.BuildTargetPlatform == TargetPlatform.Windows) + { + Log.Info("Compressing game debug symbols files..."); + var gamePackageZipPath = Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols.zip"); + Utilities.FileDelete(gamePackageZipPath); + using (var zip = new Ionic.Zip.ZipFile()) + { + zip.AddDirectory(Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols")); + zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; + zip.Comment = string.Format("Flax Game {0}.{1}.{2}\nDate: {3}", Deployer.VersionMajor, Deployer.VersionMinor, Deployer.VersionBuild, DateTime.UtcNow); + zip.Save(gamePackageZipPath); + } + Log.Info("Compressed game debug symbols package size: " + Utilities.GetFileSize(gamePackageZipPath)); + } + Utilities.DirectoryDelete(Path.Combine(Deployer.PackageOutputPath, "GameDebugSymbols")); } if (Configuration.DeployEditor) diff --git a/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs b/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs index 805cfdbe9..ef3b991d7 100644 --- a/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs +++ b/Source/Tools/Flax.Build/Deploy/Deployment.Platforms.cs @@ -28,6 +28,18 @@ namespace Flax.Deploy string dst = Path.Combine(Deployer.PackageOutputPath, platformName); Utilities.DirectoryDelete(dst); + // Deploy debug files for crashes debugging + foreach (var configuration in new[] { TargetConfiguration.Debug, TargetConfiguration.Development, TargetConfiguration.Release }) + { + if (platform == TargetPlatform.Windows) + { + var dstDebug = Path.Combine(Deployer.PackageOutputPath, $"GameDebugSymbols/{platform}/{configuration}"); + Directory.CreateDirectory(dstDebug); + var binaries = Path.Combine(src, "Binaries", "Game", "x64", configuration.ToString()); + DeployFiles(binaries, dstDebug, "*.pdb"); + } + } + // Deploy files { DeployFolder(platformsRoot, Deployer.PackageOutputPath, platformName); From e53b2b573683c71c23a4f362fc2999f7614a34c9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 11:37:25 +0100 Subject: [PATCH 73/84] Fix various compilation warnings --- .../Graphics/Materials/MaterialParams.cpp | 36 +++++++++---------- .../Materials/ParticleMaterialShader.cpp | 2 +- .../VolumeParticleMaterialShader.cpp | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index ee5b690f0..30064222b 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -282,35 +282,35 @@ void MaterialParameter::Bind(BindMeta& meta) const switch (_type) { case MaterialParameterType::Bool: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(bool)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(bool))); *((int32*)(meta.Constants.Get() + _offset)) = _asBool; break; case MaterialParameterType::Integer: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(int32)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(int32))); *((int32*)(meta.Constants.Get() + _offset)) = _asInteger; break; case MaterialParameterType::Float: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(float)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(float))); *((float*)(meta.Constants.Get() + _offset)) = _asFloat; break; case MaterialParameterType::Vector2: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float2)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float2))); *((Float2*)(meta.Constants.Get() + _offset)) = _asVector2; break; case MaterialParameterType::Vector3: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float3)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float3))); *((Float3*)(meta.Constants.Get() + _offset)) = _asVector3; break; case MaterialParameterType::Vector4: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4))); *((Float4*)(meta.Constants.Get() + _offset)) = *(Float4*)&AsData; break; case MaterialParameterType::Color: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4))); *((Color*)(meta.Constants.Get() + _offset)) = _asColor; break; case MaterialParameterType::Matrix: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Matrix)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Matrix))); Matrix::Transpose(*(Matrix*)&AsData, *(Matrix*)(meta.Constants.Get() + _offset)); break; case MaterialParameterType::NormalMap: @@ -409,44 +409,44 @@ void MaterialParameter::Bind(BindMeta& meta) const switch (e->Value.Type.Type) { case VariantType::Bool: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(bool)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(bool))); *((bool*)(meta.Constants.Get() + _offset)) = e->Value.AsBool; break; case VariantType::Int: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(int32)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(int32))); *((int32*)(meta.Constants.Get() + _offset)) = e->Value.AsInt; break; case VariantType::Uint: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(uint32)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(uint32))); *((uint32*)(meta.Constants.Get() + _offset)) = e->Value.AsUint; break; case VariantType::Float: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(float)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(float))); *((float*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat; break; case VariantType::Float2: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float2)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float2))); *((Float2*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat2(); break; case VariantType::Float3: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float3)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float3))); *((Float3*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat3(); break; case VariantType::Float4: case VariantType::Color: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4))); *((Float4*)(meta.Constants.Get() + _offset)) = e->Value.AsFloat4(); break; case VariantType::Double2: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float2)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float2))); *((Float2*)(meta.Constants.Get() + _offset)) = (Float2)e->Value.AsDouble2(); break; case VariantType::Double3: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float3)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float3))); *((Float3*)(meta.Constants.Get() + _offset)) = (Float3)e->Value.AsDouble3(); break; case VariantType::Double4: - ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)_offset + sizeof(Float4)); + ASSERT_LOW_LAYER(meta.Constants.Get() && meta.Constants.Length() >= (int32)(_offset + sizeof(Float4))); *((Float4*)(meta.Constants.Get() + _offset)) = (Float4)e->Value.AsDouble4(); break; default: ; diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index f60742ccc..33e2bb454 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -85,7 +85,7 @@ void ParticleMaterialShader::Bind(BindParameters& params) { const StringView name(param.GetName().Get() + 9, param.GetName().Length() - 9); const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name); - ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)param.GetBindOffset() + sizeof(int32)); + ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)(param.GetBindOffset() + sizeof(int32))); *((int32*)(bindMeta.Constants.Get() + param.GetBindOffset())) = offset; } } diff --git a/Source/Engine/Graphics/Materials/VolumeParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/VolumeParticleMaterialShader.cpp index 5b9bb27a6..cc04498df 100644 --- a/Source/Engine/Graphics/Materials/VolumeParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/VolumeParticleMaterialShader.cpp @@ -68,7 +68,7 @@ void VolumeParticleMaterialShader::Bind(BindParameters& params) { const StringView name(param.GetName().Get() + 9, param.GetName().Length() - 9); const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name); - ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)param.GetBindOffset() + sizeof(int32)); + ASSERT_LOW_LAYER(bindMeta.Constants.Get() && bindMeta.Constants.Length() >= (int32)(param.GetBindOffset() + sizeof(int32))); *((int32*)(bindMeta.Constants.Get() + param.GetBindOffset())) = offset; } } From 64a674f9bf75f1250d43fd0723e9b9722cb702d0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 11:57:12 +0100 Subject: [PATCH 74/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 42 ----------------------------------- 1 file changed, 42 deletions(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 755c4a65a..74b422283 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -6,45 +6,6 @@ env: DOTNET_CLI_TELEMETRY_OPTOUT: false jobs: - - # Cook on Windows - cook-windows: - name: Cook (Windows) - runs-on: "windows-2022" - if: false - steps: - - name: Checkout repo - uses: actions/checkout@v3 - - name: Setup .NET - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 8.0.x - - name: Print .NET info - run: | - dotnet --info - dotnet workload --info - - name: Checkout LFS - run: | - git lfs version - git lfs pull - - name: Get Flax Samples - uses: actions/checkout@v3 - with: - fetch-depth: 1 - repository: FlaxEngine/FlaxSamples - path: FlaxSamples - - name: Patch Files - run: | - cp .github\data\ExitOnEsc.cs FlaxSamples\MaterialsFeaturesTour\Source\Game - cp ".github\data\Build Settings.json" "FlaxSamples\MaterialsFeaturesTour\Content\Settings" - - name: Build Editor - run: | - .\Development\Scripts\Windows\CallBuildTool.bat -build -log -printSDKs -dotnet=8 -arch=x64 -platform=Windows -configuration=Development -buildtargets=FlaxEditor - - name: Cook and Test Game (Windows) - shell: pwsh - run: | - .\.github\data\Cook.ps1 - # Cook on Mac cook-mac: name: Cook (Mac) @@ -85,9 +46,6 @@ jobs: - name: Cook Game (Mac) run: | ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Mac" - - name: Test Game (Mac) - run: | - open -n ./FlaxSamples/MaterialsFeaturesTour/Output/Mac/MaterialsFeaturesTour.app -W --args -std -headless -mute -null - name: Cook Game (iOS) run: | ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS" From 2f239fe4053b338030bf2b79149d76c55bca1d0f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 12:45:44 +0100 Subject: [PATCH 75/84] Add missing license file and build script for `WinPixEventRuntime` header module --- .../ThirdParty/WinPixEventRuntime/LICENSE.txt | 21 +++++++++++++++++++ .../WinPixEventRuntime.Build.cs | 21 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 Source/ThirdParty/WinPixEventRuntime/LICENSE.txt create mode 100644 Source/ThirdParty/WinPixEventRuntime/WinPixEventRuntime.Build.cs diff --git a/Source/ThirdParty/WinPixEventRuntime/LICENSE.txt b/Source/ThirdParty/WinPixEventRuntime/LICENSE.txt new file mode 100644 index 000000000..63447fd8b --- /dev/null +++ b/Source/ThirdParty/WinPixEventRuntime/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) Microsoft Corporation. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Source/ThirdParty/WinPixEventRuntime/WinPixEventRuntime.Build.cs b/Source/ThirdParty/WinPixEventRuntime/WinPixEventRuntime.Build.cs new file mode 100644 index 000000000..3b86a55b6 --- /dev/null +++ b/Source/ThirdParty/WinPixEventRuntime/WinPixEventRuntime.Build.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +using Flax.Build; + +/// +/// https://github.com/microsoft/PixEvents +/// +public class WinPixEventRuntime : HeaderOnlyModule +{ + /// + public override void Init() + { + base.Init(); + + LicenseType = LicenseTypes.MIT; + LicenseFilePath = "LICENSE.txt"; + + // Merge third-party modules into engine binary + BinaryModuleName = "FlaxEngine"; + } +} From 31e870b086ced493214dc4d4b77a3ce701747144 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 13:13:25 +0100 Subject: [PATCH 76/84] Add C++ version and compiler path into VS Code project files #3040 --- Source/Tools/Flax.Build/Build/Toolchain.cs | 5 ++++ .../Platforms/Unix/UnixToolchain.cs | 3 +++ .../Platforms/Windows/WindowsToolchainBase.cs | 3 +++ .../VisualStudioCodeProjectGenerator.cs | 26 +++++++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/Source/Tools/Flax.Build/Build/Toolchain.cs b/Source/Tools/Flax.Build/Build/Toolchain.cs index 6d90dc850..07bd01a88 100644 --- a/Source/Tools/Flax.Build/Build/Toolchain.cs +++ b/Source/Tools/Flax.Build/Build/Toolchain.cs @@ -73,6 +73,11 @@ namespace Flax.Build /// public abstract TargetCompiler Compiler { get; } + /// + /// Gets the main native files compiler path. + /// + public virtual string NativeCompilerPath { get; } + /// /// Initializes a new instance of the class. /// diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index b03dcbb90..88f3abf44 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -267,6 +267,9 @@ namespace Flax.Build.Platforms /// public override TargetCompiler Compiler => TargetCompiler.Clang; + /// + public override string NativeCompilerPath => ClangPath; + /// public override void LogInfo() { diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index 91c3b44cd..d51a59836 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -358,6 +358,9 @@ namespace Flax.Build.Platforms /// public override TargetCompiler Compiler => TargetCompiler.MSVC; + /// + public override string NativeCompilerPath => _compilerPath; + /// public override void LogInfo() { diff --git a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs index 27092b219..268bb87e1 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs @@ -542,6 +542,8 @@ namespace Flax.Build.Projects.VisualStudioCode var configuration = TargetConfiguration.Development; var architecture = TargetArchitecture.x64; + var compilerPath = string.Empty; + var cppVersion = NativeCpp.CppVersion.Cpp14; var includePaths = new HashSet(); var preprocessorDefinitions = new HashSet(); foreach (var e in mainProject.Defines) @@ -563,6 +565,8 @@ namespace Flax.Build.Projects.VisualStudioCode module.Key.SetupEnvironment(targetBuildOptions); } + cppVersion = targetBuildOptions.CompileEnv.CppVersion; + compilerPath = toolchain.NativeCompilerPath; foreach (var e in targetBuildOptions.CompileEnv.PreprocessorDefinitions) preprocessorDefinitions.Add(e); foreach (var e in targetBuildOptions.CompileEnv.IncludePaths) @@ -570,6 +574,28 @@ namespace Flax.Build.Projects.VisualStudioCode } } + if (compilerPath.Length != 0) + json.AddField("compilerPath", compilerPath); + + switch (cppVersion) + { + case NativeCpp.CppVersion.Cpp14: + json.AddField("cStandard", "c11"); + json.AddField("cppStandard", "c++14"); + break; + case NativeCpp.CppVersion.Cpp17: + case NativeCpp.CppVersion.Latest: + json.AddField("cStandard", "c17"); + json.AddField("cppStandard", "c++17"); + break; + case NativeCpp.CppVersion.Cpp20: + json.AddField("cStandard", "c17"); + json.AddField("cppStandard", "c++20"); + break; + default: + throw new Exception($"Visual Code project generator does not support C++ standard {cppVersion}."); + } + json.BeginArray("includePath"); { foreach (var path in includePaths) From 1088a71e695bdd53fa96fc8b5a2f7e290d764887 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 13:45:37 +0100 Subject: [PATCH 77/84] Another iteration on cooking job --- .github/workflows/cooking.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/cooking.yml b/.github/workflows/cooking.yml index 74b422283..a2c4550cb 100644 --- a/.github/workflows/cooking.yml +++ b/.github/workflows/cooking.yml @@ -43,9 +43,6 @@ jobs: - name: Build Editor run: | ./Development/Scripts/Mac/CallBuildTool.sh -build -log -printSDKs -dotnet=8 -arch=ARM64 -platform=Mac -configuration=Development -buildtargets=FlaxEditor - - name: Cook Game (Mac) - run: | - ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.Mac" - name: Cook Game (iOS) run: | ./Binaries/Editor/Mac/Development/FlaxEditor -std -headless -mute -null -project "FlaxSamples/MaterialsFeaturesTour" -build "Development.iOS" From b6d2a3683c3bf30736777719b1f66a3529cb4f4f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 16:10:50 +0100 Subject: [PATCH 78/84] Add `ButtonAttribute` to display methods in editor properties panel #1917 --- .../Editor/Content/Items/VisualScriptItem.cs | 14 +++++++ Source/Editor/CustomEditors/CustomEditor.cs | 29 +++++++++++++ .../Editor/Scripting/ScriptType.Interfaces.cs | 8 ++++ Source/Editor/Scripting/ScriptType.cs | 17 ++++++++ Source/Editor/Surface/NodeFactory.cs | 1 + Source/Engine/Content/Assets/VisualScript.cpp | 8 ++++ Source/Engine/Content/Assets/VisualScript.h | 3 ++ .../Attributes/Editor/ButtonAttribute.cs | 42 +++++++++++++++++++ 8 files changed, 122 insertions(+) create mode 100644 Source/Engine/Scripting/Attributes/Editor/ButtonAttribute.cs diff --git a/Source/Editor/Content/Items/VisualScriptItem.cs b/Source/Editor/Content/Items/VisualScriptItem.cs index 54133e6e2..c3953513f 100644 --- a/Source/Editor/Content/Items/VisualScriptItem.cs +++ b/Source/Editor/Content/Items/VisualScriptItem.cs @@ -112,6 +112,12 @@ namespace FlaxEditor.Content throw new TargetException("Missing Visual Script asset."); _type.Asset.SetScriptInstanceParameterValue(_parameter.Name, (Object)obj, value); } + + /// + public object Invoke(object obj, object[] parameters) + { + throw new NotSupportedException(); + } } sealed class VisualScriptMethodInfo : IScriptMemberInfo @@ -240,6 +246,14 @@ namespace FlaxEditor.Content { throw new NotSupportedException(); } + + /// + public object Invoke(object obj, object[] parameters) + { + if (!_type.Asset) + throw new TargetException("Missing Visual Script asset."); + return _type.Asset.InvokeMethod(_index, obj, parameters); + } } /// diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index 0d2be75cc..d5f6c5667 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -127,12 +127,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(); + 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) diff --git a/Source/Editor/Scripting/ScriptType.Interfaces.cs b/Source/Editor/Scripting/ScriptType.Interfaces.cs index 01a4755ec..34294fd60 100644 --- a/Source/Editor/Scripting/ScriptType.Interfaces.cs +++ b/Source/Editor/Scripting/ScriptType.Interfaces.cs @@ -293,6 +293,14 @@ namespace FlaxEditor.Scripting /// The object whose member value will be modified. /// The new member value. void SetValue(object obj, object value); + + /// + /// Invokes the method on a specific object (null if static) using the provided parameters. + /// + /// The instance of the object to invoke its method. Use null for static methods. + /// List of parameters to provide. + /// The value returned by the method. + object Invoke(object obj, object[] parameters); } /// diff --git a/Source/Editor/Scripting/ScriptType.cs b/Source/Editor/Scripting/ScriptType.cs index ec3775b95..66c7a9b06 100644 --- a/Source/Editor/Scripting/ScriptType.cs +++ b/Source/Editor/Scripting/ScriptType.cs @@ -691,6 +691,23 @@ namespace FlaxEditor.Scripting else _custom.SetValue(obj, value); } + + /// + /// Invokes the method on a specific object (null if static) using the provided parameters. + /// + /// The instance of the object to invoke its method. Use null for static methods. + /// List of parameters to provide. + /// The value returned by the method. + public object Invoke(object obj = null, object[] parameters = null) + { + if (parameters == null) + parameters = Array.Empty(); + if (_managed is MethodInfo methodInfo) + return methodInfo.Invoke(obj, parameters); + if (_managed != null) + throw new NotSupportedException(); + return _custom.Invoke(obj, parameters); + } } /// diff --git a/Source/Editor/Surface/NodeFactory.cs b/Source/Editor/Surface/NodeFactory.cs index ee4553f2f..484b5d89b 100644 --- a/Source/Editor/Surface/NodeFactory.cs +++ b/Source/Editor/Surface/NodeFactory.cs @@ -39,6 +39,7 @@ namespace FlaxEditor.Surface typeof(TooltipAttribute), typeof(HideInEditorAttribute), typeof(NoAnimateAttribute), + typeof(ButtonAttribute), }; /// diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp index 4fd1718ab..b410d62f7 100644 --- a/Source/Engine/Content/Assets/VisualScript.cpp +++ b/Source/Engine/Content/Assets/VisualScript.cpp @@ -2263,6 +2263,14 @@ void VisualScript::GetMethodSignature(int32 index, String& name, byte& flags, St } } +Variant VisualScript::InvokeMethod(int32 index, const Variant& instance, Span parameters) const +{ + auto& method = _methods[index]; + Variant result; + VisualScriptingModule.InvokeMethod((void*)&method, instance, parameters, result); + return result; +} + Span VisualScript::GetMetaData(int32 typeID) { auto meta = Graph.Meta.GetEntry(typeID); diff --git a/Source/Engine/Content/Assets/VisualScript.h b/Source/Engine/Content/Assets/VisualScript.h index 1cd8e9749..c07e3883a 100644 --- a/Source/Engine/Content/Assets/VisualScript.h +++ b/Source/Engine/Content/Assets/VisualScript.h @@ -267,6 +267,9 @@ public: // Gets the signature data of the method. API_FUNCTION() void GetMethodSignature(int32 index, API_PARAM(Out) String& name, API_PARAM(Out) byte& flags, API_PARAM(Out) String& returnTypeName, API_PARAM(Out) Array& paramNames, API_PARAM(Out) Array& paramTypeNames, API_PARAM(Out) Array& paramOuts); + // Invokes the method. + API_FUNCTION() Variant InvokeMethod(int32 index, const Variant& instance, Span parameters) const; + // Gets the metadata of the script surface. API_FUNCTION() Span GetMetaData(int32 typeID); diff --git a/Source/Engine/Scripting/Attributes/Editor/ButtonAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/ButtonAttribute.cs new file mode 100644 index 000000000..75ee9f5f0 --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/ButtonAttribute.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// Displays the method in the properties panel where user can click and invoke this method. + /// + /// Supported on both static and member methods that are parameterless. + [AttributeUsage(AttributeTargets.Method)] + public sealed class ButtonAttribute : Attribute + { + /// + /// The button text. Empty value will use method name (auto-formatted). + /// + public string Text; + + /// + /// The button tooltip text. Empty value will use method documentation. + /// + public string Tooltip; + + /// + /// Initializes a new instance of the class. + /// + public ButtonAttribute() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The button text. + /// The button tooltip. + public ButtonAttribute(string text, string tooltip = null) + { + Text = text; + Tooltip = tooltip; + } + } +} From ce23c2efafb46156a45067fcb8af5b0875dd0fea Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 17:12:10 +0100 Subject: [PATCH 79/84] Add hiding unrelated properties in Material properties for Decal, PostFX or GUI materials #2919 --- .../Editor/Windows/Assets/MaterialWindow.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Source/Editor/Windows/Assets/MaterialWindow.cs b/Source/Editor/Windows/Assets/MaterialWindow.cs index ad72c92d9..794ad699a 100644 --- a/Source/Editor/Windows/Assets/MaterialWindow.cs +++ b/Source/Editor/Windows/Assets/MaterialWindow.cs @@ -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; /// /// Gathers parameters from the specified material. From 0cf31395b5d52c2f8ecb31320ab56eff615196d3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 6 Dec 2024 17:38:16 +0100 Subject: [PATCH 80/84] Add support for `Quaternion` in GPU particles graph #2901 --- Source/Engine/Core/Types/Variant.cpp | 1 + .../Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp | 1 + Source/Engine/Visject/ShaderGraph.cpp | 6 +++++- Source/Engine/Visject/ShaderGraphValue.cpp | 11 +---------- Source/Engine/Visject/VisjectGraph.cpp | 1 + 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index 95e815b81..49c72bfad 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -1824,6 +1824,7 @@ Variant::operator Float4() const return Float4(*(Float3*)AsData, 0.0f); case VariantType::Float4: case VariantType::Color: + case VariantType::Quaternion: return *(Float4*)AsData; case VariantType::Double2: return Float4(AsDouble2(), 0.0f, 0.0f); diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp index 5a6b28be9..83d9eee68 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.cpp @@ -456,6 +456,7 @@ void ParticleEmitterGPUGenerator::PrepareGraph(ParticleEmitterGraphGPU* graph) mp.AsFloat3 = param->Value.AsFloat3(); break; case VariantType::Float4: + case VariantType::Quaternion: mp.Type = MaterialParameterType::Vector4; *(Float4*)&mp.AsData = param->Value.AsFloat4(); break; diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index 718e9cba9..9bdc5709d 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -92,9 +92,13 @@ void ShaderGenerator::ProcessGroupConstants(Box* box, Node* node, Value& value) value = Value(cv.W); break; } + // Rotation case 8: { - value = Value::Zero; + const float pitch = (float)node->Values[0]; + const float yaw = (float)node->Values[1]; + const float roll = (float)node->Values[2]; + value = Value(Quaternion::Euler(pitch, yaw, roll)); break; } // PI diff --git a/Source/Engine/Visject/ShaderGraphValue.cpp b/Source/Engine/Visject/ShaderGraphValue.cpp index 2aeebbd2b..cdff3d1e6 100644 --- a/Source/Engine/Visject/ShaderGraphValue.cpp +++ b/Source/Engine/Visject/ShaderGraphValue.cpp @@ -460,6 +460,7 @@ ShaderGraphValue ShaderGraphValue::Cast(const ShaderGraphValue& v, VariantType:: case VariantType::Types::Float4: case VariantType::Types::Double4: case VariantType::Types::Color: + case VariantType::Types::Quaternion: switch (v.Type) { case VariantType::Types::Bool: @@ -485,16 +486,6 @@ ShaderGraphValue ShaderGraphValue::Cast(const ShaderGraphValue& v, VariantType:: break; } break; - case VariantType::Types::Quaternion: - switch (v.Type) - { - case VariantType::Types::Color: - case VariantType::Types::Float4: - case VariantType::Types::Double4: - format = TEXT("{}"); - break; - } - break; } if (format == nullptr) { diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index 199747b67..7e02f0aa5 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -79,6 +79,7 @@ void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value) value = cv.W; break; } + // Rotation case 8: { const float pitch = (float)node->Values[0]; From 3e344e789ae83665b5a567841cc2bcf0cdbb53b1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 9 Dec 2024 18:26:53 +0100 Subject: [PATCH 81/84] Optimize terrain debug shape drawing by caching lines into a vertex buffer #2841 --- Source/Engine/Debug/DebugDraw.cpp | 70 +++++++++++++++++--- Source/Engine/Debug/DebugDraw.h | 24 ++++++- Source/Engine/Graphics/GPUBuffer.cpp | 11 +++ Source/Engine/Graphics/GPUBuffer.h | 3 +- Source/Engine/Level/Scene/SceneRendering.cpp | 2 + Source/Engine/Terrain/TerrainPatch.cpp | 58 ++++++++++------ Source/Engine/Terrain/TerrainPatch.h | 7 +- 7 files changed, 141 insertions(+), 34 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 077bf9b80..447e6cabc 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -94,6 +94,13 @@ struct DebugLine float TimeLeft; }; +struct DebugGeometryBuffer +{ + GPUBuffer* Buffer; + float TimeLeft; + Matrix Transform; +}; + struct DebugTriangle { Float3 V0; @@ -122,12 +129,9 @@ struct DebugText3D float TimeLeft; }; -PACK_STRUCT(struct Vertex { - Float3 Position; - Color32 Color; - }); +typedef DebugDraw::Vertex Vertex; -GPU_CB_STRUCT(Data { +GPU_CB_STRUCT(ShaderData { Matrix ViewProjection; Float2 Padding; float ClipPosZBias; @@ -231,6 +235,7 @@ void TeleportList(const Float3& delta, Array& list) struct DebugDrawData { + Array GeometryBuffers; Array DefaultLines; Array OneFrameLines; Array DefaultTriangles; @@ -244,7 +249,7 @@ struct DebugDrawData inline int32 Count() const { - return LinesCount() + TrianglesCount() + TextCount(); + return LinesCount() + TrianglesCount() + TextCount() + GeometryBuffers.Count(); } inline int32 LinesCount() const @@ -280,6 +285,7 @@ struct DebugDrawData inline void Update(float deltaTime) { + UpdateList(deltaTime, GeometryBuffers); UpdateList(deltaTime, DefaultLines); UpdateList(deltaTime, DefaultTriangles); UpdateList(deltaTime, DefaultWireTriangles); @@ -784,7 +790,7 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe // Update constant buffer const auto cb = DebugDrawShader->GetShader()->GetCB(0); - Data data; + ShaderData data; Matrix vp; Matrix::Multiply(view.View, view.Projection, vp); Matrix::Transpose(vp, data.ViewProjection); @@ -830,6 +836,22 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe context->Draw(depthTestTriangles.StartVertex, depthTestTriangles.VertexCount); } + // Geometries + for (auto& geometry : Context->DebugDrawDepthTest.GeometryBuffers) + { + auto tmp = data; + Matrix mvp; + Matrix::Multiply(geometry.Transform, vp, mvp); + Matrix::Transpose(mvp, tmp.ViewProjection); + context->UpdateCB(cb, &tmp); + auto state = data.EnableDepthTest ? &DebugDrawPsLinesDepthTest : &DebugDrawPsLinesDefault; + context->SetState(state->Get(enableDepthWrite, true)); + context->BindVB(ToSpan(&geometry.Buffer, 1)); + context->Draw(0, geometry.Buffer->GetElementsCount()); + } + if (Context->DebugDrawDepthTest.GeometryBuffers.HasItems()) + context->UpdateCB(cb, &data); + if (data.EnableDepthTest) context->UnBindSR(0); } @@ -862,6 +884,19 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe context->BindVB(ToSpan(&vb, 1)); context->Draw(defaultTriangles.StartVertex, defaultTriangles.VertexCount); } + + // Geometries + for (auto& geometry : Context->DebugDrawDefault.GeometryBuffers) + { + auto tmp = data; + Matrix mvp; + Matrix::Multiply(geometry.Transform, vp, mvp); + Matrix::Transpose(mvp, tmp.ViewProjection); + context->UpdateCB(cb, &tmp); + context->SetState(DebugDrawPsLinesDefault.Get(false, false)); + context->BindVB(ToSpan(&geometry.Buffer, 1)); + context->Draw(0, geometry.Buffer->GetElementsCount()); + } } // Text @@ -1088,6 +1123,24 @@ void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, co } } +void DebugDraw::DrawLines(GPUBuffer* lines, const Matrix& transform, float duration, bool depthTest) +{ + if (lines == nullptr || lines->GetSize() == 0) + return; + if (lines->GetSize() % (sizeof(Vertex) * 2) != 0) + { + DebugLog::ThrowException("Cannot draw debug lines with uneven amount of items in array"); + return; + } + + // Draw lines + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + auto& geometry = debugDrawData.GeometryBuffers.AddOne(); + geometry.Buffer = lines; + geometry.TimeLeft = duration; + geometry.Transform = transform * Matrix::Translation(-Context->Origin); +} + void DebugDraw::DrawLines(const Array& lines, const Matrix& transform, const Color& color, float duration, bool depthTest) { DrawLines(Span(lines.Get(), lines.Count()), transform, color, duration, depthTest); @@ -2147,6 +2200,7 @@ void DebugDraw::DrawText(const StringView& text, const Transform& transform, con void DebugDraw::Clear(void* context) { - DebugDraw::UpdateContext(context, MAX_float); + UpdateContext(context, MAX_float); } + #endif diff --git a/Source/Engine/Debug/DebugDraw.h b/Source/Engine/Debug/DebugDraw.h index bb5cf2e50..6cfceca6d 100644 --- a/Source/Engine/Debug/DebugDraw.h +++ b/Source/Engine/Debug/DebugDraw.h @@ -6,6 +6,8 @@ #include "Engine/Scripting/ScriptingType.h" #include "Engine/Core/Math/Color.h" +#include "Engine/Core/Math/Color32.h" +#include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Types/Span.h" struct RenderView; @@ -14,6 +16,7 @@ class Light; struct RenderContext; class GPUTextureView; class GPUContext; +class GPUBuffer; class RenderTask; class SceneRenderTask; class Actor; @@ -26,6 +29,14 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw { DECLARE_SCRIPTING_TYPE_NO_SPAWN(DebugDraw); + /// + /// Vertex data for debug shapes. + /// + PACK_STRUCT(struct Vertex { + Float3 Position; + Color32 Color; + }); + #if USE_EDITOR /// /// Allocates the context for Debug Drawing. Can be use to redirect debug shapes collecting to a separate container (instead of global state). @@ -175,6 +186,15 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw /// If set to true depth test will be performed, otherwise depth will be ignored. API_FUNCTION() static void DrawLines(const Span& lines, const Matrix& transform, const Color& color = Color::White, float duration = 0.0f, bool depthTest = true); + /// + /// Draws the lines using the provided vertex buffer that contains pairs of Vertex elements. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). + /// + /// The GPU buffer with vertices for lines (must have multiple of 2 elements). + /// The custom matrix used to transform all line vertices. + /// The duration (in seconds). Use 0 to draw it only once. + /// If set to true depth test will be performed, otherwise depth will be ignored. + API_FUNCTION() static void DrawLines(GPUBuffer* lines, const Matrix& transform, float duration = 0.0f, bool depthTest = true); + /// /// Draws the lines. Line positions are located one after another (e.g. l0.start, l0.end, l1.start, l1.end,...). /// @@ -691,9 +711,9 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw API_FUNCTION() static void DrawText(const StringView& text, const Transform& transform, const Color& color = Color::White, int32 size = 32, float duration = 0.0f); /// - /// Clear all debug draw displayed on sceen. + /// Clears all debug shapes displayed on screen. /// - /// + /// The context. API_FUNCTION() static void Clear(void* context = nullptr); }; diff --git a/Source/Engine/Graphics/GPUBuffer.cpp b/Source/Engine/Graphics/GPUBuffer.cpp index 32681b4ce..6a0221b14 100644 --- a/Source/Engine/Graphics/GPUBuffer.cpp +++ b/Source/Engine/Graphics/GPUBuffer.cpp @@ -5,6 +5,7 @@ #include "GPUResourceProperty.h" #include "GPUBufferDescription.h" #include "PixelFormatExtensions.h" +#include "RenderTask.h" #include "Async/Tasks/GPUCopyResourceTask.h" #include "Engine/Core/Utilities.h" #include "Engine/Core/Types/String.h" @@ -358,6 +359,16 @@ void GPUBuffer::SetData(const void* data, uint32 size) Log::ArgumentOutOfRangeException(TEXT("Buffer.SetData")); return; } + + if (_desc.Usage == GPUResourceUsage::Default && GPUDevice::Instance->IsRendering()) + { + // Upload using the context (will use internal staging buffer inside command buffer) + RenderContext::GPULocker.Lock(); + GPUDevice::Instance->GetMainContext()->UpdateBuffer(this, data, size); + RenderContext::GPULocker.Unlock(); + return; + } + void* mapped = Map(GPUResourceMapMode::Write); if (!mapped) return; diff --git a/Source/Engine/Graphics/GPUBuffer.h b/Source/Engine/Graphics/GPUBuffer.h index cbdc39c4f..d40b7744d 100644 --- a/Source/Engine/Graphics/GPUBuffer.h +++ b/Source/Engine/Graphics/GPUBuffer.h @@ -74,8 +74,7 @@ public: /// API_PROPERTY() FORCE_INLINE uint32 GetElementsCount() const { - ASSERT(_desc.Stride > 0); - return _desc.Size / _desc.Stride; + return _desc.Stride > 0 ? _desc.Size / _desc.Stride : 0; } /// diff --git a/Source/Engine/Level/Scene/SceneRendering.cpp b/Source/Engine/Level/Scene/SceneRendering.cpp index 3dbbd113f..4e2a84661 100644 --- a/Source/Engine/Level/Scene/SceneRendering.cpp +++ b/Source/Engine/Level/Scene/SceneRendering.cpp @@ -90,6 +90,7 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c // Draw physics shapes if (EnumHasAnyFlags(view.Flags, ViewFlags::PhysicsDebug) || view.Mode == ViewMode::PhysicsColliders) { + PROFILE_CPU_NAMED("PhysicsDebug"); const PhysicsDebugCallback* physicsDebugData = PhysicsDebug.Get(); for (int32 i = 0; i < PhysicsDebug.Count(); i++) { @@ -100,6 +101,7 @@ void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory c // Draw light shapes if (EnumHasAnyFlags(view.Flags, ViewFlags::LightsDebug)) { + PROFILE_CPU_NAMED("LightsDebug"); const LightsDebugCallback* lightsDebugData = LightsDebug.Get(); for (int32 i = 0; i < LightsDebug.Count(); i++) { diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 96dc35d49..5ff02cf36 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -33,6 +33,11 @@ #if USE_EDITOR #include "Engine/Debug/DebugDraw.h" #endif +#if TERRAIN_USE_PHYSICS_DEBUG +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Graphics/DynamicBuffer.h" +#include "Engine/Engine/Units.h" +#endif #include "Engine/Content/Content.h" #include "Engine/Content/Assets/RawDataAsset.h" @@ -94,7 +99,8 @@ void TerrainPatch::Init(Terrain* terrain, int16 x, int16 z) } #endif #if TERRAIN_USE_PHYSICS_DEBUG - _debugLines.Resize(0); + SAFE_DELETE(_debugLines); + _debugLinesDirty = true; #endif #if USE_EDITOR _collisionTriangles.Resize(0); @@ -1822,7 +1828,7 @@ bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& mod // Invalidate cache #if TERRAIN_USE_PHYSICS_DEBUG - _debugLines.Resize(0); + _debugLinesDirty = true; #endif #if USE_EDITOR _collisionTriangles.Resize(0); @@ -1940,7 +1946,7 @@ bool TerrainPatch::UpdateCollision() { // Invalidate cache #if TERRAIN_USE_PHYSICS_DEBUG - _debugLines.Resize(0); + _debugLinesDirty = true; #endif #if USE_EDITOR _collisionTriangles.Resize(0); @@ -2082,7 +2088,7 @@ void TerrainPatch::UpdatePostManualDeserialization() { // Invalidate cache #if TERRAIN_USE_PHYSICS_DEBUG - _debugLines.Resize(0); + _debugLinesDirty = true; #endif #if USE_EDITOR _collisionTriangles.Resize(0); @@ -2211,7 +2217,8 @@ void TerrainPatch::DestroyCollision() _physicsShape = nullptr; _physicsHeightField = nullptr; #if TERRAIN_USE_PHYSICS_DEBUG - _debugLines.Resize(0); + _debugLinesDirty = true; + SAFE_DELETE(_debugLines); #endif #if USE_EDITOR _collisionTriangles.Resize(0); @@ -2224,15 +2231,26 @@ void TerrainPatch::DestroyCollision() void TerrainPatch::CacheDebugLines() { PROFILE_CPU(); - ASSERT(_debugLines.IsEmpty() && _physicsHeightField); + ASSERT(_physicsHeightField); + _debugLinesDirty = false; + if (!_debugLines) + _debugLines = GPUDevice::Instance->CreateBuffer(TEXT("Terrain.DebugLines")); int32 rows, cols; PhysicsBackend::GetHeightFieldSize(_physicsHeightField, rows, cols); + const int32 count = (rows - 1) * (cols - 1) * 6 + (cols + rows - 2) * 2; + typedef DebugDraw::Vertex Vertex; + if (_debugLines->GetElementsCount() != count) + { + if (_debugLines->Init(GPUBufferDescription::Vertex(sizeof(Vertex), count))) + return; + } + Array debugLines; + debugLines.Resize(count); + auto* data = debugLines.Get(); + const Color32 color(Color::GreenYellow * 0.8f); - _debugLines.Resize((rows - 1) * (cols - 1) * 6 + (cols + rows - 2) * 2); - Vector3* data = _debugLines.Get(); - -#define GET_VERTEX(x, y) const Vector3 v##x##y((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))) +#define GET_VERTEX(x, y) const Vertex v##x##y = { Float3((float)(row + (x)), PhysicsBackend::GetHeightFieldHeight(_physicsHeightField, row + (x), col + (y)) / TERRAIN_PATCH_COLLISION_QUANTIZATION, (float)(col + (y))), color } for (int32 row = 0; row < rows - 1; row++) { @@ -2243,7 +2261,7 @@ void TerrainPatch::CacheDebugLines() if (sample.MaterialIndex0 == (uint8)PhysicsBackend::HeightFieldMaterial::Hole) { for (int32 i = 0; i < 6; i++) - *data++ = Vector3::Zero; + *data++ = Vertex { Float3::Zero, Color32::Black }; continue; } @@ -2284,18 +2302,16 @@ void TerrainPatch::CacheDebugLines() } #undef GET_VERTEX + + _debugLines->SetData(debugLines.Get(), _debugLines->GetSize()); } void TerrainPatch::DrawPhysicsDebug(RenderView& view) { +#if COMPILE_WITH_DEBUG_DRAW const BoundingBox bounds(_bounds.Minimum - view.Origin, _bounds.Maximum - view.Origin); if (!_physicsShape || !view.CullingFrustum.Intersects(bounds)) return; - - const Transform terrainTransform = _terrain->_transform; - const Transform localTransform(Vector3(0, _yOffset, 0), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ)); - const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld(); - if (view.Mode == ViewMode::PhysicsColliders) { DEBUG_DRAW_TRIANGLES(GetCollisionTriangles(), Color::DarkOliveGreen, 0, true); @@ -2304,13 +2320,17 @@ void TerrainPatch::DrawPhysicsDebug(RenderView& view) { BoundingSphere sphere; BoundingSphere::FromBox(bounds, sphere); - if (Vector3::Distance(sphere.Center, view.Position) - sphere.Radius < 4000.0f) + if (Vector3::Distance(sphere.Center, view.Position) - sphere.Radius < METERS_TO_UNITS(500)) { - if (_debugLines.IsEmpty()) + if (!_debugLines || _debugLinesDirty) CacheDebugLines(); - DEBUG_DRAW_LINES(_debugLines, world, Color::GreenYellow * 0.8f, 0, true); + const Transform terrainTransform = _terrain->_transform; + const Transform localTransform(Vector3(0, _yOffset, 0), Quaternion::Identity, Vector3(_collisionScaleXZ, _yHeight, _collisionScaleXZ)); + const Matrix world = localTransform.GetWorld() * terrainTransform.GetWorld(); + DebugDraw::DrawLines(_debugLines, world); } } +#endif } #endif diff --git a/Source/Engine/Terrain/TerrainPatch.h b/Source/Engine/Terrain/TerrainPatch.h index 05a7693fe..5eedf6e6d 100644 --- a/Source/Engine/Terrain/TerrainPatch.h +++ b/Source/Engine/Terrain/TerrainPatch.h @@ -40,12 +40,13 @@ private: Array _cachedSplatMap[TERRAIN_MAX_SPLATMAPS_COUNT]; bool _wasHeightModified; bool _wasSplatmapModified[TERRAIN_MAX_SPLATMAPS_COUNT]; +#if TERRAIN_USE_PHYSICS_DEBUG + bool _debugLinesDirty = true; + class GPUBuffer* _debugLines = nullptr; +#endif TextureBase::InitData* _dataHeightmap = nullptr; TextureBase::InitData* _dataSplatmap[TERRAIN_MAX_SPLATMAPS_COUNT] = {}; #endif -#if TERRAIN_USE_PHYSICS_DEBUG - Array _debugLines; // TODO: large-worlds -#endif #if USE_EDITOR Array _collisionTriangles; // TODO: large-worlds #endif From eb5dfcd6bf9f6254c90383aa6bd0ab96b059ef06 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 9 Dec 2024 18:47:32 +0100 Subject: [PATCH 82/84] Add support for multi-control editing #1897 --- .../Dedicated/UIControlEditor.cs | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index eb20560ba..c3330330d 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -407,20 +407,13 @@ namespace FlaxEditor.CustomEditors.Dedicated /// public class UIControlControlEditor : GenericEditor { - private Type _cachedType; + private ScriptType[] _valueTypes; private bool _anchorDropDownClosed = true; private Button _pivotRelativeButton; /// 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 /// 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(); From 3e5cb093818bb5cee9e490f39a676d5b851c98db Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 9 Dec 2024 22:48:02 +0100 Subject: [PATCH 83/84] Fix vertical axis and blend points center in Multi Blend space editor #1980 --- Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index f76faa794..10a802291 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -424,7 +424,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; @@ -598,7 +598,7 @@ namespace FlaxEditor.Surface.Archetypes { float alpha = (float)i / splits; float y = blendArea.Top + blendArea.Height * alpha; - float value = Mathf.Lerp(rangeY.X, rangeY.Y, 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); } } From 09414f90020eea4b829e199bb43b00ab434982ee Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 10 Dec 2024 10:42:40 +0100 Subject: [PATCH 84/84] Add blend space drawing in Multi Blend 2D editor #1980 --- .../Archetypes/Animation.MultiBlend.cs | 326 ++++++++++++++---- Source/Engine/Render2D/Render2D.cpp | 84 ++++- Source/Engine/Render2D/Render2D.h | 25 +- Source/Engine/Utilities/Delaunay2D.h | 54 ++- 4 files changed, 395 insertions(+), 94 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs index 10a802291..b0d7169cc 100644 --- a/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs +++ b/Source/Editor/Surface/Archetypes/Animation.MultiBlend.cs @@ -30,7 +30,7 @@ namespace FlaxEditor.Surface.Archetypes /// Represents single blend point. /// /// - protected class BlendPoint : Control + internal class BlendPoint : Control { private readonly BlendPointsEditor _editor; private readonly int _index; @@ -48,6 +48,11 @@ namespace FlaxEditor.Surface.Archetypes /// public int Index => _index; + /// + /// Flag that indicates that user is moving this point with a mouse. + /// + public bool IsMouseDown => _isMouseDown; + /// /// Initializes a new instance of the class. /// @@ -211,6 +216,11 @@ namespace FlaxEditor.Surface.Archetypes /// public int PointsCount => (_node.Values.Length - 4) / 2; // 4 node values + 2 per blend point + /// + /// BLend points array. + /// + internal IReadOnlyList BlendPoints => _blendPoints; + /// /// Initializes a new instance of the class. /// @@ -374,6 +384,12 @@ namespace FlaxEditor.Surface.Archetypes /// The blend point control position. 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; } /// @@ -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); - } - /// 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, 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); - } + _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); + } + + /// + /// Custom drawing logic for blend space background. + /// + 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); + } + + /// + /// Custom drawing logic for blend space grid. + /// + 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); + } + } + /// /// Updates the editor UI. /// @@ -972,6 +1006,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; /// public MultiBlend2D(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) @@ -1040,6 +1078,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(); + 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(); + var selectedColors = new List(); + 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(); + } + } + + /// + 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); + } + + /// + 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(); + } + } + /// public override void UpdateUI(int selectedIndex, bool isValid, ref Float4 data0, ref Guid data1) { @@ -1064,6 +1239,23 @@ namespace FlaxEditor.Surface.Archetypes _animationX.Enabled = isValid; _animationYLabel.Enabled = isValid; _animationY.Enabled = isValid; + ClearTriangles(); + } + + /// + public override void OnValuesChanged() + { + base.OnValuesChanged(); + + ClearTriangles(); + } + + /// + public override void OnLoaded(SurfaceNodeActions action) + { + base.OnLoaded(action); + + ClearTriangles(); } } } diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index 6f7c7043e..942cb9e0b 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -301,12 +301,12 @@ void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2 IBIndex += 3; } -void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color0, const Color& color1, const Color& color2) +FORCE_INLINE void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Color& color0, const Color& color1, const Color& color2) { WriteTri(p0, p1, p2, Float2::Zero, Float2::Zero, Float2::Zero, color0, color1, color2); } -void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2& uv0, const Float2& uv1, const Float2& uv2) +FORCE_INLINE void WriteTri(const Float2& p0, const Float2& p1, const Float2& p2, const Float2& uv0, const Float2& uv1, const Float2& uv2) { WriteTri(p0, p1, p2, uv0, uv1, uv2, Color::Black, Color::Black, Color::Black); } @@ -1816,8 +1816,8 @@ void DrawLines(const Float2* points, int32 pointsCount, const Color& color1, con // Ending cap { - ApplyTransform(points[0], p1t); - ApplyTransform(points[1], p2t); + ApplyTransform(points[pointsCount - 2], p1t); + //ApplyTransform(points[pointsCount - 1], p2t); const Float2 capDirection = thicknessHalf * Float2::Normalize(p2t - p1t); @@ -1962,9 +1962,56 @@ void Render2D::DrawBlur(const Rectangle& rect, float blurStrength) WriteRect(rect, Color::White); } +void Render2D::DrawTriangles(const Span& vertices, const Color& color, float thickness) +{ + RENDER2D_CHECK_RENDERING_STATE; + CHECK(vertices.Length() % 3 == 0); + + Float2 points[2]; + for (int32 i = 0; i < vertices.Length(); i += 3) + { +#if 0 + // TODO: fix this + DrawLines(&vertices.Get()[i], 3, color, color, thickness); +#else + points[0] = vertices.Get()[i + 0]; + points[1] = vertices.Get()[i + 1]; + DrawLines(points, 2, color, color, thickness); + points[0] = vertices.Get()[i + 2]; + DrawLines(points, 2, color, color, thickness); + points[1] = vertices.Get()[i + 0]; + DrawLines(points, 2, color, color, thickness); +#endif + } +} + +void Render2D::DrawTriangles(const Span& vertices, const Span& colors, float thickness) +{ + RENDER2D_CHECK_RENDERING_STATE; + CHECK(vertices.Length() % 3 == 0); + + Float2 points[2]; + Color cols[2]; + for (int32 i = 0; i < vertices.Length(); i += 3) + { + points[0] = vertices.Get()[i + 0]; + points[1] = vertices.Get()[i + 1]; + cols[0] = colors.Get()[i + 0]; + cols[1] = colors.Get()[i + 1]; + DrawLines(points, 2, cols[0], cols[1], thickness); + points[0] = vertices.Get()[i + 2]; + cols[0] = colors.Get()[i + 2]; + DrawLines(points, 2, cols[0], cols[1], thickness); + points[1] = vertices.Get()[i + 0]; + cols[1] = colors.Get()[i + 0]; + DrawLines(points, 2, cols[0], cols[1], thickness); + } +} + void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& vertices, const Span& uvs) { RENDER2D_CHECK_RENDERING_STATE; + CHECK(vertices.Length() % 3 == 0); CHECK(vertices.Length() == uvs.Length()); Render2DDrawCall& drawCall = DrawCalls.AddOne(); @@ -1979,14 +2026,24 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& vertices void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& vertices, const Span& uvs, const Color& color) { - Color colors[3] = { (Color)color, (Color)color, (Color)color }; - Span spancolor(colors, 3); - DrawTexturedTriangles(t, vertices, uvs, spancolor); + RENDER2D_CHECK_RENDERING_STATE; + CHECK(vertices.Length() % 3 == 0); + CHECK(vertices.Length() == uvs.Length()); + + Render2DDrawCall& drawCall = DrawCalls.AddOne(); + drawCall.Type = DrawCallType::FillTexture; + drawCall.StartIB = IBIndex; + drawCall.CountIB = vertices.Length(); + drawCall.AsTexture.Ptr = t; + + for (int32 i = 0; i < vertices.Length(); i += 3) + WriteTri(vertices[i], vertices[i + 1], vertices[i + 2], uvs[i], uvs[i + 1], uvs[i + 2], color, color, color); } void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& vertices, const Span& uvs, const Span& colors) { RENDER2D_CHECK_RENDERING_STATE; + CHECK(vertices.Length() % 3 == 0); CHECK(vertices.Length() == uvs.Length()); CHECK(vertices.Length() == colors.Length()); @@ -2021,6 +2078,19 @@ void Render2D::DrawTexturedTriangles(GPUTexture* t, const Span& indices, } } +void Render2D::FillTriangles(const Span& vertices, const Color& color) +{ + RENDER2D_CHECK_RENDERING_STATE; + + Render2DDrawCall& drawCall = DrawCalls.AddOne(); + drawCall.Type = NeedAlphaWithTint(color) ? DrawCallType::FillRect : DrawCallType::FillRectNoAlpha; + drawCall.StartIB = IBIndex; + drawCall.CountIB = vertices.Length(); + + for (int32 i = 0; i < vertices.Length(); i += 3) + WriteTri(vertices[i], vertices[i + 1], vertices[i + 2], color, color, color); +} + void Render2D::FillTriangles(const Span& vertices, const Span& colors, bool useAlpha) { CHECK(vertices.Length() == colors.Length()); diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 5905fa434..886c9e664 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -415,6 +415,22 @@ public: /// The blur strength defines how blurry the background is. Larger numbers increase blur, resulting in a larger runtime cost on the GPU. API_FUNCTION() static void DrawBlur(const Rectangle& rect, float blurStrength); + /// + /// Draws vertices array. + /// + /// The vertices array. + /// The color. + /// The line thickness. + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Color& color, float thickness = 1.0f); + + /// + /// Draws vertices array. + /// + /// The vertices array. + /// The colors array. + /// The line thickness. + API_FUNCTION() static void DrawTriangles(const Span& vertices, const Span& colors, float thickness = 1.0f); + /// /// Draws vertices array. /// @@ -451,13 +467,20 @@ public: /// The colors array. API_FUNCTION() static void DrawTexturedTriangles(GPUTexture* t, const Span& indices, const Span& vertices, const Span& uvs, const Span& colors); + /// + /// Draws vertices array. + /// + /// The vertices array. + /// The color. + API_FUNCTION() static void FillTriangles(const Span& vertices, const Color& color); + /// /// Draws vertices array. /// /// The vertices array. /// The colors array. /// If true alpha blending will be enabled. - API_FUNCTION() static void FillTriangles(const Span& vertices, const Span& colors, bool useAlpha); + API_FUNCTION() static void FillTriangles(const Span& vertices, const Span& colors, bool useAlpha = true); /// /// Fills a triangular area. diff --git a/Source/Engine/Utilities/Delaunay2D.h b/Source/Engine/Utilities/Delaunay2D.h index 766824bb9..3f1e25a7d 100644 --- a/Source/Engine/Utilities/Delaunay2D.h +++ b/Source/Engine/Utilities/Delaunay2D.h @@ -7,10 +7,11 @@ #include "Engine/Core/Collections/Array.h" /// -/// Helper class with Delaunay triangulation algorithm implementation, +/// Helper class with Delaunay triangulation algorithm implementation (2D space). /// -class Delaunay2D +API_CLASS(Internal, Static, Namespace="FlaxEngine.Utilities") class Delaunay2D { + DECLARE_SCRIPTING_TYPE_MINIMAL(Delaunay2D); public: struct Triangle { @@ -31,14 +32,35 @@ public: } }; - template - static void Triangulate(const TVertexArray& vertices, TTrianglesArray& triangles) + /// + /// Triangulates input vertices array into the list of triangle vertices. + /// + /// Input list of vertices. + /// Result list of triangles. Each triangle is made out of sequence of 3 vertices. Empty if no valid triangle built. + API_FUNCTION() static Array Triangulate(const Array& vertices) + { + Array triangles; + Triangulate(vertices, triangles); + Array result; + result.Resize(triangles.Count() * 3); + int32 c = 0; + for (const Triangle& t : triangles) + { + result.Get()[c++] = vertices[t.Indices[0]]; + result.Get()[c++] = vertices[t.Indices[1]]; + result.Get()[c++] = vertices[t.Indices[2]]; + } + return result; + } + + template + static void Triangulate(const Array& vertices, TrianglesArray& triangles) { // Skip if no change to produce any triangles - if (vertices.Count() < 3) + if (vertices.Count() < 3) return; - TVertexArray points = vertices; + Array points = vertices; Array polygon; Rectangle rect; @@ -142,8 +164,7 @@ private: } }; - template - static bool CircumCircleContains(const TVertexArray& vertices, const Triangle& triangle, int vertexIndex) + static bool CircumCircleContains(const Array& vertices, const Triangle& triangle, int32 vertexIndex) { Float2 p1 = vertices[triangle.Indices[0]]; Float2 p2 = vertices[triangle.Indices[1]]; @@ -157,25 +178,20 @@ private: (ab * (p3.Y - p2.Y) + cd * (p1.Y - p3.Y) + ef * (p2.Y - p1.Y)) / (p1.X * (p3.Y - p2.Y) + p2.X * (p1.Y - p3.Y) + p3.X * (p2.Y - p1.Y)), (ab * (p3.X - p2.X) + cd * (p1.X - p3.X) + ef * (p2.X - p1.X)) / (p1.Y * (p3.X - p2.X) + p2.Y * (p1.X - p3.X) + p3.Y * (p2.X - p1.X))); - circum *= 0.5; + circum *= 0.5f; float r = Float2::DistanceSquared(p1, circum); float d = Float2::DistanceSquared(vertices[vertexIndex], circum); return d <= r; } - template - static bool EdgeCompare(const TVertexArray& vertices, const Edge& a, const Edge& b) + static bool EdgeCompare(const Array& vertices, const Edge& a, const Edge& b) { - if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[0]]) < ZeroTolerance && Vector2::Distance(vertices[a.Indices[1]], vertices[b.Indices[1]]) < ZeroTolerance) - { + if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[0]]) < ZeroTolerance && + Float2::Distance(vertices[a.Indices[1]], vertices[b.Indices[1]]) < ZeroTolerance) return true; - } - - if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[1]]) < ZeroTolerance && Vector2::Distance(vertices[a.Indices[1]], vertices[b.Indices[0]]) < ZeroTolerance) - { + if (Float2::Distance(vertices[a.Indices[0]], vertices[b.Indices[1]]) < ZeroTolerance && + Float2::Distance(vertices[a.Indices[1]], vertices[b.Indices[0]]) < ZeroTolerance) return true; - } - return false; } };