Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e60d3604c0 | ||
|
05600f7bc1 | ||
|
b1b129c8c1 | ||
|
a3d79f09b2 | ||
|
4cd9878a97 | ||
|
370504dcfc | ||
|
512fb50c7b | ||
|
68a08ee4cd | ||
|
9d94abf323 | ||
|
e49867f52e | ||
|
422dcc6f5b | ||
|
dfd4c498e1 | ||
|
0614e27cd5 | ||
|
b4a66d7e26 | ||
|
9c6b5e8d6d | ||
|
4f348fe66a | ||
|
499d187c96 | ||
|
c24a52bee8 | ||
|
a6e9038da5 | ||
|
0c53ac8d18 | ||
|
4985cf5748 | ||
|
462bf4ffc0 | ||
|
deb2464537 | ||
|
50dca57d23 | ||
|
df0cfb3ec4 | ||
|
d972a007d2 | ||
08d5b1bb8d | |||
c04ca0d8e6 |
@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: f6fe8f57402cf44e8bb5f1dffb3be6d8
|
guid: c221fbc5c38e72643af466921f820588
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
66
Editor/Scripts/Editor/Data Types/IPTPort.cs
Normal file
66
Editor/Scripts/Editor/Data Types/IPTPort.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using UnityEditor.Experimental.GraphView;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor
|
||||||
|
{
|
||||||
|
public class IPTPort : Port
|
||||||
|
{
|
||||||
|
public FieldInfo fieldInfo;
|
||||||
|
private VisualElement _exposedPropertyContainer;
|
||||||
|
|
||||||
|
public delegate void OnPortConnectedEvent();
|
||||||
|
public OnPortConnectedEvent OnPortConnected;
|
||||||
|
|
||||||
|
public delegate void OnPortDisconnectedEvent();
|
||||||
|
public OnPortDisconnectedEvent OnPortDisconnected;
|
||||||
|
|
||||||
|
public VisualElement ExposedPropertyContainer
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_exposedPropertyContainer = value;
|
||||||
|
}
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _exposedPropertyContainer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IPTPort(Orientation portOrientation, Direction portDirection, Capacity portCapacity, Type type) : base(portOrientation, portDirection, portCapacity, type)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Connect(Edge edge)
|
||||||
|
{
|
||||||
|
base.Connect(edge);
|
||||||
|
OnPortConnected?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Disconnect(Edge edge)
|
||||||
|
{
|
||||||
|
base.Disconnect(edge);
|
||||||
|
OnPortDisconnected?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void PublicOnConnected(Port obj)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IPTPort Create(IEdgeConnectorListener connectorListener, bool isInput, Type type)
|
||||||
|
{
|
||||||
|
var port = new IPTPort(Orientation.Horizontal, isInput ? Direction.Input : Direction.Output,
|
||||||
|
isInput ? Capacity.Single : Capacity.Multi, type)
|
||||||
|
{
|
||||||
|
m_EdgeConnector = new EdgeConnector<Edge>(connectorListener),
|
||||||
|
};
|
||||||
|
port.AddManipulator(port.m_EdgeConnector);
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Editor/Scripts/Editor/Data Types/IPT_Preferences.cs
Normal file
11
Editor/Scripts/Editor/Data Types/IPT_Preferences.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor
|
||||||
|
{
|
||||||
|
public class IPT_Preferences : ScriptableObject
|
||||||
|
{
|
||||||
|
public bool debugMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A structure that holds image pixel data and its dimensions.
|
/// A structure that holds image pixel data and its dimensions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
public struct ImageData
|
public struct ImageData
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -36,7 +39,7 @@ public struct ImageData
|
|||||||
/// <param name="sourceTexture">The source texture to extract pixel data from.</param>
|
/// <param name="sourceTexture">The source texture to extract pixel data from.</param>
|
||||||
public ImageData(Texture2D sourceTexture)
|
public ImageData(Texture2D sourceTexture)
|
||||||
{
|
{
|
||||||
PixelData = sourceTexture.GetPixelData<Color32>(0);
|
PixelData = new NativeArray<Color32>(sourceTexture.GetPixelData<Color32>(0), Allocator.Persistent);
|
||||||
Dimensions = (sourceTexture.width, sourceTexture.height);
|
Dimensions = (sourceTexture.width, sourceTexture.height);
|
||||||
isRGBA = sourceTexture.format == TextureFormat.RGBA32;
|
isRGBA = sourceTexture.format == TextureFormat.RGBA32;
|
||||||
}
|
}
|
||||||
@ -62,4 +65,15 @@ public struct ImageData
|
|||||||
texture.Apply();
|
texture.Apply();
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ExportPNG(string path)
|
||||||
|
{
|
||||||
|
Texture2D texture = this.ToTexture2D();
|
||||||
|
byte[] data = texture.EncodeToPNG();
|
||||||
|
|
||||||
|
if (data != null)
|
||||||
|
{
|
||||||
|
System.IO.File.WriteAllBytes(path, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,11 +16,16 @@ namespace ImageProcessingGraph.Editor
|
|||||||
{
|
{
|
||||||
[SerializeReference] private List<BaseImageNode> nodes;
|
[SerializeReference] private List<BaseImageNode> nodes;
|
||||||
[SerializeField] private List<GraphConnection> connections;
|
[SerializeField] private List<GraphConnection> connections;
|
||||||
[SerializeField] public List<BaseImageNode> runOrder;
|
[SerializeReference] public List<BaseImageNode> runOrder;
|
||||||
[SerializeField] public List<StickyNote> stickyNotes;
|
[SerializeField] public List<StickyNote> stickyNotes;
|
||||||
|
|
||||||
public List<BaseImageNode> Nodes => nodes;
|
public List<BaseImageNode> Nodes => nodes;
|
||||||
public List<GraphConnection> Connections => connections;
|
public List<GraphConnection> Connections => connections;
|
||||||
|
|
||||||
|
public delegate void OnRunEvent();
|
||||||
|
public event OnRunEvent OnRun;
|
||||||
|
public OnRunEvent OnRunEnd;
|
||||||
|
|
||||||
public ImageProcessingGraphAsset()
|
public ImageProcessingGraphAsset()
|
||||||
{
|
{
|
||||||
nodes = new List<BaseImageNode>();
|
nodes = new List<BaseImageNode>();
|
||||||
@ -42,16 +47,29 @@ namespace ImageProcessingGraph.Editor
|
|||||||
|
|
||||||
public void RunGraph()
|
public void RunGraph()
|
||||||
{
|
{
|
||||||
|
OnRun?.Invoke();
|
||||||
|
|
||||||
// Create and start the stopwatch to measure time
|
// Create and start the stopwatch to measure time
|
||||||
Stopwatch stopwatch = new Stopwatch();
|
Stopwatch stopwatch = new Stopwatch();
|
||||||
stopwatch.Start();
|
stopwatch.Start();
|
||||||
|
|
||||||
if (runOrder == null)
|
runOrder = GetExecutionOrder(this.nodes, this.connections);
|
||||||
runOrder = GetExecutionOrder(this.nodes, this.connections);
|
|
||||||
|
bool failed = false;
|
||||||
|
|
||||||
foreach (var VARIABLE in runOrder)
|
foreach (var VARIABLE in runOrder)
|
||||||
{
|
{
|
||||||
VARIABLE.RunNode();
|
if (!VARIABLE.RunNode())
|
||||||
|
{
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var VARIABLE in runOrder)
|
||||||
|
{
|
||||||
|
if(!VARIABLE.RunCleanUp())
|
||||||
|
failed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the stopwatch after running the nodes
|
// Stop the stopwatch after running the nodes
|
||||||
@ -59,6 +77,8 @@ namespace ImageProcessingGraph.Editor
|
|||||||
|
|
||||||
// Log the elapsed time
|
// Log the elapsed time
|
||||||
UnityEngine.Debug.Log($"Graph execution took {stopwatch.ElapsedMilliseconds} milliseconds.");
|
UnityEngine.Debug.Log($"Graph execution took {stopwatch.ElapsedMilliseconds} milliseconds.");
|
||||||
|
|
||||||
|
AssetDatabase.Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,6 +26,9 @@ namespace ImageProcessingGraph.Editor
|
|||||||
|
|
||||||
public ImageProcessingGraphAsset asset;
|
public ImageProcessingGraphAsset asset;
|
||||||
|
|
||||||
|
public delegate void OnFailed();
|
||||||
|
public event OnFailed onFailed;
|
||||||
|
|
||||||
public BaseImageNode()
|
public BaseImageNode()
|
||||||
{
|
{
|
||||||
guid = Guid.NewGuid().ToString();
|
guid = Guid.NewGuid().ToString();
|
||||||
@ -61,7 +64,7 @@ namespace ImageProcessingGraph.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (connection.Equals((default(GraphConnection))))
|
if (connection.Equals((default(GraphConnection))))
|
||||||
return;
|
continue;
|
||||||
|
|
||||||
// Get the output node from the connection
|
// Get the output node from the connection
|
||||||
var outputNode = asset.Nodes.FirstOrDefault(n => n.ID == connection.outputPort.nodeID);
|
var outputNode = asset.Nodes.FirstOrDefault(n => n.ID == connection.outputPort.nodeID);
|
||||||
@ -101,10 +104,20 @@ namespace ImageProcessingGraph.Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void RunNode()
|
public bool RunNode()
|
||||||
{
|
{
|
||||||
SetNodeInputs();
|
try
|
||||||
this.Process();
|
{
|
||||||
|
SetNodeInputs();
|
||||||
|
this.Process();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
onFailed?.Invoke();
|
||||||
|
Debug.LogError(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Process()
|
public virtual void Process()
|
||||||
@ -112,6 +125,26 @@ namespace ImageProcessingGraph.Editor
|
|||||||
Debug.Log("Uppies");
|
Debug.Log("Uppies");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool RunCleanUp()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.CleanUp();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
onFailed?.Invoke();
|
||||||
|
Debug.LogError(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void CleanUp()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void SetPosition(Rect position) => this.position = position;
|
public void SetPosition(Rect position) => this.position = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,7 @@
|
|||||||
namespace ImageProcessingGraph.Editor
|
using UnityEditor.Experimental.GraphView;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor
|
||||||
{
|
{
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public struct GraphConnection
|
public struct GraphConnection
|
||||||
@ -6,16 +9,25 @@
|
|||||||
public GraphPort inputPort;
|
public GraphPort inputPort;
|
||||||
public GraphPort outputPort;
|
public GraphPort outputPort;
|
||||||
|
|
||||||
public GraphConnection(GraphPort inputPort, GraphPort outputPort)
|
[SerializeField] public Edge internalEdge;
|
||||||
|
|
||||||
|
public GraphConnection(GraphPort inputPort, GraphPort outputPort, Edge internalEdge)
|
||||||
{
|
{
|
||||||
this.inputPort = inputPort;
|
this.inputPort = inputPort;
|
||||||
this.outputPort = outputPort;
|
this.outputPort = outputPort;
|
||||||
|
this.internalEdge = internalEdge;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GraphConnection(string inputNodeGuid, int inputPortID, string inputNodeType, string outputNodeGuid, int outputPortID, string outputNodeType)
|
public GraphConnection(string inputNodeGuid, int inputPortID, string inputNodeType, string outputNodeGuid, int outputPortID, string outputNodeType, Edge internalEdge)
|
||||||
{
|
{
|
||||||
this.inputPort = new GraphPort(inputNodeGuid, inputPortID, inputNodeType);
|
this.inputPort = new GraphPort(inputNodeGuid, inputPortID, inputNodeType);
|
||||||
this.outputPort = new GraphPort(outputNodeGuid, outputPortID, outputNodeType);
|
this.outputPort = new GraphPort(outputNodeGuid, outputPortID, outputNodeType);
|
||||||
|
this.internalEdge = internalEdge;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetInternalEdge(Edge edge)
|
||||||
|
{
|
||||||
|
this.internalEdge = edge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,6 +38,7 @@
|
|||||||
public string nodeType;
|
public string nodeType;
|
||||||
public int portID;
|
public int portID;
|
||||||
|
|
||||||
|
|
||||||
public GraphPort(string nodeID, int portID, string nodeType)
|
public GraphPort(string nodeID, int portID, string nodeType)
|
||||||
{
|
{
|
||||||
this.nodeID = nodeID;
|
this.nodeID = nodeID;
|
23
Editor/Scripts/Editor/Nodes/Base/RangeProperty.cs
Normal file
23
Editor/Scripts/Editor/Nodes/Base/RangeProperty.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class GreyscaleValue
|
||||||
|
{
|
||||||
|
public int value = 255;
|
||||||
|
|
||||||
|
public GreyscaleValue()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GreyscaleField : IntegerField
|
||||||
|
{
|
||||||
|
public (int, int) minMax = (0,255);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
3
Editor/Scripts/Editor/Nodes/Base/RangeProperty.cs.meta
Normal file
3
Editor/Scripts/Editor/Nodes/Base/RangeProperty.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3ab37c25c4884480b8777a62e042a37c
|
||||||
|
timeCreated: 1745703416
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows;
|
||||||
|
|
||||||
namespace ImageProcessingGraph.Editor.Nodes.NodeAttributes
|
namespace ImageProcessingGraph.Editor.Nodes.NodeAttributes
|
||||||
{
|
{
|
||||||
@ -8,16 +9,19 @@ namespace ImageProcessingGraph.Editor.Nodes.NodeAttributes
|
|||||||
private string name;
|
private string name;
|
||||||
private string menuItem;
|
private string menuItem;
|
||||||
private string ussPath;
|
private string ussPath;
|
||||||
|
private Type editorType;
|
||||||
|
|
||||||
public string Title => name;
|
public string Title => name;
|
||||||
public string MenuItem => menuItem;
|
public string MenuItem => menuItem;
|
||||||
public string UssPath => ussPath;
|
public string UssPath => ussPath;
|
||||||
|
public Type EditorType => editorType;
|
||||||
|
|
||||||
public NodeInfoAttribute(string name, string menuItem = "", bool requiresImage = false, string ussPath = null)
|
public NodeInfoAttribute(string name, string menuItem = "", bool requiresImage = false, string ussPath = null, Type editorType = null)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.menuItem = menuItem;
|
this.menuItem = menuItem;
|
||||||
this.ussPath = ussPath;
|
this.ussPath = ussPath;
|
||||||
|
this.editorType = editorType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 22b0f0e553dc85e489979a232505a332
|
guid: cf47dea8bd47175418a3f462e2ddb060
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: a4036f11a5b3def48b6cf5e8b0654a54
|
guid: 3d053e80c499447499397a64b22ee639
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
@ -1,8 +1,8 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d04964bb165ef4820be34fe3f050be61
|
guid: e6bd53786a8dee54790032792e0b03da
|
||||||
NativeFormatImporter:
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
assetBundleVariant:
|
assetBundleVariant:
|
@ -0,0 +1,52 @@
|
|||||||
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using Unity.Burst;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Jobs;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture
|
||||||
|
{
|
||||||
|
[NodeInfoAttribute("Invert Channel", "Adjustments/InvertChannel", true)]
|
||||||
|
public class ChannelInvertNode : BaseImageNode
|
||||||
|
{
|
||||||
|
[NodeAttributes.Input("Input Channel")]
|
||||||
|
public SplitChannelData inputChannel;
|
||||||
|
|
||||||
|
[NodeAttributes.Output("Output Channel")]
|
||||||
|
public SplitChannelData outputChannel;
|
||||||
|
|
||||||
|
public override void Process()
|
||||||
|
{
|
||||||
|
NativeArray<byte> input = new NativeArray<byte>(inputChannel.ChannelData, Allocator.Persistent);
|
||||||
|
NativeArray<byte> output = new NativeArray<byte>(inputChannel.ChannelData.Length, Allocator.Persistent);
|
||||||
|
|
||||||
|
InvertChannelJob job = new InvertChannelJob
|
||||||
|
{
|
||||||
|
input = input,
|
||||||
|
output = output
|
||||||
|
};
|
||||||
|
|
||||||
|
job.Run();
|
||||||
|
|
||||||
|
byte[] outputArray = output.ToArray();
|
||||||
|
|
||||||
|
outputChannel = new SplitChannelData(outputArray, (inputChannel.Width, inputChannel.Height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BurstCompile]
|
||||||
|
public struct InvertChannelJob : IJob
|
||||||
|
{
|
||||||
|
[ReadOnly] public NativeArray<byte> input;
|
||||||
|
[WriteOnly] public NativeArray<byte> output;
|
||||||
|
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
int length = input.Length;
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
output[i] = (byte)(255 - input[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 51378877ff184d868ef4c507dba15ef9
|
||||||
|
timeCreated: 1745794420
|
8
Editor/Scripts/Editor/Nodes/Types/Image/Channels.meta
Normal file
8
Editor/Scripts/Editor/Nodes/Types/Image/Channels.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6957ba197a1612a4c8f0f588d40e374f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -1,4 +1,5 @@
|
|||||||
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using Unity.Burst;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using Unity.Jobs;
|
using Unity.Jobs;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -24,7 +25,9 @@ namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture
|
|||||||
public ImageData inputTexture;
|
public ImageData inputTexture;
|
||||||
|
|
||||||
// Job struct for combining RGBA channels
|
// Job struct for combining RGBA channels
|
||||||
|
[BurstCompile]
|
||||||
struct RGBACombineJob : IJob
|
struct RGBACombineJob : IJob
|
||||||
|
|
||||||
{
|
{
|
||||||
public NativeArray<byte> rData;
|
public NativeArray<byte> rData;
|
||||||
public NativeArray<byte> gData;
|
public NativeArray<byte> gData;
|
@ -1,4 +1,5 @@
|
|||||||
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using Unity.Burst;
|
||||||
using Unity.Collections;
|
using Unity.Collections;
|
||||||
using Unity.Jobs;
|
using Unity.Jobs;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@ -22,10 +23,10 @@ namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture
|
|||||||
|
|
||||||
public override void Process()
|
public override void Process()
|
||||||
{
|
{
|
||||||
int length = inputTexture.PixelData.Length;
|
int length = inputTexture.PixelData.Length;;
|
||||||
|
|
||||||
// Allocate NativeArrays for the pixel data and the individual RGBA channels
|
// Allocate NativeArrays for the pixel data and the individual RGBA channels
|
||||||
NativeArray<Color32> pixelData = new NativeArray<Color32>(inputTexture.PixelData, Allocator.Persistent);
|
NativeArray<Color32> pixelData = inputTexture.PixelData;
|
||||||
NativeArray<byte> rChannel = new NativeArray<byte>(length, Allocator.Persistent);
|
NativeArray<byte> rChannel = new NativeArray<byte>(length, Allocator.Persistent);
|
||||||
NativeArray<byte> gChannel = new NativeArray<byte>(length, Allocator.Persistent);
|
NativeArray<byte> gChannel = new NativeArray<byte>(length, Allocator.Persistent);
|
||||||
NativeArray<byte> bChannel = new NativeArray<byte>(length, Allocator.Persistent);
|
NativeArray<byte> bChannel = new NativeArray<byte>(length, Allocator.Persistent);
|
||||||
@ -53,6 +54,7 @@ namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Job struct for processing the image data
|
// Job struct for processing the image data
|
||||||
|
[BurstCompile]
|
||||||
struct RGBAJob : IJob
|
struct RGBAJob : IJob
|
||||||
{
|
{
|
||||||
public NativeArray<Color32> pixelData;
|
public NativeArray<Color32> pixelData;
|
8
Editor/Scripts/Editor/Nodes/Types/Image/Dimensions.meta
Normal file
8
Editor/Scripts/Editor/Nodes/Types/Image/Dimensions.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d31e8b213de87c54caaf096620201846
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,27 @@
|
|||||||
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using Unity.Burst;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Jobs;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture
|
||||||
|
{
|
||||||
|
[NodeInfoAttribute("Get Dimensions", "Dimensions/Get Dimensions", false)]
|
||||||
|
public class TextureGetDimensions : BaseImageNode
|
||||||
|
{
|
||||||
|
[NodeAttributes.Input("")]
|
||||||
|
public ImageData inputTexture;
|
||||||
|
|
||||||
|
[NodeAttributes.Output("Width")]
|
||||||
|
public int width;
|
||||||
|
|
||||||
|
[NodeAttributes.Output("Height")]
|
||||||
|
public int height;
|
||||||
|
|
||||||
|
public override void Process()
|
||||||
|
{
|
||||||
|
this.width = inputTexture.Width;
|
||||||
|
this.height = inputTexture.Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 71e1db2a5bfe4a54a6ce2a99fe4d02fb
|
||||||
|
timeCreated: 1745699625
|
@ -0,0 +1,57 @@
|
|||||||
|
using System.ComponentModel.Composition.Primitives;
|
||||||
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using Unity.Collections;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor.Nodes.Output
|
||||||
|
{
|
||||||
|
[NodeInfo("Texture Export", "Export/Texture Export", true)]
|
||||||
|
public class Texture2DOutput : BaseImageNode
|
||||||
|
{
|
||||||
|
[NodeAttributes.Input("")] public ImageData inputPixels;
|
||||||
|
[NodeAttributes.Input("File Name")] public string fileName;
|
||||||
|
[NodeAttributes.Input("File Directory")] public string fileDirectory;
|
||||||
|
|
||||||
|
public enum ExportType
|
||||||
|
{
|
||||||
|
Texture2D,
|
||||||
|
PNG
|
||||||
|
}
|
||||||
|
|
||||||
|
[NodeAttributes.Input("Export Type")] public ExportType exportType;
|
||||||
|
[NodeAttributes.Input("Texture Type")] public TextureImporterType textureType;
|
||||||
|
|
||||||
|
[NodeAttributes.Output("Output Texture")] public Texture2D textureOutput;
|
||||||
|
|
||||||
|
public override void Process()
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (exportType)
|
||||||
|
{
|
||||||
|
case ExportType.Texture2D:
|
||||||
|
string pathT2D = $"{fileDirectory}/{fileName}.asset";
|
||||||
|
AssetDatabase.CreateAsset(inputPixels.ToTexture2D(), pathT2D);
|
||||||
|
textureOutput = AssetDatabase.LoadAssetAtPath<Texture2D>(pathT2D);
|
||||||
|
break;
|
||||||
|
case ExportType.PNG:
|
||||||
|
string pathPNG = $"{fileDirectory}/{fileName}.png";
|
||||||
|
inputPixels.ExportPNG(pathPNG);
|
||||||
|
|
||||||
|
AssetDatabase.ImportAsset(pathPNG);
|
||||||
|
|
||||||
|
TextureImporter textureImporter = AssetImporter.GetAtPath(pathPNG) as TextureImporter;
|
||||||
|
if (textureImporter != null)
|
||||||
|
{
|
||||||
|
textureImporter.textureType = textureType;
|
||||||
|
EditorUtility.SetDirty(AssetDatabase.LoadAssetAtPath<Texture2D>(pathPNG));
|
||||||
|
textureImporter.SaveAndReimport();
|
||||||
|
}
|
||||||
|
AssetDatabase.ImportAsset(pathPNG);
|
||||||
|
|
||||||
|
textureOutput = AssetDatabase.LoadAssetAtPath<Texture2D>(pathPNG);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Editor/Scripts/Editor/Nodes/Types/Image/Filters.meta
Normal file
8
Editor/Scripts/Editor/Nodes/Types/Image/Filters.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 96b583a3bbd8b2c45a5eaa9e0f00d1b2
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Editor/Scripts/Editor/Nodes/Types/Image/Image.meta
Normal file
8
Editor/Scripts/Editor/Nodes/Types/Image/Image.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bf8e2895a77954d45864e9f8edae29c3
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,49 @@
|
|||||||
|
using System.IO;
|
||||||
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using Unity.Collections;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor.Nodes.Import_Nodes
|
||||||
|
{
|
||||||
|
[NodeInfo("Texture Import", "Imports/Import Texture")]
|
||||||
|
public class Texture2DImport : BaseImageNode
|
||||||
|
{
|
||||||
|
[NodeAttributes.Input("")]
|
||||||
|
public Texture2D textureImport;
|
||||||
|
|
||||||
|
[NodeAttributes.Output("")]
|
||||||
|
public ImageData textureOutput;
|
||||||
|
[NodeAttributes.Output("File Name")]
|
||||||
|
public string fileName;
|
||||||
|
[NodeAttributes.Output("File Directory")]
|
||||||
|
public string filePath;
|
||||||
|
|
||||||
|
public override void Process()
|
||||||
|
{
|
||||||
|
if (this.textureImport != null)
|
||||||
|
{
|
||||||
|
TextureImporter textureImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(textureImport)) as TextureImporter;
|
||||||
|
|
||||||
|
TextureImporterPlatformSettings texset = textureImporter.GetDefaultPlatformTextureSettings();
|
||||||
|
texset.format=TextureImporterFormat.RGBA32;
|
||||||
|
texset.maxTextureSize=16384;
|
||||||
|
textureImporter.SetPlatformTextureSettings(texset);
|
||||||
|
|
||||||
|
TextureImporterSettings settings = new TextureImporterSettings();
|
||||||
|
textureImporter.ReadTextureSettings(settings);
|
||||||
|
settings.readable = true;
|
||||||
|
textureImporter.SetTextureSettings(settings);
|
||||||
|
EditorUtility.SetDirty(textureImport);
|
||||||
|
textureImporter.SaveAndReimport();
|
||||||
|
|
||||||
|
|
||||||
|
this.textureOutput = new ImageData(textureImport);
|
||||||
|
this.fileName = textureImport.name;
|
||||||
|
this.filePath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(textureImport));
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
// Debug.LogError("UH!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Editor/Scripts/Editor/Nodes/Types/Image/Utilities.meta
Normal file
8
Editor/Scripts/Editor/Nodes/Types/Image/Utilities.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c524131b4dbc3414ab755a10441b3841
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,61 @@
|
|||||||
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using Unity.Burst;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Jobs;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor.Nodes.Types.Image.Utilities
|
||||||
|
{
|
||||||
|
public class SingleChannelColor
|
||||||
|
{
|
||||||
|
[NodeInfoAttribute("Channel Color", "Utility/Channel Color", false)]
|
||||||
|
public class SingleColorChannel : BaseImageNode
|
||||||
|
{
|
||||||
|
[NodeAttributes.Input("Color")] public GreyscaleValue range;
|
||||||
|
|
||||||
|
[NodeAttributes.Input("Width")] public int Width;
|
||||||
|
|
||||||
|
[NodeAttributes.Input("Height")] public int Height;
|
||||||
|
|
||||||
|
[NodeAttributes.Output("Color")] public SplitChannelData OutputColor;
|
||||||
|
|
||||||
|
public override void Process()
|
||||||
|
{
|
||||||
|
int pixelCount = Width * Height;
|
||||||
|
|
||||||
|
NativeArray<byte> outputData = new NativeArray<byte>(pixelCount, Allocator.Persistent);
|
||||||
|
|
||||||
|
CreateSingleColorJob job = new CreateSingleColorJob
|
||||||
|
{
|
||||||
|
color32 = range.value,
|
||||||
|
outputData = outputData,
|
||||||
|
width = Width,
|
||||||
|
height = Height
|
||||||
|
};
|
||||||
|
|
||||||
|
job.Run();
|
||||||
|
|
||||||
|
OutputColor = new SplitChannelData(outputData.ToArray(), (Width, Height));
|
||||||
|
}
|
||||||
|
|
||||||
|
[BurstCompile]
|
||||||
|
struct CreateSingleColorJob : IJob
|
||||||
|
{
|
||||||
|
public int color32;
|
||||||
|
[WriteOnly]
|
||||||
|
public NativeArray<byte> outputData;
|
||||||
|
public int width;
|
||||||
|
public int height;
|
||||||
|
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
// More efficient linear write pattern
|
||||||
|
for (int i = 0; i < width*height; i++)
|
||||||
|
{
|
||||||
|
outputData[i] = (byte)color32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aef9d7bde3d544e6bca5eef8ff58a60e
|
||||||
|
timeCreated: 1745699866
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b462f37a291b49a696e3b4b554d26868
|
||||||
|
timeCreated: 1745899248
|
@ -0,0 +1,135 @@
|
|||||||
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using UnityEngine;
|
||||||
|
using Unity.Collections;
|
||||||
|
using Unity.Jobs;
|
||||||
|
using Unity.Burst;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor.Nodes.Types.Image.Utilities.ViewNode
|
||||||
|
{
|
||||||
|
[NodeInfo("View Texture", "Utility/View Texture", false, null ,typeof(ViewTextureNodeEditor))]
|
||||||
|
public partial class ViewTextureNode : BaseImageNode
|
||||||
|
{
|
||||||
|
[NodeAttributes.Input("Texture")] public Texture2D texture;
|
||||||
|
[NodeAttributes.Input("Image Data")] public ImageData imageData;
|
||||||
|
|
||||||
|
public delegate void OnImageUpdated();
|
||||||
|
public OnImageUpdated onImageUpdated;
|
||||||
|
|
||||||
|
public Texture2D cachedRGB;
|
||||||
|
public Texture2D cachedR;
|
||||||
|
public Texture2D cachedG;
|
||||||
|
public Texture2D cachedB;
|
||||||
|
public Texture2D cachedA;
|
||||||
|
|
||||||
|
public override void Process()
|
||||||
|
{
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
texture = imageData.ToTexture2D();
|
||||||
|
}
|
||||||
|
|
||||||
|
GenerateChannelTextures(texture);
|
||||||
|
|
||||||
|
onImageUpdated?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void CleanUp()
|
||||||
|
{
|
||||||
|
texture = null;
|
||||||
|
imageData = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateChannelTextures(Texture2D source)
|
||||||
|
{
|
||||||
|
var pixels = source.GetPixels();
|
||||||
|
int length = pixels.Length;
|
||||||
|
|
||||||
|
NativeArray<Color> inputPixels = new NativeArray<Color>(pixels, Allocator.TempJob);
|
||||||
|
|
||||||
|
NativeArray<Color> outputR = new NativeArray<Color>(length, Allocator.TempJob);
|
||||||
|
NativeArray<Color> outputG = new NativeArray<Color>(length, Allocator.TempJob);
|
||||||
|
NativeArray<Color> outputB = new NativeArray<Color>(length, Allocator.TempJob);
|
||||||
|
NativeArray<Color> outputA = new NativeArray<Color>(length, Allocator.TempJob);
|
||||||
|
|
||||||
|
var jobR = new ChannelJob(ChannelType.R, inputPixels, outputR);
|
||||||
|
var jobG = new ChannelJob(ChannelType.G, inputPixels, outputG);
|
||||||
|
var jobB = new ChannelJob(ChannelType.B, inputPixels, outputB);
|
||||||
|
var jobA = new ChannelJob(ChannelType.A, inputPixels, outputA);
|
||||||
|
|
||||||
|
JobHandle handleR = jobR.Schedule(length, 64);
|
||||||
|
JobHandle handleG = jobG.Schedule(length, 64);
|
||||||
|
JobHandle handleB = jobB.Schedule(length, 64);
|
||||||
|
JobHandle handleA = jobA.Schedule(length, 64);
|
||||||
|
|
||||||
|
var handles = new NativeArray<JobHandle>(4, Allocator.Temp);
|
||||||
|
handles[0] = handleR;
|
||||||
|
handles[1] = handleG;
|
||||||
|
handles[2] = handleB;
|
||||||
|
handles[3] = handleA;
|
||||||
|
|
||||||
|
JobHandle.CompleteAll(handles);
|
||||||
|
handles.Dispose();
|
||||||
|
|
||||||
|
|
||||||
|
cachedRGB = source;
|
||||||
|
cachedR = CreateTexture(source.width, source.height, outputR);
|
||||||
|
cachedG = CreateTexture(source.width, source.height, outputG);
|
||||||
|
cachedB = CreateTexture(source.width, source.height, outputB);
|
||||||
|
cachedA = CreateTexture(source.width, source.height, outputA);
|
||||||
|
|
||||||
|
inputPixels.Dispose();
|
||||||
|
outputR.Dispose();
|
||||||
|
outputG.Dispose();
|
||||||
|
outputB.Dispose();
|
||||||
|
outputA.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Texture2D CreateTexture(int width, int height, NativeArray<Color> colors)
|
||||||
|
{
|
||||||
|
Texture2D tex = new Texture2D(width, height, TextureFormat.RGBA32, false);
|
||||||
|
tex.SetPixels(colors.ToArray());
|
||||||
|
tex.Apply();
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BurstCompile]
|
||||||
|
private struct ChannelJob : IJobParallelFor
|
||||||
|
{
|
||||||
|
public ChannelType channelType;
|
||||||
|
[ReadOnly] public NativeArray<Color> input;
|
||||||
|
public NativeArray<Color> output;
|
||||||
|
|
||||||
|
public ChannelJob(ChannelType type, NativeArray<Color> input, NativeArray<Color> output)
|
||||||
|
{
|
||||||
|
this.channelType = type;
|
||||||
|
this.input = input;
|
||||||
|
this.output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(int index)
|
||||||
|
{
|
||||||
|
Color c = input[index];
|
||||||
|
switch (channelType)
|
||||||
|
{
|
||||||
|
case ChannelType.R:
|
||||||
|
output[index] = new Color(c.r, c.r, c.r, 1f);
|
||||||
|
break;
|
||||||
|
case ChannelType.G:
|
||||||
|
output[index] = new Color(c.g, c.g, c.g, 1f);
|
||||||
|
break;
|
||||||
|
case ChannelType.B:
|
||||||
|
output[index] = new Color(c.b, c.b, c.b, 1f);
|
||||||
|
break;
|
||||||
|
case ChannelType.A:
|
||||||
|
output[index] = new Color(c.a, c.a, c.a, 1f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ChannelType
|
||||||
|
{
|
||||||
|
R, G, B, A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 461687d0e0d346ac9dd5960b76db7538
|
||||||
|
timeCreated: 1745899335
|
@ -0,0 +1,104 @@
|
|||||||
|
using ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor.Nodes.Types.Image.Utilities.ViewNode
|
||||||
|
{
|
||||||
|
public class ViewTextureNodeEditor : ImageProcessingGraphNodeVisual
|
||||||
|
{
|
||||||
|
private UnityEngine.UIElements.Image viewableImage;
|
||||||
|
private Foldout foldout;
|
||||||
|
private VisualElement buttonRow;
|
||||||
|
|
||||||
|
public ViewTextureNodeEditor(BaseImageNode node, ImageProcessingGraphViewWindow window) : base(node, window)
|
||||||
|
{
|
||||||
|
//Port 0 is Texture2D
|
||||||
|
//Port 1 is ImageData
|
||||||
|
|
||||||
|
IPTPort tex2DPort = InputPorts[0] as IPTPort;
|
||||||
|
IPTPort imageDataPort = InputPorts[1] as IPTPort;
|
||||||
|
|
||||||
|
tex2DPort.OnPortConnected += () => { imageDataPort.style.display = DisplayStyle.None; };
|
||||||
|
|
||||||
|
tex2DPort.OnPortDisconnected += () => { imageDataPort.style.display = DisplayStyle.Flex; };
|
||||||
|
|
||||||
|
imageDataPort.OnPortConnected += () => { tex2DPort.style.display = DisplayStyle.None; };
|
||||||
|
|
||||||
|
imageDataPort.OnPortDisconnected += () => { tex2DPort.style.display = DisplayStyle.Flex; };
|
||||||
|
|
||||||
|
this.Q("output").style.display = DisplayStyle.None;
|
||||||
|
|
||||||
|
ViewTextureNode viewImageNode = node as ViewTextureNode;
|
||||||
|
|
||||||
|
foldout = new Foldout
|
||||||
|
{
|
||||||
|
text = "Texture Viewer",
|
||||||
|
value = true
|
||||||
|
};
|
||||||
|
foldout.style.backgroundColor = new Color(63f / 255f, 63f / 255f, 63f / 255f, 205f / 255f);
|
||||||
|
foldout.contentContainer.style.justifyContent = Justify.Center;
|
||||||
|
Add(foldout);
|
||||||
|
|
||||||
|
viewableImage = new UnityEngine.UIElements.Image
|
||||||
|
{
|
||||||
|
scaleMode = ScaleMode.ScaleToFit
|
||||||
|
};
|
||||||
|
viewableImage.style.width = 256;
|
||||||
|
viewableImage.style.height = 256;
|
||||||
|
viewableImage.style.backgroundColor = Color.black;
|
||||||
|
foldout.Add(viewableImage);
|
||||||
|
|
||||||
|
viewImageNode.onImageUpdated += () => { viewableImage.image = viewImageNode.texture; };
|
||||||
|
|
||||||
|
buttonRow = new VisualElement
|
||||||
|
{
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
flexDirection = FlexDirection.Row,
|
||||||
|
justifyContent = Justify.SpaceBetween,
|
||||||
|
alignItems = Align.Center,
|
||||||
|
marginTop = 8,
|
||||||
|
marginBottom = 8,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
string[] channels = { "RGB", "R", "G", "B", "A" };
|
||||||
|
foreach (var channel in channels)
|
||||||
|
{
|
||||||
|
var btn = new Button(() => OnChannelClicked(viewImageNode, channel))
|
||||||
|
{
|
||||||
|
text = channel
|
||||||
|
};
|
||||||
|
btn.style.flexGrow = 1;
|
||||||
|
buttonRow.Add(btn);
|
||||||
|
}
|
||||||
|
|
||||||
|
foldout.Add(buttonRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnChannelClicked(ViewTextureNode viewNode, string channel)
|
||||||
|
{
|
||||||
|
switch (channel)
|
||||||
|
{
|
||||||
|
case "RGB":
|
||||||
|
viewableImage.image = viewNode.cachedRGB;
|
||||||
|
break;
|
||||||
|
case "R":
|
||||||
|
viewableImage.image = viewNode.cachedR;
|
||||||
|
break;
|
||||||
|
case "G":
|
||||||
|
viewableImage.image = viewNode.cachedG;
|
||||||
|
break;
|
||||||
|
case "B":
|
||||||
|
viewableImage.image = viewNode.cachedB;
|
||||||
|
break;
|
||||||
|
case "A":
|
||||||
|
viewableImage.image = viewNode.cachedA;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Debug.LogError("[ViewTextureNodeEditor] Unknown channel clicked! Did you hack reality again?");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 83fd847c22794f0a92efbecd18c528c0
|
||||||
|
timeCreated: 1745899443
|
8
Editor/Scripts/Editor/Nodes/Types/String.meta
Normal file
8
Editor/Scripts/Editor/Nodes/Types/String.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 77d8c5639ebc40947940b6b150aad306
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEditor.Experimental.GraphView;
|
using UnityEditor.Experimental.GraphView;
|
||||||
|
using UnityEditor.MemoryProfiler;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
@ -25,11 +26,49 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
|
|||||||
public void OnDropOutsidePort(Edge edge, Vector2 position)
|
public void OnDropOutsidePort(Edge edge, Vector2 position)
|
||||||
{
|
{
|
||||||
window.searchProvider.target = (VisualElement)window.focusController.focusedElement;
|
window.searchProvider.target = (VisualElement)window.focusController.focusedElement;
|
||||||
SearchWindow.Open(new SearchWindowContext(position), window.searchProvider);
|
|
||||||
|
// ⚡ Override the search window open with a customized search tree
|
||||||
|
SearchWindow.Open(new SearchWindowContext(position), new ImageProcessingGraphSearchProvider.CustomSearchProviderForEdge(window.searchProvider, edge, (IPTPort)edge.input, (IPTPort)edge.output));
|
||||||
|
|
||||||
|
// Remove connections as you did
|
||||||
|
List<GraphConnection> connections = new List<GraphConnection>();
|
||||||
|
foreach (var conn in window.asset.Connections)
|
||||||
|
{
|
||||||
|
if (conn.internalEdge == edge)
|
||||||
|
connections.Add(conn);
|
||||||
|
}
|
||||||
|
foreach (var VARIABLE in connections)
|
||||||
|
{
|
||||||
|
window.asset.Connections.Remove(VARIABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edge.input != null)
|
||||||
|
if (edge.input.node != null)
|
||||||
|
((ImageProcessingGraphNodeVisual)edge.input.node).ToggleExposedVariable((IPTPort)edge.input, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void OnDrop(UnityEditor.Experimental.GraphView.GraphView graphView, Edge edge)
|
public void OnDrop(UnityEditor.Experimental.GraphView.GraphView graphView, Edge edge)
|
||||||
{
|
{
|
||||||
|
List<GraphConnection> connections = new List<GraphConnection>();
|
||||||
|
|
||||||
|
foreach (var conn in window.asset.Connections)
|
||||||
|
{
|
||||||
|
if (conn.internalEdge == edge)
|
||||||
|
{
|
||||||
|
connections.Add(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var VARIABLE in connections)
|
||||||
|
{
|
||||||
|
window.asset.Connections.Remove(VARIABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
((ImageProcessingGraphNodeVisual)edge.input.node).ToggleExposedVariable((IPTPort)edge.input, true);
|
||||||
|
|
||||||
|
|
||||||
this.m_EdgesToCreate.Clear();
|
this.m_EdgesToCreate.Clear();
|
||||||
this.m_EdgesToCreate.Add(edge);
|
this.m_EdgesToCreate.Add(edge);
|
||||||
this.m_EdgesToDelete.Clear();
|
this.m_EdgesToDelete.Clear();
|
@ -14,23 +14,25 @@ namespace ImageProcessingGraph.Editor.Windows
|
|||||||
|
|
||||||
public static void Open(ImageProcessingGraphAsset asset)
|
public static void Open(ImageProcessingGraphAsset asset)
|
||||||
{
|
{
|
||||||
ImageProcessingGraphEditorWindow[] windows = Resources.FindObjectsOfTypeAll<ImageProcessingGraphEditorWindow>();
|
var existingWindows = Resources.FindObjectsOfTypeAll<ImageProcessingGraphEditorWindow>();
|
||||||
|
foreach (var w in existingWindows)
|
||||||
foreach (var w in windows)
|
|
||||||
{
|
{
|
||||||
w.Focus();
|
if (w.CurrentGraph == asset)
|
||||||
return;
|
{
|
||||||
|
w.Focus(); // 👁 focus the OG window
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageProcessingGraphEditorWindow window =
|
var window = CreateWindow<ImageProcessingGraphEditorWindow>(typeof(SceneView));
|
||||||
CreateWindow<ImageProcessingGraphEditorWindow>(typeof(ImageProcessingGraphEditorWindow),
|
window.titleContent = new GUIContent($"{asset.name}",
|
||||||
typeof(SceneView));
|
EditorGUIUtility.ObjectContent(null, typeof(ImageProcessingGraphAsset)).image);
|
||||||
|
|
||||||
window.titleContent = new GUIContent($"{asset.name}", EditorGUIUtility.ObjectContent(null, typeof(ImageProcessingGraphAsset)).image
|
|
||||||
);
|
|
||||||
window.Load(asset);
|
window.Load(asset);
|
||||||
|
window.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void OnEnable()
|
void OnEnable()
|
||||||
{
|
{
|
||||||
if(currentGraph != null)
|
if(currentGraph != null)
|
318
Editor/Scripts/Editor/Windows/ImageProcessingGraphNodeVisual.cs
Normal file
318
Editor/Scripts/Editor/Windows/ImageProcessingGraphNodeVisual.cs
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Experimental.GraphView;
|
||||||
|
using UnityEditor.UIElements;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
using Input = ImageProcessingGraph.Editor.Nodes.NodeAttributes.Input;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows
|
||||||
|
{
|
||||||
|
public class ImageProcessingGraphNodeVisual : Node
|
||||||
|
{
|
||||||
|
private BaseImageNode graphNode;
|
||||||
|
public BaseImageNode GraphNode => graphNode;
|
||||||
|
|
||||||
|
public List<Port> InputPorts { get; }
|
||||||
|
public List<Port> OutputPorts { get; }
|
||||||
|
|
||||||
|
private ImageProcessingGraphViewWindow window;
|
||||||
|
|
||||||
|
private StyleSheet defaaStyleSheet;
|
||||||
|
private StyleSheet errorStyleSheet;
|
||||||
|
|
||||||
|
public ImageProcessingGraphNodeVisual(BaseImageNode node, ImageProcessingGraphViewWindow window)
|
||||||
|
{
|
||||||
|
this.AddToClassList("image-node-visual");
|
||||||
|
this.window = window;
|
||||||
|
graphNode = node;
|
||||||
|
|
||||||
|
|
||||||
|
Type typeInfo = node.GetType();
|
||||||
|
NodeInfoAttribute info = typeInfo.GetCustomAttribute<NodeInfoAttribute>();
|
||||||
|
title = info.Title;
|
||||||
|
this.name = typeInfo.Name;
|
||||||
|
|
||||||
|
string[] depths = info.MenuItem.Split('/');
|
||||||
|
foreach (var depth in depths)
|
||||||
|
{
|
||||||
|
this.AddToClassList(depth.ToLower().Replace(' ', '-'));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.InputPorts = new List<Port>();
|
||||||
|
this.OutputPorts = new List<Port>();
|
||||||
|
|
||||||
|
List<Input> inputs = new List<Input>();
|
||||||
|
List<FieldInfo> inputFieldInfo = new List<FieldInfo>();
|
||||||
|
List<FieldInfo> outputFieldInfo = new List<FieldInfo>();
|
||||||
|
|
||||||
|
FieldInfo[] fields = typeInfo.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
if (field.GetCustomAttribute(typeof(Input)) != null)
|
||||||
|
{
|
||||||
|
Input input = field.GetCustomAttribute<Input>();
|
||||||
|
inputs.Add(input);
|
||||||
|
inputFieldInfo.Add(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.GetCustomAttribute(typeof(Output)) != null)
|
||||||
|
{
|
||||||
|
Output output = field.GetCustomAttribute<Output>();
|
||||||
|
outputFieldInfo.Add(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateInputPorts(inputFieldInfo);
|
||||||
|
CreateOutputPorts(outputFieldInfo);
|
||||||
|
|
||||||
|
defaaStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Unity Image Processing/Node.uss");
|
||||||
|
if (defaaStyleSheet == null)
|
||||||
|
{
|
||||||
|
defaaStyleSheet = EditorGUIUtility.Load("Packages/com.chromium.imageprocessingrah/Node.uss") as StyleSheet;
|
||||||
|
}
|
||||||
|
styleSheets.Add(defaaStyleSheet);
|
||||||
|
|
||||||
|
errorStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Unity Image Processing/NodeError.uss");
|
||||||
|
if (errorStyleSheet == null)
|
||||||
|
{
|
||||||
|
errorStyleSheet = EditorGUIUtility.Load("Packages/com.chromium.imageprocessingrah/NodeError.uss") as StyleSheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
graphNode.onFailed += () =>
|
||||||
|
{
|
||||||
|
styleSheets.Add(errorStyleSheet);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.asset.OnRun += () =>
|
||||||
|
{
|
||||||
|
if (styleSheets.Contains(errorStyleSheet))
|
||||||
|
styleSheets.Remove(errorStyleSheet);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateInputPorts(List<FieldInfo> fields)
|
||||||
|
{
|
||||||
|
for (var index = 0; index < fields.Count; index++)
|
||||||
|
{
|
||||||
|
var field = fields[index];
|
||||||
|
var port = IPTPort.Create(window.edgeConnectorListener, true, field.FieldType);
|
||||||
|
|
||||||
|
string label = field.GetCustomAttribute<Input>().Label;
|
||||||
|
if (label != "")
|
||||||
|
port.portName = label;
|
||||||
|
InputPorts.Add(port);
|
||||||
|
|
||||||
|
|
||||||
|
inputContainer.Add(port);
|
||||||
|
ExposeVariableToPort(port, field);
|
||||||
|
port.fieldInfo = field;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateOutputPorts(List<FieldInfo> fields)
|
||||||
|
{
|
||||||
|
for (var index = 0; index < fields.Count; index++)
|
||||||
|
{
|
||||||
|
var field = fields[index];
|
||||||
|
var port = IPTPort.Create(window.edgeConnectorListener, false, field.FieldType);
|
||||||
|
|
||||||
|
string label = field.GetCustomAttribute<Output>().Label;
|
||||||
|
if (label != "")
|
||||||
|
port.portName = label;
|
||||||
|
OutputPorts.Add(port);
|
||||||
|
outputContainer.Add(port);
|
||||||
|
|
||||||
|
port.fieldInfo = field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exposes a variable on the port for editing when it's not connected
|
||||||
|
public void ExposeVariableToPort(Port port, FieldInfo field)
|
||||||
|
{
|
||||||
|
VisualElement NewElement = new VisualElement();
|
||||||
|
var ExposedPropertyContainer = ((IPTPort)port).ExposedPropertyContainer;
|
||||||
|
Type containerType = null;
|
||||||
|
|
||||||
|
if (ExposedPropertyContainer == null)
|
||||||
|
{
|
||||||
|
NewElement.name = "property-field-container";
|
||||||
|
VisualElement the = CreatePropertyFieldForType(field.FieldType, field.GetValue(graphNode));
|
||||||
|
|
||||||
|
if(the != null)
|
||||||
|
containerType = the.GetType();
|
||||||
|
|
||||||
|
NewElement.Add(the);
|
||||||
|
((IPTPort)port).ExposedPropertyContainer = the;
|
||||||
|
ExposedPropertyContainer = ((IPTPort)port).ExposedPropertyContainer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
containerType = ExposedPropertyContainer.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containerType == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ExposedPropertyContainer.GetType() == typeof(IntegerField))
|
||||||
|
{
|
||||||
|
var intField = ExposedPropertyContainer as IntegerField;
|
||||||
|
intField.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ExposedPropertyContainer is FloatField floatField)
|
||||||
|
{
|
||||||
|
floatField.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ExposedPropertyContainer is Toggle boolField)
|
||||||
|
{
|
||||||
|
boolField.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ExposedPropertyContainer is TextField stringField)
|
||||||
|
{
|
||||||
|
stringField.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ExposedPropertyContainer is ColorField colorField)
|
||||||
|
{
|
||||||
|
colorField.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ExposedPropertyContainer is Vector3Field vector3Field)
|
||||||
|
{
|
||||||
|
vector3Field.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ExposedPropertyContainer is Vector2Field vector2Field)
|
||||||
|
{
|
||||||
|
vector2Field.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ExposedPropertyContainer is ObjectField objectField)
|
||||||
|
{
|
||||||
|
objectField.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ExposedPropertyContainer is EnumField enumField)
|
||||||
|
{
|
||||||
|
enumField.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
field.SetValue(graphNode, evt.newValue); // 🎯 Update the field with the new enum value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (ExposedPropertyContainer.GetType() == typeof(GreyscaleField))
|
||||||
|
{
|
||||||
|
var greyscaleField = ExposedPropertyContainer as GreyscaleField;
|
||||||
|
greyscaleField.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
var value = (GreyscaleValue)field.GetValue(graphNode);
|
||||||
|
|
||||||
|
if (evt.newValue > greyscaleField.minMax.Item2)
|
||||||
|
value.value = greyscaleField.minMax.Item2;
|
||||||
|
else if (evt.newValue < greyscaleField.minMax.Item1) value.value = greyscaleField.minMax.Item1;
|
||||||
|
|
||||||
|
value.value = evt.newValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
port.Add(NewElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToggleExposedVariable(Port port, bool value)
|
||||||
|
{
|
||||||
|
IPTPort iptPort = port as IPTPort;
|
||||||
|
if(iptPort.ExposedPropertyContainer != null)
|
||||||
|
iptPort.ExposedPropertyContainer.style.display = value ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private VisualElement CreatePropertyFieldForType(Type type, object value)
|
||||||
|
{
|
||||||
|
if (type == typeof(int))
|
||||||
|
{
|
||||||
|
var intField = new IntegerField { value = (int)value };
|
||||||
|
return intField;
|
||||||
|
}
|
||||||
|
else if (type == typeof(float))
|
||||||
|
{
|
||||||
|
var floatField = new FloatField { value = (float)value };
|
||||||
|
return floatField;
|
||||||
|
}
|
||||||
|
else if (type == typeof(bool))
|
||||||
|
{
|
||||||
|
var boolField = new Toggle { value = (bool)value };
|
||||||
|
return boolField;
|
||||||
|
}
|
||||||
|
else if (type == typeof(string))
|
||||||
|
{
|
||||||
|
var stringField = new TextField { value = (string)value };
|
||||||
|
return stringField;
|
||||||
|
}
|
||||||
|
else if (type == typeof(Color))
|
||||||
|
{
|
||||||
|
var colorField = new ColorField() { value = (Color)value };
|
||||||
|
return colorField;
|
||||||
|
}
|
||||||
|
else if (type == typeof(Vector3))
|
||||||
|
{
|
||||||
|
var vector3Field = new Vector3Field { value = (Vector3)value };
|
||||||
|
return vector3Field;
|
||||||
|
}
|
||||||
|
else if (type == typeof(Vector2))
|
||||||
|
{
|
||||||
|
var vector2Field = new Vector2Field { value = (Vector2)value };
|
||||||
|
return vector2Field;
|
||||||
|
}
|
||||||
|
else if (type == typeof(Texture2D))
|
||||||
|
{
|
||||||
|
var objectField = new ObjectField { value = (Texture2D)value, objectType = typeof(Texture2D) };
|
||||||
|
return objectField;
|
||||||
|
}
|
||||||
|
else if (type.IsEnum) // 💥✨ ENUMS, BABY! 💥✨
|
||||||
|
{
|
||||||
|
var enumField = new EnumField((Enum)value);
|
||||||
|
return enumField;
|
||||||
|
}
|
||||||
|
else if (type == typeof(GreyscaleValue))
|
||||||
|
{
|
||||||
|
|
||||||
|
var greyscaleValue = (GreyscaleValue)value;
|
||||||
|
var intField = new GreyscaleField { value = (int)greyscaleValue.value };
|
||||||
|
return intField;
|
||||||
|
}
|
||||||
|
else if (typeof(UnityEngine.Object).IsAssignableFrom(type))
|
||||||
|
{
|
||||||
|
var objectField = new ObjectField { value = (UnityEngine.Object)value, objectType = typeof(UnityEngine.Object) };
|
||||||
|
return objectField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add more types as needed
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SavePosition() => graphNode.SetPosition(GetPosition());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,282 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Codice.Client.Common;
|
||||||
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.Experimental.GraphView;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
using Input = ImageProcessingGraph.Editor.Nodes.NodeAttributes.Input;
|
||||||
|
|
||||||
|
namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows
|
||||||
|
{
|
||||||
|
public struct SearchContextElement
|
||||||
|
{
|
||||||
|
public object target { get; private set; }
|
||||||
|
public string title { get; private set; }
|
||||||
|
|
||||||
|
public Dictionary<int, Type> ImportPortTypes { get; private set; }
|
||||||
|
public Dictionary<int, Type> ExportPortTypes { get; private set; }
|
||||||
|
public int portID;
|
||||||
|
|
||||||
|
public SearchContextElement(object target, string title, Dictionary<int, Type> importPortTypes,
|
||||||
|
Dictionary<int, Type> exportPortTypes, int portID)
|
||||||
|
{
|
||||||
|
this.target = target;
|
||||||
|
this.title = title;
|
||||||
|
this.ImportPortTypes = importPortTypes;
|
||||||
|
this.ExportPortTypes = exportPortTypes;
|
||||||
|
this.portID = portID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ImageProcessingGraphSearchProvider : ScriptableObject, ISearchWindowProvider
|
||||||
|
{
|
||||||
|
public ImageProcessingGraphViewWindow graph;
|
||||||
|
public VisualElement target;
|
||||||
|
|
||||||
|
public static List<SearchContextElement> elements;
|
||||||
|
private Assembly[] assemblies;
|
||||||
|
|
||||||
|
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
|
||||||
|
{
|
||||||
|
List<SearchTreeEntry> tree = new List<SearchTreeEntry>();
|
||||||
|
tree.Add(new SearchTreeGroupEntry(new GUIContent("Nodes"), 0));
|
||||||
|
|
||||||
|
elements = new List<SearchContextElement>();
|
||||||
|
|
||||||
|
foreach (var type in TypeCache.GetTypesWithAttribute<NodeInfoAttribute>())
|
||||||
|
{
|
||||||
|
var attr = type.GetCustomAttribute<NodeInfoAttribute>();
|
||||||
|
NodeInfoAttribute info = attr as NodeInfoAttribute;
|
||||||
|
var node = Activator.CreateInstance(type);
|
||||||
|
if (string.IsNullOrEmpty(info.MenuItem)) continue;
|
||||||
|
|
||||||
|
var Fields = type.GetFields();
|
||||||
|
Dictionary<int, Type> ImportPortTypes = new Dictionary<int, Type>();
|
||||||
|
Dictionary<int, Type> OutputPortTypes = new Dictionary<int, Type>();
|
||||||
|
|
||||||
|
foreach (var field in Fields)
|
||||||
|
{
|
||||||
|
if (field.GetCustomAttribute<Input>() != null)
|
||||||
|
ImportPortTypes.Add(ImportPortTypes.Count, field.FieldType);
|
||||||
|
if (field.GetCustomAttribute<Output>() != null)
|
||||||
|
OutputPortTypes.Add(OutputPortTypes.Count, field.FieldType);
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.Add(new SearchContextElement(node, info.MenuItem, ImportPortTypes, OutputPortTypes, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.Sort((entry1, entry2) =>
|
||||||
|
{
|
||||||
|
string[] splits1 = entry1.title.Split('/');
|
||||||
|
string[] splits2 = entry2.title.Split('/');
|
||||||
|
|
||||||
|
for (int i = 0; i < splits1.Length; i++)
|
||||||
|
{
|
||||||
|
if (i >= splits2.Length) return 1;
|
||||||
|
|
||||||
|
int value = splits1[i].CompareTo(splits2[i]);
|
||||||
|
if (value != 0)
|
||||||
|
{
|
||||||
|
if (splits1.Length != splits2.Length && (i == splits1.Length - 1 || i == splits2.Length - 1))
|
||||||
|
return splits1.Length < splits2.Length ? 1 : -1;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
List<string> groups = new List<string>();
|
||||||
|
|
||||||
|
foreach (var element in elements)
|
||||||
|
{
|
||||||
|
string[] entryTitle = element.title.Split('/');
|
||||||
|
|
||||||
|
string groupName = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < entryTitle.Length - 1; i++)
|
||||||
|
{
|
||||||
|
groupName += entryTitle[i];
|
||||||
|
if (!groups.Contains(groupName))
|
||||||
|
{
|
||||||
|
tree.Add(new SearchTreeGroupEntry(new GUIContent(groupName), i + 1));
|
||||||
|
groups.Add(groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
groupName += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(entryTitle.Last()));
|
||||||
|
entry.level = entryTitle.Length;
|
||||||
|
entry.userData = element;
|
||||||
|
tree.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
|
||||||
|
{
|
||||||
|
var mousePos =
|
||||||
|
graph.ChangeCoordinatesTo(graph, context.screenMousePosition - graph.Window.position.position);
|
||||||
|
var graphMousePosition = graph.contentViewContainer.WorldToLocal(mousePos);
|
||||||
|
|
||||||
|
SearchContextElement element = (SearchContextElement)SearchTreeEntry.userData;
|
||||||
|
|
||||||
|
BaseImageNode node = (BaseImageNode)element.target;
|
||||||
|
node.SetPosition(new Rect(graphMousePosition, new Vector2()));
|
||||||
|
graph.Add(node);
|
||||||
|
node.asset = graph.asset;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SearchTreeEntry> CreateSearchTreeForEdge(SearchWindowContext context, Edge edge)
|
||||||
|
{
|
||||||
|
var tree = new List<SearchTreeEntry>();
|
||||||
|
tree.Add(new SearchTreeGroupEntry(new GUIContent("Compatible Nodes"), 0));
|
||||||
|
|
||||||
|
elements = new List<SearchContextElement>();
|
||||||
|
var groups = new List<string>();
|
||||||
|
|
||||||
|
var sourcePort = edge.output ?? edge.input;
|
||||||
|
bool isSourceOutput = edge.output != null;
|
||||||
|
Type targetType = (sourcePort as IPTPort)?.portType;
|
||||||
|
|
||||||
|
if (targetType == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Could not determine port type from edge!");
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var type in TypeCache.GetTypesWithAttribute<NodeInfoAttribute>())
|
||||||
|
{
|
||||||
|
var attr = type.GetCustomAttribute<NodeInfoAttribute>();
|
||||||
|
if (string.IsNullOrEmpty(attr.MenuItem)) continue;
|
||||||
|
|
||||||
|
var node = Activator.CreateInstance(type);
|
||||||
|
var fields = type.GetFields();
|
||||||
|
|
||||||
|
// Get all ports
|
||||||
|
var inputPorts = fields.Where(f => f.GetCustomAttribute<Input>() != null).ToList();
|
||||||
|
var outputPorts = fields.Where(f => f.GetCustomAttribute<Output>() != null).ToList();
|
||||||
|
|
||||||
|
// REVERSED LOGIC:
|
||||||
|
// If dragging from output, we want compatible INPUT ports
|
||||||
|
// If dragging from input, we want compatible OUTPUT ports
|
||||||
|
var compatiblePorts = isSourceOutput
|
||||||
|
? inputPorts.Where(f => f.FieldType.IsAssignableFrom(targetType)).ToList()
|
||||||
|
: outputPorts.Where(f => targetType.IsAssignableFrom(f.FieldType)).ToList();
|
||||||
|
|
||||||
|
if (compatiblePorts.Count == 0) continue;
|
||||||
|
|
||||||
|
// Build group hierarchy
|
||||||
|
var menuPath = attr.MenuItem.Split('/');
|
||||||
|
var currentGroupPath = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < menuPath.Length - 1; i++)
|
||||||
|
{
|
||||||
|
currentGroupPath += menuPath[i];
|
||||||
|
if (!groups.Contains(currentGroupPath))
|
||||||
|
{
|
||||||
|
tree.Add(new SearchTreeGroupEntry(new GUIContent(menuPath[i]), i + 1));
|
||||||
|
groups.Add(currentGroupPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentGroupPath += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Add node entry
|
||||||
|
// var nodeTitle = $"{type.Name}: {menuPath.Last()}";
|
||||||
|
// tree.Add(new SearchTreeEntry(new GUIContent(nodeTitle))
|
||||||
|
// {
|
||||||
|
// level = menuPath.Length,
|
||||||
|
// userData = new SearchContextElement(
|
||||||
|
// node,
|
||||||
|
// nodeTitle,
|
||||||
|
// inputPorts.ToDictionary(f => inputPorts.IndexOf(f), f => f.FieldType),
|
||||||
|
// outputPorts.ToDictionary(f => outputPorts.IndexOf(f), f => f.FieldType))
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Add compatible port entries
|
||||||
|
foreach (var portField in compatiblePorts)
|
||||||
|
{
|
||||||
|
var portList = portField.GetCustomAttribute<Input>() != null ? inputPorts : outputPorts;
|
||||||
|
int portIndex = portList.IndexOf(portField);
|
||||||
|
|
||||||
|
var portTitle = $"{attr.Title}: {portField.Name}";
|
||||||
|
tree.Add(new SearchTreeEntry(new GUIContent(portTitle))
|
||||||
|
{
|
||||||
|
level = menuPath.Length,
|
||||||
|
userData = new SearchContextElement(
|
||||||
|
node,
|
||||||
|
portTitle,
|
||||||
|
inputPorts.ToDictionary(f => inputPorts.IndexOf(f), f => f.FieldType),
|
||||||
|
outputPorts.ToDictionary(f => outputPorts.IndexOf(f), f => f.FieldType),
|
||||||
|
portIndex)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomSearchProviderForEdge : ScriptableObject, ISearchWindowProvider
|
||||||
|
{
|
||||||
|
private readonly ImageProcessingGraphSearchProvider original;
|
||||||
|
private readonly Edge edge;
|
||||||
|
private readonly IPTPort inputPort;
|
||||||
|
private readonly IPTPort outputPort;
|
||||||
|
|
||||||
|
public CustomSearchProviderForEdge(ImageProcessingGraphSearchProvider original, Edge edge, IPTPort inputPort, IPTPort outputPort)
|
||||||
|
{
|
||||||
|
this.original = original;
|
||||||
|
this.edge = edge;
|
||||||
|
this.inputPort = inputPort;
|
||||||
|
this.outputPort = outputPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
|
||||||
|
{
|
||||||
|
return original.CreateSearchTreeForEdge(context, edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnSelectEntry(SearchTreeEntry selectedEntry, SearchWindowContext context)
|
||||||
|
{
|
||||||
|
var mousePos =
|
||||||
|
original.graph.ChangeCoordinatesTo(original.graph, context.screenMousePosition - original.graph.Window.position.position);
|
||||||
|
var graphMousePosition = original.graph.contentViewContainer.WorldToLocal(mousePos);
|
||||||
|
|
||||||
|
SearchContextElement element = (SearchContextElement)selectedEntry.userData;
|
||||||
|
|
||||||
|
BaseImageNode node = (BaseImageNode)element.target;
|
||||||
|
node.SetPosition(new Rect(graphMousePosition, new Vector2()));
|
||||||
|
original.graph.Add(node);
|
||||||
|
node.asset = original.graph.asset;
|
||||||
|
|
||||||
|
|
||||||
|
Edge edge = new Edge();
|
||||||
|
|
||||||
|
if (inputPort != null)
|
||||||
|
{
|
||||||
|
edge = inputPort.ConnectTo(original.graph.nodeDictionary[node.ID].OutputPorts[element.portID]);
|
||||||
|
}
|
||||||
|
else if (outputPort != null)
|
||||||
|
{
|
||||||
|
edge = outputPort.ConnectTo(original.graph.nodeDictionary[node.ID].InputPorts[element.portID]);
|
||||||
|
}
|
||||||
|
|
||||||
|
original.graph.CreateEdge(edge);
|
||||||
|
original.graph.AddElement((GraphElement)edge);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
||||||
using ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows;
|
using ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows;
|
||||||
using ImageProcessingGraph.Editor.Windows;
|
using ImageProcessingGraph.Editor.Windows;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
@ -14,7 +17,6 @@ namespace ImageProcessingGraph.Editor
|
|||||||
internal ImageProcessingGraphAsset asset;
|
internal ImageProcessingGraphAsset asset;
|
||||||
private SerializedObject serializedObject;
|
private SerializedObject serializedObject;
|
||||||
private ImageProcessingGraphEditorWindow window;
|
private ImageProcessingGraphEditorWindow window;
|
||||||
|
|
||||||
public ImageProcessingGraphEditorWindow Window => window;
|
public ImageProcessingGraphEditorWindow Window => window;
|
||||||
|
|
||||||
public List<ImageProcessingGraphNodeVisual> graphNodes;
|
public List<ImageProcessingGraphNodeVisual> graphNodes;
|
||||||
@ -24,6 +26,11 @@ namespace ImageProcessingGraph.Editor
|
|||||||
internal ImageProcessingGraphSearchProvider searchProvider;
|
internal ImageProcessingGraphSearchProvider searchProvider;
|
||||||
internal ImageProcessingGraphEdgeConnectorListener edgeConnectorListener;
|
internal ImageProcessingGraphEdgeConnectorListener edgeConnectorListener;
|
||||||
|
|
||||||
|
private bool isDropdownEnabled = false;
|
||||||
|
|
||||||
|
private Button debugModeButton;
|
||||||
|
private Button outputRunOrder;
|
||||||
|
|
||||||
public ImageProcessingGraphViewWindow(SerializedObject obeject, ImageProcessingGraphEditorWindow window)
|
public ImageProcessingGraphViewWindow(SerializedObject obeject, ImageProcessingGraphEditorWindow window)
|
||||||
{
|
{
|
||||||
this.serializedObject = obeject;
|
this.serializedObject = obeject;
|
||||||
@ -43,17 +50,20 @@ namespace ImageProcessingGraph.Editor
|
|||||||
this.window = window;
|
this.window = window;
|
||||||
|
|
||||||
StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Unity Image Processing/GraphView.uss");
|
StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Unity Image Processing/GraphView.uss");
|
||||||
|
if (styleSheet == null)
|
||||||
|
{
|
||||||
|
styleSheet = EditorGUIUtility.Load("Packages/com.chromium.imageprocessingrah/GraphView.uss") as StyleSheet;
|
||||||
|
}
|
||||||
styleSheets.Add(styleSheet);
|
styleSheets.Add(styleSheet);
|
||||||
|
|
||||||
GridBackground background = new GridBackground();
|
GridBackground background = new GridBackground();
|
||||||
|
|
||||||
background.name = "Grid";
|
background.name = "Grid";
|
||||||
|
|
||||||
|
|
||||||
Add(background);
|
Add(background);
|
||||||
|
|
||||||
background.SendToBack();
|
background.SendToBack();
|
||||||
|
|
||||||
|
CreateButtons();
|
||||||
|
|
||||||
|
|
||||||
this.AddManipulator(new ContentDragger());
|
this.AddManipulator(new ContentDragger());
|
||||||
this.AddManipulator(new SelectionDragger());
|
this.AddManipulator(new SelectionDragger());
|
||||||
this.AddManipulator(new RectangleSelector());
|
this.AddManipulator(new RectangleSelector());
|
||||||
@ -63,10 +73,122 @@ namespace ImageProcessingGraph.Editor
|
|||||||
DrawNodes();
|
DrawNodes();
|
||||||
DrawConnections();
|
DrawConnections();
|
||||||
|
|
||||||
|
foreach (var conn in asset.Connections)
|
||||||
|
{
|
||||||
|
if (conn.internalEdge == null)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetNode(conn.inputPort.nodeID).ToggleExposedVariable(conn.internalEdge.input, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
graphViewChanged += OnGraphViewChanged;
|
graphViewChanged += OnGraphViewChanged;
|
||||||
Undo.undoRedoEvent += UndoEvent;
|
Undo.undoRedoEvent += UndoEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CreateButtons()
|
||||||
|
{
|
||||||
|
#region Run Button
|
||||||
|
Button runButton = new Button
|
||||||
|
{
|
||||||
|
text = "Run",
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
width = 100,
|
||||||
|
height = 40,
|
||||||
|
position = Position.Absolute,
|
||||||
|
top = 10,
|
||||||
|
left = 10
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
runButton.clicked += () =>
|
||||||
|
{
|
||||||
|
asset.RunGraph();
|
||||||
|
};
|
||||||
|
|
||||||
|
Add(runButton);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#if false
|
||||||
|
debugModeButton = new Button
|
||||||
|
{
|
||||||
|
text = "Debug Mode",
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
width = 120,
|
||||||
|
height = 40,
|
||||||
|
position = Position.Absolute,
|
||||||
|
top = 55,
|
||||||
|
right = 10
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
debugModeButton.clicked += () =>
|
||||||
|
{
|
||||||
|
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
outputRunOrder = new Button
|
||||||
|
{
|
||||||
|
text = "Recalculate Run Order",
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
width = 120,
|
||||||
|
height = 40,
|
||||||
|
position = Position.Absolute,
|
||||||
|
top = 55,
|
||||||
|
right = 10,
|
||||||
|
fontSize = 9
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
outputRunOrder.clicked += () =>
|
||||||
|
{
|
||||||
|
asset.runOrder = asset.GetExecutionOrder(asset.Nodes, asset.Connections);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Button dropdownButton = new Button
|
||||||
|
{
|
||||||
|
text = "Options",
|
||||||
|
style =
|
||||||
|
{
|
||||||
|
width = 120,
|
||||||
|
height = 40,
|
||||||
|
position = Position.Absolute,
|
||||||
|
top = 10,
|
||||||
|
right = 10
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dropdownButton.clicked+= () =>
|
||||||
|
{
|
||||||
|
if (!isDropdownEnabled)
|
||||||
|
{
|
||||||
|
#if false
|
||||||
|
Add(debugModeButton);
|
||||||
|
#endif
|
||||||
|
Add(outputRunOrder);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if false
|
||||||
|
Remove(debugModeButton);
|
||||||
|
#endif
|
||||||
|
Remove(outputRunOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
isDropdownEnabled = !isDropdownEnabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Add(dropdownButton);
|
||||||
|
}
|
||||||
|
|
||||||
private ImageProcessingGraphNodeVisual GetNode(string NodeID)
|
private ImageProcessingGraphNodeVisual GetNode(string NodeID)
|
||||||
{
|
{
|
||||||
ImageProcessingGraphNodeVisual node = null;
|
ImageProcessingGraphNodeVisual node = null;
|
||||||
@ -118,7 +240,7 @@ namespace ImageProcessingGraph.Editor
|
|||||||
private void UndoEvent(in UndoRedoInfo undo)
|
private void UndoEvent(in UndoRedoInfo undo)
|
||||||
{
|
{
|
||||||
DrawNodes();
|
DrawNodes();
|
||||||
|
DrawConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
private GraphViewChange OnGraphViewChanged(GraphViewChange graphviewchange)
|
private GraphViewChange OnGraphViewChanged(GraphViewChange graphviewchange)
|
||||||
@ -136,6 +258,7 @@ namespace ImageProcessingGraph.Editor
|
|||||||
if (graphviewchange.elementsToRemove != null)
|
if (graphviewchange.elementsToRemove != null)
|
||||||
{
|
{
|
||||||
List<ImageProcessingGraphNodeVisual> nodesToRemove = graphviewchange.elementsToRemove.OfType<ImageProcessingGraphNodeVisual>().ToList();
|
List<ImageProcessingGraphNodeVisual> nodesToRemove = graphviewchange.elementsToRemove.OfType<ImageProcessingGraphNodeVisual>().ToList();
|
||||||
|
List<Edge> edges = graphviewchange.elementsToRemove.OfType<Edge>().ToList();
|
||||||
|
|
||||||
if (nodesToRemove.Count > 0)
|
if (nodesToRemove.Count > 0)
|
||||||
{
|
{
|
||||||
@ -147,6 +270,17 @@ namespace ImageProcessingGraph.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (edges.Count > 0)
|
||||||
|
{
|
||||||
|
Undo.RecordObject(serializedObject.targetObject, "Remove Edge");
|
||||||
|
|
||||||
|
foreach (var VARIABLE in edges)
|
||||||
|
{
|
||||||
|
RemoveEdge(VARIABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var VARIABLE in graphviewchange.elementsToRemove.OfType<Edge>())
|
foreach (var VARIABLE in graphviewchange.elementsToRemove.OfType<Edge>())
|
||||||
{
|
{
|
||||||
RemoveEdge(VARIABLE);
|
RemoveEdge(VARIABLE);
|
||||||
@ -168,7 +302,7 @@ namespace ImageProcessingGraph.Editor
|
|||||||
|
|
||||||
#region Edges
|
#region Edges
|
||||||
|
|
||||||
void CreateEdge(Edge edge)
|
public void CreateEdge(Edge edge)
|
||||||
{
|
{
|
||||||
ImageProcessingGraphNodeVisual outputNode = (ImageProcessingGraphNodeVisual)edge.output.node;
|
ImageProcessingGraphNodeVisual outputNode = (ImageProcessingGraphNodeVisual)edge.output.node;
|
||||||
ImageProcessingGraphNodeVisual inputNode = (ImageProcessingGraphNodeVisual)edge.input.node;
|
ImageProcessingGraphNodeVisual inputNode = (ImageProcessingGraphNodeVisual)edge.input.node;
|
||||||
@ -180,22 +314,48 @@ namespace ImageProcessingGraph.Editor
|
|||||||
string inputType = inputNode.GraphNode.GetType().Name;
|
string inputType = inputNode.GraphNode.GetType().Name;
|
||||||
string outputType = outputNode.GraphNode.GetType().Name;
|
string outputType = outputNode.GraphNode.GetType().Name;
|
||||||
|
|
||||||
GraphConnection connection = new GraphConnection(inputNode.GraphNode.ID,inputIndex, inputType,outputNode.GraphNode.ID, outputIndex, outputType);
|
GraphConnection connection = new GraphConnection(inputNode.GraphNode.ID,inputIndex, inputType,outputNode.GraphNode.ID, outputIndex, outputType, edge);
|
||||||
|
|
||||||
|
edge.output.Connect(edge);
|
||||||
|
edge.input.Connect(edge);
|
||||||
|
|
||||||
|
IPTPort portIn = (IPTPort)edge.output;
|
||||||
|
IPTPort portOut = (IPTPort)edge.output;
|
||||||
|
|
||||||
|
if (portIn.fieldInfo != null)
|
||||||
|
inputNode.ToggleExposedVariable(edge.input, false);
|
||||||
|
|
||||||
asset.Connections.Add(connection);
|
asset.Connections.Add(connection);
|
||||||
|
connectionDictionary.Add(edge, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveEdge(Edge variable)
|
private void RemoveEdge(Edge edge)
|
||||||
{
|
{
|
||||||
if (connectionDictionary.TryGetValue(variable, out GraphConnection connection))
|
if (connectionDictionary.TryGetValue(edge, out GraphConnection connection))
|
||||||
{
|
{
|
||||||
asset.Connections.Remove(connection);
|
asset.Connections.Remove(connection);
|
||||||
connectionDictionary.Remove(variable);
|
connectionDictionary.Remove(edge);
|
||||||
|
|
||||||
|
edge.output.Disconnect(edge);
|
||||||
|
edge.input.Disconnect(edge);
|
||||||
|
|
||||||
|
ImageProcessingGraphNodeVisual inputNode = (ImageProcessingGraphNodeVisual)edge.input.node;
|
||||||
|
|
||||||
|
IPTPort portIn = (IPTPort)edge.input;
|
||||||
|
|
||||||
|
if(portIn.fieldInfo != null)
|
||||||
|
inputNode.ToggleExposedVariable(edge.input, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawConnections()
|
private void DrawConnections()
|
||||||
{
|
{
|
||||||
|
foreach (KeyValuePair<Edge, GraphConnection> node in connectionDictionary)
|
||||||
|
{
|
||||||
|
RemoveElement(node.Key);
|
||||||
|
}
|
||||||
|
connectionDictionary.Clear();
|
||||||
|
|
||||||
if (asset.Connections != null)
|
if (asset.Connections != null)
|
||||||
{
|
{
|
||||||
foreach (GraphConnection conn in asset.Connections)
|
foreach (GraphConnection conn in asset.Connections)
|
||||||
@ -205,12 +365,15 @@ namespace ImageProcessingGraph.Editor
|
|||||||
|
|
||||||
if (inputNode != null && outputNode != null)
|
if (inputNode != null && outputNode != null)
|
||||||
{
|
{
|
||||||
Port inPort = inputNode.InputPorts[conn.inputPort.portID];
|
IPTPort inPort = inputNode.InputPorts[conn.inputPort.portID] as IPTPort;
|
||||||
Port outPort = outputNode.OutputPorts[conn.outputPort.portID];
|
IPTPort outPort = outputNode.OutputPorts[conn.outputPort.portID] as IPTPort;
|
||||||
|
|
||||||
Edge edge = inPort.ConnectTo(outPort);
|
Edge edge = inPort.ConnectTo(outPort);
|
||||||
AddElement(edge);
|
AddElement(edge);
|
||||||
connectionDictionary.Add(edge, conn);
|
connectionDictionary.Add(edge, conn);
|
||||||
|
conn.SetInternalEdge(edge);
|
||||||
|
|
||||||
|
((ImageProcessingGraphNodeVisual)inPort.node).ToggleExposedVariable(inPort, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -283,7 +446,18 @@ namespace ImageProcessingGraph.Editor
|
|||||||
{
|
{
|
||||||
node.typeName = node.GetType().AssemblyQualifiedName;
|
node.typeName = node.GetType().AssemblyQualifiedName;
|
||||||
|
|
||||||
ImageProcessingGraphNodeVisual editorNode = new ImageProcessingGraphNodeVisual(node, this);
|
var infoAttr = node.GetType().GetCustomAttribute<NodeInfoAttribute>();
|
||||||
|
|
||||||
|
ImageProcessingGraphNodeVisual editorNode = null;
|
||||||
|
if (typeof(ImageProcessingGraphNodeVisual).IsAssignableFrom(infoAttr.EditorType))
|
||||||
|
{
|
||||||
|
editorNode = (ImageProcessingGraphNodeVisual)Activator.CreateInstance(infoAttr.EditorType, node, this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
editorNode = new ImageProcessingGraphNodeVisual(node, this);
|
||||||
|
}
|
||||||
|
|
||||||
editorNode.SetPosition(node.Position);
|
editorNode.SetPosition(node.Position);
|
||||||
|
|
||||||
graphNodes.Add(editorNode);
|
graphNodes.Add(editorNode);
|
@ -1,232 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 1da462fd3f736d04e80556b4ac8b470f, type: 3}
|
|
||||||
m_Name: URP-MAS
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
nodes:
|
|
||||||
- rid: 6869590814762467406
|
|
||||||
- rid: 6869590814762467407
|
|
||||||
- rid: 6869590814762467408
|
|
||||||
- rid: 6869590814762467409
|
|
||||||
- rid: 6869590814762467410
|
|
||||||
- rid: 6869590814762467411
|
|
||||||
- rid: 6869590814762467412
|
|
||||||
connections:
|
|
||||||
- inputPort:
|
|
||||||
nodeID: c72eea15-501d-41b1-add3-4f0816ef3373
|
|
||||||
nodeType: StringAppend
|
|
||||||
portID: 0
|
|
||||||
outputPort:
|
|
||||||
nodeID: c21a6722-828f-4372-a718-298a0fe7b79b
|
|
||||||
nodeType: StringAppend
|
|
||||||
portID: 0
|
|
||||||
- inputPort:
|
|
||||||
nodeID: 9ccc58ba-79a7-4a7f-8057-3bdfbf8fb5c3
|
|
||||||
nodeType: Texture2DOutput
|
|
||||||
portID: 1
|
|
||||||
outputPort:
|
|
||||||
nodeID: c72eea15-501d-41b1-add3-4f0816ef3373
|
|
||||||
nodeType: StringAppend
|
|
||||||
portID: 0
|
|
||||||
- inputPort:
|
|
||||||
nodeID: 87c7a966-55ec-42aa-a537-73ebe906412b
|
|
||||||
nodeType: RGBASplit
|
|
||||||
portID: 0
|
|
||||||
outputPort:
|
|
||||||
nodeID: ac4be66e-d3b8-4633-a692-60cb30579f97
|
|
||||||
nodeType: Texture2DImport
|
|
||||||
portID: 0
|
|
||||||
- inputPort:
|
|
||||||
nodeID: 1c21d8e6-5b93-4a6f-a7ff-7322be2d20be
|
|
||||||
nodeType: RGBASplit
|
|
||||||
portID: 0
|
|
||||||
outputPort:
|
|
||||||
nodeID: 9b32fc10-fda3-4faa-82ac-efcff95a5138
|
|
||||||
nodeType: Texture2DImport
|
|
||||||
portID: 0
|
|
||||||
- inputPort:
|
|
||||||
nodeID: 3abef457-f0ec-456e-baa8-e8ae657b49e4
|
|
||||||
nodeType: RGBASCombine
|
|
||||||
portID: 1
|
|
||||||
outputPort:
|
|
||||||
nodeID: 87c7a966-55ec-42aa-a537-73ebe906412b
|
|
||||||
nodeType: RGBASplit
|
|
||||||
portID: 0
|
|
||||||
- inputPort:
|
|
||||||
nodeID: 3abef457-f0ec-456e-baa8-e8ae657b49e4
|
|
||||||
nodeType: RGBASCombine
|
|
||||||
portID: 0
|
|
||||||
outputPort:
|
|
||||||
nodeID: 1c21d8e6-5b93-4a6f-a7ff-7322be2d20be
|
|
||||||
nodeType: RGBASplit
|
|
||||||
portID: 0
|
|
||||||
- inputPort:
|
|
||||||
nodeID: 3abef457-f0ec-456e-baa8-e8ae657b49e4
|
|
||||||
nodeType: RGBASCombine
|
|
||||||
portID: 3
|
|
||||||
outputPort:
|
|
||||||
nodeID: 1c21d8e6-5b93-4a6f-a7ff-7322be2d20be
|
|
||||||
nodeType: RGBASplit
|
|
||||||
portID: 3
|
|
||||||
- inputPort:
|
|
||||||
nodeID: b882f7b7-9f8b-43ef-a79a-33d4e874edd0
|
|
||||||
nodeType: Texture2DOutput
|
|
||||||
portID: 0
|
|
||||||
outputPort:
|
|
||||||
nodeID: 3abef457-f0ec-456e-baa8-e8ae657b49e4
|
|
||||||
nodeType: RGBASCombine
|
|
||||||
portID: 0
|
|
||||||
- inputPort:
|
|
||||||
nodeID: b882f7b7-9f8b-43ef-a79a-33d4e874edd0
|
|
||||||
nodeType: Texture2DOutput
|
|
||||||
portID: 2
|
|
||||||
outputPort:
|
|
||||||
nodeID: 9b32fc10-fda3-4faa-82ac-efcff95a5138
|
|
||||||
nodeType: Texture2DImport
|
|
||||||
portID: 2
|
|
||||||
- inputPort:
|
|
||||||
nodeID: 97953833-bdf9-47b5-b1d2-b4cbfa19f57a
|
|
||||||
nodeType: StringAppend
|
|
||||||
portID: 0
|
|
||||||
outputPort:
|
|
||||||
nodeID: 9b32fc10-fda3-4faa-82ac-efcff95a5138
|
|
||||||
nodeType: Texture2DImport
|
|
||||||
portID: 1
|
|
||||||
- inputPort:
|
|
||||||
nodeID: b882f7b7-9f8b-43ef-a79a-33d4e874edd0
|
|
||||||
nodeType: Texture2DOutput
|
|
||||||
portID: 1
|
|
||||||
outputPort:
|
|
||||||
nodeID: 97953833-bdf9-47b5-b1d2-b4cbfa19f57a
|
|
||||||
nodeType: StringAppend
|
|
||||||
portID: 0
|
|
||||||
- inputPort:
|
|
||||||
nodeID: 3abef457-f0ec-456e-baa8-e8ae657b49e4
|
|
||||||
nodeType: RGBASCombine
|
|
||||||
portID: 2
|
|
||||||
outputPort:
|
|
||||||
nodeID: 1c21d8e6-5b93-4a6f-a7ff-7322be2d20be
|
|
||||||
nodeType: RGBASplit
|
|
||||||
portID: 3
|
|
||||||
- inputPort:
|
|
||||||
nodeID: 3abef457-f0ec-456e-baa8-e8ae657b49e4
|
|
||||||
nodeType: RGBASCombine
|
|
||||||
portID: 3
|
|
||||||
outputPort:
|
|
||||||
nodeID: 1c21d8e6-5b93-4a6f-a7ff-7322be2d20be
|
|
||||||
nodeType: RGBASplit
|
|
||||||
portID: 2
|
|
||||||
references:
|
|
||||||
version: 2
|
|
||||||
RefIds:
|
|
||||||
- rid: 6869590814762467406
|
|
||||||
type: {class: Texture2DImport, ns: ImageProcessingGraph.Editor.Nodes.Import_Nodes, asm: ImageProcessingGraphEditor}
|
|
||||||
data:
|
|
||||||
guid: ac4be66e-d3b8-4633-a692-60cb30579f97
|
|
||||||
position:
|
|
||||||
serializedVersion: 2
|
|
||||||
x: -285.5
|
|
||||||
y: 18
|
|
||||||
width: 311
|
|
||||||
height: 125
|
|
||||||
typeName: ImageProcessingGraph.Editor.Nodes.Import_Nodes.Texture2DImport,
|
|
||||||
ImageProcessingGraphEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
|
||||||
asset: {fileID: 11400000}
|
|
||||||
textureImport: {fileID: 2800000, guid: 44da9e368ce69454aa6ccc40919a7f50, type: 3}
|
|
||||||
fileName:
|
|
||||||
filePath:
|
|
||||||
- rid: 6869590814762467407
|
|
||||||
type: {class: Texture2DImport, ns: ImageProcessingGraph.Editor.Nodes.Import_Nodes, asm: ImageProcessingGraphEditor}
|
|
||||||
data:
|
|
||||||
guid: 9b32fc10-fda3-4faa-82ac-efcff95a5138
|
|
||||||
position:
|
|
||||||
serializedVersion: 2
|
|
||||||
x: -287
|
|
||||||
y: 292
|
|
||||||
width: 288
|
|
||||||
height: 125
|
|
||||||
typeName: ImageProcessingGraph.Editor.Nodes.Import_Nodes.Texture2DImport,
|
|
||||||
ImageProcessingGraphEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
|
||||||
asset: {fileID: 11400000}
|
|
||||||
textureImport: {fileID: 2800000, guid: 1640d081a2287448cb2a75fba48b9dcf, type: 3}
|
|
||||||
fileName:
|
|
||||||
filePath:
|
|
||||||
- rid: 6869590814762467408
|
|
||||||
type: {class: RGBASplit, ns: ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture, asm: ImageProcessingGraphEditor}
|
|
||||||
data:
|
|
||||||
guid: 87c7a966-55ec-42aa-a537-73ebe906412b
|
|
||||||
position:
|
|
||||||
serializedVersion: 2
|
|
||||||
x: 124
|
|
||||||
y: 18
|
|
||||||
width: 138
|
|
||||||
height: 149
|
|
||||||
typeName: ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture.RGBASplit,
|
|
||||||
ImageProcessingGraphEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
|
||||||
asset: {fileID: 11400000}
|
|
||||||
- rid: 6869590814762467409
|
|
||||||
type: {class: RGBASplit, ns: ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture, asm: ImageProcessingGraphEditor}
|
|
||||||
data:
|
|
||||||
guid: 1c21d8e6-5b93-4a6f-a7ff-7322be2d20be
|
|
||||||
position:
|
|
||||||
serializedVersion: 2
|
|
||||||
x: 124
|
|
||||||
y: 167
|
|
||||||
width: 138
|
|
||||||
height: 149
|
|
||||||
typeName: ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture.RGBASplit,
|
|
||||||
ImageProcessingGraphEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
|
||||||
asset: {fileID: 11400000}
|
|
||||||
- rid: 6869590814762467410
|
|
||||||
type: {class: RGBASCombine, ns: ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture, asm: ImageProcessingGraphEditor}
|
|
||||||
data:
|
|
||||||
guid: 3abef457-f0ec-456e-baa8-e8ae657b49e4
|
|
||||||
position:
|
|
||||||
serializedVersion: 2
|
|
||||||
x: 315.5
|
|
||||||
y: 92.5
|
|
||||||
width: 138
|
|
||||||
height: 149
|
|
||||||
typeName: ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture.RGBASCombine,
|
|
||||||
ImageProcessingGraphEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
|
||||||
asset: {fileID: 11400000}
|
|
||||||
- rid: 6869590814762467411
|
|
||||||
type: {class: Texture2DOutput, ns: ImageProcessingGraph.Editor.Nodes.Output, asm: ImageProcessingGraphEditor}
|
|
||||||
data:
|
|
||||||
guid: b882f7b7-9f8b-43ef-a79a-33d4e874edd0
|
|
||||||
position:
|
|
||||||
serializedVersion: 2
|
|
||||||
x: 559.5
|
|
||||||
y: 143
|
|
||||||
width: 129.5
|
|
||||||
height: 125
|
|
||||||
typeName: ImageProcessingGraph.Editor.Nodes.Output.Texture2DOutput, ImageProcessingGraphEditor,
|
|
||||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
|
||||||
asset: {fileID: 11400000}
|
|
||||||
fileName:
|
|
||||||
fileDirectory:
|
|
||||||
- rid: 6869590814762467412
|
|
||||||
type: {class: StringAppend, ns: ImageProcessingGraph.Editor.Nodes.String_Nodes, asm: ImageProcessingGraphEditor}
|
|
||||||
data:
|
|
||||||
guid: 97953833-bdf9-47b5-b1d2-b4cbfa19f57a
|
|
||||||
position:
|
|
||||||
serializedVersion: 2
|
|
||||||
x: 124
|
|
||||||
y: 450.5
|
|
||||||
width: 248
|
|
||||||
height: 101
|
|
||||||
typeName: ImageProcessingGraph.Editor.Nodes.String_Nodes.StringAppend, ImageProcessingGraphEditor,
|
|
||||||
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
|
|
||||||
asset: {fileID: 11400000}
|
|
||||||
baseString:
|
|
||||||
appendString: _MAS
|
|
||||||
output:
|
|
3
Node.uss.meta
Normal file
3
Node.uss.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f213bce165884ca7ae5d567d6bb7f3cb
|
||||||
|
timeCreated: 1745777804
|
7
NodeError.uss
Normal file
7
NodeError.uss
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#node-border
|
||||||
|
{
|
||||||
|
border-bottom-color: red;
|
||||||
|
border-left-color: red;
|
||||||
|
border-right-color: red;
|
||||||
|
border-top-color: red;
|
||||||
|
}
|
3
NodeError.uss.meta
Normal file
3
NodeError.uss.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7ecdb9925f9c4d018e1e54b8ae734ccb
|
||||||
|
timeCreated: 1745808720
|
12
README.md
12
README.md
@ -1,2 +1,14 @@
|
|||||||
# Graph-Based Image Processing Tool
|
# Graph-Based Image Processing Tool
|
||||||
|
|
||||||
A tool for applying image transformations and effects using a graph-based node system.
|
A tool for applying image transformations and effects using a graph-based node system.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
- **Tested on:**
|
||||||
|
- Unity 2022.3.50f1
|
||||||
|
- Unity 6000.0.19f1 (Unity 6)
|
||||||
|
|
||||||
|
- **Supported versions:**
|
||||||
|
- Unity 2022.0
|
||||||
|
- Unity 2023.0
|
||||||
|
- Unity 6000.0
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
using System;
|
|
||||||
using UnityEditor.Experimental.GraphView;
|
|
||||||
using UnityEngine.UIElements;
|
|
||||||
|
|
||||||
namespace ImageProcessingGraph.Editor
|
|
||||||
{
|
|
||||||
public class IPTPort : Port
|
|
||||||
{
|
|
||||||
protected IPTPort(Orientation portOrientation, Direction portDirection, Capacity portCapacity, Type type) : base(portOrientation, portDirection, portCapacity, type)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IPTPort Create(IEdgeConnectorListener connectorListener, bool isInput, Type type)
|
|
||||||
{
|
|
||||||
var port = new IPTPort(Orientation.Horizontal, isInput ? Direction.Input : Direction.Output,
|
|
||||||
Capacity.Multi, type)
|
|
||||||
{
|
|
||||||
m_EdgeConnector = new EdgeConnector<Edge>(connectorListener),
|
|
||||||
};
|
|
||||||
port.AddManipulator(port.m_EdgeConnector);
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace ImageProcessingGraph.Editor
|
|
||||||
{
|
|
||||||
#if true
|
|
||||||
[CreateAssetMenu(menuName = "PreferenceCreate")]
|
|
||||||
#endif
|
|
||||||
public class IPT_Preferences : ScriptableObject
|
|
||||||
{
|
|
||||||
[Header("Grid Settings")]
|
|
||||||
public float thickLines;
|
|
||||||
public float spacing;
|
|
||||||
public Color gridBackgroundColour;
|
|
||||||
public Color lineColour;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: ca37b9337d41484892c89933479f1e7f
|
|
||||||
timeCreated: 1743745541
|
|
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 1646a2bcaf6b4de488cadad1d7cdf795
|
|
||||||
timeCreated: 1743747481
|
|
@ -1,34 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
|
||||||
using Unity.Collections;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace ImageProcessingGraph.Editor.Nodes.Import_Nodes
|
|
||||||
{
|
|
||||||
[NodeInfo("Texture Import", "Imports/Texture2D", true)]
|
|
||||||
public class Texture2DImport : BaseImageNode
|
|
||||||
{
|
|
||||||
[NodeAttributes.Input("")]
|
|
||||||
public Texture2D textureImport;
|
|
||||||
|
|
||||||
[NodeAttributes.Output("")]
|
|
||||||
public ImageData textureOutput;
|
|
||||||
[NodeAttributes.Output("File Name")]
|
|
||||||
public string fileName;
|
|
||||||
[NodeAttributes.Output("File Path")]
|
|
||||||
public string filePath;
|
|
||||||
|
|
||||||
public override void Process()
|
|
||||||
{
|
|
||||||
if (this.textureImport != null)
|
|
||||||
{
|
|
||||||
this.textureOutput = new ImageData(textureImport);
|
|
||||||
this.fileName = textureImport.name;
|
|
||||||
this.filePath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(textureImport));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Debug.LogError("UH!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
|
||||||
using Unity.Collections;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace ImageProcessingGraph.Editor.Nodes.Output
|
|
||||||
{
|
|
||||||
[NodeInfo("Texture Export", "Export/Texture2D", true)]
|
|
||||||
public class Texture2DOutput : BaseImageNode
|
|
||||||
{
|
|
||||||
[NodeAttributes.Input("")] public ImageData inputPixels;
|
|
||||||
[NodeAttributes.Input("File Name")] public string fileName;
|
|
||||||
[NodeAttributes.Input("File Path")] public string fileDirectory;
|
|
||||||
|
|
||||||
public override void Process()
|
|
||||||
{
|
|
||||||
AssetDatabase.CreateAsset(inputPixels.ToTexture2D(), $"{fileDirectory}/{fileName}.asset");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,245 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
|
||||||
using UnityEditor.Experimental.GraphView;
|
|
||||||
using UnityEditor.UIElements;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UIElements;
|
|
||||||
using Input = ImageProcessingGraph.Editor.Nodes.NodeAttributes.Input;
|
|
||||||
|
|
||||||
namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows
|
|
||||||
{
|
|
||||||
public class ImageProcessingGraphNodeVisual : Node
|
|
||||||
{
|
|
||||||
private BaseImageNode graphNode;
|
|
||||||
public BaseImageNode GraphNode => graphNode;
|
|
||||||
|
|
||||||
public List<Port> InputPorts { get; }
|
|
||||||
public List<Port> OutputPorts { get; }
|
|
||||||
|
|
||||||
private ImageProcessingGraphViewWindow window;
|
|
||||||
|
|
||||||
public ImageProcessingGraphNodeVisual(BaseImageNode node, ImageProcessingGraphViewWindow window)
|
|
||||||
{
|
|
||||||
this.AddToClassList("image-node-visual");
|
|
||||||
this.window = window;
|
|
||||||
|
|
||||||
graphNode = node;
|
|
||||||
Type typeInfo = node.GetType();
|
|
||||||
|
|
||||||
NodeInfoAttribute info = typeInfo.GetCustomAttribute<NodeInfoAttribute>();
|
|
||||||
title = info.Title;
|
|
||||||
|
|
||||||
string[] depths = info.MenuItem.Split('/');
|
|
||||||
foreach (var depth in depths)
|
|
||||||
{
|
|
||||||
this.AddToClassList(depth.ToLower().Replace(' ', '-'));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.InputPorts = new List<Port>();
|
|
||||||
this.OutputPorts = new List<Port>();
|
|
||||||
|
|
||||||
List<Input> inputs = new List<Input>();
|
|
||||||
List<FieldInfo> inputFieldInfo = new List<FieldInfo>();
|
|
||||||
List<FieldInfo> outputFieldInfo = new List<FieldInfo>();
|
|
||||||
|
|
||||||
FieldInfo[] fields = typeInfo.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|
||||||
foreach (var field in fields)
|
|
||||||
{
|
|
||||||
if (field.GetCustomAttribute(typeof(Input)) != null)
|
|
||||||
{
|
|
||||||
Input input = field.GetCustomAttribute<Input>();
|
|
||||||
inputs.Add(input);
|
|
||||||
inputFieldInfo.Add(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.GetCustomAttribute(typeof(Output)) != null)
|
|
||||||
{
|
|
||||||
Output output = field.GetCustomAttribute<Output>();
|
|
||||||
outputFieldInfo.Add(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateInputPorts(inputFieldInfo);
|
|
||||||
CreateOutputPorts(outputFieldInfo);
|
|
||||||
|
|
||||||
this.name = typeInfo.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateInputPorts(List<FieldInfo> fields)
|
|
||||||
{
|
|
||||||
for (var index = 0; index < fields.Count; index++)
|
|
||||||
{
|
|
||||||
var field = fields[index];
|
|
||||||
/*
|
|
||||||
Port port = InstantiatePort(Orientation.Horizontal, Direction.Input, Port.Capacity.Multi, field.FieldType);
|
|
||||||
*/
|
|
||||||
|
|
||||||
var port = IPTPort.Create(window.edgeConnectorListener, true, field.FieldType);
|
|
||||||
|
|
||||||
string label = field.GetCustomAttribute<Input>().Label;
|
|
||||||
if (label != "")
|
|
||||||
port.portName = label;
|
|
||||||
InputPorts.Add(port);
|
|
||||||
|
|
||||||
inputContainer.Add(port);
|
|
||||||
ExposeVariableToPort(port, field);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateOutputPorts(List<FieldInfo> fields)
|
|
||||||
{
|
|
||||||
for (var index = 0; index < fields.Count; index++)
|
|
||||||
{
|
|
||||||
var field = fields[index];
|
|
||||||
var port = IPTPort.Create(window.edgeConnectorListener, false, field.FieldType);
|
|
||||||
|
|
||||||
string label = field.GetCustomAttribute<Output>().Label;
|
|
||||||
if (label != "")
|
|
||||||
port.portName = label;
|
|
||||||
OutputPorts.Add(port);
|
|
||||||
|
|
||||||
outputContainer.Add(port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exposes a variable on the port for editing when it's not connected
|
|
||||||
private void ExposeVariableToPort(Port port, FieldInfo field)
|
|
||||||
{
|
|
||||||
// Only expose when the port is not connected
|
|
||||||
if (port.connections.Count() == 0)
|
|
||||||
{
|
|
||||||
var propertyFieldContainer = new VisualElement();
|
|
||||||
propertyFieldContainer.name = "property-field-container";
|
|
||||||
var propertyField = CreatePropertyFieldForType(field.FieldType, field.GetValue(graphNode));
|
|
||||||
|
|
||||||
if (propertyField != null)
|
|
||||||
{
|
|
||||||
// Register a callback for when the value changes
|
|
||||||
if (propertyField is IntegerField intField)
|
|
||||||
{
|
|
||||||
intField.RegisterValueChangedCallback(evt =>
|
|
||||||
{
|
|
||||||
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (propertyField is FloatField floatField)
|
|
||||||
{
|
|
||||||
floatField.RegisterValueChangedCallback(evt =>
|
|
||||||
{
|
|
||||||
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (propertyField is Toggle boolField)
|
|
||||||
{
|
|
||||||
boolField.RegisterValueChangedCallback(evt =>
|
|
||||||
{
|
|
||||||
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (propertyField is TextField stringField)
|
|
||||||
{
|
|
||||||
stringField.RegisterValueChangedCallback(evt =>
|
|
||||||
{
|
|
||||||
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (propertyField is ColorField colorField)
|
|
||||||
{
|
|
||||||
colorField.RegisterValueChangedCallback(evt =>
|
|
||||||
{
|
|
||||||
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (propertyField is Vector3Field vector3Field)
|
|
||||||
{
|
|
||||||
vector3Field.RegisterValueChangedCallback(evt =>
|
|
||||||
{
|
|
||||||
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (propertyField is Vector2Field vector2Field)
|
|
||||||
{
|
|
||||||
vector2Field.RegisterValueChangedCallback(evt =>
|
|
||||||
{
|
|
||||||
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (propertyField is ObjectField objectField)
|
|
||||||
{
|
|
||||||
objectField.RegisterValueChangedCallback(evt =>
|
|
||||||
{
|
|
||||||
field.SetValue(graphNode, evt.newValue); // Update the field with the new value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
propertyFieldContainer.Add(propertyField);
|
|
||||||
port.Add(propertyFieldContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If the port is connected, remove the exposed UI element
|
|
||||||
var existingPropertyFieldContainer = port.Q<VisualElement>("property-field-container");
|
|
||||||
if (existingPropertyFieldContainer != null)
|
|
||||||
{
|
|
||||||
port.Remove(existingPropertyFieldContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create appropriate property field based on the type of the variable
|
|
||||||
private VisualElement CreatePropertyFieldForType(Type type, object value)
|
|
||||||
{
|
|
||||||
if (type == typeof(int))
|
|
||||||
{
|
|
||||||
var intField = new IntegerField { value = (int)value };
|
|
||||||
return intField;
|
|
||||||
}
|
|
||||||
else if (type == typeof(float))
|
|
||||||
{
|
|
||||||
var floatField = new FloatField { value = (float)value };
|
|
||||||
return floatField;
|
|
||||||
}
|
|
||||||
else if (type == typeof(bool))
|
|
||||||
{
|
|
||||||
var boolField = new Toggle { value = (bool)value };
|
|
||||||
return boolField;
|
|
||||||
}
|
|
||||||
else if (type == typeof(string))
|
|
||||||
{
|
|
||||||
var stringField = new TextField { value = (string)value };
|
|
||||||
return stringField;
|
|
||||||
}
|
|
||||||
else if (type == typeof(Color))
|
|
||||||
{
|
|
||||||
var colorField = new ColorField() { value = (Color)value };
|
|
||||||
return colorField;
|
|
||||||
}
|
|
||||||
else if (type == typeof(Vector3))
|
|
||||||
{
|
|
||||||
var vector3Field = new Vector3Field { value = (Vector3)value };
|
|
||||||
return vector3Field;
|
|
||||||
}
|
|
||||||
else if (type == typeof(Vector2))
|
|
||||||
{
|
|
||||||
var vector2Field = new Vector2Field { value = (Vector2)value };
|
|
||||||
return vector2Field;
|
|
||||||
}
|
|
||||||
else if (type == typeof(Texture2D))
|
|
||||||
{
|
|
||||||
var objectField = new ObjectField { value = (Texture2D)value, objectType = typeof(Texture2D) };
|
|
||||||
return objectField;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add more types as needed (Vector3, etc.)
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SavePosition() => graphNode.SetPosition(GetPosition());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using Codice.Client.Common;
|
|
||||||
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
|
|
||||||
using UnityEditor;
|
|
||||||
using UnityEditor.Experimental.GraphView;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UIElements;
|
|
||||||
|
|
||||||
namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows
|
|
||||||
{
|
|
||||||
public struct SearchContextElement
|
|
||||||
{
|
|
||||||
public object target { get; private set; }
|
|
||||||
public string title { get; private set; }
|
|
||||||
|
|
||||||
public SearchContextElement(object target, string title)
|
|
||||||
{
|
|
||||||
this.target = target;
|
|
||||||
this.title = title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ImageProcessingGraphSearchProvider : ScriptableObject, ISearchWindowProvider
|
|
||||||
{
|
|
||||||
public ImageProcessingGraphViewWindow graph;
|
|
||||||
public VisualElement target;
|
|
||||||
|
|
||||||
public static List<SearchContextElement> elements;
|
|
||||||
private Assembly[] assemblies;
|
|
||||||
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
|
|
||||||
{
|
|
||||||
List<SearchTreeEntry> tree = new List<SearchTreeEntry>();
|
|
||||||
tree.Add(new SearchTreeGroupEntry(new GUIContent("Nodes"), 0));
|
|
||||||
|
|
||||||
elements = new List<SearchContextElement>();
|
|
||||||
|
|
||||||
/*
|
|
||||||
assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*foreach (var assembly in assemblies)
|
|
||||||
{
|
|
||||||
foreach (Type type in assembly.GetTypes())
|
|
||||||
{
|
|
||||||
if (type.CustomAttributes.ToList() != null)
|
|
||||||
{
|
|
||||||
var attr = type.GetCustomAttribute(typeof(NodeInfoAttribute));
|
|
||||||
if (attr != null)
|
|
||||||
{
|
|
||||||
NodeInfoAttribute info = attr as NodeInfoAttribute;
|
|
||||||
var node = Activator.CreateInstance(type);
|
|
||||||
if(string.IsNullOrEmpty(info.MenuItem)) continue;
|
|
||||||
|
|
||||||
elements.Add(new SearchContextElement(node, info.MenuItem));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
foreach (var type in TypeCache.GetTypesWithAttribute<NodeInfoAttribute>())
|
|
||||||
{
|
|
||||||
var attr = type.GetCustomAttribute<NodeInfoAttribute>();
|
|
||||||
NodeInfoAttribute info = attr as NodeInfoAttribute;
|
|
||||||
var node = Activator.CreateInstance(type);
|
|
||||||
if(string.IsNullOrEmpty(info.MenuItem)) continue;
|
|
||||||
|
|
||||||
elements.Add(new SearchContextElement(node, info.MenuItem));
|
|
||||||
}
|
|
||||||
|
|
||||||
elements.Sort((entry1, entry2) =>
|
|
||||||
{
|
|
||||||
string[] splits1 = entry1.title.Split('/');
|
|
||||||
string[] splits2 = entry2.title.Split('/');
|
|
||||||
|
|
||||||
for (int i = 0; i < splits1.Length; i++)
|
|
||||||
{
|
|
||||||
if (i >= splits2.Length) return 1;
|
|
||||||
|
|
||||||
int value = splits1[i].CompareTo(splits2[i]);
|
|
||||||
if (value != 0)
|
|
||||||
{
|
|
||||||
if(splits1.Length != splits2.Length && (i == splits1.Length - 1 || i == splits2.Length - 1))
|
|
||||||
return splits1.Length < splits2.Length ? 1 : -1;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
List<string> groups = new List<string>();
|
|
||||||
|
|
||||||
foreach (var element in elements)
|
|
||||||
{
|
|
||||||
string[] entryTitle = element.title.Split('/');
|
|
||||||
|
|
||||||
string groupName = "";
|
|
||||||
|
|
||||||
for (int i = 0; i < entryTitle.Length - 1; i++)
|
|
||||||
{
|
|
||||||
groupName += entryTitle[i];
|
|
||||||
if (!groups.Contains(groupName))
|
|
||||||
{
|
|
||||||
tree.Add(new SearchTreeGroupEntry(new GUIContent(groupName), i + 1));
|
|
||||||
groups.Add(groupName);
|
|
||||||
}
|
|
||||||
groupName += '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(entryTitle.Last()));
|
|
||||||
entry.level = entryTitle.Length;
|
|
||||||
entry.userData = new SearchContextElement(element.target, element.title);
|
|
||||||
tree.Add(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
|
|
||||||
{
|
|
||||||
var mousePos = graph.ChangeCoordinatesTo(graph, context.screenMousePosition - graph.Window.position.position);
|
|
||||||
var graphMousePosition = graph.contentViewContainer.WorldToLocal(mousePos);
|
|
||||||
|
|
||||||
SearchContextElement element = (SearchContextElement)SearchTreeEntry.userData;
|
|
||||||
|
|
||||||
BaseImageNode node = (BaseImageNode)element.target;
|
|
||||||
node.SetPosition(new Rect(graphMousePosition, new Vector2()));
|
|
||||||
graph.Add(node);
|
|
||||||
node.asset = graph.asset;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
%YAML 1.1
|
|
||||||
%TAG !u! tag:unity3d.com,2011:
|
|
||||||
--- !u!114 &11400000
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 0}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 80dcc02594d68e3439df7dc743c9d4b2, type: 3}
|
|
||||||
m_Name: New IPT_Preferences
|
|
||||||
m_EditorClassIdentifier:
|
|
||||||
thickLines: 0
|
|
||||||
spacing: 0
|
|
||||||
gridBackgroundColour: {r: 0, g: 0, b: 0, a: 0}
|
|
||||||
lineColour: {r: 0, g: 0, b: 0, a: 0}
|
|
@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d4bfee84a7579474c8626fb17fcc299b
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user