diff --git a/.gitignore b/.gitignore index 22e7db6..9ea803e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,75 +1,75 @@ -# ---> Unity -# This .gitignore file should be placed at the root of your Unity project directory -# -# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore -# -/[Ll]ibrary/ -/[Tt]emp/ -/[Oo]bj/ -/[Bb]uild/ -/[Bb]uilds/ -/[Ll]ogs/ -/[Uu]ser[Ss]ettings/ +# User-specific files +.idea/ +.vscode/ +*.suo +*.user +*.userosscache +*.sln.docstates -# MemoryCaptures can get excessive in size. -# They also could contain extremely sensitive data -/[Mm]emoryCaptures/ +# Windows image file thumbs +Thumbs.db +Desktop.ini -# Recordings can get excessive in size -/[Rr]ecordings/ +# Windows Installer files +*.cab +*.msi +*.msm +*.msp -# Uncomment this line if you wish to ignore the asset store tools plugin -# /[Aa]ssets/AssetStoreTools* +# Build directories +[Ll]ibrary/ +[Ll]ogs/ +[Tt]emp/ +[Tt]arget/ -# Autogenerated Jetbrains Rider plugin -/[Aa]ssets/Plugins/Editor/JetBrains* +# User-generated files +*.booproj +*.pidb +*.userprefs -# Visual Studio cache directory -.vs/ - -# Gradle cache directory -.gradle/ - -# Autogenerated VS/MD/Consulo solution and project files -ExportedObj/ -.consulo/ +# Unity-generated file types *.csproj *.unityproj *.sln -*.suo *.tmp -*.user -*.userprefs -*.pidb -*.booproj -*.svd -*.pdb -*.mdb -*.opendb -*.VC.db -# Unity3D generated meta files -*.pidb.meta -*.pdb.meta -*.mdb.meta +# Unity's Auto-generated Visual Studio files +.vs/ -# Unity3D generated file on crash reports -sysinfo.txt +# Memory Captures and crash dumps +*.stackdump +*.dmp +*.bak -# Builds +# Build Results *.apk -*.aab *.unitypackage -*.unitypackage.meta *.app +*.exe +*.xcodeproj +*.log +*.tlog -# Crashlytics generated file -crashlytics-build.properties +# External tools / packages (can be useful to keep if necessary) +Packages/ -# Packed Addressables -/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* +# .NET configuration files +project.settings/ -# Temporary auto-generated Android Assets -/[Aa]ssets/[Ss]treamingAssets/aa.meta -/[Aa]ssets/[Ss]treamingAssets/aa/* +# Logs and statistics +*.log +*.bak +*.swp +# JetBrains Rider files +.idea/ + +# Mac OS files +.DS_Store + +# Linux files +*~ + +# Package and Unity Asset files +AssetBundles/ +Build/ diff --git a/GraphView.uss b/GraphView.uss new file mode 100644 index 0000000..99c9258 --- /dev/null +++ b/GraphView.uss @@ -0,0 +1,12 @@ +ImageProcessingGraphViewWindow +{ + flex-grow: 1; +} + +#Grid +{ + --thick-lines: 0; + --spacing: 15; + --grid-background-color: rgb(25,25,25); + --line-color: rgb(32,32,32); +} diff --git a/GraphView.uss.meta b/GraphView.uss.meta new file mode 100644 index 0000000..29f1bf2 --- /dev/null +++ b/GraphView.uss.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0432b5222a07aad498018736ca300a6f +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0} + disableValidation: 0 diff --git a/Graphs.meta b/Graphs.meta new file mode 100644 index 0000000..b4c9dea --- /dev/null +++ b/Graphs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f6fe8f57402cf44e8bb5f1dffb3be6d8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Graphs/URP-MAS.asset b/Graphs/URP-MAS.asset new file mode 100644 index 0000000..b23b5fa --- /dev/null +++ b/Graphs/URP-MAS.asset @@ -0,0 +1,232 @@ +%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: diff --git a/Graphs/URP-MAS.asset.meta b/Graphs/URP-MAS.asset.meta new file mode 100644 index 0000000..cb411e6 --- /dev/null +++ b/Graphs/URP-MAS.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d04964bb165ef4820be34fe3f050be61 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Icons.meta b/Icons.meta new file mode 100644 index 0000000..ae553eb --- /dev/null +++ b/Icons.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2d724fe0ba9ad8949bac2c328d3d8288 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Icons/imageprocessor_icon.png b/Icons/imageprocessor_icon.png new file mode 100644 index 0000000..c26374c Binary files /dev/null and b/Icons/imageprocessor_icon.png differ diff --git a/Icons/imageprocessor_icon.png.meta b/Icons/imageprocessor_icon.png.meta new file mode 100644 index 0000000..875a56e --- /dev/null +++ b/Icons/imageprocessor_icon.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: 91cfc70686fca0145b2b37b8b1db5dff +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 0 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md index cab894d..977be5e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# Unity Image Processing Package - +# Graph-Based Image Processing Tool +A tool for applying image transformations and effects using a graph-based node system. diff --git a/README.md.meta b/README.md.meta new file mode 100644 index 0000000..f775ce2 --- /dev/null +++ b/README.md.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7c410edad4eb41efb5a4419a92f19da4 +timeCreated: 1745693574 \ No newline at end of file diff --git a/Scripts.meta b/Scripts.meta new file mode 100644 index 0000000..0635aeb --- /dev/null +++ b/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d50733debc2a73c43b07c45bcac43761 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor.meta b/Scripts/Editor.meta new file mode 100644 index 0000000..102c749 --- /dev/null +++ b/Scripts/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7dab1883baf68bf47b782e97d8b6aa42 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Data Types.meta b/Scripts/Editor/Data Types.meta new file mode 100644 index 0000000..95e1644 --- /dev/null +++ b/Scripts/Editor/Data Types.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 92c5327ff293bff4392ca3d8d16245cc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Data Types/IPTPort.cs b/Scripts/Editor/Data Types/IPTPort.cs new file mode 100644 index 0000000..e835880 --- /dev/null +++ b/Scripts/Editor/Data Types/IPTPort.cs @@ -0,0 +1,25 @@ +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(connectorListener), + }; + port.AddManipulator(port.m_EdgeConnector); + return port; + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Data Types/IPTPort.cs.meta b/Scripts/Editor/Data Types/IPTPort.cs.meta new file mode 100644 index 0000000..441a6b6 --- /dev/null +++ b/Scripts/Editor/Data Types/IPTPort.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0dd88ad7d397423897de6832d315ee7f +timeCreated: 1745310679 \ No newline at end of file diff --git a/Scripts/Editor/Data Types/IPT_Preferences.cs b/Scripts/Editor/Data Types/IPT_Preferences.cs new file mode 100644 index 0000000..bcae272 --- /dev/null +++ b/Scripts/Editor/Data Types/IPT_Preferences.cs @@ -0,0 +1,18 @@ + +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; + } + +} diff --git a/Scripts/Editor/Data Types/IPT_Preferences.cs.meta b/Scripts/Editor/Data Types/IPT_Preferences.cs.meta new file mode 100644 index 0000000..cc8c92e --- /dev/null +++ b/Scripts/Editor/Data Types/IPT_Preferences.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 80dcc02594d68e3439df7dc743c9d4b2 \ No newline at end of file diff --git a/Scripts/Editor/Data Types/ImageData.cs b/Scripts/Editor/Data Types/ImageData.cs new file mode 100644 index 0000000..3ddc4c8 --- /dev/null +++ b/Scripts/Editor/Data Types/ImageData.cs @@ -0,0 +1,65 @@ +using Unity.Collections; +using UnityEngine; + +/// +/// A structure that holds image pixel data and its dimensions. +/// +public struct ImageData +{ + /// + /// The pixel data of the image in Color32 format. + /// + public NativeArray PixelData; + + /// + /// The width and height of the image (width, height). + /// + public (int width, int height) Dimensions; + + public bool isRGBA; + + /// + /// Initializes a new instance of the struct using existing pixel data and dimensions. + /// + /// The pixel data as a NativeArray of Color32. + /// A tuple containing the width and height of the image. + public ImageData(NativeArray imageData, (int width, int height) widthHeight, bool isRgba) + { + PixelData = imageData; + Dimensions = widthHeight; + isRGBA = isRgba; + } + + /// + /// Initializes a new instance of the struct from a Texture2D object. + /// + /// The source texture to extract pixel data from. + public ImageData(Texture2D sourceTexture) + { + PixelData = sourceTexture.GetPixelData(0); + Dimensions = (sourceTexture.width, sourceTexture.height); + isRGBA = sourceTexture.format == TextureFormat.RGBA32; + } + + /// + /// Gets the width of the image. + /// + public int Width => Dimensions.width; + + /// + /// Gets the height of the image. + /// + public int Height => Dimensions.height; + + /// + /// Converts the ImageData back into a new Texture2D. + /// + /// A Texture2D object created from the stored pixel data. + public Texture2D ToTexture2D() + { + var texture = new Texture2D(Width, Height, TextureFormat.RGBA32, false); + texture.LoadRawTextureData(PixelData); + texture.Apply(); + return texture; + } +} diff --git a/Scripts/Editor/Data Types/ImageData.cs.meta b/Scripts/Editor/Data Types/ImageData.cs.meta new file mode 100644 index 0000000..1e6c3fe --- /dev/null +++ b/Scripts/Editor/Data Types/ImageData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2bb77fbc2c734e888e52e39ab92264a9 +timeCreated: 1745264169 \ No newline at end of file diff --git a/Scripts/Editor/Data Types/SplitChannelData.cs b/Scripts/Editor/Data Types/SplitChannelData.cs new file mode 100644 index 0000000..2a83491 --- /dev/null +++ b/Scripts/Editor/Data Types/SplitChannelData.cs @@ -0,0 +1,47 @@ + + +using UnityEngine; + +/// +/// Represents a single image channel (e.g., Red, Green, Blue, Alpha) as a 1D byte array, +/// along with the original image dimensions. +/// +public struct SplitChannelData +{ + /// + /// The 8-bit intensity values for a single image channel, stored in row-major order. + /// + public byte[] ChannelData; + + /// + /// The width and height of the channel data (width, height). + /// + public (int width, int height) Dimensions; + + /// + /// Initializes a new instance of the struct + /// using channel data and dimensions. + /// + /// The byte array representing the single channel values. + /// A tuple of (width, height) representing the dimensions. + public SplitChannelData(byte[] channelData, (int width, int height) widthHeight) + { + ChannelData = channelData; + Dimensions = widthHeight; + } + + /// + /// Gets the width of the channel data. + /// + public int Width => Dimensions.width; + + /// + /// Gets the height of the channel data. + /// + public int Height => Dimensions.height; + + /// + /// Checks whether the channel data is valid (non-null and matches the expected size). + /// + public bool IsValid => ChannelData != null && ChannelData.Length == Width * Height; +} diff --git a/Scripts/Editor/Data Types/SplitChannelData.cs.meta b/Scripts/Editor/Data Types/SplitChannelData.cs.meta new file mode 100644 index 0000000..6b073c6 --- /dev/null +++ b/Scripts/Editor/Data Types/SplitChannelData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c36c5c00054e46b191b4d21913512433 +timeCreated: 1745278300 \ No newline at end of file diff --git a/Scripts/Editor/ImageProcessingGraph.asmdef b/Scripts/Editor/ImageProcessingGraph.asmdef new file mode 100644 index 0000000..1c1c4b7 --- /dev/null +++ b/Scripts/Editor/ImageProcessingGraph.asmdef @@ -0,0 +1,18 @@ +{ + "name": "ImageProcessingGraphEditor", + "rootNamespace": "ImageProcessingGraph.Editor", + "references": [ + "GUID:2665a8d13d1b3f18800f46e256720795" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Scripts/Editor/ImageProcessingGraph.asmdef.meta b/Scripts/Editor/ImageProcessingGraph.asmdef.meta new file mode 100644 index 0000000..79fe0bb --- /dev/null +++ b/Scripts/Editor/ImageProcessingGraph.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3f5213b0645cf234f941e3b3d706a65a +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/ImageProcessingGraphAsset.cs b/Scripts/Editor/ImageProcessingGraphAsset.cs new file mode 100644 index 0000000..8d01e9d --- /dev/null +++ b/Scripts/Editor/ImageProcessingGraphAsset.cs @@ -0,0 +1,150 @@ +using ImageProcessingGraph.Editor.Windows; +using UnityEngine; +using UnityEditor; +using UnityEditor.Callbacks; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using UnityEditor.Experimental.GraphView; +using Debug = UnityEngine.Debug; + +// ReSharper disable once CheckNamespace +namespace ImageProcessingGraph.Editor +{ + [CreateAssetMenu(menuName = "Image Processing Graph/New Graph")] + public class ImageProcessingGraphAsset : ScriptableObject + { + [SerializeReference] private List nodes; + [SerializeField] private List connections; + [SerializeField] public List runOrder; + [SerializeField] public List stickyNotes; + + public List Nodes => nodes; + public List Connections => connections; + public ImageProcessingGraphAsset() + { + nodes = new List(); + connections = new List(); + } + + + [OnOpenAsset(1)] + public static bool OpenGraphAsset(int instanceID, int line) + { + var asset = EditorUtility.InstanceIDToObject(instanceID) as ImageProcessingGraphAsset; + if (asset != null) + { + ImageProcessingGraphEditorWindow.Open(asset); + return true; + } + return false; + } + + public void RunGraph() + { + // Create and start the stopwatch to measure time + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + + if (runOrder == null) + runOrder = GetExecutionOrder(this.nodes, this.connections); + + foreach (var VARIABLE in runOrder) + { + VARIABLE.RunNode(); + } + + // Stop the stopwatch after running the nodes + stopwatch.Stop(); + + // Log the elapsed time + UnityEngine.Debug.Log($"Graph execution took {stopwatch.ElapsedMilliseconds} milliseconds."); + } + + + /// + /// Computes a topological execution order of nodes based on their dependencies. + /// Throws if a cycle is detected or nodes are missing from the input list. + /// + public List GetExecutionOrder(List nodes, List connections, bool debug = false) + { + // === Initialization === + var nodeMap = new Dictionary(nodes.Count); + var adjList = new Dictionary>(nodes.Count); + var inDegree = new Dictionary(nodes.Count); + + foreach (var node in nodes) + { + string id = node.ID; + nodeMap[id] = node; + adjList[id] = new List(); + inDegree[id] = 0; + } + + // === Build Graph === + foreach (var conn in connections) + { + string from = conn.outputPort.nodeID; + string to = conn.inputPort.nodeID; + + if (!nodeMap.ContainsKey(from) || !nodeMap.ContainsKey(to)) + throw new System.Exception($"Invalid connection: '{from}' → '{to}' (One or both node IDs not found)"); + + adjList[from].Add(to); + inDegree[to]++; + } + + if (debug) + { + Debug.Log("=== Dependency Graph ==="); + foreach (var from in adjList.Keys) + { + var outputs = string.Join(", ", adjList[from].Select(t => $"{nodeMap[t].typeName} ({t})")); + Debug.Log($"[{nodeMap[from].typeName}] ({from}) → [{outputs}]"); + } + Debug.Log("========================="); + } + + // === Topological Sort (Kahn's Algorithm) === + var executionOrder = new List(nodes.Count); + var queue = new Queue(nodes.Count); + + foreach (var kvp in inDegree) + { + if (kvp.Value == 0) + queue.Enqueue(kvp.Key); + } + + while (queue.Count > 0) + { + string currentID = queue.Dequeue(); + executionOrder.Add(nodeMap[currentID]); + + foreach (var neighbor in adjList[currentID]) + { + inDegree[neighbor]--; + if (inDegree[neighbor] == 0) + queue.Enqueue(neighbor); + } + } + + if (executionOrder.Count != nodes.Count) + { + var remaining = string.Join(", ", inDegree.Where(p => p.Value > 0).Select(p => $"{nodeMap[p.Key].typeName} ({p.Key})")); + throw new System.Exception($"Cycle detected in graph. Remaining nodes: {remaining}"); + } + + if (debug) + { + Debug.Log("=== Execution Order ==="); + foreach (var n in executionOrder) + Debug.Log($"→ {n.typeName} ({n.ID})"); + } + + return executionOrder; + } + + + + } +} \ No newline at end of file diff --git a/Scripts/Editor/ImageProcessingGraphAsset.cs.meta b/Scripts/Editor/ImageProcessingGraphAsset.cs.meta new file mode 100644 index 0000000..b7c8858 --- /dev/null +++ b/Scripts/Editor/ImageProcessingGraphAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1da462fd3f736d04e80556b4ac8b470f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 91cfc70686fca0145b2b37b8b1db5dff, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Nodes.meta b/Scripts/Editor/Nodes.meta new file mode 100644 index 0000000..c5ec07a --- /dev/null +++ b/Scripts/Editor/Nodes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8726affebc717064db4f447f8569089d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Nodes/Base Node.meta b/Scripts/Editor/Nodes/Base Node.meta new file mode 100644 index 0000000..ba520b1 --- /dev/null +++ b/Scripts/Editor/Nodes/Base Node.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b0d7dde0b49a3224f9876502af464973 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Nodes/Base Node/BaseImageNode.cs b/Scripts/Editor/Nodes/Base Node/BaseImageNode.cs new file mode 100644 index 0000000..45eb740 --- /dev/null +++ b/Scripts/Editor/Nodes/Base Node/BaseImageNode.cs @@ -0,0 +1,117 @@ +using System; +using System.Configuration; +using UnityEditor.Experimental.GraphView; +using UnityEngine; +using ImageProcessingGraph.Editor.Nodes.NodeAttributes; +using System.Collections.Generic; +using Input = ImageProcessingGraph.Editor.Nodes.NodeAttributes.Input; +using System.Linq; +using System.Reflection; + +namespace ImageProcessingGraph.Editor +{ + [System.Serializable] + public abstract class BaseImageNode + { + [SerializeField] private string guid; + + [SerializeField] private Rect position; + + [SerializeField] public string typeName; + + [SerializeField] public (int, int) widthHeight; + + public string ID => guid; + public Rect Position => position; + + public ImageProcessingGraphAsset asset; + + public BaseImageNode() + { + guid = Guid.NewGuid().ToString(); + + /*Type t = this.GetType(); + Debug.Log(t.Name);*/ + } + + public void SetNodeInputs() + { + Type type = this.GetType(); + string t = type.Name; + FieldInfo[] properties = type.GetFields(); + + var inputProperties = properties + .Where(p => p.IsDefined(typeof(Input), false)).ToList(); + + foreach (var inputProperty in inputProperties) + { + // Find the connection that leads to this input property + GraphConnection connection = default; + + foreach (var conn in asset.Connections) + { + if (conn.inputPort.nodeID == this.ID) + { + if (conn.inputPort.portID == inputProperties.IndexOf(inputProperty)) + { + connection = conn; + break; + } + } + } + + if (connection.Equals((default(GraphConnection)))) + return; + + // Get the output node from the connection + var outputNode = asset.Nodes.FirstOrDefault(n => n.ID == connection.outputPort.nodeID); + + if (outputNode != null) + { + // Use reflection to get the output data from the output node's corresponding output property + Type outputType = outputNode.GetType(); + FieldInfo[] fields = outputType.GetFields(); + + var outputFields = fields + .Where(p => p.IsDefined(typeof(Output), false)).ToList(); + + FieldInfo outputProperty = null; + + foreach (var da in outputFields) + { + if (outputFields.IndexOf(da) == connection.outputPort.portID) + { + outputProperty = da; + break; + } + } + + /*var outputProperty = outputNode.GetType().GetProperties() + .FirstOrDefault(p => p.IsDefined(typeof(Output), false) && + outputFields == connection.outputPort.portID);*/ + + if (outputProperty != null) + { + // Assign the output data to the input property + var outputData = outputProperty.GetValue(outputNode); + inputProperty.SetValue(this, outputData); + } + } + } + } + + + public void RunNode() + { + SetNodeInputs(); + this.Process(); + } + + public virtual void Process() + { + Debug.Log("Uppies"); + } + + public void SetPosition(Rect position) => this.position = position; + } +} diff --git a/Scripts/Editor/Nodes/Base Node/BaseImageNode.cs.meta b/Scripts/Editor/Nodes/Base Node/BaseImageNode.cs.meta new file mode 100644 index 0000000..5ecbcfb --- /dev/null +++ b/Scripts/Editor/Nodes/Base Node/BaseImageNode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 16aeb4367cd62fa4f8a0f72ee6eaa03b \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Base Node/ExposedPropertyPort.cs b/Scripts/Editor/Nodes/Base Node/ExposedPropertyPort.cs new file mode 100644 index 0000000..b210f60 --- /dev/null +++ b/Scripts/Editor/Nodes/Base Node/ExposedPropertyPort.cs @@ -0,0 +1,13 @@ +using System; +using UnityEditor.Experimental.GraphView; + +namespace ImageProcessingGraph.Editor +{ + public class ExposedPropertyPort : Port + { + protected ExposedPropertyPort(Orientation portOrientation, Direction portDirection, Capacity portCapacity, Type type) : base(portOrientation, portDirection, portCapacity, type) + { + + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Base Node/ExposedPropertyPort.cs.meta b/Scripts/Editor/Nodes/Base Node/ExposedPropertyPort.cs.meta new file mode 100644 index 0000000..2aa1d23 --- /dev/null +++ b/Scripts/Editor/Nodes/Base Node/ExposedPropertyPort.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 31313dec455d4407bb6fffc7b167dabb +timeCreated: 1743744035 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Base Node/PortConnection.cs b/Scripts/Editor/Nodes/Base Node/PortConnection.cs new file mode 100644 index 0000000..b3e7180 --- /dev/null +++ b/Scripts/Editor/Nodes/Base Node/PortConnection.cs @@ -0,0 +1,36 @@ +namespace ImageProcessingGraph.Editor +{ + [System.Serializable] + public struct GraphConnection + { + public GraphPort inputPort; + public GraphPort outputPort; + + public GraphConnection(GraphPort inputPort, GraphPort outputPort) + { + this.inputPort = inputPort; + this.outputPort = outputPort; + } + + public GraphConnection(string inputNodeGuid, int inputPortID, string inputNodeType, string outputNodeGuid, int outputPortID, string outputNodeType) + { + this.inputPort = new GraphPort(inputNodeGuid, inputPortID, inputNodeType); + this.outputPort = new GraphPort(outputNodeGuid, outputPortID, outputNodeType); + } + } + + [System.Serializable] + public struct GraphPort + { + public string nodeID; + public string nodeType; + public int portID; + + public GraphPort(string nodeID, int portID, string nodeType) + { + this.nodeID = nodeID; + this.portID = portID; + this.nodeType = nodeType; + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Base Node/PortConnection.cs.meta b/Scripts/Editor/Nodes/Base Node/PortConnection.cs.meta new file mode 100644 index 0000000..dc0a3b5 --- /dev/null +++ b/Scripts/Editor/Nodes/Base Node/PortConnection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 10b473e8e8494d6eb4c4f34a4b41d4bd +timeCreated: 1743739124 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Fun Nodes.meta b/Scripts/Editor/Nodes/Fun Nodes.meta new file mode 100644 index 0000000..b40f50d --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ca37b9337d41484892c89933479f1e7f +timeCreated: 1743745541 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Fun Nodes/Texture.meta b/Scripts/Editor/Nodes/Fun Nodes/Texture.meta new file mode 100644 index 0000000..35f736c --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes/Texture.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1646a2bcaf6b4de488cadad1d7cdf795 +timeCreated: 1743747481 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBACombine.cs b/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBACombine.cs new file mode 100644 index 0000000..deb7d80 --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBACombine.cs @@ -0,0 +1,75 @@ +using ImageProcessingGraph.Editor.Nodes.NodeAttributes; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine; + +namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture +{ + [NodeInfo("RGBA Combine", "Channels/RGBA Combine", true)] + public class RGBASCombine : BaseImageNode + { + [NodeAttributes.Input("R")] + public SplitChannelData r; + + [NodeAttributes.Input("G")] + public SplitChannelData g; + + [NodeAttributes.Input("B")] + public SplitChannelData b; + + [NodeAttributes.Input("A")] + public SplitChannelData a; + + [NodeAttributes.Output("")] + public ImageData inputTexture; + + // Job struct for combining RGBA channels + struct RGBACombineJob : IJob + { + public NativeArray rData; + public NativeArray gData; + public NativeArray bData; + public NativeArray aData; + public NativeArray outputData; + public int length; + + // Execute method to combine the RGBA channels into a Color32 array + public void Execute() + { + for (int i = 0; i < length; i++) + { + outputData[i] = new Color32(rData[i], gData[i], bData[i], aData[i]); + } + } + } + + public override void Process() + { + int length = r.ChannelData.Length; + + // Allocate NativeArrays for the RGBA channels and the output pixel data + NativeArray rChannel = new NativeArray(r.ChannelData, Allocator.Persistent); + NativeArray gChannel = new NativeArray(g.ChannelData, Allocator.Persistent); + NativeArray bChannel = new NativeArray(b.ChannelData, Allocator.Persistent); + NativeArray aChannel = new NativeArray(a.ChannelData, Allocator.Persistent); + + NativeArray outputPixels = new NativeArray(length, Allocator.Persistent); + + // Create the job to combine RGBA channels + RGBACombineJob combineJob = new RGBACombineJob + { + rData = rChannel, + gData = gChannel, + bData = bChannel, + aData = aChannel, + outputData = outputPixels, + length = length + }; + + combineJob.Run(); + + // Create the ImageData object with the combined pixel data + inputTexture = new ImageData(outputPixels, (r.Width, r.Height), false); + } + } +} diff --git a/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBACombine.cs.meta b/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBACombine.cs.meta new file mode 100644 index 0000000..5abd460 --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBACombine.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3fa83fcfe5464559975d25d6d55cb799 +timeCreated: 1743750468 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBASplit.cs b/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBASplit.cs new file mode 100644 index 0000000..674a833 --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBASplit.cs @@ -0,0 +1,78 @@ +using ImageProcessingGraph.Editor.Nodes.NodeAttributes; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine; + +namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture +{ + [NodeInfoAttribute("RGBA Split", "Channels/RGBA Split", true)] + public class RGBASplit : BaseImageNode + { + [NodeAttributes.Input("")] + public ImageData inputTexture; + + [NodeAttributes.Output("R")] + public SplitChannelData r; + [NodeAttributes.Output("G")] + public SplitChannelData g; + [NodeAttributes.Output("B")] + public SplitChannelData b; + [NodeAttributes.Output("A")] + public SplitChannelData a; + + public override void Process() + { + int length = inputTexture.PixelData.Length; + + // Allocate NativeArrays for the pixel data and the individual RGBA channels + NativeArray pixelData = new NativeArray(inputTexture.PixelData, Allocator.Persistent); + NativeArray rChannel = new NativeArray(length, Allocator.Persistent); + NativeArray gChannel = new NativeArray(length, Allocator.Persistent); + NativeArray bChannel = new NativeArray(length, Allocator.Persistent); + NativeArray aChannel = new NativeArray(length, Allocator.Persistent); + + // Create the job and set up the input/output data + RGBAJob rgbaJob = new RGBAJob + { + pixelData = pixelData, + rData = rChannel, + gData = gChannel, + bData = bChannel, + aData = aChannel, + length = length + }; + + rgbaJob.Run(); + + // Store the results in the SplitChannelData objects + r = new SplitChannelData(rChannel.ToArray(), (inputTexture.Width, inputTexture.Height)); + g = new SplitChannelData(gChannel.ToArray(), (inputTexture.Width, inputTexture.Height)); + b = new SplitChannelData(bChannel.ToArray(), (inputTexture.Width, inputTexture.Height)); + a = new SplitChannelData(aChannel.ToArray(), (inputTexture.Width, inputTexture.Height)); + } + } + + // Job struct for processing the image data + struct RGBAJob : IJob + { + public NativeArray pixelData; + public NativeArray rData; + public NativeArray gData; + public NativeArray bData; + public NativeArray aData; + public int length; + + // Execute method that splits the RGBA channels + public void Execute() + { + for (int i = 0; i < length; i++) + { + Color32 pixel = pixelData[i]; + rData[i] = pixel.r; + gData[i] = pixel.g; + bData[i] = pixel.b; + aData[i] = pixel.a; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBASplit.cs.meta b/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBASplit.cs.meta new file mode 100644 index 0000000..cf309b1 --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes/Texture/RGBASplit.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c185fc298d59474cb453bf54b4f0aea8 +timeCreated: 1743747489 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DDesaturate.cs b/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DDesaturate.cs new file mode 100644 index 0000000..472c137 --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DDesaturate.cs @@ -0,0 +1,53 @@ +using ImageProcessingGraph.Editor.Nodes.NodeAttributes; +using Unity.Burst; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine; + + + +namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture +{ + [NodeInfoAttribute("Desaturate", "Adjustments/Desaturate", true)] + public class Texture2DDesaturate : BaseImageNode + { + [NodeAttributes.Input("")] + public ImageData inputTexture; // Changed to ImageData + + [NodeAttributes.Output("")] + public ImageData outputTexture; // Changed to ImageData + + public override void Process() + { + NativeArray output = new NativeArray(inputTexture.PixelData.Length, Allocator.Persistent); + + DesaturateJob job = new DesaturateJob + { + pixels = inputTexture.PixelData, + outputPixels = output + }; + + job.Run(); + + outputTexture = new ImageData(output, (inputTexture.Width, inputTexture.Height), inputTexture.isRGBA); + } + } + + public struct DesaturateJob : IJob + { + [ReadOnly] public NativeArray pixels; + public NativeArray outputPixels; + + public void Execute() + { + for (int i = 0; i < pixels.Length; i++) + { + Color32 pixel = pixels[i]; + + byte grayValue = (byte)(0.2989f * pixel.r + 0.5870f * pixel.g + 0.1140f * pixel.b); + + outputPixels[i] = new Color32(grayValue, grayValue, grayValue, pixel.a); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DDesaturate.cs.meta b/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DDesaturate.cs.meta new file mode 100644 index 0000000..19a58b8 --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DDesaturate.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a3e2d8c461684005a1286dcbc985dbbf +timeCreated: 1745291300 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DInvert.cs b/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DInvert.cs new file mode 100644 index 0000000..ea4b3bd --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DInvert.cs @@ -0,0 +1,59 @@ +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", "Adjustments/Invert", true)] + public class Texture2DInvert : BaseImageNode + { + [NodeAttributes.Input("")] + public ImageData inputTexture; // Changed to ImageData + + [NodeAttributes.Output("")] + public ImageData outputTexture; // Changed to ImageData + + public override void Process() + { + // Create an empty NativeArray for the output + NativeArray output = new NativeArray(inputTexture.PixelData.Length, Allocator.Persistent); + + // Create and run the InvertJob + InvertJob job = new InvertJob + { + pixels = inputTexture.PixelData, + outputPixels = output + }; + + job.Run(); + + // Store the result in the outputImage as an ImageData instance + outputTexture = new ImageData(output, (inputTexture.Width, inputTexture.Height), inputTexture.isRGBA); + } + } + + [BurstCompile] + public struct InvertJob : IJob + { + [ReadOnly] public NativeArray pixels; // Input pixels + [WriteOnly] public NativeArray outputPixels; // Output pixels + + public void Execute() + { + int length = pixels.Length; + + // Invert each pixel color + for (int i = 0; i < length; i++) + { + Color32 pixel = pixels[i]; + outputPixels[i] = new Color32( + (byte)(255 - pixel.r), + (byte)(255 - pixel.g), + (byte)(255 - pixel.b), + pixel.a); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DInvert.cs.meta b/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DInvert.cs.meta new file mode 100644 index 0000000..8a68d39 --- /dev/null +++ b/Scripts/Editor/Nodes/Fun Nodes/Texture/Texture2DInvert.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d95b796b51a64fac8947aa4325b0373d +timeCreated: 1743747802 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Import Nodes.meta b/Scripts/Editor/Nodes/Import Nodes.meta new file mode 100644 index 0000000..5940a09 --- /dev/null +++ b/Scripts/Editor/Nodes/Import Nodes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e0ffc7091a5ca0244a56716dc57b6c86 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Nodes/Import Nodes/Base.meta b/Scripts/Editor/Nodes/Import Nodes/Base.meta new file mode 100644 index 0000000..4af4f45 --- /dev/null +++ b/Scripts/Editor/Nodes/Import Nodes/Base.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 22b0f0e553dc85e489979a232505a332 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Nodes/Import Nodes/Texture2DImport.cs b/Scripts/Editor/Nodes/Import Nodes/Texture2DImport.cs new file mode 100644 index 0000000..6394633 --- /dev/null +++ b/Scripts/Editor/Nodes/Import Nodes/Texture2DImport.cs @@ -0,0 +1,34 @@ +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!"); + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Import Nodes/Texture2DImport.cs.meta b/Scripts/Editor/Nodes/Import Nodes/Texture2DImport.cs.meta new file mode 100644 index 0000000..0934275 --- /dev/null +++ b/Scripts/Editor/Nodes/Import Nodes/Texture2DImport.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 349192a0abdc4544888b8b537dea9c74 +timeCreated: 1743745590 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/NodeAttributes.meta b/Scripts/Editor/Nodes/NodeAttributes.meta new file mode 100644 index 0000000..919d81c --- /dev/null +++ b/Scripts/Editor/Nodes/NodeAttributes.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dbd7cd37cae147f3a54f12c52ef54217 +timeCreated: 1742158168 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/NodeAttributes/NewMonoBehaviour.cs b/Scripts/Editor/Nodes/NodeAttributes/NewMonoBehaviour.cs new file mode 100644 index 0000000..507e7ab --- /dev/null +++ b/Scripts/Editor/Nodes/NodeAttributes/NewMonoBehaviour.cs @@ -0,0 +1,17 @@ +using UnityEngine; +using System.Collections; + +public class NewMonoBehaviour : MonoBehaviour +{ + // Use this for initialization + void Start() + { + + } + + // Update is called once per frame + void Update() + { + + } +} diff --git a/Scripts/Editor/Nodes/NodeAttributes/NewMonoBehaviour.cs.meta b/Scripts/Editor/Nodes/NodeAttributes/NewMonoBehaviour.cs.meta new file mode 100644 index 0000000..923ba9f --- /dev/null +++ b/Scripts/Editor/Nodes/NodeAttributes/NewMonoBehaviour.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 78c0de359ceae406cb2985637a2e6c97 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/NodeAttributes/NodeAttributes.cs b/Scripts/Editor/Nodes/NodeAttributes/NodeAttributes.cs new file mode 100644 index 0000000..513c39c --- /dev/null +++ b/Scripts/Editor/Nodes/NodeAttributes/NodeAttributes.cs @@ -0,0 +1,45 @@ +using System; + +namespace ImageProcessingGraph.Editor.Nodes.NodeAttributes +{ + [AttributeUsage(AttributeTargets.Class)] + public class NodeInfoAttribute : Attribute + { + private string name; + private string menuItem; + private string ussPath; + + public string Title => name; + public string MenuItem => menuItem; + public string UssPath => ussPath; + + public NodeInfoAttribute(string name, string menuItem = "", bool requiresImage = false, string ussPath = null) + { + this.name = name; + this.menuItem = menuItem; + this.ussPath = ussPath; + } + } + + [AttributeUsage(AttributeTargets.Field)] + public class Input : Attribute + { + public string Label { get; } + + public Input(string _label) + { + Label = _label; + } + } + + [AttributeUsage(AttributeTargets.Field)] + public class Output : Attribute + { + public string Label { get; } + + public Output(string _label) + { + Label = _label; + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Nodes/NodeAttributes/NodeAttributes.cs.meta b/Scripts/Editor/Nodes/NodeAttributes/NodeAttributes.cs.meta new file mode 100644 index 0000000..0dbe3ec --- /dev/null +++ b/Scripts/Editor/Nodes/NodeAttributes/NodeAttributes.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a46f5cb221ec44348c340aeb91b54b6c +timeCreated: 1742158180 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Output.meta b/Scripts/Editor/Nodes/Output.meta new file mode 100644 index 0000000..700e66c --- /dev/null +++ b/Scripts/Editor/Nodes/Output.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 80a64ea121f9494d931173b10cce68b3 +timeCreated: 1743749784 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Output/Texture2DOutput.cs b/Scripts/Editor/Nodes/Output/Texture2DOutput.cs new file mode 100644 index 0000000..1252400 --- /dev/null +++ b/Scripts/Editor/Nodes/Output/Texture2DOutput.cs @@ -0,0 +1,20 @@ +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"); + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Nodes/Output/Texture2DOutput.cs.meta b/Scripts/Editor/Nodes/Output/Texture2DOutput.cs.meta new file mode 100644 index 0000000..b635b93 --- /dev/null +++ b/Scripts/Editor/Nodes/Output/Texture2DOutput.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 10f83f7df222436c8495916067139b91 +timeCreated: 1743750878 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/String Nodes.meta b/Scripts/Editor/Nodes/String Nodes.meta new file mode 100644 index 0000000..ff11a01 --- /dev/null +++ b/Scripts/Editor/Nodes/String Nodes.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 587f3b2d6fad453fbf6664066f807f21 +timeCreated: 1745299284 \ No newline at end of file diff --git a/Scripts/Editor/Nodes/String Nodes/StringAppend.cs b/Scripts/Editor/Nodes/String Nodes/StringAppend.cs new file mode 100644 index 0000000..f82a814 --- /dev/null +++ b/Scripts/Editor/Nodes/String Nodes/StringAppend.cs @@ -0,0 +1,18 @@ +using ImageProcessingGraph.Editor.Nodes.NodeAttributes; + +namespace ImageProcessingGraph.Editor.Nodes.String_Nodes +{ + [NodeInfo("String Append", "String/Append")] + public class StringAppend : BaseImageNode + { + [NodeAttributes.Input("String A")] public string baseString; + [NodeAttributes.Input("String B")] public string appendString; + + [NodeAttributes.Output("Appended String")] public string output; + + public override void Process() + { + this.output = baseString + appendString; + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Nodes/String Nodes/StringAppend.cs.meta b/Scripts/Editor/Nodes/String Nodes/StringAppend.cs.meta new file mode 100644 index 0000000..727b716 --- /dev/null +++ b/Scripts/Editor/Nodes/String Nodes/StringAppend.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 52ee2568ed5f4c5da8fb920672d1aa96 +timeCreated: 1745299310 \ No newline at end of file diff --git a/Scripts/Editor/Windows.meta b/Scripts/Editor/Windows.meta new file mode 100644 index 0000000..a11d9b5 --- /dev/null +++ b/Scripts/Editor/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1754e13556323a64ea0fe2a83fe4c82d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Windows/ImageProcessingGraphAssetEditorWindow.cs b/Scripts/Editor/Windows/ImageProcessingGraphAssetEditorWindow.cs new file mode 100644 index 0000000..71825a4 --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphAssetEditorWindow.cs @@ -0,0 +1,30 @@ +using UnityEditor; +using UnityEngine; + +namespace ImageProcessingGraph.Editor.Windows +{ + [CustomEditor(typeof(ImageProcessingGraphAsset))] + public class ImageProcessingGraphAssetEditorWindow : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + if (GUILayout.Button("Open")) + { + ImageProcessingGraphEditorWindow.Open((ImageProcessingGraphAsset)target); + } + + if(GUILayout.Button("Calculate Dependancy Graph")) + { + var bleh = (ImageProcessingGraphAsset)target; + bleh.runOrder = bleh.GetExecutionOrder(bleh.Nodes, bleh.Connections, true); + } + + if(GUILayout.Button("Run Graph")) + { + var bleh = (ImageProcessingGraphAsset)target; + bleh.RunGraph(); + } + + } + } +} diff --git a/Scripts/Editor/Windows/ImageProcessingGraphAssetEditorWindow.cs.meta b/Scripts/Editor/Windows/ImageProcessingGraphAssetEditorWindow.cs.meta new file mode 100644 index 0000000..3c68996 --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphAssetEditorWindow.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5341664b296612f4dbc6d242c853bd7d \ No newline at end of file diff --git a/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs b/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs new file mode 100644 index 0000000..680a569 --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using UnityEditor.Experimental.GraphView; +using UnityEngine; +using UnityEngine.UIElements; + +namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows +{ + + public class ImageProcessingGraphEdgeConnectorListener : IEdgeConnectorListener + { + private GraphViewChange m_GraphViewChange; + private List m_EdgesToCreate; + private List m_EdgesToDelete; + + private ImageProcessingGraphViewWindow window; + + public ImageProcessingGraphEdgeConnectorListener(ImageProcessingGraphViewWindow window) + { + this.m_EdgesToCreate = new List(); + this.m_EdgesToDelete = new List(); + this.m_GraphViewChange.edgesToCreate = this.m_EdgesToCreate; + this.window = window; + } + + public void OnDropOutsidePort(Edge edge, Vector2 position) + { + window.searchProvider.target = (VisualElement)window.focusController.focusedElement; + SearchWindow.Open(new SearchWindowContext(position), window.searchProvider); + } + + public void OnDrop(UnityEditor.Experimental.GraphView.GraphView graphView, Edge edge) + { + this.m_EdgesToCreate.Clear(); + this.m_EdgesToCreate.Add(edge); + this.m_EdgesToDelete.Clear(); + if (edge.input.capacity == Port.Capacity.Single) + { + foreach (Edge connection in edge.input.connections) + { + if (connection != edge) + this.m_EdgesToDelete.Add((GraphElement)connection); + } + } + + if (edge.output.capacity == Port.Capacity.Single) + { + foreach (Edge connection in edge.output.connections) + { + if (connection != edge) + this.m_EdgesToDelete.Add((GraphElement)connection); + } + } + + if (this.m_EdgesToDelete.Count > 0) + graphView.DeleteElements((IEnumerable)this.m_EdgesToDelete); + List edgesToCreate = this.m_EdgesToCreate; + if (graphView.graphViewChanged != null) + edgesToCreate = graphView.graphViewChanged(this.m_GraphViewChange).edgesToCreate; + foreach (Edge edge1 in edgesToCreate) + { + graphView.AddElement((GraphElement)edge1); + edge.input.Connect(edge1); + edge.output.Connect(edge1); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs.meta b/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs.meta new file mode 100644 index 0000000..53ab8b6 --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphEdgeConnectorListener.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e55abc4baf7246aca9ebafb2c3aa793b +timeCreated: 1745309249 \ No newline at end of file diff --git a/Scripts/Editor/Windows/ImageProcessingGraphEditorWindow.cs b/Scripts/Editor/Windows/ImageProcessingGraphEditorWindow.cs new file mode 100644 index 0000000..d7d9057 --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphEditorWindow.cs @@ -0,0 +1,71 @@ +using System; +using UnityEditor; +using UnityEditor.Experimental.GraphView; +using UnityEngine; + +namespace ImageProcessingGraph.Editor.Windows +{ + public class ImageProcessingGraphEditorWindow : EditorWindow + { + [SerializeField] private ImageProcessingGraphAsset currentGraph; + [SerializeField] private SerializedObject serializedObject; + [SerializeField] private ImageProcessingGraphViewWindow currentView; + public ImageProcessingGraphAsset CurrentGraph => currentGraph; + + public static void Open(ImageProcessingGraphAsset asset) + { + ImageProcessingGraphEditorWindow[] windows = Resources.FindObjectsOfTypeAll(); + + foreach (var w in windows) + { + w.Focus(); + return; + } + + ImageProcessingGraphEditorWindow window = + CreateWindow(typeof(ImageProcessingGraphEditorWindow), + typeof(SceneView)); + + window.titleContent = new GUIContent($"{asset.name}", EditorGUIUtility.ObjectContent(null, typeof(ImageProcessingGraphAsset)).image + ); + window.Load(asset); + } + + void OnEnable() + { + if(currentGraph != null) + DrawGraph(); + } + + private void OnGUI() + { + if (currentGraph != null) + { + if(EditorUtility.IsDirty(currentGraph)) + this.hasUnsavedChanges = true; + else + this.hasUnsavedChanges = false; + } + } + + public void Load(ImageProcessingGraphAsset asset) + { + currentGraph = asset; + DrawGraph(); + } + + public void DrawGraph() + { + serializedObject = new SerializedObject(currentGraph); + currentView = new ImageProcessingGraphViewWindow(serializedObject, this); + currentView.graphViewChanged += OnChange; + rootVisualElement.Add(currentView); + } + + private GraphViewChange OnChange(GraphViewChange graphviewchange) + { + EditorUtility.SetDirty(currentGraph); + return graphviewchange; + } + } +} diff --git a/Scripts/Editor/Windows/ImageProcessingGraphEditorWindow.cs.meta b/Scripts/Editor/Windows/ImageProcessingGraphEditorWindow.cs.meta new file mode 100644 index 0000000..e93502d --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphEditorWindow.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 42279c09a8e81c844ab9651b6165a1e8 \ No newline at end of file diff --git a/Scripts/Editor/Windows/ImageProcessingGraphNodeVisual.cs b/Scripts/Editor/Windows/ImageProcessingGraphNodeVisual.cs new file mode 100644 index 0000000..2e23f0c --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphNodeVisual.cs @@ -0,0 +1,245 @@ +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 InputPorts { get; } + public List 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(); + title = info.Title; + + string[] depths = info.MenuItem.Split('/'); + foreach (var depth in depths) + { + this.AddToClassList(depth.ToLower().Replace(' ', '-')); + } + + this.InputPorts = new List(); + this.OutputPorts = new List(); + + List inputs = new List(); + List inputFieldInfo = new List(); + List outputFieldInfo = new List(); + + 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(); + inputs.Add(input); + inputFieldInfo.Add(field); + } + + if (field.GetCustomAttribute(typeof(Output)) != null) + { + Output output = field.GetCustomAttribute(); + outputFieldInfo.Add(field); + } + + } + + CreateInputPorts(inputFieldInfo); + CreateOutputPorts(outputFieldInfo); + + this.name = typeInfo.Name; + } + + private void CreateInputPorts(List 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().Label; + if (label != "") + port.portName = label; + InputPorts.Add(port); + + inputContainer.Add(port); + ExposeVariableToPort(port, field); + + } + } + + private void CreateOutputPorts(List 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().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("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()); + } +} diff --git a/Scripts/Editor/Windows/ImageProcessingGraphNodeVisual.cs.meta b/Scripts/Editor/Windows/ImageProcessingGraphNodeVisual.cs.meta new file mode 100644 index 0000000..2715150 --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphNodeVisual.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4f6cc3303a2442938105c3bc990cfd18 +timeCreated: 1742158018 \ No newline at end of file diff --git a/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs b/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs new file mode 100644 index 0000000..4b7e2d6 --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs @@ -0,0 +1,138 @@ +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 elements; + private Assembly[] assemblies; + public List CreateSearchTree(SearchWindowContext context) + { + List tree = new List(); + tree.Add(new SearchTreeGroupEntry(new GUIContent("Nodes"), 0)); + + elements = new List(); + + /* + 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()) + { + var attr = type.GetCustomAttribute(); + 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 groups = new List(); + + 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; + } + } +} \ No newline at end of file diff --git a/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs.meta b/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs.meta new file mode 100644 index 0000000..5ca5963 --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphSearchProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4b3b053637c34e86b2bda50c9cc5ccbb +timeCreated: 1742159126 \ No newline at end of file diff --git a/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs b/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs new file mode 100644 index 0000000..568865e --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs @@ -0,0 +1,342 @@ +using System.Collections.Generic; +using System.Linq; +using ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Windows; +using ImageProcessingGraph.Editor.Windows; +using UnityEditor; +using UnityEditor.Experimental.GraphView; +using UnityEngine; +using UnityEngine.UIElements; + +namespace ImageProcessingGraph.Editor +{ + public class ImageProcessingGraphViewWindow : GraphView + { + internal ImageProcessingGraphAsset asset; + private SerializedObject serializedObject; + private ImageProcessingGraphEditorWindow window; + + public ImageProcessingGraphEditorWindow Window => window; + + public List graphNodes; + public Dictionary nodeDictionary; + public Dictionary connectionDictionary; + + internal ImageProcessingGraphSearchProvider searchProvider; + internal ImageProcessingGraphEdgeConnectorListener edgeConnectorListener; + + public ImageProcessingGraphViewWindow(SerializedObject obeject, ImageProcessingGraphEditorWindow window) + { + this.serializedObject = obeject; + this.asset = obeject.targetObject as ImageProcessingGraphAsset; + + this.graphNodes = new List(); + nodeDictionary = new Dictionary(); + connectionDictionary = new Dictionary(); + + searchProvider = ScriptableObject.CreateInstance(); + searchProvider.graph = this; + + edgeConnectorListener = new ImageProcessingGraphEdgeConnectorListener(this); + + this.nodeCreationRequest = ShowSearchWindow; + + this.window = window; + + StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath("Assets/Unity Image Processing/GraphView.uss"); + styleSheets.Add(styleSheet); + + GridBackground background = new GridBackground(); + + background.name = "Grid"; + + + Add(background); + + background.SendToBack(); + + this.AddManipulator(new ContentDragger()); + this.AddManipulator(new SelectionDragger()); + this.AddManipulator(new RectangleSelector()); + this.AddManipulator(new ClickSelector()); + this.AddManipulator(new ContentZoomer() ); + + DrawNodes(); + DrawConnections(); + + graphViewChanged += OnGraphViewChanged; + Undo.undoRedoEvent += UndoEvent; + } + + private ImageProcessingGraphNodeVisual GetNode(string NodeID) + { + ImageProcessingGraphNodeVisual node = null; + nodeDictionary.TryGetValue(NodeID, out node); + return node; + } + + public override List GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter) + { + List compatiblePorts = new List(); + + + foreach (var node in graphNodes) + { + // Prevent connections to self + if (node == startPort.node) + continue; + + foreach (var port in node.inputContainer.Children().Concat(node.outputContainer.Children()).OfType()) + { + // Prevent connecting input to input or output to output + if (port.direction == startPort.direction) + continue; + + if (port.portType != startPort.portType) + { + if (DoesConversionNodeExist()) + { + + } + else + continue; + } + + // Prevent connection if it creates a cycle + if (startPort.direction == Direction.Output && CreatesCycle(startPort, port)) + continue; + + if (startPort.direction == Direction.Input && CreatesCycle(port, startPort)) + continue; + + compatiblePorts.Add(port); + } + } + + return compatiblePorts; + } + + private void UndoEvent(in UndoRedoInfo undo) + { + DrawNodes(); + + } + + private GraphViewChange OnGraphViewChanged(GraphViewChange graphviewchange) + { + + if (graphviewchange.movedElements != null) + { + Undo.RecordObject(serializedObject.targetObject, "Moved Graph Elements"); + foreach (var VARIABLE in graphviewchange.movedElements.OfType()) + { + VARIABLE.SavePosition(); + } + } + + if (graphviewchange.elementsToRemove != null) + { + List nodesToRemove = graphviewchange.elementsToRemove.OfType().ToList(); + + if (nodesToRemove.Count > 0) + { + Undo.RecordObject(serializedObject.targetObject, "Remove Node"); + + foreach (var VARIABLE in nodesToRemove) + { + RemoveNode(VARIABLE); + } + } + + foreach (var VARIABLE in graphviewchange.elementsToRemove.OfType()) + { + RemoveEdge(VARIABLE); + } + + } + + if (graphviewchange.edgesToCreate != null) + { + foreach (Edge edge in graphviewchange.edgesToCreate) + { + Undo.RecordObject(serializedObject.targetObject, "Created Connection"); + CreateEdge(edge); + } + } + + return graphviewchange; + } + + #region Edges + + void CreateEdge(Edge edge) + { + ImageProcessingGraphNodeVisual outputNode = (ImageProcessingGraphNodeVisual)edge.output.node; + ImageProcessingGraphNodeVisual inputNode = (ImageProcessingGraphNodeVisual)edge.input.node; + + + int outputIndex = outputNode.OutputPorts.IndexOf(edge.output); + int inputIndex = inputNode.InputPorts.IndexOf(edge.input); + + string inputType = inputNode.GraphNode.GetType().Name; + string outputType = outputNode.GraphNode.GetType().Name; + + GraphConnection connection = new GraphConnection(inputNode.GraphNode.ID,inputIndex, inputType,outputNode.GraphNode.ID, outputIndex, outputType); + + asset.Connections.Add(connection); + } + + private void RemoveEdge(Edge variable) + { + if (connectionDictionary.TryGetValue(variable, out GraphConnection connection)) + { + asset.Connections.Remove(connection); + connectionDictionary.Remove(variable); + } + } + + private void DrawConnections() + { + if (asset.Connections != null) + { + foreach (GraphConnection conn in asset.Connections) + { + ImageProcessingGraphNodeVisual inputNode = GetNode(conn.inputPort.nodeID); + ImageProcessingGraphNodeVisual outputNode = GetNode(conn.outputPort.nodeID); + + if (inputNode != null && outputNode != null) + { + Port inPort = inputNode.InputPorts[conn.inputPort.portID]; + Port outPort = outputNode.OutputPorts[conn.outputPort.portID]; + + Edge edge = inPort.ConnectTo(outPort); + AddElement(edge); + connectionDictionary.Add(edge, conn); + } + + } + } + } + + + private bool DoesConversionNodeExist() + { + return false; + } + + #region Cycle Logic + + private bool CreatesCycle(Port fromPort, Port toPort) + { + var visited = new HashSet(); + return HasPathTo(toPort.node, fromPort.node, visited); + } + + private bool HasPathTo(Node current, Node target, HashSet visited) + { + if (current == target) + return true; + + if (visited.Contains(current)) + return false; + + visited.Add(current); + + foreach (var outputPort in current.outputContainer.Children().OfType()) + { + foreach (var edge in outputPort.connections) + { + var nextNode = edge.input.node; + if (HasPathTo(nextNode, target, visited)) + return true; + } + } + + return false; + } + + #endregion + + #endregion + + #region Nodes + private void DrawNodes() + { + foreach (ImageProcessingGraphNodeVisual node in graphNodes) + { + RemoveElement(node); + } + graphNodes.Clear(); + + foreach (KeyValuePair node in nodeDictionary) + { + RemoveElement(node.Value); + } + nodeDictionary.Clear(); + + foreach (var variable in asset.Nodes) + { + AddNodeToGraph(variable); + } + } + + private void AddNodeToGraph(BaseImageNode node) + { + node.typeName = node.GetType().AssemblyQualifiedName; + + ImageProcessingGraphNodeVisual editorNode = new ImageProcessingGraphNodeVisual(node, this); + editorNode.SetPosition(node.Position); + + graphNodes.Add(editorNode); + nodeDictionary.Add(node.ID, editorNode); + + + AddElement(editorNode); + } + + public void Add(BaseImageNode node) + { + Undo.RecordObject(serializedObject.targetObject, "Added Node"); + asset.Nodes.Add(node); + + serializedObject.Update(); + + AddNodeToGraph(node); + + } + + private void RemoveNode(ImageProcessingGraphNodeVisual variable) + { + List connectionsToRemove = new List(); + + foreach (var connection in asset.Connections) + { + if (connection.inputPort.nodeID == variable.GraphNode.ID || connection.outputPort.nodeID == variable.GraphNode.ID) + connectionsToRemove.Add(connection); + } + + foreach (var connection in connectionsToRemove) + { + asset.Connections.Remove(connection); + } + + asset.Nodes.Remove(variable.GraphNode); + nodeDictionary.Remove(variable.GraphNode.ID); + graphNodes.Remove(variable); + serializedObject.Update(); + } + + #endregion + + #region Search Window + + private void ShowSearchWindow(NodeCreationContext obj) + { + searchProvider.target = (VisualElement)focusController.focusedElement; + SearchWindow.Open(new SearchWindowContext(obj.screenMousePosition), searchProvider); + } + + #endregion + + + } +} diff --git a/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs.meta b/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs.meta new file mode 100644 index 0000000..833c348 --- /dev/null +++ b/Scripts/Editor/Windows/ImageProcessingGraphViewWindow.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f8b07435c36617f48bbb9c51889caf81 \ No newline at end of file diff --git a/Test Assets.meta b/Test Assets.meta new file mode 100644 index 0000000..6582a85 --- /dev/null +++ b/Test Assets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a4036f11a5b3def48b6cf5e8b0654a54 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Test Assets/New IPT_Preferences.asset b/Test Assets/New IPT_Preferences.asset new file mode 100644 index 0000000..ed3a85b --- /dev/null +++ b/Test Assets/New IPT_Preferences.asset @@ -0,0 +1,18 @@ +%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} diff --git a/Test Assets/New IPT_Preferences.asset.meta b/Test Assets/New IPT_Preferences.asset.meta new file mode 100644 index 0000000..8634319 --- /dev/null +++ b/Test Assets/New IPT_Preferences.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d4bfee84a7579474c8626fb17fcc299b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Test Assets/New Shader Graph.shadergraph b/Test Assets/New Shader Graph.shadergraph new file mode 100644 index 0000000..66005d6 --- /dev/null +++ b/Test Assets/New Shader Graph.shadergraph @@ -0,0 +1,658 @@ +{ + "m_SGVersion": 3, + "m_Type": "UnityEditor.ShaderGraph.GraphData", + "m_ObjectId": "a14387ccdc0e4230b24cf16376062b37", + "m_Properties": [], + "m_Keywords": [], + "m_Dropdowns": [], + "m_CategoryData": [ + { + "m_Id": "71cd3153db9241d78583f494f3175e65" + } + ], + "m_Nodes": [ + { + "m_Id": "0e3586066bf84a19b29835e8f00f18c8" + }, + { + "m_Id": "4a4c9b7678bf48649738dd347fbf691d" + }, + { + "m_Id": "6cc2f2b53819431db80cdc902c44b0a2" + }, + { + "m_Id": "170a5a0274d84f83b5b3409d7ebccc3c" + }, + { + "m_Id": "b6f01d1b41104807b982a044c5e1e005" + }, + { + "m_Id": "329dddc156c148b887205cd362845c53" + }, + { + "m_Id": "574823597594424bb34f730b62c44aef" + }, + { + "m_Id": "a77037c58c28419ba14f3220b7d8fbff" + }, + { + "m_Id": "265ae6dabc6f4c57bb9681736466b11f" + } + ], + "m_GroupDatas": [], + "m_StickyNoteDatas": [], + "m_Edges": [], + "m_VertexContext": { + "m_Position": { + "x": 0.0, + "y": 0.0 + }, + "m_Blocks": [ + { + "m_Id": "0e3586066bf84a19b29835e8f00f18c8" + }, + { + "m_Id": "4a4c9b7678bf48649738dd347fbf691d" + }, + { + "m_Id": "6cc2f2b53819431db80cdc902c44b0a2" + } + ] + }, + "m_FragmentContext": { + "m_Position": { + "x": 0.0, + "y": 200.0 + }, + "m_Blocks": [ + { + "m_Id": "170a5a0274d84f83b5b3409d7ebccc3c" + }, + { + "m_Id": "b6f01d1b41104807b982a044c5e1e005" + }, + { + "m_Id": "329dddc156c148b887205cd362845c53" + }, + { + "m_Id": "574823597594424bb34f730b62c44aef" + }, + { + "m_Id": "a77037c58c28419ba14f3220b7d8fbff" + }, + { + "m_Id": "265ae6dabc6f4c57bb9681736466b11f" + } + ] + }, + "m_PreviewData": { + "serializedMesh": { + "m_SerializedMesh": "{\"mesh\":{\"instanceID\":0}}", + "m_Guid": "" + }, + "preventRotation": false + }, + "m_Path": "Shader Graphs", + "m_GraphPrecision": 1, + "m_PreviewMode": 2, + "m_OutputNode": { + "m_Id": "" + }, + "m_SubDatas": [], + "m_ActiveTargets": [ + { + "m_Id": "7dffcc0f872e4798bc7d7c277c01ca46" + } + ] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.ColorRGBMaterialSlot", + "m_ObjectId": "0000b8c7f15240e8a92c2c75a833f48f", + "m_Id": 0, + "m_DisplayName": "Base Color", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "BaseColor", + "m_StageCapability": 2, + "m_Value": { + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "m_DefaultValue": { + "x": 0.5, + "y": 0.5, + "z": 0.5 + }, + "m_Labels": [], + "m_ColorMode": 0, + "m_DefaultColor": { + "r": 0.5, + "g": 0.5, + "b": 0.5, + "a": 1.0 + } +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "0468751a1c344e38b0aec9b2b4467e38", + "m_Id": 0, + "m_DisplayName": "Smoothness", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Smoothness", + "m_StageCapability": 2, + "m_Value": 0.5, + "m_DefaultValue": 0.5, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "0e3586066bf84a19b29835e8f00f18c8", + "m_Group": { + "m_Id": "" + }, + "m_Name": "VertexDescription.Position", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "8e9794e0177a4feebf5eb676d84fe1f3" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "VertexDescription.Position" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "0f43a512a2ae49f3959b2c0347ca55b2", + "m_Id": 0, + "m_DisplayName": "Ambient Occlusion", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Occlusion", + "m_StageCapability": 2, + "m_Value": 1.0, + "m_DefaultValue": 1.0, + "m_Labels": [] +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "170a5a0274d84f83b5b3409d7ebccc3c", + "m_Group": { + "m_Id": "" + }, + "m_Name": "SurfaceDescription.BaseColor", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "0000b8c7f15240e8a92c2c75a833f48f" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "SurfaceDescription.BaseColor" +} + +{ + "m_SGVersion": 2, + "m_Type": "UnityEditor.Rendering.Universal.ShaderGraph.UniversalLitSubTarget", + "m_ObjectId": "1fe22ff03eb64399a2fd9c06b88eafa8", + "m_WorkflowMode": 1, + "m_NormalDropOffSpace": 0, + "m_ClearCoat": false, + "m_BlendModePreserveSpecular": true +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "265ae6dabc6f4c57bb9681736466b11f", + "m_Group": { + "m_Id": "" + }, + "m_Name": "SurfaceDescription.Occlusion", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "0f43a512a2ae49f3959b2c0347ca55b2" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "SurfaceDescription.Occlusion" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "329dddc156c148b887205cd362845c53", + "m_Group": { + "m_Id": "" + }, + "m_Name": "SurfaceDescription.Metallic", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "bd184b28488f4573bef8c2af64be7436" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "SurfaceDescription.Metallic" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.TangentMaterialSlot", + "m_ObjectId": "32e096cd3bb849e7bb32f646989794db", + "m_Id": 0, + "m_DisplayName": "Tangent", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Tangent", + "m_StageCapability": 1, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [], + "m_Space": 0 +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.ColorRGBMaterialSlot", + "m_ObjectId": "382845f601e84d2292ccb7886a370bcb", + "m_Id": 0, + "m_DisplayName": "Emission", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Emission", + "m_StageCapability": 2, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [], + "m_ColorMode": 1, + "m_DefaultColor": { + "r": 0.0, + "g": 0.0, + "b": 0.0, + "a": 1.0 + } +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "4a4c9b7678bf48649738dd347fbf691d", + "m_Group": { + "m_Id": "" + }, + "m_Name": "VertexDescription.Normal", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "83dd0d2576664436bb795eba7ffb4883" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "VertexDescription.Normal" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "574823597594424bb34f730b62c44aef", + "m_Group": { + "m_Id": "" + }, + "m_Name": "SurfaceDescription.Smoothness", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "0468751a1c344e38b0aec9b2b4467e38" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "SurfaceDescription.Smoothness" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "6cc2f2b53819431db80cdc902c44b0a2", + "m_Group": { + "m_Id": "" + }, + "m_Name": "VertexDescription.Tangent", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "32e096cd3bb849e7bb32f646989794db" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "VertexDescription.Tangent" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.CategoryData", + "m_ObjectId": "71cd3153db9241d78583f494f3175e65", + "m_Name": "", + "m_ChildObjectList": [] +} + +{ + "m_SGVersion": 1, + "m_Type": "UnityEditor.Rendering.Universal.ShaderGraph.UniversalTarget", + "m_ObjectId": "7dffcc0f872e4798bc7d7c277c01ca46", + "m_Datas": [], + "m_ActiveSubTarget": { + "m_Id": "1fe22ff03eb64399a2fd9c06b88eafa8" + }, + "m_AllowMaterialOverride": false, + "m_SurfaceType": 0, + "m_ZTestMode": 4, + "m_ZWriteControl": 0, + "m_AlphaMode": 0, + "m_RenderFace": 2, + "m_AlphaClip": false, + "m_CastShadows": true, + "m_ReceiveShadows": true, + "m_DisableTint": false, + "m_AdditionalMotionVectorMode": 0, + "m_AlembicMotionVectors": false, + "m_SupportsLODCrossFade": false, + "m_CustomEditorGUI": "", + "m_SupportVFX": false +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.NormalMaterialSlot", + "m_ObjectId": "83dd0d2576664436bb795eba7ffb4883", + "m_Id": 0, + "m_DisplayName": "Normal", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Normal", + "m_StageCapability": 1, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [], + "m_Space": 0 +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.PositionMaterialSlot", + "m_ObjectId": "8e9794e0177a4feebf5eb676d84fe1f3", + "m_Id": 0, + "m_DisplayName": "Position", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Position", + "m_StageCapability": 1, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [], + "m_Space": 0 +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.NormalMaterialSlot", + "m_ObjectId": "a72d7540c45d4b7e8a8d554f6274d745", + "m_Id": 0, + "m_DisplayName": "Normal (Tangent Space)", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "NormalTS", + "m_StageCapability": 2, + "m_Value": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_DefaultValue": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "m_Labels": [], + "m_Space": 3 +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "a77037c58c28419ba14f3220b7d8fbff", + "m_Group": { + "m_Id": "" + }, + "m_Name": "SurfaceDescription.Emission", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "382845f601e84d2292ccb7886a370bcb" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "SurfaceDescription.Emission" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.BlockNode", + "m_ObjectId": "b6f01d1b41104807b982a044c5e1e005", + "m_Group": { + "m_Id": "" + }, + "m_Name": "SurfaceDescription.NormalTS", + "m_DrawState": { + "m_Expanded": true, + "m_Position": { + "serializedVersion": "2", + "x": 0.0, + "y": 0.0, + "width": 0.0, + "height": 0.0 + } + }, + "m_Slots": [ + { + "m_Id": "a72d7540c45d4b7e8a8d554f6274d745" + } + ], + "synonyms": [], + "m_Precision": 0, + "m_PreviewExpanded": true, + "m_DismissedVersion": 0, + "m_PreviewMode": 0, + "m_CustomColors": { + "m_SerializableColors": [] + }, + "m_SerializedDescriptor": "SurfaceDescription.NormalTS" +} + +{ + "m_SGVersion": 0, + "m_Type": "UnityEditor.ShaderGraph.Vector1MaterialSlot", + "m_ObjectId": "bd184b28488f4573bef8c2af64be7436", + "m_Id": 0, + "m_DisplayName": "Metallic", + "m_SlotType": 0, + "m_Hidden": false, + "m_ShaderOutputName": "Metallic", + "m_StageCapability": 2, + "m_Value": 0.0, + "m_DefaultValue": 0.0, + "m_Labels": [] +} + diff --git a/Test Assets/New Shader Graph.shadergraph.meta b/Test Assets/New Shader Graph.shadergraph.meta new file mode 100644 index 0000000..7928258 --- /dev/null +++ b/Test Assets/New Shader Graph.shadergraph.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: c56761e16709479478032d95d74c0274 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 625f186215c104763be7675aa2d941aa, type: 3} diff --git a/Test Assets/Test Graph One.asset b/Test Assets/Test Graph One.asset new file mode 100644 index 0000000..f591796 --- /dev/null +++ b/Test Assets/Test Graph One.asset @@ -0,0 +1,95 @@ +%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: Test Graph One + m_EditorClassIdentifier: + nodes: + - rid: 6869590814762467392 + - rid: 6869590814762467393 + - rid: 6869590814762467394 + - rid: 6869590814762467395 + connections: + - inputPort: + nodeID: 64b814fe-8177-4620-9cf6-cacbe0b8ddd7 + nodeType: Texture2DDesaturate + portID: 0 + outputPort: + nodeID: b8924b16-c816-4168-8ee6-9ee30c6b63a9 + nodeType: Texture2DImport + portID: 0 + - inputPort: + nodeID: aa4b4ff2-61d6-4f84-a4ab-5b6b15c2ba97 + nodeType: Texture2DOutput + portID: 0 + outputPort: + nodeID: 64b814fe-8177-4620-9cf6-cacbe0b8ddd7 + nodeType: Texture2DDesaturate + portID: 0 + references: + version: 2 + RefIds: + - rid: 6869590814762467392 + type: {class: Texture2DImport, ns: ImageProcessingGraph.Editor.Nodes.Import_Nodes, asm: ImageProcessingGraphEditor} + data: + guid: b8924b16-c816-4168-8ee6-9ee30c6b63a9 + position: + serializedVersion: 2 + x: -1110 + y: 171.5 + width: 311 + height: 77 + typeName: ImageProcessingGraph.Editor.Nodes.Import_Nodes.Texture2DImport, + ImageProcessingGraphEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + asset: {fileID: 11400000} + textureImport: {fileID: 2800000, guid: 727a75301c3d24613a3ebcec4a24c2c8, type: 3} + - rid: 6869590814762467393 + type: {class: Texture2DDesaturate, ns: ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture, asm: ImageProcessingGraphEditor} + data: + guid: 64b814fe-8177-4620-9cf6-cacbe0b8ddd7 + position: + serializedVersion: 2 + x: -782 + y: 171.5 + width: 186 + height: 77 + typeName: ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture.Texture2DDesaturate, + ImageProcessingGraphEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + asset: {fileID: 11400000} + - rid: 6869590814762467394 + type: {class: Texture2DOutput, ns: ImageProcessingGraph.Editor.Nodes.Output, asm: ImageProcessingGraphEditor} + data: + guid: aa4b4ff2-61d6-4f84-a4ab-5b6b15c2ba97 + position: + serializedVersion: 2 + x: -541 + y: 186 + 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: test123432 + fileDirectory: Assets/ + - rid: 6869590814762467395 + type: {class: Texture2DImport, ns: ImageProcessingGraph.Editor.Nodes.Import_Nodes, asm: ImageProcessingGraphEditor} + data: + guid: 31f0135e-bc96-4c58-b808-210652f702bb + position: + serializedVersion: 2 + x: 152 + y: -9 + width: 311 + height: 77 + typeName: ImageProcessingGraph.Editor.Nodes.Import_Nodes.Texture2DImport, + ImageProcessingGraphEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + asset: {fileID: 11400000} + textureImport: {fileID: 0} diff --git a/Test Assets/Test Graph One.asset.meta b/Test Assets/Test Graph One.asset.meta new file mode 100644 index 0000000..34b9699 --- /dev/null +++ b/Test Assets/Test Graph One.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cef427fd668d96d47b164a934e97ba0d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json new file mode 100644 index 0000000..c857007 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "com.chromium.imageprocessingrah", + "version": "6000.0.19f1", + "displayName": "Image Processing Graph", + "description": "Stuffs", + "unity": "6000.0.19f1", + "dependencies": { + "com.unity.burst" : "1.8.17" + }, + "author": { + "name": "Sam Green", + "email": "samgreen432@hotmail.com", + "url": "https://sam-green.dev" + } +} diff --git a/package.json.meta b/package.json.meta new file mode 100644 index 0000000..194211a --- /dev/null +++ b/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0092e49564121a74aafb744bcc1282ac +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: