Compare commits

..

1 Commits

Author SHA1 Message Date
Chromium
c76d20e362 yeah i dont think this is gon work 2025-04-29 00:14:27 +01:00
19 changed files with 151 additions and 673 deletions

View File

@ -10,13 +10,6 @@ namespace ImageProcessingGraph.Editor
{
public FieldInfo fieldInfo;
private VisualElement _exposedPropertyContainer;
public delegate void OnPortConnectedEvent();
public OnPortConnectedEvent OnPortConnected;
public delegate void OnPortDisconnectedEvent();
public OnPortDisconnectedEvent OnPortDisconnected;
public VisualElement ExposedPropertyContainer
{
set
@ -31,27 +24,9 @@ namespace ImageProcessingGraph.Editor
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,

View File

@ -53,7 +53,8 @@ namespace ImageProcessingGraph.Editor
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
runOrder = GetExecutionOrder(this.nodes, this.connections);
if (runOrder == null)
runOrder = GetExecutionOrder(this.nodes, this.connections);
bool failed = false;
@ -66,12 +67,6 @@ namespace ImageProcessingGraph.Editor
}
}
foreach (var VARIABLE in runOrder)
{
if(!VARIABLE.RunCleanUp())
failed = true;
}
// Stop the stopwatch after running the nodes
stopwatch.Stop();

View File

@ -124,26 +124,6 @@ namespace ImageProcessingGraph.Editor
{
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;
}

View File

@ -1,10 +1,5 @@
using System;
#if UNITY_2022_1_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEditor.UIElements;
#endif
namespace ImageProcessingGraph.Editor
{
@ -18,12 +13,11 @@ namespace ImageProcessingGraph.Editor
}
}
public class GreyscaleField : IntegerField
{
public (int, int) minMax = (0,255);
}
}

View File

@ -1,5 +1,4 @@
using System;
using ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows;
namespace ImageProcessingGraph.Editor.Nodes.NodeAttributes
{
@ -9,19 +8,16 @@ namespace ImageProcessingGraph.Editor.Nodes.NodeAttributes
private string name;
private string menuItem;
private string ussPath;
private Type editorType;
public string Title => name;
public string MenuItem => menuItem;
public string UssPath => ussPath;
public Type EditorType => editorType;
public NodeInfoAttribute(string name, string menuItem = "", bool requiresImage = false, string ussPath = null, Type editorType = null)
public NodeInfoAttribute(string name, string menuItem = "", bool requiresImage = false, string ussPath = null)
{
this.name = name;
this.menuItem = menuItem;
this.ussPath = ussPath;
this.editorType = editorType;
}
}

View File

@ -6,12 +6,12 @@ using UnityEngine;
namespace ImageProcessingGraph.Editor.Nodes.Output
{
[NodeInfo("Texture Export", "Export/Texture Export", true)]
[NodeInfo("Texture Export", "Export/Export Texture", true)]
public class Texture2DOutput : BaseImageNode
{
[NodeAttributes.Input("")] public ImageData inputPixels;
[NodeAttributes.Input("File Name")] public string fileName;
[NodeAttributes.Input("File Directory")] public string fileDirectory;
[NodeAttributes.Input("File Path")] public string fileDirectory;
public enum ExportType
{
@ -20,36 +20,16 @@ namespace ImageProcessingGraph.Editor.Nodes.Output
}
[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);
AssetDatabase.CreateAsset(inputPixels.ToTexture2D(), $"{fileDirectory}/{fileName}.asset");
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);
inputPixels.ExportPNG($"{fileDirectory}/{fileName}.png");
break;
}
}

View File

@ -6,7 +6,7 @@ using UnityEngine;
namespace ImageProcessingGraph.Editor.Nodes.Import_Nodes
{
[NodeInfo("Texture Import", "Imports/Import Texture")]
[NodeInfo("Texture Import", "Imports/Import Texture", true)]
public class Texture2DImport : BaseImageNode
{
[NodeAttributes.Input("")]
@ -16,7 +16,7 @@ namespace ImageProcessingGraph.Editor.Nodes.Import_Nodes
public ImageData textureOutput;
[NodeAttributes.Output("File Name")]
public string fileName;
[NodeAttributes.Output("File Directory")]
[NodeAttributes.Output("File Path")]
public string filePath;
public override void Process()
@ -42,8 +42,8 @@ namespace ImageProcessingGraph.Editor.Nodes.Import_Nodes
this.fileName = textureImport.name;
this.filePath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(textureImport));
}
// else
// Debug.LogError("UH!");
else
Debug.LogError("UH!");
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: b462f37a291b49a696e3b4b554d26868
timeCreated: 1745899248

View File

@ -1,135 +0,0 @@
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 , editorType: 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
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 461687d0e0d346ac9dd5960b76db7538
timeCreated: 1745899335

View File

@ -1,104 +0,0 @@
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;
}
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 83fd847c22794f0a92efbecd18c528c0
timeCreated: 1745899443

View File

@ -25,28 +25,29 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
public void OnDropOutsidePort(Edge edge, Vector2 position)
{
window.searchProvider.target = (VisualElement)window.focusController.focusedElement;
// ⚡ 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
// window.searchProvider.target = (VisualElement)window.focusController.focusedElement;
// SearchWindow.Open(new SearchWindowContext(position), window.searchProvider);
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)
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)

View File

@ -14,25 +14,23 @@ namespace ImageProcessingGraph.Editor.Windows
public static void Open(ImageProcessingGraphAsset asset)
{
var existingWindows = Resources.FindObjectsOfTypeAll<ImageProcessingGraphEditorWindow>();
foreach (var w in existingWindows)
ImageProcessingGraphEditorWindow[] windows = Resources.FindObjectsOfTypeAll<ImageProcessingGraphEditorWindow>();
foreach (var w in windows)
{
if (w.CurrentGraph == asset)
{
w.Focus(); // 👁 focus the OG window
return;
}
w.Focus();
return;
}
var window = CreateWindow<ImageProcessingGraphEditorWindow>(typeof(SceneView));
window.titleContent = new GUIContent($"{asset.name}",
EditorGUIUtility.ObjectContent(null, typeof(ImageProcessingGraphAsset)).image);
ImageProcessingGraphEditorWindow window =
CreateWindow<ImageProcessingGraphEditorWindow>(typeof(ImageProcessingGraphEditorWindow),
typeof(SceneView));
window.titleContent = new GUIContent($"{asset.name}", EditorGUIUtility.ObjectContent(null, typeof(ImageProcessingGraphAsset)).image
);
window.Load(asset);
window.Focus();
}
void OnEnable()
{
if(currentGraph != null)

View File

@ -29,13 +29,12 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
{
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)
@ -70,6 +69,16 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
CreateInputPorts(inputFieldInfo);
CreateOutputPorts(outputFieldInfo);
foreach (Port input in InputPorts)
{
}
foreach (Port input in OutputPorts)
{
}
defaaStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Unity Image Processing/Node.uss");
if (defaaStyleSheet == null)
@ -94,6 +103,9 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
if (styleSheets.Contains(errorStyleSheet))
styleSheets.Remove(errorStyleSheet);
};
this.name = typeInfo.Name;
}
private void CreateInputPorts(List<FieldInfo> fields)
@ -107,7 +119,6 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
if (label != "")
port.portName = label;
InputPorts.Add(port);
inputContainer.Add(port);
ExposeVariableToPort(port, field);
@ -313,17 +324,6 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
return null;
}
public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
{
evt.menu.InsertAction(evt.menu.MenuItems().Count, "Open Documentation", OpenDocumentation);
evt.menu.AppendSeparator();
base.BuildContextualMenu(evt);
}
private void OpenDocumentation(DropdownMenuAction obj)
{
}
public void SavePosition() => graphNode.SetPosition(GetPosition());
}

