diff --git a/Source/Editor/Surface/Archetypes/Constants.cs b/Source/Editor/Surface/Archetypes/Constants.cs index f845e2fc1..e58d917d1 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,109 @@ 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(); + Surface.AddBatchedUndoAction(paramAction); + + // 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!"); + Surface.AddBatchedUndoAction(new AddRemoveNodeAction(getNode, true)); + + // Recreate connections of constant node + // Constant nodes and parameter nodes should have the same ports, so we can just iterate through the connections + var editConnectionsAction1 = new EditNodeConnections(Context, this); + var editConnectionsAction2 = new EditNodeConnections(Context, node); + 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); + } + } + editConnectionsAction1.End(); + editConnectionsAction2.End(); + Surface.AddBatchedUndoAction(editConnectionsAction1); + Surface.AddBatchedUndoAction(editConnectionsAction2); + + // Add undo actions and remove constant node + var removeConstantAction = new AddRemoveNodeAction(this, false); + Surface.AddBatchedUndoAction(removeConstantAction); + removeConstantAction.Do(); + Surface.MarkAsEdited(); + } + + 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,6 +460,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 1, Title = "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), @@ -388,6 +493,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 2, Title = "Integer", + 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), @@ -415,6 +521,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 3, Title = "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), @@ -442,6 +549,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 4, Title = "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), @@ -472,6 +580,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 5, Title = "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), @@ -504,6 +613,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 6, Title = "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), @@ -538,6 +648,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 7, Title = "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), @@ -570,6 +681,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 8, Title = "Rotation", + Create = (id, context, arch, groupArch) => + 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), @@ -594,6 +707,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 9, Title = "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), @@ -644,7 +758,8 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 12, Title = "Unsigned Integer", - AlternativeTitles = new[] { "UInt" , "U Int" }, + 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), @@ -684,6 +799,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 15, Title = "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), @@ -701,6 +817,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 16, Title = "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), @@ -721,6 +838,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 17, Title = "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), @@ -743,6 +861,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 18, Title = "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 18c21ecea..e35af7192 100644 --- a/Source/Editor/Surface/Archetypes/Textures.cs +++ b/Source/Editor/Surface/Archetypes/Textures.cs @@ -3,6 +3,7 @@ using System; using FlaxEditor.Content.Settings; using FlaxEditor.GUI; +using FlaxEditor.Scripting; using FlaxEngine; namespace FlaxEditor.Surface.Archetypes @@ -95,6 +96,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 1, Title = "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), @@ -131,6 +133,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 3, Title = "Cube Texture", + 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), @@ -154,6 +157,7 @@ namespace FlaxEditor.Surface.Archetypes { TypeID = 4, Title = "Normal Map", + 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/ParticleEmitterSurface.cs b/Source/Editor/Surface/ParticleEmitterSurface.cs index 76f96f06c..b7cf83c62 100644 --- a/Source/Editor/Surface/ParticleEmitterSurface.cs +++ b/Source/Editor/Surface/ParticleEmitterSurface.cs @@ -93,7 +93,7 @@ namespace FlaxEditor.Surface } /// - protected override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId) + protected internal override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId) { groupId = 6; return Archetypes.Parameters.Nodes[1]; diff --git a/Source/Editor/Surface/SurfaceParameter.cs b/Source/Editor/Surface/SurfaceParameter.cs index 6eafdc2aa..dae82e111 100644 --- a/Source/Editor/Surface/SurfaceParameter.cs +++ b/Source/Editor/Surface/SurfaceParameter.cs @@ -3,7 +3,6 @@ using System; using FlaxEditor.Scripting; using FlaxEngine; -using FlaxEngine.Utilities; namespace FlaxEditor.Surface { @@ -27,7 +26,7 @@ namespace FlaxEditor.Surface /// /// Parameter unique ID /// - public Guid ID; + public Guid ID = Guid.Empty; /// /// Parameter name @@ -49,23 +48,5 @@ namespace FlaxEditor.Surface /// [NoSerialize, HideInEditor] public readonly SurfaceMeta Meta = new SurfaceMeta(); - - /// - /// Creates the new parameter of the given type. - /// - /// The type. - /// The name. - /// The created parameter. - public static SurfaceParameter Create(ScriptType type, string name) - { - return new SurfaceParameter - { - ID = Guid.NewGuid(), - IsPublic = true, - Name = name, - Type = type, - Value = TypeUtils.GetDefaultValue(type), - }; - } } } diff --git a/Source/Editor/Surface/VisjectSurface.DragDrop.cs b/Source/Editor/Surface/VisjectSurface.DragDrop.cs index 1728c282f..2ff82c269 100644 --- a/Source/Editor/Surface/VisjectSurface.DragDrop.cs +++ b/Source/Editor/Surface/VisjectSurface.DragDrop.cs @@ -151,7 +151,7 @@ namespace FlaxEditor.Surface /// /// The group ID. /// The node archetype. - protected virtual NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId) + protected internal virtual NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId) { groupId = 6; return Archetypes.Parameters.Nodes[0]; diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index f676f6010..cce86d5c0 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -17,6 +17,7 @@ using FlaxEditor.Viewport.Previews; using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEngine.Utilities; namespace FlaxEditor.Surface { @@ -258,6 +259,11 @@ namespace FlaxEditor.Surface /// public IVisjectSurfaceWindow Window; + /// + /// The identifier of the parameter. Empty to auto generate it. + /// + public Guid Id = Guid.NewGuid(); + /// /// True if adding, false if removing parameter. /// @@ -278,6 +284,11 @@ namespace FlaxEditor.Surface /// public ScriptType Type; + /// + /// The value to initialize the parameter with. Can be null to use default one for the parameter type. + /// + public object InitValue; + /// public string ActionString => IsAdd ? "Add parameter" : "Remove parameter"; @@ -304,7 +315,14 @@ namespace FlaxEditor.Surface var type = Type; if (IsAdd && type.Type == typeof(NormalMap)) type = new ScriptType(typeof(Texture)); - var param = SurfaceParameter.Create(type, Name); + var param = new SurfaceParameter + { + ID = Id, + IsPublic = true, + Name = Name, + Type = type, + Value = InitValue ?? TypeUtils.GetDefaultValue(type), + }; if (IsAdd && Type.Type == typeof(NormalMap)) param.Value = FlaxEngine.Content.LoadAsyncInternal("Engine/Textures/NormalTexture"); Window.VisjectSurface.Parameters.Insert(Index, param); @@ -1060,7 +1078,6 @@ namespace FlaxEditor.Surface public virtual void OnParamRemoveUndo() { _refreshPropertiesOnLoad = true; - //_propertiesEditor.BuildLayoutOnUpdate(); _propertiesEditor.BuildLayout(); } diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs index 902582311..2f8d4cda8 100644 --- a/Source/Editor/Surface/VisualScriptSurface.cs +++ b/Source/Editor/Surface/VisualScriptSurface.cs @@ -144,7 +144,7 @@ namespace FlaxEditor.Surface } /// - protected override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId) + protected internal override NodeArchetype GetParameterGetterNodeArchetype(out ushort groupId) { groupId = 6; return Archetypes.Parameters.Nodes[2];