Added Single Channel Node and Get Dimensions

As well as various bug fixes
This commit is contained in:
Chromium 2025-04-26 23:26:24 +01:00
parent deb2464537
commit 462bf4ffc0
42 changed files with 305 additions and 32 deletions

View File

@ -1,3 +1,4 @@
using System.IO;
using Unity.Collections; using Unity.Collections;
using UnityEngine; using UnityEngine;
@ -62,4 +63,25 @@ public struct ImageData
texture.Apply(); texture.Apply();
return texture; return texture;
} }
public void ExportPNG(string path)
{
Texture2D texture = this.ToTexture2D();
byte[] data = texture.EncodeToPNG();
string thePath;
if (path.StartsWith("Assets\\"))
{
thePath = path.Substring("Assets/".Length);
}
else
{
thePath = path;
}
if (data != null)
{
System.IO.File.WriteAllBytes(Path.Combine(Application.dataPath, thePath), data);
}
}
} }

View File

@ -59,6 +59,8 @@ namespace ImageProcessingGraph.Editor
// Log the elapsed time // Log the elapsed time
UnityEngine.Debug.Log($"Graph execution took {stopwatch.ElapsedMilliseconds} milliseconds."); UnityEngine.Debug.Log($"Graph execution took {stopwatch.ElapsedMilliseconds} milliseconds.");
AssetDatabase.Refresh();
} }

View File

@ -61,7 +61,7 @@ namespace ImageProcessingGraph.Editor
} }
if (connection.Equals((default(GraphConnection)))) if (connection.Equals((default(GraphConnection))))
return; continue;
// Get the output node from the connection // Get the output node from the connection
var outputNode = asset.Nodes.FirstOrDefault(n => n.ID == connection.outputPort.nodeID); var outputNode = asset.Nodes.FirstOrDefault(n => n.ID == connection.outputPort.nodeID);

View File