View File

@ -1,282 +1,92 @@
using System;
using System.Reflection;
using UnityEditor;
using UnityEditor.Search;
using UnityEditor.Experimental.GraphView;
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
using Object = UnityEngine.Object;
static class IPT_SearchProvider
{
public struct SearchContextElement
internal static string id = "IPT_Tree";
internal static List<SearchContextElement> elements;
[SearchItemProvider]
internal static SearchProvider CreateProvider()
{
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)
return new SearchProvider(id, "IPT Nodes")
{
this.target = target;
this.title = title;
this.ImportPortTypes = importPortTypes;
this.ExportPortTypes = exportPortTypes;
this.portID = portID;
filterId = "tree:",
priority = 99999,
showDetailsOptions = ShowDetailsOptions.Inspector | ShowDetailsOptions.Actions,
fetchItems = (context, items, provider) => FetchItems(context, provider),
fetchLabel = (item, context) => item.label,
fetchDescription = (item, context) => item.description,
toObject = (item, type) => item.data as Object
};
}
private static IEnumerable<SearchItem> FetchItems(SearchContext context, SearchProvider provider)
{
if (elements == null)
BuildElements();
foreach (var element in elements)
{
if (!element.title.ToLower().Contains(context.searchQuery.ToLower()))
continue;
// var item = provider.CreateItem(context, element.title, element.title, $"Node: {element.title}", 0, element.target);
var item = provider.CreateItem(context, element.title, element.title, element.title, null, element.target);
yield return item;
}
}
public class ImageProcessingGraphSearchProvider : ScriptableObject, ISearchWindowProvider
private static void BuildElements()
{
public ImageProcessingGraphViewWindow graph;
public VisualElement target;
elements = new List<SearchContextElement>();
public static List<SearchContextElement> elements;
private Assembly[] assemblies;
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
foreach (var type in TypeCache.GetTypesWithAttribute<NodeInfoAttribute>())
{
List<SearchTreeEntry> tree = new List<SearchTreeEntry>();
tree.Add(new SearchTreeGroupEntry(new GUIContent("Nodes"), 0));
var attr = type.GetCustomAttribute<NodeInfoAttribute>();
if (string.IsNullOrEmpty(attr.MenuItem))
continue;
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;
var node = Activator.CreateInstance(type);
elements.Add(new SearchContextElement(node, attr.MenuItem));
}
public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
elements.Sort((entry1, entry2) =>
{
var mousePos =
graph.ChangeCoordinatesTo(graph, context.screenMousePosition - graph.Window.position.position);
var graphMousePosition = graph.contentViewContainer.WorldToLocal(mousePos);
string[] splits1 = entry1.title.Split('/');
string[] splits2 = entry2.title.Split('/');
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)
for (int i = 0; i < splits1.Length; i++)
{
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++)
if (i >= splits2.Length) return 1;
int value = splits1[i].CompareTo(splits2[i]);
if (value != 0)
{
currentGroupPath += menuPath[i];
if (!groups.Contains(currentGroupPath))
{
tree.Add(new SearchTreeGroupEntry(new GUIContent(menuPath[i]), i + 1));
groups.Add(currentGroupPath);
}
if (splits1.Length != splits2.Length && (i == splits1.Length - 1 || i == splits2.Length - 1))
return splits1.Length < splits2.Length ? 1 : -1;
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 value;
}
}
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;
}
}
return 0;
});
}
}
}
public class SearchContextElement
{
public object target;
public string title;
public SearchContextElement(object target, string title)
{
this.target = target;
this.title = title;
}
}

View File

