Browse Source

Crash fixes and improved noise node

flabbet 1 year ago
parent
commit
ee8675f18a

+ 3 - 0
src/PixiEditor.AvaloniaUI/Models/DocumentModels/DocumentStructureHelper.cs

@@ -77,6 +77,9 @@ internal class DocumentStructureHelper
             Guid guid = Guid.NewGuid();
             //put member above the layer
             INodeHandler parent = doc.StructureHelper.GetFirstForwardNode(layer);
+            if(parent is null)
+                parent = doc.NodeGraphHandler.OutputNode;
+            
             internals.ActionAccumulator.AddActions(new CreateStructureMember_Action(parent.Id, guid, type));
             name ??= GetUniqueName(
                 type == StructureMemberType.Layer

+ 1 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs

@@ -13,12 +13,6 @@ public class CombineChannelsNode : Node
     private readonly ColorFilter _redFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseRed + ColorMatrix.OpaqueAlphaOffset);
     private readonly ColorFilter _greenFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseGreen + ColorMatrix.OpaqueAlphaOffset);
     private readonly ColorFilter _blueFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseBlue + ColorMatrix.OpaqueAlphaOffset);
-    
-    private readonly ColorFilter _alphaGrayscaleFilter = ColorFilter.CreateColorMatrix(new ColorMatrix(
-        (0, 0, 0, 0, 0),
-        (0, 0, 0, 0, 0),
-        (0, 0, 0, 0, 0),
-        (1, 0, 0, 0, 0)));
 
     public InputProperty<Surface> Red { get; }
     
@@ -75,7 +69,7 @@ public class CombineChannelsNode : Node
 
         if (Alpha.Value is { } alpha)
         {
-            _clearPaint.ColorFilter = Grayscale.Value ? _alphaGrayscaleFilter : null;
+            _clearPaint.ColorFilter = Grayscale.Value ? Filters.AlphaGrayscaleFilter : null;
 
             workingSurface.DrawingSurface.Canvas.DrawSurface(alpha.DrawingSurface, 0, 0, _clearPaint);
         }

+ 22 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Filters.cs

@@ -0,0 +1,22 @@
+using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+
+public static class Filters
+{
+    public static readonly ColorFilter RedGrayscaleFilter =
+        ColorFilter.CreateColorMatrix(
+            ColorMatrix.UseRed + ColorMatrix.MapRedToGreenBlue + ColorMatrix.OpaqueAlphaOffset);
+
+    public static readonly ColorFilter GreenGrayscaleFilter =
+        ColorFilter.CreateColorMatrix(ColorMatrix.UseGreen + ColorMatrix.MapGreenToRedBlue +
+                                      ColorMatrix.OpaqueAlphaOffset);
+
+    public static readonly ColorFilter BlueGrayscaleFilter =
+        ColorFilter.CreateColorMatrix(ColorMatrix.UseBlue + ColorMatrix.MapBlueToRedGreen +
+                                      ColorMatrix.OpaqueAlphaOffset);
+
+    public static readonly ColorFilter AlphaGrayscaleFilter =
+        ColorFilter.CreateColorMatrix(ColorMatrix.MapAlphaToRedGreenBlue + ColorMatrix.OpaqueAlphaOffset);
+}

+ 58 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs

@@ -1,6 +1,7 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
 using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
 using PixiEditor.Numerics;
