From 8430f28fa703f430919c6773201a6f6517b2b421 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 5 Dec 2019 20:19:40 +0100 Subject: [PATCH] Implemented animated simplex noise --- src/Artemis.Core/Models/Profile/Layer.cs | 72 +-- ...Artemis.Plugins.LayerElements.Noise.csproj | 49 +- .../NoiseLayerElement.cs | 56 +-- .../NoiseLayerElementSettings.cs | 52 +- .../NoiseLayerElementView.xaml | 68 +-- .../NoiseLayerElementViewModel.cs | 6 +- .../OpenSimplexNoise.cs | 448 ++++++++++++++++++ 7 files changed, 621 insertions(+), 130 deletions(-) create mode 100644 src/Artemis.Plugins.LayerElements.Noise/OpenSimplexNoise.cs diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 4d613d614..731969af2 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -68,33 +68,35 @@ namespace Artemis.Core.Models.Profile return; canvas.Save(); - - foreach (var layerElement in LayerElements) - layerElement.RenderPreProcess(surface, canvas); - - _renderCanvas.Clear(); - foreach (var layerElement in LayerElements) - layerElement.Render(surface, _renderCanvas); - - var baseShader = SKShader.CreateBitmap(_renderBitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, SKMatrix.MakeTranslation(RenderRectangle.Left, RenderRectangle.Top)); - foreach (var layerElement in LayerElements) + lock (_renderBitmap) { - var newBaseShader = layerElement.RenderPostProcess(surface, _renderBitmap, baseShader); - if (newBaseShader == null) - continue; + foreach (var layerElement in LayerElements) + layerElement.RenderPreProcess(surface, canvas); - // Dispose the old base shader if the layer element provided a new one - if (!ReferenceEquals(baseShader, newBaseShader)) - baseShader.Dispose(); + _renderCanvas.Clear(); + foreach (var layerElement in LayerElements) + layerElement.Render(surface, _renderCanvas); - baseShader = newBaseShader; + var baseShader = SKShader.CreateBitmap(_renderBitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, SKMatrix.MakeTranslation(RenderRectangle.Left, RenderRectangle.Top)); + foreach (var layerElement in LayerElements) + { + var newBaseShader = layerElement.RenderPostProcess(surface, _renderBitmap, baseShader); + if (newBaseShader == null) + continue; + + // Dispose the old base shader if the layer element provided a new one + if (!ReferenceEquals(baseShader, newBaseShader)) + baseShader.Dispose(); + + baseShader = newBaseShader; + } + + canvas.ClipPath(RenderPath); + canvas.DrawRect(RenderRectangle, new SKPaint {Shader = baseShader, FilterQuality = SKFilterQuality.Low}); + baseShader.Dispose(); + + canvas.Restore(); } - - canvas.ClipPath(RenderPath); - canvas.DrawRect(RenderRectangle, new SKPaint {Shader = baseShader, FilterQuality = SKFilterQuality.Low}); - baseShader.Dispose(); - - canvas.Restore(); } internal override void ApplyToEntity() @@ -206,12 +208,24 @@ namespace Artemis.Core.Models.Profile RenderPath = path; - var oldBitmap = _renderBitmap; - var oldCanvas = _renderCanvas; - _renderBitmap = new SKBitmap(new SKImageInfo((int) RenderRectangle.Width, (int) RenderRectangle.Height)); - _renderCanvas = new SKCanvas(_renderBitmap); - oldBitmap?.Dispose(); - oldCanvas?.Dispose(); + if (_renderBitmap != null) + { + lock (_renderBitmap) + { + var oldBitmap = _renderBitmap; + var oldCanvas = _renderCanvas; + _renderBitmap = new SKBitmap(new SKImageInfo((int) RenderRectangle.Width, (int) RenderRectangle.Height)); + _renderCanvas = new SKCanvas(_renderBitmap); + oldBitmap?.Dispose(); + oldCanvas?.Dispose(); + } + } + else + { + _renderBitmap = new SKBitmap(new SKImageInfo((int) RenderRectangle.Width, (int) RenderRectangle.Height)); + _renderCanvas = new SKCanvas(_renderBitmap); + } + OnRenderPropertiesUpdated(); } diff --git a/src/Artemis.Plugins.LayerElements.Noise/Artemis.Plugins.LayerElements.Noise.csproj b/src/Artemis.Plugins.LayerElements.Noise/Artemis.Plugins.LayerElements.Noise.csproj index c2585910e..811d6582c 100644 --- a/src/Artemis.Plugins.LayerElements.Noise/Artemis.Plugins.LayerElements.Noise.csproj +++ b/src/Artemis.Plugins.LayerElements.Noise/Artemis.Plugins.LayerElements.Noise.csproj @@ -12,6 +12,7 @@ v4.7.2 512 true + @@ -23,6 +24,7 @@ DEBUG;TRACE prompt 4 + 7 pdbonly @@ -33,10 +35,24 @@ 4 + + ..\packages\MaterialDesignColors.1.2.0\lib\net45\MaterialDesignColors.dll + False + + + ..\packages\MaterialDesignThemes.2.6.0\lib\net45\MaterialDesignThemes.Wpf.dll + False + + + False + ..\..\..\RGB.NET\bin\net45\RGB.NET.Core.dll + False + ..\packages\SkiaSharp.1.68.1\lib\net45\SkiaSharp.dll + False ..\packages\Stylet.1.3.0\lib\net45\Stylet.dll @@ -56,6 +72,12 @@ ..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + False + + + @@ -65,17 +87,19 @@ - - + + + - - MSBuild:Compile - Designer - + + {9b811f9b-86b9-4771-87af-72bae7078a36} + Artemis.Core + False + @@ -85,12 +109,17 @@ - - {9b811f9b-86b9-4771-87af-72bae7078a36} - Artemis.Core - + + Designer + MSBuild:Compile + + + + echo Copying plugin to Artemis.UI output directory +XCOPY "$(TargetDir.TrimEnd('\'))" "$(SolutionDir)\Artemis.UI\$(OutDir)Plugins\$(ProjectName)" /s /q /i /y + diff --git a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElement.cs b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElement.cs index 8480880d4..3e3dac6fb 100644 --- a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElement.cs +++ b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElement.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.LayerElement; @@ -10,42 +8,26 @@ namespace Artemis.Plugins.LayerElements.Noise { public class NoiseLayerElement : LayerElement { - private SKShader _shader; - private List _testColors; - private SKPaint _paint; + private readonly OpenSimplexNoise _noise; + private float _z; public NoiseLayerElement(Layer layer, Guid guid, NoiseLayerElementSettings settings, LayerElementDescriptor descriptor) : base(layer, guid, settings, descriptor) { Settings = settings; - _testColors = new List(); - for (var i = 0; i < 9; i++) - { - if (i != 8) - _testColors.Add(SKColor.FromHsv(i * 32, 100, 100)); - else - _testColors.Add(SKColor.FromHsv(0, 100, 100)); - } - - CreateShader(); - Layer.RenderPropertiesUpdated += (sender, args) => CreateShader(); - Settings.PropertyChanged += (sender, args) => CreateShader(); - } - - private void CreateShader() - { - var shader = SKShader.CreatePerlinNoiseFractalNoise(1, 1, 1, 1); - - var oldShader = _shader; - var oldPaint = _paint; - _shader = shader; - _paint = new SKPaint {Shader = _shader, FilterQuality = SKFilterQuality.Low}; - oldShader?.Dispose(); - oldPaint?.Dispose(); + _z = 0.001f; + _noise = new OpenSimplexNoise(Guid.GetHashCode()); } public new NoiseLayerElementSettings Settings { get; } + + public override void Update(double deltaTime) + { + _z += Settings.AnimationSpeed; + base.Update(deltaTime); + } + public override LayerElementViewModel GetViewModel() { return new NoiseLayerElementViewModel(this); @@ -53,7 +35,21 @@ namespace Artemis.Plugins.LayerElements.Noise public override void Render(ArtemisSurface surface, SKCanvas canvas) { - canvas.DrawRect(Layer.AbsoluteRenderRectangle, _paint); + var width = Layer.AbsoluteRenderRectangle.Width / 2; + var height = Layer.AbsoluteRenderRectangle.Height / 2; + using (var bitmap = new SKBitmap(new SKImageInfo((int) Layer.AbsoluteRenderRectangle.Width, (int) Layer.AbsoluteRenderRectangle.Height))) + { + for (var x = 0; x < width; x++) + { + for (var y = 0; y < height; y++) + { + var v = _noise.Evaluate(Settings.XScale * x / width, Settings.YScale * y / height, _z); + bitmap.SetPixel(x, y, new SKColor(255, 255, 255, (byte) ((v + 1) * 127))); + } + } + + canvas.DrawBitmap(bitmap, SKRect.Create(0, 0, width, height), Layer.AbsoluteRenderRectangle, new SKPaint {BlendMode = Settings.BlendMode}); + } } } } \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementSettings.cs b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementSettings.cs index d21103516..3123a620f 100644 --- a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementSettings.cs +++ b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementSettings.cs @@ -1,46 +1,46 @@ -using System.Collections.Generic; -using System.ComponentModel; -using Artemis.Core.Plugins.LayerElement; +using Artemis.Core.Plugins.LayerElement; using SkiaSharp; namespace Artemis.Plugins.LayerElements.Noise { public class NoiseLayerElementSettings : LayerElementSettings { - private BrushType _brushType; - private List _colors; + private SKBlendMode _blendMode; + private float _xScale; + private float _yScale; + private float _animationSpeed; + public NoiseLayerElementSettings() { - BrushType = BrushType.Solid; - Colors = new List(); + BlendMode = SKBlendMode.Color; + XScale = 0.5f; + YScale = 0.5f; + AnimationSpeed = 0.1f; } - public BrushType BrushType + public float XScale { - get => _brushType; - set => SetAndNotify(ref _brushType, value); + get => _xScale; + set => SetAndNotify(ref _xScale, value); } - public List Colors + public float YScale { - get => _colors; - set => SetAndNotify(ref _colors, value); + get => _yScale; + set => SetAndNotify(ref _yScale, value); } - } - public enum BrushType - { - [Description("Solid")] - Solid, + public float AnimationSpeed + { + get => _animationSpeed; + set => SetAndNotify(ref _animationSpeed, value); + } - [Description("Linear Gradient")] - LinearGradient, - - [Description("Radial Gradient")] - RadialGradient, - - [Description("Sweep Gradient")] - SweepGradient + public SKBlendMode BlendMode + { + get => _blendMode; + set => SetAndNotify(ref _blendMode, value); + } } } \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementView.xaml b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementView.xaml index c2f752827..516e6ea41 100644 --- a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementView.xaml +++ b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementView.xaml @@ -1,12 +1,13 @@ - + d:DesignHeight="450" d:DesignWidth="800" + d:DataContext="{d:DesignInstance {x:Type noiseLayer:NoiseLayerElementViewModel}}"> @@ -28,8 +29,30 @@ - - + + + + + + + + + + + Blend mode + Affects how the noise is rendered on the rest of the layer + + + + + + + @@ -40,11 +63,10 @@ - Setting title - Setting subtitle + X Scale - + @@ -60,11 +82,10 @@ - Setting title - Setting subtitle + Y Scale - + @@ -80,31 +101,10 @@ - Setting title - Setting subtitle + Animation speed - - - - - - - - - - - - - - - - - Setting title - Setting subtitle - - - + diff --git a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementViewModel.cs b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementViewModel.cs index 9d081dd9b..dd113db8a 100644 --- a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementViewModel.cs +++ b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElementViewModel.cs @@ -1,4 +1,7 @@ -using Artemis.Core.Plugins.LayerElement; +using System.Collections.Generic; +using Artemis.Core.Plugins.LayerElement; +using Artemis.Core.Utilities; +using SkiaSharp; namespace Artemis.Plugins.LayerElements.Noise { @@ -10,5 +13,6 @@ namespace Artemis.Plugins.LayerElements.Noise } public new NoiseLayerElement LayerElement { get; } + public IEnumerable BlendModes => EnumUtilities.GetAllValuesAndDescriptions(typeof(SKBlendMode)); } } \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerElements.Noise/OpenSimplexNoise.cs b/src/Artemis.Plugins.LayerElements.Noise/OpenSimplexNoise.cs new file mode 100644 index 000000000..ddbeb5e9f --- /dev/null +++ b/src/Artemis.Plugins.LayerElements.Noise/OpenSimplexNoise.cs @@ -0,0 +1,448 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Artemis.Plugins.LayerElements.Noise +{ + public class OpenSimplexNoise + { + private const double STRETCH_2D = -0.211324865405187; //(1/Math.sqrt(2+1)-1)/2; + private const double STRETCH_3D = -1.0 / 6.0; //(1/Math.sqrt(3+1)-1)/3; + private const double STRETCH_4D = -0.138196601125011; //(1/Math.sqrt(4+1)-1)/4; + private const double SQUISH_2D = 0.366025403784439; //(Math.sqrt(2+1)-1)/2; + private const double SQUISH_3D = 1.0 / 3.0; //(Math.sqrt(3+1)-1)/3; + private const double SQUISH_4D = 0.309016994374947; //(Math.sqrt(4+1)-1)/4; + private const double NORM_2D = 1.0 / 47.0; + private const double NORM_3D = 1.0 / 103.0; + private const double NORM_4D = 1.0 / 30.0; + + private byte[] perm; + private byte[] perm2D; + private byte[] perm3D; + private byte[] perm4D; + + private static double[] gradients2D = new double[] + { + 5, 2, 2, 5, + -5, 2, -2, 5, + 5, -2, 2, -5, + -5, -2, -2, -5, + }; + + private static double[] gradients3D = + { + -11, 4, 4, -4, 11, 4, -4, 4, 11, + 11, 4, 4, 4, 11, 4, 4, 4, 11, + -11, -4, 4, -4, -11, 4, -4, -4, 11, + 11, -4, 4, 4, -11, 4, 4, -4, 11, + -11, 4, -4, -4, 11, -4, -4, 4, -11, + 11, 4, -4, 4, 11, -4, 4, 4, -11, + -11, -4, -4, -4, -11, -4, -4, -4, -11, + 11, -4, -4, 4, -11, -4, 4, -4, -11, + }; + + private static double[] gradients4D = + { + 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, + -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, + 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, + -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, + 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, + -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, + 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, + -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, + 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, + -3, 1, 1, -1, -1, 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, + 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, + -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, + 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, + -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, + 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, -1, 1, -1, -1, -3, + -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, + }; + + private static Contribution2[] lookup2D; + private static Contribution3[] lookup3D; + private static Contribution4[] lookup4D; + + static OpenSimplexNoise() + { + var base2D = new int[][] + { + new int[] { 1, 1, 0, 1, 0, 1, 0, 0, 0 }, + new int[] { 1, 1, 0, 1, 0, 1, 2, 1, 1 } + }; + var p2D = new int[] { 0, 0, 1, -1, 0, 0, -1, 1, 0, 2, 1, 1, 1, 2, 2, 0, 1, 2, 0, 2, 1, 0, 0, 0 }; + var lookupPairs2D = new int[] { 0, 1, 1, 0, 4, 1, 17, 0, 20, 2, 21, 2, 22, 5, 23, 5, 26, 4, 39, 3, 42, 4, 43, 3 }; + + var contributions2D = new Contribution2[p2D.Length / 4]; + for (int i = 0; i < p2D.Length; i += 4) + { + var baseSet = base2D[p2D[i]]; + Contribution2 previous = null, current = null; + for (int k = 0; k < baseSet.Length; k += 3) + { + current = new Contribution2(baseSet[k], baseSet[k + 1], baseSet[k + 2]); + if (previous == null) + { + contributions2D[i / 4] = current; + } + else + { + previous.Next = current; + } + previous = current; + } + current.Next = new Contribution2(p2D[i + 1], p2D[i + 2], p2D[i + 3]); + } + + lookup2D = new Contribution2[64]; + for (var i = 0; i < lookupPairs2D.Length; i += 2) + { + lookup2D[lookupPairs2D[i]] = contributions2D[lookupPairs2D[i + 1]]; + } + + + var base3D = new int[][] + { + new int[] { 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1 }, + new int[] { 2, 1, 1, 0, 2, 1, 0, 1, 2, 0, 1, 1, 3, 1, 1, 1 }, + new int[] { 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 2, 1, 1, 0, 2, 1, 0, 1, 2, 0, 1, 1 } + }; + var p3D = new int[] { 0, 0, 1, -1, 0, 0, 1, 0, -1, 0, 0, -1, 1, 0, 0, 0, 1, -1, 0, 0, -1, 0, 1, 0, 0, -1, 1, 0, 2, 1, 1, 0, 1, 1, 1, -1, 0, 2, 1, 0, 1, 1, 1, -1, 1, 0, 2, 0, 1, 1, 1, -1, 1, 1, 1, 3, 2, 1, 0, 3, 1, 2, 0, 1, 3, 2, 0, 1, 3, 1, 0, 2, 1, 3, 0, 2, 1, 3, 0, 1, 2, 1, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1, 0, 1, 0, 2, 0, 2, 0, 1, 1, 0, 0, 1, 2, 0, 0, 2, 2, 0, 0, 0, 0, 1, 1, -1, 1, 2, 0, 0, 0, 0, 1, -1, 1, 1, 2, 0, 0, 0, 0, 1, 1, 1, -1, 2, 3, 1, 1, 1, 2, 0, 0, 2, 2, 3, 1, 1, 1, 2, 2, 0, 0, 2, 3, 1, 1, 1, 2, 0, 2, 0, 2, 1, 1, -1, 1, 2, 0, 0, 2, 2, 1, 1, -1, 1, 2, 2, 0, 0, 2, 1, -1, 1, 1, 2, 0, 0, 2, 2, 1, -1, 1, 1, 2, 0, 2, 0, 2, 1, 1, 1, -1, 2, 2, 0, 0, 2, 1, 1, 1, -1, 2, 0, 2, 0 }; + var lookupPairs3D = new int[] { 0, 2, 1, 1, 2, 2, 5, 1, 6, 0, 7, 0, 32, 2, 34, 2, 129, 1, 133, 1, 160, 5, 161, 5, 518, 0, 519, 0, 546, 4, 550, 4, 645, 3, 647, 3, 672, 5, 673, 5, 674, 4, 677, 3, 678, 4, 679, 3, 680, 13, 681, 13, 682, 12, 685, 14, 686, 12, 687, 14, 712, 20, 714, 18, 809, 21, 813, 23, 840, 20, 841, 21, 1198, 19, 1199, 22, 1226, 18, 1230, 19, 1325, 23, 1327, 22, 1352, 15, 1353, 17, 1354, 15, 1357, 17, 1358, 16, 1359, 16, 1360, 11, 1361, 10, 1362, 11, 1365, 10, 1366, 9, 1367, 9, 1392, 11, 1394, 11, 1489, 10, 1493, 10, 1520, 8, 1521, 8, 1878, 9, 1879, 9, 1906, 7, 1910, 7, 2005, 6, 2007, 6, 2032, 8, 2033, 8, 2034, 7, 2037, 6, 2038, 7, 2039, 6 }; + + var contributions3D = new Contribution3[p3D.Length / 9]; + for (int i = 0; i < p3D.Length; i += 9) + { + var baseSet = base3D[p3D[i]]; + Contribution3 previous = null, current = null; + for (int k = 0; k < baseSet.Length; k += 4) + { + current = new Contribution3(baseSet[k], baseSet[k + 1], baseSet[k + 2], baseSet[k + 3]); + if (previous == null) + { + contributions3D[i / 9] = current; + } + else + { + previous.Next = current; + } + previous = current; + } + current.Next = new Contribution3(p3D[i + 1], p3D[i + 2], p3D[i + 3], p3D[i + 4]); + current.Next.Next = new Contribution3(p3D[i + 5], p3D[i + 6], p3D[i + 7], p3D[i + 8]); + } + + lookup3D = new Contribution3[2048]; + for (var i = 0; i < lookupPairs3D.Length; i += 2) + { + lookup3D[lookupPairs3D[i]] = contributions3D[lookupPairs3D[i + 1]]; + } + + var base4D = new int[][] + { + new int[] { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1 }, + new int[] { 3, 1, 1, 1, 0, 3, 1, 1, 0, 1, 3, 1, 0, 1, 1, 3, 0, 1, 1, 1, 4, 1, 1, 1, 1 }, + new int[] { 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 2, 1, 1, 0, 0, 2, 1, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 1, 0, 2, 0, 1, 0, 1, 2, 0, 0, 1, 1 }, + new int[] { 3, 1, 1, 1, 0, 3, 1, 1, 0, 1, 3, 1, 0, 1, 1, 3, 0, 1, 1, 1, 2, 1, 1, 0, 0, 2, 1, 0, 1, 0, 2, 1, 0, 0, 1, 2, 0, 1, 1, 0, 2, 0, 1, 0, 1, 2, 0, 0, 1, 1 } + }; + var p4D = new int[] { 0, 0, 1, -1, 0, 0, 0, 1, 0, -1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 1, 0, -1, 0, 0, -1, 0, 1, 0, 0, 0, -1, 1, 0, 0, 0, 0, 1, -1, 0, 0, -1, 0, 0, 1, 0, 0, -1, 0, 1, 0, 0, 0, -1, 1, 0, 2, 1, 1, 0, 0, 1, 1, 1, -1, 0, 1, 1, 1, 0, -1, 0, 2, 1, 0, 1, 0, 1, 1, -1, 1, 0, 1, 1, 0, 1, -1, 0, 2, 0, 1, 1, 0, 1, -1, 1, 1, 0, 1, 0, 1, 1, -1, 0, 2, 1, 0, 0, 1, 1, 1, -1, 0, 1, 1, 1, 0, -1, 1, 0, 2, 0, 1, 0, 1, 1, -1, 1, 0, 1, 1, 0, 1, -1, 1, 0, 2, 0, 0, 1, 1, 1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 1, 4, 2, 1, 1, 0, 4, 1, 2, 1, 0, 4, 1, 1, 2, 0, 1, 4, 2, 1, 0, 1, 4, 1, 2, 0, 1, 4, 1, 1, 0, 2, 1, 4, 2, 0, 1, 1, 4, 1, 0, 2, 1, 4, 1, 0, 1, 2, 1, 4, 0, 2, 1, 1, 4, 0, 1, 2, 1, 4, 0, 1, 1, 2, 1, 2, 1, 1, 0, 0, 3, 2, 1, 0, 0, 3, 1, 2, 0, 0, 1, 2, 1, 0, 1, 0, 3, 2, 0, 1, 0, 3, 1, 0, 2, 0, 1, 2, 0, 1, 1, 0, 3, 0, 2, 1, 0, 3, 0, 1, 2, 0, 1, 2, 1, 0, 0, 1, 3, 2, 0, 0, 1, 3, 1, 0, 0, 2, 1, 2, 0, 1, 0, 1, 3, 0, 2, 0, 1, 3, 0, 1, 0, 2, 1, 2, 0, 0, 1, 1, 3, 0, 0, 2, 1, 3, 0, 0, 1, 2, 2, 3, 1, 1, 1, 0, 2, 1, 1, 1, -1, 2, 2, 0, 0, 0, 2, 3, 1, 1, 0, 1, 2, 1, 1, -1, 1, 2, 2, 0, 0, 0, 2, 3, 1, 0, 1, 1, 2, 1, -1, 1, 1, 2, 2, 0, 0, 0, 2, 3, 1, 1, 1, 0, 2, 1, 1, 1, -1, 2, 0, 2, 0, 0, 2, 3, 1, 1, 0, 1, 2, 1, 1, -1, 1, 2, 0, 2, 0, 0, 2, 3, 0, 1, 1, 1, 2, -1, 1, 1, 1, 2, 0, 2, 0, 0, 2, 3, 1, 1, 1, 0, 2, 1, 1, 1, -1, 2, 0, 0, 2, 0, 2, 3, 1, 0, 1, 1, 2, 1, -1, 1, 1, 2, 0, 0, 2, 0, 2, 3, 0, 1, 1, 1, 2, -1, 1, 1, 1, 2, 0, 0, 2, 0, 2, 3, 1, 1, 0, 1, 2, 1, 1, -1, 1, 2, 0, 0, 0, 2, 2, 3, 1, 0, 1, 1, 2, 1, -1, 1, 1, 2, 0, 0, 0, 2, 2, 3, 0, 1, 1, 1, 2, -1, 1, 1, 1, 2, 0, 0, 0, 2, 2, 1, 1, 1, -1, 0, 1, 1, 1, 0, -1, 0, 0, 0, 0, 0, 2, 1, 1, -1, 1, 0, 1, 1, 0, 1, -1, 0, 0, 0, 0, 0, 2, 1, -1, 1, 1, 0, 1, 0, 1, 1, -1, 0, 0, 0, 0, 0, 2, 1, 1, -1, 0, 1, 1, 1, 0, -1, 1, 0, 0, 0, 0, 0, 2, 1, -1, 1, 0, 1, 1, 0, 1, -1, 1, 0, 0, 0, 0, 0, 2, 1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 0, 0, 0, 0, 0, 2, 1, 1, 1, -1, 0, 1, 1, 1, 0, -1, 2, 2, 0, 0, 0, 2, 1, 1, -1, 1, 0, 1, 1, 0, 1, -1, 2, 2, 0, 0, 0, 2, 1, 1, -1, 0, 1, 1, 1, 0, -1, 1, 2, 2, 0, 0, 0, 2, 1, 1, 1, -1, 0, 1, 1, 1, 0, -1, 2, 0, 2, 0, 0, 2, 1, -1, 1, 1, 0, 1, 0, 1, 1, -1, 2, 0, 2, 0, 0, 2, 1, -1, 1, 0, 1, 1, 0, 1, -1, 1, 2, 0, 2, 0, 0, 2, 1, 1, -1, 1, 0, 1, 1, 0, 1, -1, 2, 0, 0, 2, 0, 2, 1, -1, 1, 1, 0, 1, 0, 1, 1, -1, 2, 0, 0, 2, 0, 2, 1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 2, 0, 0, 2, 0, 2, 1, 1, -1, 0, 1, 1, 1, 0, -1, 1, 2, 0, 0, 0, 2, 2, 1, -1, 1, 0, 1, 1, 0, 1, -1, 1, 2, 0, 0, 0, 2, 2, 1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 2, 0, 0, 0, 2, 3, 1, 1, 0, 0, 0, 2, 2, 0, 0, 0, 2, 1, 1, 1, -1, 3, 1, 0, 1, 0, 0, 2, 0, 2, 0, 0, 2, 1, 1, 1, -1, 3, 1, 0, 0, 1, 0, 2, 0, 0, 2, 0, 2, 1, 1, 1, -1, 3, 1, 1, 0, 0, 0, 2, 2, 0, 0, 0, 2, 1, 1, -1, 1, 3, 1, 0, 1, 0, 0, 2, 0, 2, 0, 0, 2, 1, 1, -1, 1, 3, 1, 0, 0, 0, 1, 2, 0, 0, 0, 2, 2, 1, 1, -1, 1, 3, 1, 1, 0, 0, 0, 2, 2, 0, 0, 0, 2, 1, -1, 1, 1, 3, 1, 0, 0, 1, 0, 2, 0, 0, 2, 0, 2, 1, -1, 1, 1, 3, 1, 0, 0, 0, 1, 2, 0, 0, 0, 2, 2, 1, -1, 1, 1, 3, 1, 0, 1, 0, 0, 2, 0, 2, 0, 0, 2, -1, 1, 1, 1, 3, 1, 0, 0, 1, 0, 2, 0, 0, 2, 0, 2, -1, 1, 1, 1, 3, 1, 0, 0, 0, 1, 2, 0, 0, 0, 2, 2, -1, 1, 1, 1, 3, 3, 2, 1, 0, 0, 3, 1, 2, 0, 0, 4, 1, 1, 1, 1, 3, 3, 2, 0, 1, 0, 3, 1, 0, 2, 0, 4, 1, 1, 1, 1, 3, 3, 0, 2, 1, 0, 3, 0, 1, 2, 0, 4, 1, 1, 1, 1, 3, 3, 2, 0, 0, 1, 3, 1, 0, 0, 2, 4, 1, 1, 1, 1, 3, 3, 0, 2, 0, 1, 3, 0, 1, 0, 2, 4, 1, 1, 1, 1, 3, 3, 0, 0, 2, 1, 3, 0, 0, 1, 2, 4, 1, 1, 1, 1, 3, 3, 2, 1, 0, 0, 3, 1, 2, 0, 0, 2, 1, 1, 1, -1, 3, 3, 2, 0, 1, 0, 3, 1, 0, 2, 0, 2, 1, 1, 1, -1, 3, 3, 0, 2, 1, 0, 3, 0, 1, 2, 0, 2, 1, 1, 1, -1, 3, 3, 2, 1, 0, 0, 3, 1, 2, 0, 0, 2, 1, 1, -1, 1, 3, 3, 2, 0, 0, 1, 3, 1, 0, 0, 2, 2, 1, 1, -1, 1, 3, 3, 0, 2, 0, 1, 3, 0, 1, 0, 2, 2, 1, 1, -1, 1, 3, 3, 2, 0, 1, 0, 3, 1, 0, 2, 0, 2, 1, -1, 1, 1, 3, 3, 2, 0, 0, 1, 3, 1, 0, 0, 2, 2, 1, -1, 1, 1, 3, 3, 0, 0, 2, 1, 3, 0, 0, 1, 2, 2, 1, -1, 1, 1, 3, 3, 0, 2, 1, 0, 3, 0, 1, 2, 0, 2, -1, 1, 1, 1, 3, 3, 0, 2, 0, 1, 3, 0, 1, 0, 2, 2, -1, 1, 1, 1, 3, 3, 0, 0, 2, 1, 3, 0, 0, 1, 2, 2, -1, 1, 1, 1 }; + var lookupPairs4D = new int[] { 0, 3, 1, 2, 2, 3, 5, 2, 6, 1, 7, 1, 8, 3, 9, 2, 10, 3, 13, 2, 16, 3, 18, 3, 22, 1, 23, 1, 24, 3, 26, 3, 33, 2, 37, 2, 38, 1, 39, 1, 41, 2, 45, 2, 54, 1, 55, 1, 56, 0, 57, 0, 58, 0, 59, 0, 60, 0, 61, 0, 62, 0, 63, 0, 256, 3, 258, 3, 264, 3, 266, 3, 272, 3, 274, 3, 280, 3, 282, 3, 2049, 2, 2053, 2, 2057, 2, 2061, 2, 2081, 2, 2085, 2, 2089, 2, 2093, 2, 2304, 9, 2305, 9, 2312, 9, 2313, 9, 16390, 1, 16391, 1, 16406, 1, 16407, 1, 16422, 1, 16423, 1, 16438, 1, 16439, 1, 16642, 8, 16646, 8, 16658, 8, 16662, 8, 18437, 6, 18439, 6, 18469, 6, 18471, 6, 18688, 9, 18689, 9, 18690, 8, 18693, 6, 18694, 8, 18695, 6, 18696, 9, 18697, 9, 18706, 8, 18710, 8, 18725, 6, 18727, 6, 131128, 0, 131129, 0, 131130, 0, 131131, 0, 131132, 0, 131133, 0, 131134, 0, 131135, 0, 131352, 7, 131354, 7, 131384, 7, 131386, 7, 133161, 5, 133165, 5, 133177, 5, 133181, 5, 133376, 9, 133377, 9, 133384, 9, 133385, 9, 133400, 7, 133402, 7, 133417, 5, 133421, 5, 133432, 7, 133433, 5, 133434, 7, 133437, 5, 147510, 4, 147511, 4, 147518, 4, 147519, 4, 147714, 8, 147718, 8, 147730, 8, 147734, 8, 147736, 7, 147738, 7, 147766, 4, 147767, 4, 147768, 7, 147770, 7, 147774, 4, 147775, 4, 149509, 6, 149511, 6, 149541, 6, 149543, 6, 149545, 5, 149549, 5, 149558, 4, 149559, 4, 149561, 5, 149565, 5, 149566, 4, 149567, 4, 149760, 9, 149761, 9, 149762, 8, 149765, 6, 149766, 8, 149767, 6, 149768, 9, 149769, 9, 149778, 8, 149782, 8, 149784, 7, 149786, 7, 149797, 6, 149799, 6, 149801, 5, 149805, 5, 149814, 4, 149815, 4, 149816, 7, 149817, 5, 149818, 7, 149821, 5, 149822, 4, 149823, 4, 149824, 37, 149825, 37, 149826, 36, 149829, 34, 149830, 36, 149831, 34, 149832, 37, 149833, 37, 149842, 36, 149846, 36, 149848, 35, 149850, 35, 149861, 34, 149863, 34, 149865, 33, 149869, 33, 149878, 32, 149879, 32, 149880, 35, 149881, 33, 149882, 35, 149885, 33, 149886, 32, 149887, 32, 150080, 49, 150082, 48, 150088, 49, 150098, 48, 150104, 47, 150106, 47, 151873, 46, 151877, 45, 151881, 46, 151909, 45, 151913, 44, 151917, 44, 152128, 49, 152129, 46, 152136, 49, 152137, 46, 166214, 43, 166215, 42, 166230, 43, 166247, 42, 166262, 41, 166263, 41, 166466, 48, 166470, 43, 166482, 48, 166486, 43, 168261, 45, 168263, 42, 168293, 45, 168295, 42, 168512, 31, 168513, 28, 168514, 31, 168517, 28, 168518, 25, 168519, 25, 280952, 40, 280953, 39, 280954, 40, 280957, 39, 280958, 38, 280959, 38, 281176, 47, 281178, 47, 281208, 40, 281210, 40, 282985, 44, 282989, 44, 283001, 39, 283005, 39, 283208, 30, 283209, 27, 283224, 30, 283241, 27, 283256, 22, 283257, 22, 297334, 41, 297335, 41, 297342, 38, 297343, 38, 297554, 29, 297558, 24, 297562, 29, 297590, 24, 297594, 21, 297598, 21, 299365, 26, 299367, 23, 299373, 26, 299383, 23, 299389, 20, 299391, 20, 299584, 31, 299585, 28, 299586, 31, 299589, 28, 299590, 25, 299591, 25, 299592, 30, 299593, 27, 299602, 29, 299606, 24, 299608, 30, 299610, 29, 299621, 26, 299623, 23, 299625, 27, 299629, 26, 299638, 24, 299639, 23, 299640, 22, 299641, 22, 299642, 21, 299645, 20, 299646, 21, 299647, 20, 299648, 61, 299649, 60, 299650, 61, 299653, 60, 299654, 59, 299655, 59, 299656, 58, 299657, 57, 299666, 55, 299670, 54, 299672, 58, 299674, 55, 299685, 52, 299687, 51, 299689, 57, 299693, 52, 299702, 54, 299703, 51, 299704, 56, 299705, 56, 299706, 53, 299709, 50, 299710, 53, 299711, 50, 299904, 61, 299906, 61, 299912, 58, 299922, 55, 299928, 58, 299930, 55, 301697, 60, 301701, 60, 301705, 57, 301733, 52, 301737, 57, 301741, 52, 301952, 79, 301953, 79, 301960, 76, 301961, 76, 316038, 59, 316039, 59, 316054, 54, 316071, 51, 316086, 54, 316087, 51, 316290, 78, 316294, 78, 316306, 73, 316310, 73, 318085, 77, 318087, 77, 318117, 70, 318119, 70, 318336, 79, 318337, 79, 318338, 78, 318341, 77, 318342, 78, 318343, 77, 430776, 56, 430777, 56, 430778, 53, 430781, 50, 430782, 53, 430783, 50, 431000, 75, 431002, 72, 431032, 75, 431034, 72, 432809, 74, 432813, 69, 432825, 74, 432829, 69, 433032, 76, 433033, 76, 433048, 75, 433065, 74, 433080, 75, 433081, 74, 447158, 71, 447159, 68, 447166, 71, 447167, 68, 447378, 73, 447382, 73, 447386, 72, 447414, 71, 447418, 72, 447422, 71, 449189, 70, 449191, 70, 449197, 69, 449207, 68, 449213, 69, 449215, 68, 449408, 67, 449409, 67, 449410, 66, 449413, 64, 449414, 66, 449415, 64, 449416, 67, 449417, 67, 449426, 66, 449430, 66, 449432, 65, 449434, 65, 449445, 64, 449447, 64, 449449, 63, 449453, 63, 449462, 62, 449463, 62, 449464, 65, 449465, 63, 449466, 65, 449469, 63, 449470, 62, 449471, 62, 449472, 19, 449473, 19, 449474, 18, 449477, 16, 449478, 18, 449479, 16, 449480, 19, 449481, 19, 449490, 18, 449494, 18, 449496, 17, 449498, 17, 449509, 16, 449511, 16, 449513, 15, 449517, 15, 449526, 14, 449527, 14, 449528, 17, 449529, 15, 449530, 17, 449533, 15, 449534, 14, 449535, 14, 449728, 19, 449729, 19, 449730, 18, 449734, 18, 449736, 19, 449737, 19, 449746, 18, 449750, 18, 449752, 17, 449754, 17, 449784, 17, 449786, 17, 451520, 19, 451521, 19, 451525, 16, 451527, 16, 451528, 19, 451529, 19, 451557, 16, 451559, 16, 451561, 15, 451565, 15, 451577, 15, 451581, 15, 451776, 19, 451777, 19, 451784, 19, 451785, 19, 465858, 18, 465861, 16, 465862, 18, 465863, 16, 465874, 18, 465878, 18, 465893, 16, 465895, 16, 465910, 14, 465911, 14, 465918, 14, 465919, 14, 466114, 18, 466118, 18, 466130, 18, 466134, 18, 467909, 16, 467911, 16, 467941, 16, 467943, 16, 468160, 13, 468161, 13, 468162, 13, 468163, 13, 468164, 13, 468165, 13, 468166, 13, 468167, 13, 580568, 17, 580570, 17, 580585, 15, 580589, 15, 580598, 14, 580599, 14, 580600, 17, 580601, 15, 580602, 17, 580605, 15, 580606, 14, 580607, 14, 580824, 17, 580826, 17, 580856, 17, 580858, 17, 582633, 15, 582637, 15, 582649, 15, 582653, 15, 582856, 12, 582857, 12, 582872, 12, 582873, 12, 582888, 12, 582889, 12, 582904, 12, 582905, 12, 596982, 14, 596983, 14, 596990, 14, 596991, 14, 597202, 11, 597206, 11, 597210, 11, 597214, 11, 597234, 11, 597238, 11, 597242, 11, 597246, 11, 599013, 10, 599015, 10, 599021, 10, 599023, 10, 599029, 10, 599031, 10, 599037, 10, 599039, 10, 599232, 13, 599233, 13, 599234, 13, 599235, 13, 599236, 13, 599237, 13, 599238, 13, 599239, 13, 599240, 12, 599241, 12, 599250, 11, 599254, 11, 599256, 12, 599257, 12, 599258, 11, 599262, 11, 599269, 10, 599271, 10, 599272, 12, 599273, 12, 599277, 10, 599279, 10, 599282, 11, 599285, 10, 599286, 11, 599287, 10, 599288, 12, 599289, 12, 599290, 11, 599293, 10, 599294, 11, 599295, 10 }; + var contributions4D = new Contribution4[p4D.Length / 16]; + for (int i = 0; i < p4D.Length; i += 16) + { + var baseSet = base4D[p4D[i]]; + Contribution4 previous = null, current = null; + for (int k = 0; k < baseSet.Length; k += 5) + { + current = new Contribution4(baseSet[k], baseSet[k + 1], baseSet[k + 2], baseSet[k + 3], baseSet[k + 4]); + if (previous == null) + { + contributions4D[i / 16] = current; + } + else + { + previous.Next = current; + } + previous = current; + } + current.Next = new Contribution4(p4D[i + 1], p4D[i + 2], p4D[i + 3], p4D[i + 4], p4D[i + 5]); + current.Next.Next = new Contribution4(p4D[i + 6], p4D[i + 7], p4D[i + 8], p4D[i + 9], p4D[i + 10]); + current.Next.Next.Next = new Contribution4(p4D[i + 11], p4D[i + 12], p4D[i + 13], p4D[i + 14], p4D[i + 15]); + } + + lookup4D = new Contribution4[1048576]; + for (var i = 0; i < lookupPairs4D.Length; i += 2) + { + lookup4D[lookupPairs4D[i]] = contributions4D[lookupPairs4D[i + 1]]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int FastFloor(double x) + { + var xi = (int)x; + return x < xi ? xi - 1 : xi; + } + + public OpenSimplexNoise() + : this(DateTime.Now.Ticks) + { + } + + public OpenSimplexNoise(long seed) + { + perm = new byte[256]; + perm2D = new byte[256]; + perm3D = new byte[256]; + perm4D = new byte[256]; + var source = new byte[256]; + for (int i = 0; i < 256; i++) + { + source[i] = (byte)i; + } + seed = seed * 6364136223846793005L + 1442695040888963407L; + seed = seed * 6364136223846793005L + 1442695040888963407L; + seed = seed * 6364136223846793005L + 1442695040888963407L; + for (int i = 255; i >= 0; i--) + { + seed = seed * 6364136223846793005L + 1442695040888963407L; + int r = (int)((seed + 31) % (i + 1)); + if (r < 0) + { + r += (i + 1); + } + perm[i] = source[r]; + perm2D[i] = (byte)(perm[i] & 0x0E); + perm3D[i] = (byte)((perm[i] % 24) * 3); + perm4D[i] = (byte)(perm[i] & 0xFC); + source[r] = source[i]; + } + } + + public double Evaluate(double x, double y) + { + var stretchOffset = (x + y) * STRETCH_2D; + var xs = x + stretchOffset; + var ys = y + stretchOffset; + + var xsb = FastFloor(xs); + var ysb = FastFloor(ys); + + var squishOffset = (xsb + ysb) * SQUISH_2D; + var dx0 = x - (xsb + squishOffset); + var dy0 = y - (ysb + squishOffset); + + var xins = xs - xsb; + var yins = ys - ysb; + + var inSum = xins + yins; + + var hash = + (int)(xins - yins + 1) | + (int)(inSum) << 1 | + (int)(inSum + yins) << 2 | + (int)(inSum + xins) << 4; + + var c = lookup2D[hash]; + + var value = 0.0; + while (c != null) + { + var dx = dx0 + c.dx; + var dy = dy0 + c.dy; + var attn = 2 - dx * dx - dy * dy; + if (attn > 0) + { + var px = xsb + c.xsb; + var py = ysb + c.ysb; + + var i = perm2D[(perm[px & 0xFF] + py) & 0xFF]; + var valuePart = gradients2D[i] * dx + gradients2D[i + 1] * dy; + + attn *= attn; + value += attn * attn * valuePart; + } + c = c.Next; + } + return value * NORM_2D; + } + + public double Evaluate(double x, double y, double z) + { + var stretchOffset = (x + y + z) * STRETCH_3D; + var xs = x + stretchOffset; + var ys = y + stretchOffset; + var zs = z + stretchOffset; + + var xsb = FastFloor(xs); + var ysb = FastFloor(ys); + var zsb = FastFloor(zs); + + var squishOffset = (xsb + ysb + zsb) * SQUISH_3D; + var dx0 = x - (xsb + squishOffset); + var dy0 = y - (ysb + squishOffset); + var dz0 = z - (zsb + squishOffset); + + var xins = xs - xsb; + var yins = ys - ysb; + var zins = zs - zsb; + + var inSum = xins + yins + zins; + + var hash = + (int)(yins - zins + 1) | + (int)(xins - yins + 1) << 1 | + (int)(xins - zins + 1) << 2 | + (int)inSum << 3 | + (int)(inSum + zins) << 5 | + (int)(inSum + yins) << 7 | + (int)(inSum + xins) << 9; + + var c = lookup3D[hash]; + + var value = 0.0; + while (c != null) + { + var dx = dx0 + c.dx; + var dy = dy0 + c.dy; + var dz = dz0 + c.dz; + var attn = 2 - dx * dx - dy * dy - dz * dz; + if (attn > 0) + { + var px = xsb + c.xsb; + var py = ysb + c.ysb; + var pz = zsb + c.zsb; + + var i = perm3D[(perm[(perm[px & 0xFF] + py) & 0xFF] + pz) & 0xFF]; + var valuePart = gradients3D[i] * dx + gradients3D[i + 1] * dy + gradients3D[i + 2] * dz; + + attn *= attn; + value += attn * attn * valuePart; + } + + c = c.Next; + } + return value * NORM_3D; + } + + public double Evaluate(double x, double y, double z, double w) + { + var stretchOffset = (x + y + z + w) * STRETCH_4D; + var xs = x + stretchOffset; + var ys = y + stretchOffset; + var zs = z + stretchOffset; + var ws = w + stretchOffset; + + var xsb = FastFloor(xs); + var ysb = FastFloor(ys); + var zsb = FastFloor(zs); + var wsb = FastFloor(ws); + + var squishOffset = (xsb + ysb + zsb + wsb) * SQUISH_4D; + var dx0 = x - (xsb + squishOffset); + var dy0 = y - (ysb + squishOffset); + var dz0 = z - (zsb + squishOffset); + var dw0 = w - (wsb + squishOffset); + + var xins = xs - xsb; + var yins = ys - ysb; + var zins = zs - zsb; + var wins = ws - wsb; + + var inSum = xins + yins + zins + wins; + + var hash = + (int)(zins - wins + 1) | + (int)(yins - zins + 1) << 1 | + (int)(yins - wins + 1) << 2 | + (int)(xins - yins + 1) << 3 | + (int)(xins - zins + 1) << 4 | + (int)(xins - wins + 1) << 5 | + (int)inSum << 6 | + (int)(inSum + wins) << 8 | + (int)(inSum + zins) << 11 | + (int)(inSum + yins) << 14 | + (int)(inSum + xins) << 17; + + var c = lookup4D[hash]; + + var value = 0.0; + while (c != null) + { + var dx = dx0 + c.dx; + var dy = dy0 + c.dy; + var dz = dz0 + c.dz; + var dw = dw0 + c.dw; + var attn = 2 - dx * dx - dy * dy - dz * dz - dw * dw; + if (attn > 0) + { + var px = xsb + c.xsb; + var py = ysb + c.ysb; + var pz = zsb + c.zsb; + var pw = wsb + c.wsb; + + var i = perm4D[(perm[(perm[(perm[px & 0xFF] + py) & 0xFF] + pz) & 0xFF] + pw) & 0xFF]; + var valuePart = gradients4D[i] * dx + gradients4D[i + 1] * dy + gradients4D[i + 2] * dz + gradients4D[i + 3] * dw; + + attn *= attn; + value += attn * attn * valuePart; + } + + c = c.Next; + } + return value * NORM_4D; + } + + private class Contribution2 + { + public double dx, dy; + public int xsb, ysb; + public Contribution2 Next; + + public Contribution2(double multiplier, int xsb, int ysb) + { + dx = -xsb - multiplier * SQUISH_2D; + dy = -ysb - multiplier * SQUISH_2D; + this.xsb = xsb; + this.ysb = ysb; + } + } + + private class Contribution3 + { + public double dx, dy, dz; + public int xsb, ysb, zsb; + public Contribution3 Next; + + public Contribution3(double multiplier, int xsb, int ysb, int zsb) + { + dx = -xsb - multiplier * SQUISH_3D; + dy = -ysb - multiplier * SQUISH_3D; + dz = -zsb - multiplier * SQUISH_3D; + this.xsb = xsb; + this.ysb = ysb; + this.zsb = zsb; + } + } + + private class Contribution4 + { + public double dx, dy, dz, dw; + public int xsb, ysb, zsb, wsb; + public Contribution4 Next; + + public Contribution4(double multiplier, int xsb, int ysb, int zsb, int wsb) + { + dx = -xsb - multiplier * SQUISH_4D; + dy = -ysb - multiplier * SQUISH_4D; + dz = -zsb - multiplier * SQUISH_4D; + dw = -wsb - multiplier * SQUISH_4D; + this.xsb = xsb; + this.ysb = ysb; + this.zsb = zsb; + this.wsb = wsb; + } + } + } +}