@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
using ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows;
using ImageProcessingGraph.Editor.Windows;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.Search;
using UnityEngine;
using UnityEngine.UIElements;
@ -23,7 +21,7 @@ namespace ImageProcessingGraph.Editor
public Dictionary<string, ImageProcessingGraphNodeVisual> nodeDictionary;
public Dictionary<Edge, GraphConnection> connectionDictionary;
internal ImageProcessingGraphSearchProvider searchProvider;
// internal ImageProcessingGraphSearchProvider searchProvider;
internal ImageProcessingGraphEdgeConnectorListener edgeConnectorListener;
private bool isDropdownEnabled = false;
@ -40,8 +38,8 @@ namespace ImageProcessingGraph.Editor
nodeDictionary = new Dictionary<string, ImageProcessingGraphNodeVisual>();
connectionDictionary = new Dictionary<Edge, GraphConnection>();
searchProvider = ScriptableObject.CreateInstance<ImageProcessingGraphSearchProvider>();
searchProvider.graph = this;
// searchProvider = ScriptableObject.CreateInstance<ImageProcessingGraphSearchProvider>();
// searchProvider.graph = this;
edgeConnectorListener = new ImageProcessingGraphEdgeConnectorListener(this);
@ -72,16 +70,20 @@ namespace ImageProcessingGraph.Editor
DrawNodes();
DrawConnections();
foreach (var conn in asset.Connections)
{
if (conn.internalEdge == null)
{
}
//GetNode(conn.inputPort.nodeID).ToggleExposedVariable(conn.internalEdge.input, true);
}
graphViewChanged += OnGraphViewChanged;
Undo.undoRedoPerformed += UndoRedoPerformed;
}
private void UndoRedoPerformed()
{
DrawNodes();
DrawConnections();
Undo.undoRedoEvent += UndoEvent;
}
private void CreateButtons()
@ -233,6 +235,12 @@ namespace ImageProcessingGraph.Editor
return compatiblePorts;
}
private void UndoEvent(in UndoRedoInfo undo)
{
DrawNodes();
DrawConnections();
}
private GraphViewChange OnGraphViewChanged(GraphViewChange graphviewchange)
{
@ -292,7 +300,7 @@ namespace ImageProcessingGraph.Editor
#region Edges
public void CreateEdge(Edge edge)
void CreateEdge(Edge edge)
{
ImageProcessingGraphNodeVisual outputNode = (ImageProcessingGraphNodeVisual)edge.output.node;
ImageProcessingGraphNodeVisual inputNode = (ImageProcessingGraphNodeVisual)edge.input.node;
@ -436,18 +444,7 @@ namespace ImageProcessingGraph.Editor
{
node.typeName = node.GetType().AssemblyQualifiedName;
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);
}
ImageProcessingGraphNodeVisual editorNode = new ImageProcessingGraphNodeVisual(node, this);
editorNode.SetPosition(node.Position);
graphNodes.Add(editorNode);
@ -495,8 +492,10 @@ namespace ImageProcessingGraph.Editor
private void ShowSearchWindow(NodeCreationContext obj)
{
searchProvider.target = (VisualElement)focusController.focusedElement;
SearchWindow.Open(new SearchWindowContext(obj.screenMousePosition), searchProvider);
// searchProvider.target = (VisualElement)focusController.focusedElement;
// SearchWindow.Open(new SearchWindowContext(obj.screenMousePosition), searchProvider);
SearchService.ShowContextual("IPT_Tree");
}
#endregion

View File

@ -6,11 +6,9 @@ A tool for applying image transformations and effects using a graph-based node s
- **Tested on:**
- Unity 2022.3.50f1
- Unity 2021.3.16f1
- Unity 6000.0.19f1 (Unity 6)
- **Supported versions:**
- Unity 2021.3
- Unity 2022.0
- Unity 2023.0
- Unity 6000.0

View File

@ -1,9 +1,9 @@
{
"name": "com.chromium.imageprocessingrah",
"version": "0.0.1",
"version": "0.0.0",
"displayName": "Image Processing Graph",
"description": "Stuffs",
"unity": "2021.3",
"unity": "2022.3",
"dependencies": {
"com.unity.burst": "1.8.17"
},