diff --git a/Source/Editor/Surface/Archetypes/Constants.cs b/Source/Editor/Surface/Archetypes/Constants.cs index 76efc9f60..458fd4af3 100644 --- a/Source/Editor/Surface/Archetypes/Constants.cs +++ b/Source/Editor/Surface/Archetypes/Constants.cs @@ -7,11 +7,12 @@ using Real = System.Single; #endif using System; -using System.Reflection; +using System.Linq; using FlaxEditor.CustomEditors.Editors; using FlaxEditor.GUI; using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; +using FlaxEditor.Surface.Undo; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Utilities; @@ -24,6 +25,101 @@ namespace FlaxEditor.Surface.Archetypes [HideInEditor] public static class Constants { + /// + /// A special type of node that adds the functionality to convert nodes to parameters. + /// + internal class ConvertToParameterNode : SurfaceNode + { + private readonly ScriptType _type; + private readonly Func _convertFunction; + + /// + public ConvertToParameterNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch, ScriptType type, Func convertFunction = null) + : base(id, context, nodeArch, groupArch) + { + _type = type; + _convertFunction = convertFunction; + } + + /// + public override void OnShowSecondaryContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu menu, Float2 location) + { + base.OnShowSecondaryContextMenu(menu, location); + + menu.AddSeparator(); + menu.AddButton("Convert to Parameter", OnConvertToParameter); + } + + private void OnConvertToParameter() + { + if (Surface.Owner is not IVisjectSurfaceWindow window) + throw new Exception("Surface owner is not a Visject Surface Window"); + + Asset asset = Surface.Owner.SurfaceAsset; + if (asset == null || !asset.IsLoaded) + { + Editor.LogError("Asset is null or not loaded"); + return; + } + + // Add parameter to editor + var paramIndex = Surface.Parameters.Count; + var initValue = _convertFunction == null ? Values[0] : _convertFunction.Invoke(Values); + var paramAction = new AddRemoveParamAction + { + Window = window, + IsAdd = true, + Name = Utilities.Utils.IncrementNameNumber("New parameter", OnParameterRenameValidate), + Type = _type, + Index = paramIndex, + InitValue = initValue, + }; + paramAction.Do(); + + // Spawn Get Parameter Node based on the added parameter + Guid parameterGuid = Surface.Parameters[paramIndex].ID; + bool undoEnabled = Surface.Undo.Enabled; + Surface.Undo.Enabled = false; + NodeArchetype arch = Surface.GetParameterGetterNodeArchetype(out var groupId); + SurfaceNode node = Surface.Context.SpawnNode(groupId, arch.TypeID, Location, new object[] { parameterGuid }); + Surface.Undo.Enabled = undoEnabled; + if (node is not Parameters.SurfaceNodeParamsGet getNode) + throw new Exception("Node is not a ParamsGet node!"); + + // Recreate connections of constant node + // Constant nodes and parameter nodes should have the same ports, so we can just iterate through the connections + var boxes = GetBoxes(); + for (int i = 0; i < boxes.Count; i++) + { + Box box = boxes[i]; + if (!box.HasAnyConnection) + continue; + if (!getNode.TryGetBox(box.ID, out Box paramBox)) + continue; + + // Iterating backwards, because the CreateConnection method deletes entries from the box connections when target box IsSingle is set to true + for (int k = box.Connections.Count - 1; k >= 0; k--) + { + Box connectedBox = box.Connections[k]; + paramBox.CreateConnection(connectedBox); + } + } + + // Add undo actions and remove constant node + var spawnNodeAction = new AddRemoveNodeAction(getNode, true); + var removeConstantAction = new AddRemoveNodeAction(this, false); + Surface.AddBatchedUndoAction(new MultiUndoAction(paramAction, spawnNodeAction, removeConstantAction)); + removeConstantAction.Do(); + } + + private bool OnParameterRenameValidate(string value) + { + if (Surface.Owner is not IVisjectSurfaceWindow window) + throw new Exception("Surface owner is not a Visject Surface Window"); + return !string.IsNullOrWhiteSpace(value) && window.VisjectSurface.Parameters.All(x => x.Name != value); + } + } + private class EnumNode : SurfaceNode { private EnumComboBox _picker; @@ -356,7 +452,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 1, Title = "Bool", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(bool))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(bool))), Description = "Constant boolean value", Flags = NodeFlags.AllGraphs, Size = new Float2(110, 20), @@ -389,7 +485,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 2, Title = "Integer", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(int))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(int))), Description = "Constant integer value", Flags = NodeFlags.AllGraphs, Size = new Float2(110, 20), @@ -417,7 +513,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 3, Title = "Float", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(float))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(float))), Description = "Constant floating point", Flags = NodeFlags.AllGraphs, Size = new Float2(110, 20), @@ -445,7 +541,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 4, Title = "Float2", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(Float2))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Float2))), Description = "Constant Float2", Flags = NodeFlags.AllGraphs, Size = new Float2(130, 60), @@ -476,7 +572,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 5, Title = "Float3", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(Float3))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Float3))), Description = "Constant Float3", Flags = NodeFlags.AllGraphs, Size = new Float2(130, 80), @@ -509,7 +605,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 6, Title = "Float4", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(Float4))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Float4))), Description = "Constant Float4", Flags = NodeFlags.AllGraphs, Size = new Float2(130, 100), @@ -544,7 +640,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 7, Title = "Color", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(Color))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Color))), Description = "RGBA color", Flags = NodeFlags.AllGraphs, Size = new Float2(70, 100), @@ -577,10 +673,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 8, Title = "Rotation", - // TODO: Way too long and ugly - find a better way to add conversion Create = (id, context, arch, groupArch) => - new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(Quaternion)), - values => Quaternion.Euler((float)values[0], (float)values[1], (float)values[2])), + new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Quaternion)), values => Quaternion.Euler((float)values[0], (float)values[1], (float)values[2])), Description = "Euler angle rotation", Flags = NodeFlags.AnimGraph | NodeFlags.VisualScriptGraph | NodeFlags.ParticleEmitterGraph, Size = new Float2(110, 60), @@ -605,7 +699,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 9, Title = "String", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(string))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(string))), Description = "Text", Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph, Size = new Float2(200, 20), @@ -656,8 +750,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 12, Title = "Unsigned Integer", - AlternativeTitles = new[] { "UInt" , "U Int" }, - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(uint))), + AlternativeTitles = new[] { "UInt", "U Int" }, + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(uint))), Description = "Constant unsigned integer value", Flags = NodeFlags.AllGraphs, Size = new Float2(170, 20), @@ -697,7 +791,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 15, Title = "Double", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(double))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(double))), Description = "Constant floating point", Flags = NodeFlags.AllGraphs, Size = new Float2(110, 20), @@ -715,7 +809,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 16, Title = "Vector2", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(Vector2))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Vector2))), Description = "Constant Vector2", Flags = NodeFlags.AllGraphs, Size = new Float2(130, 60), @@ -736,7 +830,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 17, Title = "Vector3", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(Vector3))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Vector3))), Description = "Constant Vector3", Flags = NodeFlags.AllGraphs, Size = new Float2(130, 80), @@ -759,7 +853,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 18, Title = "Vector4", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(Vector4))), + Create = (id, context, arch, groupArch) => new ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Vector4))), Description = "Constant Vector4", Flags = NodeFlags.AllGraphs, Size = new Float2(130, 100), diff --git a/Source/Editor/Surface/Archetypes/Textures.cs b/Source/Editor/Surface/Archetypes/Textures.cs index 85523961f..e35af7192 100644 --- a/Source/Editor/Surface/Archetypes/Textures.cs +++ b/Source/Editor/Surface/Archetypes/Textures.cs @@ -96,7 +96,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 1, Title = "Texture", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(Texture))), + Create = (id, context, arch, groupArch) => new Constants.ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(Texture))), Description = "Two dimensional texture object", Flags = NodeFlags.MaterialGraph, Size = new Float2(140, 120), @@ -133,7 +133,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 3, Title = "Cube Texture", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(CubeTexture))), + Create = (id, context, arch, groupArch) => new Constants.ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(CubeTexture))), Description = "Set of 6 textures arranged in a cube", Flags = NodeFlags.MaterialGraph, Size = new Float2(140, 120), @@ -157,7 +157,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 4, Title = "Normal Map", - Create = (id, context, arch, groupArch) => new ConvertibleNode(id, context, arch, groupArch, new ScriptType(typeof(NormalMap))), + Create = (id, context, arch, groupArch) => new Constants.ConvertToParameterNode(id, context, arch, groupArch, new ScriptType(typeof(NormalMap))), Description = "Two dimensional texture object sampled as a normal map", Flags = NodeFlags.MaterialGraph, Size = new Float2(140, 120), diff --git a/Source/Editor/Surface/ConvertibleNode.cs b/Source/Editor/Surface/ConvertibleNode.cs deleted file mode 100644 index 9c4a65ab9..000000000 --- a/Source/Editor/Surface/ConvertibleNode.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Linq; -using FlaxEditor.Scripting; -using FlaxEditor.Surface.Elements; -using FlaxEditor.Surface.Undo; -using FlaxEngine; - -namespace FlaxEditor.Surface -{ - /// - /// A special type of node that adds the functionality to convert nodes to parameters - /// - internal class ConvertibleNode : SurfaceNode - { - private readonly ScriptType _type; - private readonly Func _convertFunction; - - /// - public ConvertibleNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch, ScriptType type, Func convertFunction = null) - : base(id, context, nodeArch, groupArch) - { - _type = type; - _convertFunction = convertFunction; - } - - /// - public override void OnShowSecondaryContextMenu(FlaxEditor.GUI.ContextMenu.ContextMenu menu, Float2 location) - { - base.OnShowSecondaryContextMenu(menu, location); - - menu.AddSeparator(); - menu.AddButton("Convert to Parameter", OnConvertToParameter); - } - - private void OnConvertToParameter() - { - if (Surface.Owner is not IVisjectSurfaceWindow window) - throw new Exception("Surface owner is not a Visject Surface Window"); - - Asset asset = Surface.Owner.SurfaceAsset; - if (asset == null || !asset.IsLoaded) - { - Editor.LogError("Asset is null or not loaded"); - return; - } - - // Add parameter to editor - var paramIndex = Surface.Parameters.Count; - object initValue = _convertFunction == null ? Values[0] : _convertFunction.Invoke(Values); - var paramAction = new AddRemoveParamAction - { - Window = window, - IsAdd = true, - Name = Utilities.Utils.IncrementNameNumber("New parameter", x => OnParameterRenameValidate(x)), - Type = _type, - Index = paramIndex, - InitValue = initValue, - }; - paramAction.Do(); - - // Spawn Get Parameter Node based on the added parameter - Guid parameterGuid = Surface.Parameters[paramIndex].ID; - bool undoEnabled = Surface.Undo.Enabled; - Surface.Undo.Enabled = false; - NodeArchetype arch = Surface.GetParameterGetterNodeArchetype(out var groupId); - SurfaceNode node = Surface.Context.SpawnNode(groupId, arch.TypeID, this.Location, new object[] { parameterGuid }); - Surface.Undo.Enabled = undoEnabled; - - if (node is not Archetypes.Parameters.SurfaceNodeParamsGet getNode) - throw new Exception("Node is not a ParamsGet node!"); - - // Recreate connections of constant node - // Constant nodes and parameter nodes should have the same ports, so we can just iterate through the connections - var boxes = GetBoxes(); - for (int i = 0; i < boxes.Count; i++) - { - Box box = boxes[i]; - if (!box.HasAnyConnection) - continue; - - if (!getNode.TryGetBox(box.ID, out Box paramBox)) - continue; - - // Iterating backwards, because the CreateConnection method deletes entries from the box connections when target box IsSingle is set to true - for (int k = box.Connections.Count - 1; k >= 0; k--) - { - Box connectedBox = box.Connections[k]; - paramBox.CreateConnection(connectedBox); - } - } - - // Add undo actions and remove constant node - var spawnNodeAction = new AddRemoveNodeAction(getNode, true); - var removeConstantAction = new AddRemoveNodeAction(this, false); - - Surface.AddBatchedUndoAction(new MultiUndoAction(paramAction, spawnNodeAction, removeConstantAction)); - removeConstantAction.Do(); - } - - private bool OnParameterRenameValidate(string value) - { - if (Surface.Owner is not IVisjectSurfaceWindow window) - throw new Exception("Surface owner is not a Visject Surface Window"); - return !string.IsNullOrWhiteSpace(value) && window.VisjectSurface.Parameters.All(x => x.Name != value); - } - } -} diff --git a/Source/Editor/Surface/SurfaceParameter.cs b/Source/Editor/Surface/SurfaceParameter.cs index e74b44eb9..c22e5bde5 100644 --- a/Source/Editor/Surface/SurfaceParameter.cs +++ b/Source/Editor/Surface/SurfaceParameter.cs @@ -55,6 +55,7 @@ namespace FlaxEditor.Surface /// /// The type. /// The name. + /// The initial value to use. Null to use automatic default value. /// The created parameter. public static SurfaceParameter Create(ScriptType type, string name, object initValue = null) { diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index d2b9822a1..2232b39e5 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -279,7 +279,7 @@ namespace FlaxEditor.Surface public ScriptType Type; /// - /// The value to initialize the parameter with. (Can be null) + /// The value to initialize the parameter with. Can be null to use default one for the parameter type. /// public object InitValue; @@ -1065,7 +1065,6 @@ namespace FlaxEditor.Surface public virtual void OnParamRemoveUndo() { _refreshPropertiesOnLoad = true; - //_propertiesEditor.BuildLayoutOnUpdate(); _propertiesEditor.BuildLayout(); }