@@ -10,21 +11,33 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 public class NoiseNode : Node
 {
     private double previousScale = double.NaN;
+    private double previousSeed = double.NaN;
+    private NoiseType previousNoiseType = Nodes.NoiseType.TurbulencePerlin;
+    private int previousOctaves = -1;
+    
     private Paint paint = new();
     
+    private static readonly ColorFilter grayscaleFilter = ColorFilter.CreateColorMatrix(
+        ColorMatrix.MapAlphaToRedGreenBlue + ColorMatrix.OpaqueAlphaOffset);
+    
     public OutputProperty<Surface> Noise { get; }
-
+    
+    public InputProperty<NoiseType> NoiseType { get; }
     public InputProperty<VecI> Size { get; }
     
     public InputProperty<double> Scale { get; }
     
+    public InputProperty<int> Octaves { get; }
+    
     public InputProperty<double> Seed { get; }
 
     public NoiseNode()
     {
         Noise = CreateOutput<Surface>(nameof(Noise), "NOISE", null);
-        Size = CreateInput(nameof(Size), "SIZE", new VecI());
-        Scale = CreateInput(nameof(Scale), "SCALE", 0d);
+        NoiseType = CreateInput(nameof(NoiseType), "NOISE_TYPE", Nodes.NoiseType.TurbulencePerlin);
+        Size = CreateInput(nameof(Size), "SIZE", new VecI(64, 64));
+        Scale = CreateInput(nameof(Scale), "SCALE", 10d);
+        Octaves = CreateInput(nameof(Octaves), "OCTAVES", 1);
         Seed = CreateInput(nameof(Seed), "SEED", 0d);
     }
 
@@ -32,18 +45,34 @@ public class NoiseNode : Node
 
     protected override Surface OnExecute(RenderingContext context)
     {
-        if (Math.Abs(previousScale - Scale.Value) > 0.000001 || double.IsNaN(previousScale))
+        if (Math.Abs(previousScale - Scale.Value) > 0.000001 
+            || previousSeed != Seed.Value
+            || previousOctaves != Octaves.Value
+            || previousNoiseType != NoiseType.Value
+            || double.IsNaN(previousScale))
         {
-            var shader = Shader.CreatePerlinNoiseTurbulence((float)(1d / Scale.Value), (float)(1d / Scale.Value), 4, (float)Seed.Value);
+            var shader = SelectShader();
+            if (shader == null)
+            {
+                Noise.Value = null;
+                return null;
+            }
+            
             paint.Shader = shader;
-
+            
+            // Define a grayscale color filter to apply to the image
+            paint.ColorFilter = grayscaleFilter; 
+            
             previousScale = Scale.Value;
+            previousSeed = Seed.Value;
+            previousOctaves = Octaves.Value;
+            previousNoiseType = NoiseType.Value;
         }
         
         var size = Size.Value;
         
         var workingSurface = new Surface(size);
-        
+       
         workingSurface.DrawingSurface.Canvas.DrawPaint(paint);
 
         Noise.Value = workingSurface;
@@ -51,8 +80,30 @@ public class NoiseNode : Node
         return Noise.Value;
     }
 
+    private Shader SelectShader()
+    {
+        Shader shader = NoiseType.Value switch
+        {
+            Nodes.NoiseType.TurbulencePerlin => Shader.CreatePerlinNoiseTurbulence(
+                (float)(1d / Scale.Value),
+                (float)(1d / Scale.Value), Octaves.Value, (float)Seed.Value),
+            Nodes.NoiseType.FractalPerlin => Shader.CreatePerlinFractalNoise(
+                (float)(1d / Scale.Value),
+                (float)(1d / Scale.Value), Octaves.Value, (float)Seed.Value),
+            _ => null
+        };
+
+        return shader;
+    }
+
     public override string DisplayName { get; set; } = "NOISE_NODE";
     public override bool AreInputsLegal() => Size.Value is { X: > 0, Y: > 0 }; 
 
     public override Node CreateCopy() => new NoiseNode();
 }
+
+public enum NoiseType
+{
+    TurbulencePerlin,
+    FractalPerlin
+}

+ 10 - 0
src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs

@@ -48,6 +48,16 @@ public class DocumentRenderer
             int height = (int)(ChunkyImage.FullChunkSize * resolution.Multiplier());
 
             RectD sourceRect = new(x, y, width, height);
+            
+            RectD availableRect = new(0, 0, evaluated.Size.X, evaluated.Size.Y);
+            
+            sourceRect = sourceRect.Intersect(availableRect);
+            
+            if (sourceRect.IsZeroOrNegativeArea)
+            {
+                chunk.Dispose();
+                return new EmptyChunk();
+            }
 
             using var chunkSnapshot = evaluated.DrawingSurface.Snapshot((RectI)sourceRect);
 

+ 1 - 0
src/PixiEditor.DrawingApi.Core/Bridge/NativeObjectsImpl/IShaderImplementation.cs

@@ -12,5 +12,6 @@ public interface IShaderImplementation
     public Shader? CreateFromSksl(string sksl, bool isOpaque, out string errors);
     public Shader CreateLinearGradient(VecI p1, VecI p2, Color[] colors);
     public Shader CreatePerlinNoiseTurbulence(float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed);
+    public Shader CreatePerlinFractalNoise(float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed);
     public object GetNativeShader(IntPtr objectPointer);
 }

+ 5 - 0
src/PixiEditor.DrawingApi.Core/Surface/PaintImpl/Shader.cs

@@ -32,4 +32,9 @@ public class Shader : NativeObject
     {
         return DrawingBackendApi.Current.ShaderImplementation.CreatePerlinNoiseTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed);
     }
+
+    public static Shader CreatePerlinFractalNoise(float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed)
+    {
+        return DrawingBackendApi.Current.ShaderImplementation.CreatePerlinFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed);
+    }
 }

+ 12 - 0
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaShaderImplementation.cs

@@ -57,6 +57,18 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             ManagedInstances[shader.Handle] = shader;
             return new Shader(shader.Handle);
         }
+        
+        public Shader CreatePerlinFractalNoise(float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed)
+        {
+            SKShader shader = SKShader.CreatePerlinNoiseFractalNoise(
+                baseFrequencyX,
+                baseFrequencyY,
+                numOctaves,
+                seed);
+
+            ManagedInstances[shader.Handle] = shader;
+            return new Shader(shader.Handle);
+        }
 
         public object GetNativeShader(IntPtr objectPointer)
         {