@ -0,0 +1,23 @@
using System;
using UnityEngine.UIElements;
namespace ImageProcessingGraph.Editor
{
[Serializable]
public class GreyscaleValue
{
public int value = 255;
public GreyscaleValue()
{
}
}
public class GreyscaleField : IntegerField
{
public (int, int) minMax = (0,255);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3ab37c25c4884480b8777a62e042a37c
timeCreated: 1745703416

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: ca37b9337d41484892c89933479f1e7f
timeCreated: 1743745541

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 1646a2bcaf6b4de488cadad1d7cdf795
timeCreated: 1743747481

View File

@ -1,20 +0,0 @@
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
using Unity.Collections;
using UnityEditor;
using UnityEngine;
namespace ImageProcessingGraph.Editor.Nodes.Output
{
[NodeInfo("Texture Export", "Export/Texture2D", true)]
public class Texture2DOutput : BaseImageNode
{
[NodeAttributes.Input("")] public ImageData inputPixels;
[NodeAttributes.Input("File Name")] public string fileName;
[NodeAttributes.Input("File Path")] public string fileDirectory;
public override void Process()
{
AssetDatabase.CreateAsset(inputPixels.ToTexture2D(), $"{fileDirectory}/{fileName}.asset");
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 22b0f0e553dc85e489979a232505a332 guid: cf47dea8bd47175418a3f462e2ddb060
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d053e80c499447499397a64b22ee639
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e6bd53786a8dee54790032792e0b03da
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6957ba197a1612a4c8f0f588d40e374f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,4 +1,5 @@
using ImageProcessingGraph.Editor.Nodes.NodeAttributes; using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
using Unity.Burst;
using Unity.Collections; using Unity.Collections;
using Unity.Jobs; using Unity.Jobs;
using UnityEngine; using UnityEngine;
@ -53,6 +54,7 @@ namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture
} }
// Job struct for processing the image data // Job struct for processing the image data
[BurstCompile]
struct RGBAJob : IJob struct RGBAJob : IJob
{ {
public NativeArray<Color32> pixelData; public NativeArray<Color32> pixelData;

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d31e8b213de87c54caaf096620201846
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
namespace ImageProcessingGraph.Editor.Nodes.Fun_Nodes.Texture
{
[NodeInfoAttribute("Get Dimensions", "Dimensions/Get Dimensions", false)]
public class TextureGetDimensions : BaseImageNode
{
[NodeAttributes.Input("")]
public ImageData inputTexture;
[NodeAttributes.Output("Width")]
public int width;
[NodeAttributes.Output("Height")]
public int height;
public override void Process()
{
this.width = inputTexture.Width;
this.height = inputTexture.Height;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 71e1db2a5bfe4a54a6ce2a99fe4d02fb
timeCreated: 1745699625

View File

@ -0,0 +1,37 @@
using System.ComponentModel.Composition.Primitives;
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
using Unity.Collections;
using UnityEditor;
using UnityEngine;
namespace ImageProcessingGraph.Editor.Nodes.Output
{
[NodeInfo("Texture Export", "Export/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 enum ExportType
{
Texture2D,
PNG
}
[NodeAttributes.Input("Export Type")] public ExportType exportType;
public override void Process()
{
switch (exportType)
{
case ExportType.Texture2D:
AssetDatabase.CreateAsset(inputPixels.ToTexture2D(), $"{fileDirectory}/{fileName}.asset");
break;
case ExportType.PNG:
inputPixels.ExportPNG($"{fileDirectory}/{fileName}.png");
break;
}
}
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 96b583a3bbd8b2c45a5eaa9e0f00d1b2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bf8e2895a77954d45864e9f8edae29c3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -6,7 +6,7 @@ using UnityEngine;
namespace ImageProcessingGraph.Editor.Nodes.Import_Nodes namespace ImageProcessingGraph.Editor.Nodes.Import_Nodes
{ {
[NodeInfo("Texture Import", "Imports/Texture2D", true)] [NodeInfo("Texture Import", "Imports/Import Texture", true)]
public class Texture2DImport : BaseImageNode public class Texture2DImport : BaseImageNode
{ {
[NodeAttributes.Input("")] [NodeAttributes.Input("")]
@ -23,6 +23,20 @@ namespace ImageProcessingGraph.Editor.Nodes.Import_Nodes
{ {
if (this.textureImport != null) if (this.textureImport != null)
{ {
TextureImporter textureImporter = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(textureImport)) as TextureImporter;
TextureImporterPlatformSettings texset = textureImporter.GetDefaultPlatformTextureSettings();
texset.format=TextureImporterFormat.RGBA32;
texset.maxTextureSize=16384;
textureImporter.SetPlatformTextureSettings(texset);
TextureImporterSettings settings = new TextureImporterSettings();
textureImporter.ReadTextureSettings(settings);
settings.readable = true;
textureImporter.SetTextureSettings(settings);
textureImporter.SaveAndReimport();
this.textureOutput = new ImageData(textureImport); this.textureOutput = new ImageData(textureImport);
this.fileName = textureImport.name; this.fileName = textureImport.name;
this.filePath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(textureImport)); this.filePath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(textureImport));

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c524131b4dbc3414ab755a10441b3841
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,63 @@
using ImageProcessingGraph.Editor.Nodes.NodeAttributes;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
namespace ImageProcessingGraph.Editor.Nodes.Types.Image.Utilities
{
public class SingleChannelColor
{
[NodeInfoAttribute("Channel Color", "Utility/Channel Color", false)]
public class SingleColorChannel : BaseImageNode
{
[NodeAttributes.Input("Color")] public GreyscaleValue range;
[NodeAttributes.Input("Width")] public int Width;
[NodeAttributes.Input("Height")] public int Height;
[NodeAttributes.Output("Color")] public SplitChannelData OutputColor;
public override void Process()
{
int pixelCount = Width * Height;
NativeArray<byte> outputData = new NativeArray<byte>(pixelCount * 4, Allocator.TempJob);
CreateSingleColorJob job = new CreateSingleColorJob
{
color32 = range.value,
outputData = outputData,
width = Width,
height = Height
};
job.Run();
OutputColor = new SplitChannelData(outputData.ToArray(), (Width, Height));
outputData.Dispose();
}
[BurstCompile]
struct CreateSingleColorJob : IJob
{
public int color32;
[WriteOnly]
public NativeArray<byte> outputData;
public int width;
public int height;
public void Execute()
{
// More efficient linear write pattern
for (int i = 0; i < width*height*4; i++)
{
outputData[i] = (byte)color32;
}
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aef9d7bde3d544e6bca5eef8ff58a60e
timeCreated: 1745699866

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 77d8c5639ebc40947940b6b150aad306
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -120,8 +120,9 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
if (propertyField != null) if (propertyField != null)
{ {
// Register a callback for when the value changes // Register a callback for when the value changes
if (propertyField is IntegerField intField) if (propertyField.GetType() == typeof(IntegerField))
{ {
var intField = propertyField as IntegerField;
intField.RegisterValueChangedCallback(evt => intField.RegisterValueChangedCallback(evt =>
{ {
field.SetValue(graphNode, evt.newValue); // Update the field with the new value field.SetValue(graphNode, evt.newValue); // Update the field with the new value
@ -176,6 +177,29 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
field.SetValue(graphNode, evt.newValue); // Update the field with the new value field.SetValue(graphNode, evt.newValue); // Update the field with the new value
}); });
} }
else if (propertyField is EnumField enumField)
{
enumField.RegisterValueChangedCallback(evt =>
{
field.SetValue(graphNode, evt.newValue); // 🎯 Update the field with the new enum value
});
}
else if (propertyField.GetType() == typeof(GreyscaleField))
{
var greyscaleField = propertyField as GreyscaleField;
greyscaleField.RegisterValueChangedCallback(evt =>
{
var value = (GreyscaleValue)field.GetValue(graphNode);
if(evt.newValue > greyscaleField.minMax.Item2)
value.value = greyscaleField.minMax.Item2;
else if(evt.newValue < greyscaleField.minMax.Item1)
value.value = greyscaleField.minMax.Item1;
value.value = (int)evt.newValue;
});
}
propertyFieldContainer.Add(propertyField); propertyFieldContainer.Add(propertyField);
port.Add(propertyFieldContainer); port.Add(propertyFieldContainer);
@ -192,7 +216,6 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
} }
} }
// Create appropriate property field based on the type of the variable
private VisualElement CreatePropertyFieldForType(Type type, object value) private VisualElement CreatePropertyFieldForType(Type type, object value)
{ {
if (type == typeof(int)) if (type == typeof(int))
@ -235,11 +258,24 @@ namespace ImageProcessingGraph.Editor.Unity_Image_Processing.Scripts.Editor.Wind
var objectField = new ObjectField { value = (Texture2D)value, objectType = typeof(Texture2D) }; var objectField = new ObjectField { value = (Texture2D)value, objectType = typeof(Texture2D) };
return objectField; return objectField;
} }
else if (type.IsEnum) // 💥✨ ENUMS, BABY! 💥✨
{
var enumField = new EnumField((Enum)value);
return enumField;
}
else if (type == typeof(GreyscaleValue))
{
// Add more types as needed (Vector3, etc.) var greyscaleValue = (GreyscaleValue)value;
var intField = new GreyscaleField { value = (int)greyscaleValue.value };
return intField;
}
// Add more types as needed
return null; return null;
} }
public void SavePosition() => graphNode.SetPosition(GetPosition()); public void SavePosition() => graphNode.SetPosition(GetPosition());
} }
} }