From ef3007a60344e6fa1b0a1df0936f23c4ada0d85c Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 30 Nov 2018 21:54:30 +0100 Subject: [PATCH] Parallax (#131) --- BuildChecker/BuildChecker.csproj | 2 +- Content.Client/Content.Client.csproj | 14 +- Content.Client/EntryPoint.cs | 8 + .../Interfaces/Parallax/IParallaxManager.cs | 12 + Content.Client/Parallax/ParallaxGenerator.cs | 417 ++++++++++++++++++ Content.Client/Parallax/ParallaxManager.cs | 107 +++++ Content.Client/Parallax/ParallaxOverlay.cs | 63 +++ Content.Server/Content.Server.csproj | 4 +- Content.Shared/Content.Shared.csproj | 2 +- Content.Tests/Content.Tests.csproj | 4 +- Resources/parallax_config.toml | 89 ++++ engine | 2 +- 12 files changed, 714 insertions(+), 10 deletions(-) create mode 100644 Content.Client/Interfaces/Parallax/IParallaxManager.cs create mode 100644 Content.Client/Parallax/ParallaxGenerator.cs create mode 100644 Content.Client/Parallax/ParallaxManager.cs create mode 100644 Content.Client/Parallax/ParallaxOverlay.cs create mode 100644 Resources/parallax_config.toml diff --git a/BuildChecker/BuildChecker.csproj b/BuildChecker/BuildChecker.csproj index e2d01a14f6..49d17dca77 100644 --- a/BuildChecker/BuildChecker.csproj +++ b/BuildChecker/BuildChecker.csproj @@ -17,7 +17,7 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild python3 py -3 {C899FCA4-7037-4E49-ABC2-44DE72487110} - .NETFramework, Version=v4.5.1 + .NETFramework, Version=v4.7.1 Library diff --git a/Content.Client/Content.Client.csproj b/Content.Client/Content.Client.csproj index 4f035dd9a9..01eac19f9b 100644 --- a/Content.Client/Content.Client.csproj +++ b/Content.Client/Content.Client.csproj @@ -9,7 +9,7 @@ Properties Content.Client Content.Client - v4.5.1 + v4.7.1 512 ..\engine\bin\Client\Resources\Assemblies\ 7.2 @@ -53,8 +53,12 @@ portable + + + + @@ -81,6 +85,10 @@ + + + + @@ -112,7 +120,7 @@ - + @@ -131,4 +139,4 @@ - \ No newline at end of file + diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 14854641ca..556f6149bb 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -8,8 +8,12 @@ using Content.Client.GameTicking; using Content.Client.Input; using Content.Client.Interfaces; using Content.Client.Interfaces.GameObjects; +using Content.Client.Interfaces.Parallax; +using Content.Client.Parallax; using Content.Shared.Interfaces; using SS14.Client; +using SS14.Client.Interfaces; +using SS14.Client.Interfaces.Graphics.Overlays; using SS14.Client.Interfaces.Input; using SS14.Client.Utility; using SS14.Shared.ContentPack; @@ -79,7 +83,10 @@ namespace Content.Client IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); IoCManager.BuildGraph(); + + IoCManager.Resolve().LoadParallax(); } public override void PostInit() @@ -92,6 +99,7 @@ namespace Content.Client IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); + IoCManager.Resolve().AddOverlay(new ParallaxOverlay()); } public override void Update(AssemblyLoader.UpdateLevel level, float frameTime) diff --git a/Content.Client/Interfaces/Parallax/IParallaxManager.cs b/Content.Client/Interfaces/Parallax/IParallaxManager.cs new file mode 100644 index 0000000000..8956eade7a --- /dev/null +++ b/Content.Client/Interfaces/Parallax/IParallaxManager.cs @@ -0,0 +1,12 @@ +using System; +using SS14.Client.Graphics; + +namespace Content.Client.Interfaces.Parallax +{ + public interface IParallaxManager + { + event Action OnTextureLoaded; + Texture ParallaxTexture { get; } + void LoadParallax(); + } +} diff --git a/Content.Client/Parallax/ParallaxGenerator.cs b/Content.Client/Parallax/ParallaxGenerator.cs new file mode 100644 index 0000000000..b974182c37 --- /dev/null +++ b/Content.Client/Parallax/ParallaxGenerator.cs @@ -0,0 +1,417 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Nett; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; +using SS14.Client.Utility; +using SS14.Shared.Log; +using SS14.Shared.Maths; +using SS14.Shared.Noise; +using BlendFactor = SS14.Shared.Maths.Color.BlendFactor; + +namespace Content.Client.Parallax +{ + public class ParallaxGenerator + { + private readonly List Layers = new List(); + + public static Image GenerateParallax(TomlTable config, Size size) + { + Logger.DebugS("parallax", "Generating parallax!"); + var generator = new ParallaxGenerator(); + generator._loadConfig(config); + + var image = new Image(Configuration.Default, size.Width, size.Height, Rgba32.Black); + var count = 0; + foreach (var layer in generator.Layers) + { + layer.Apply(image); + Logger.DebugS("parallax", "Layer {0} done!", count++); + } + + return image; + } + + private void _loadConfig(TomlTable config) + { + foreach (var layerArray in config.Get("layers").Items) + { + var layer = layerArray.Get(); + switch (layer.Get("type")) + { + case "noise": + var layerNoise = new LayerNoise(layer); + Layers.Add(layerNoise); + break; + + case "points": + var layerPoint = new LayerPoints(layer); + Layers.Add(layerPoint); + break; + + default: + throw new NotSupportedException(); + } + } + } + + private abstract class Layer + { + public abstract void Apply(Image bitmap); + } + + private class LayerNoise : Layer + { + private readonly Color InnerColor = Color.White; + private readonly Color OuterColor = Color.Black; + private readonly NoiseGenerator.NoiseType NoiseType = NoiseGenerator.NoiseType.Fbm; + private readonly uint Seed = 1234; + private readonly double Persistence = 0.5; + private readonly double Lacunarity = Math.PI * 2 / 3; + private readonly double Frequency = 1; + private readonly uint Octaves = 3; + private readonly double Threshold; + private readonly double Power = 1; + private readonly BlendFactor SrcFactor = BlendFactor.One; + private readonly BlendFactor DstFactor = BlendFactor.One; + + public LayerNoise(TomlTable table) + { + if (table.TryGetValue("innercolor", out var tomlObject)) + { + InnerColor = Color.FromHex(tomlObject.Get()); + } + + if (table.TryGetValue("outercolor", out tomlObject)) + { + OuterColor = Color.FromHex(tomlObject.Get()); + } + + if (table.TryGetValue("seed", out tomlObject)) + { + Seed = (uint) tomlObject.Get(); + } + + if (table.TryGetValue("persistence", out tomlObject)) + { + Persistence = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + + if (table.TryGetValue("lacunarity", out tomlObject)) + { + Lacunarity = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + + if (table.TryGetValue("frequency", out tomlObject)) + { + Frequency = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + + if (table.TryGetValue("octaves", out tomlObject)) + { + Octaves = (uint) tomlObject.Get(); + } + + if (table.TryGetValue("threshold", out tomlObject)) + { + Threshold = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + + if (table.TryGetValue("sourcefactor", out tomlObject)) + { + SrcFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get()); + } + + if (table.TryGetValue("destfactor", out tomlObject)) + { + DstFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get()); + } + + if (table.TryGetValue("power", out tomlObject)) + { + Power = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + + if (table.TryGetValue("noise_type", out tomlObject)) + { + switch (tomlObject.Get()) + { + case "fbm": + NoiseType = NoiseGenerator.NoiseType.Fbm; + break; + case "ridged": + NoiseType = NoiseGenerator.NoiseType.Ridged; + break; + default: + throw new InvalidOperationException(); + } + } + } + + public override void Apply(Image bitmap) + { + var noise = new NoiseGenerator(NoiseType); + noise.SetSeed(Seed); + noise.SetFrequency(Frequency); + noise.SetPersistence(Persistence); + noise.SetLacunarity(Lacunarity); + noise.SetOctaves(Octaves); + noise.SetPeriodX(bitmap.Width); + noise.SetPeriodY(bitmap.Height); + var threshVal = 1 / (1 - Threshold); + var powFactor = 1 / Power; + for (var x = 0; x < bitmap.Width; x++) + { + for (var y = 0; y < bitmap.Height; y++) + { + // Do noise calculations. + var noiseVal = Math.Min(1, Math.Max(0, (noise.GetNoiseTiled(x, y) + 1) / 2)); + + // Threshold + noiseVal = Math.Max(0, noiseVal - Threshold); + noiseVal *= threshVal; + noiseVal = Math.Pow(noiseVal, powFactor); + + // Get colors based on noise values. + var srcColor = Color.InterpolateBetween(InnerColor, OuterColor, (float) noiseVal) + .WithAlpha((float) noiseVal); + + // Apply blending factors & write back. + var dstColor = bitmap[x, y].ConvertImgSharp(); + bitmap[x, y] = Color.Blend(dstColor, srcColor, DstFactor, SrcFactor).ConvertImgSharp(); + } + } + } + } + + private class LayerPoints : Layer + { + private readonly int Seed = 1234; + private readonly int PointCount = 100; + + private readonly Color CloseColor = Color.White; + private readonly Color FarColor = Color.Black; + + private readonly BlendFactor SrcFactor = BlendFactor.One; + private readonly BlendFactor DstFactor = BlendFactor.One; + + // Noise mask stuff. + private readonly bool Masked; + private readonly NoiseGenerator.NoiseType MaskNoiseType = NoiseGenerator.NoiseType.Fbm; + private readonly uint MaskSeed = 1234; + private readonly double MaskPersistence = 0.5; + private readonly double MaskLacunarity = Math.PI * 2 / 3; + private readonly double MaskFrequency = 1; + private readonly uint MaskOctaves = 3; + private readonly double MaskThreshold; + private readonly int PointSize = 1; + private readonly double MaskPower = 1; + + + public LayerPoints(TomlTable table) + { + if (table.TryGetValue("seed", out var tomlObject)) + { + Seed = tomlObject.Get(); + } + + if (table.TryGetValue("count", out tomlObject)) + { + PointCount = tomlObject.Get(); + } + + if (table.TryGetValue("sourcefactor", out tomlObject)) + { + SrcFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get()); + } + + if (table.TryGetValue("destfactor", out tomlObject)) + { + DstFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get()); + } + + if (table.TryGetValue("farcolor", out tomlObject)) + { + FarColor = Color.FromHex(tomlObject.Get()); + } + + if (table.TryGetValue("closecolor", out tomlObject)) + { + CloseColor = Color.FromHex(tomlObject.Get()); + } + + if (table.TryGetValue("pointsize", out tomlObject)) + { + PointSize = tomlObject.Get(); + } + + // Noise mask stuff. + if (table.TryGetValue("mask", out tomlObject)) + { + Masked = tomlObject.Get(); + } + + if (table.TryGetValue("maskseed", out tomlObject)) + { + MaskSeed = (uint) tomlObject.Get(); + } + + if (table.TryGetValue("maskpersistence", out tomlObject)) + { + MaskPersistence = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + + if (table.TryGetValue("masklacunarity", out tomlObject)) + { + MaskLacunarity = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + + if (table.TryGetValue("maskfrequency", out tomlObject)) + { + MaskFrequency = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + + if (table.TryGetValue("maskoctaves", out tomlObject)) + { + MaskOctaves = (uint) tomlObject.Get(); + } + + if (table.TryGetValue("maskthreshold", out tomlObject)) + { + MaskThreshold = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + + if (table.TryGetValue("masknoise_type", out tomlObject)) + { + switch (tomlObject.Get()) + { + case "fbm": + MaskNoiseType = NoiseGenerator.NoiseType.Fbm; + break; + case "ridged": + MaskNoiseType = NoiseGenerator.NoiseType.Ridged; + break; + default: + throw new InvalidOperationException(); + } + } + + if (table.TryGetValue("maskpower", out tomlObject)) + { + MaskPower = double.Parse(tomlObject.Get(), CultureInfo.InvariantCulture); + } + } + + public override void Apply(Image bitmap) + { + // Temporary buffer so we don't mess up blending. + var buffer = new Image(Configuration.Default, bitmap.Width, bitmap.Height, Rgba32.Black); + + if (Masked) + { + GenPointsMasked(buffer); + } + else + { + GenPoints(buffer); + } + + for (var x = 0; x < bitmap.Width; x++) + { + for (var y = 0; y < bitmap.Height; y++) + { + var dstColor = bitmap[x, y].ConvertImgSharp(); + var srcColor = buffer[x, y].ConvertImgSharp(); + + bitmap[x, y] = Color.Blend(dstColor, srcColor, DstFactor, SrcFactor).ConvertImgSharp(); + } + } + } + + private void GenPoints(Image buffer) + { + var o = PointSize - 1; + var random = new Random(Seed); + for (var i = 0; i < PointCount; i++) + { + var relX = random.NextDouble(); + var relY = random.NextDouble(); + + var x = (int) (relX * buffer.Width); + var y = (int) (relY * buffer.Height); + + var dist = random.NextDouble(); + + for (var ox = x - o; ox <= x + o; ox++) + { + for (var oy = y - o; oy <= y + o; oy++) + { + var color = Color.InterpolateBetween(FarColor, CloseColor, (float) dist).ConvertImgSharp(); + buffer[MathHelper.Mod(ox, buffer.Width), MathHelper.Mod(oy, buffer.Width)] = color; + } + } + } + } + + void GenPointsMasked(Image buffer) + { + var o = PointSize - 1; + var random = new Random(Seed); + var noise = new NoiseGenerator(MaskNoiseType); + noise.SetSeed(MaskSeed); + noise.SetFrequency(MaskFrequency); + noise.SetPersistence(MaskPersistence); + noise.SetLacunarity(MaskLacunarity); + noise.SetOctaves(MaskOctaves); + noise.SetPeriodX(buffer.Width); + noise.SetPeriodY(buffer.Height); + + var threshVal = 1 / (1 - MaskThreshold); + var powFactor = 1 / MaskPower; + + const int maxPointAttemptCount = 9999; + var pointAttemptCount = 0; + + for (var i = 0; i < PointCount; i++) + { + var relX = random.NextDouble(); + var relY = random.NextDouble(); + + var x = (int) (relX * buffer.Width); + var y = (int) (relY * buffer.Height); + + // Grab noise at this point. + var noiseVal = Math.Min(1, Math.Max(0, (noise.GetNoiseTiled(x, y) + 1) / 2)); + // Threshold + noiseVal = Math.Max(0, noiseVal - MaskThreshold); + noiseVal *= threshVal; + noiseVal = Math.Pow(noiseVal, powFactor); + + var randomThresh = random.NextDouble(); + if (randomThresh > noiseVal) + { + if (++pointAttemptCount <= maxPointAttemptCount) + { + i--; + } + + continue; + } + + var dist = random.NextDouble(); + + for (var ox = x - o; ox <= x + o; ox++) + { + for (var oy = y - o; oy <= y + o; oy++) + { + var color = Color.InterpolateBetween(FarColor, CloseColor, (float) dist).ConvertImgSharp(); + buffer[MathHelper.Mod(ox, buffer.Width), MathHelper.Mod(oy, buffer.Height)] = color; + } + } + } + } + } + } +} diff --git a/Content.Client/Parallax/ParallaxManager.cs b/Content.Client/Parallax/ParallaxManager.cs new file mode 100644 index 0000000000..a99eed0f68 --- /dev/null +++ b/Content.Client/Parallax/ParallaxManager.cs @@ -0,0 +1,107 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Content.Client.Interfaces.Parallax; +using ICSharpCode.SharpZipLib.Checksum; +using Nett; +using SixLabors.ImageSharp; +using SixLabors.Primitives; +using SS14.Client.Graphics; +using SS14.Client.Interfaces.ResourceManagement; +using SS14.Shared.IoC; +using SS14.Shared.Log; +using SS14.Shared.Utility; + +namespace Content.Client.Parallax +{ + public class ParallaxManager : IParallaxManager + { +#pragma warning disable 649 + [Dependency] private readonly IResourceCache _resourceCache; +#pragma warning restore 649 + + private static readonly ResourcePath ParallaxConfigPath = new ResourcePath("/parallax_config.toml"); + + // Both of these below are in the user directory. + private static readonly ResourcePath ParallaxPath = new ResourcePath("/parallax_cache.png"); + private static readonly ResourcePath ParallaxConfigCrcPath = new ResourcePath("/parallax_config_crc"); + + public event Action OnTextureLoaded; + public Texture ParallaxTexture { get; private set; } + + public async void LoadParallax() + { + MemoryStream configStream = null; + long crcValue; + TomlTable table; + try + { + // Load normal config into memory + if (!_resourceCache.TryContentFileRead(ParallaxConfigPath, out configStream)) + { + Logger.ErrorS("parallax", "Parallax config not found."); + return; + } + + // Calculate CRC32 of the config file. + var crc = new Crc32(); + crc.Update(configStream.ToArray()); + crcValue = crc.Value; + + // See if we we have a previous CRC stored. + if (_resourceCache.UserData.Exists(ParallaxConfigCrcPath)) + { + bool match; + using (var data = _resourceCache.UserData.Open(ParallaxConfigCrcPath, FileMode.Open)) + using (var binaryReader = new BinaryReader(data)) + { + match = binaryReader.ReadInt64() == crcValue; + } + + // If the previous CRC matches, just load the old texture. + if (match) + { + using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Open)) + { + ParallaxTexture = Texture.LoadFromPNGStream(stream); + } + + OnTextureLoaded?.Invoke(ParallaxTexture); + return; + } + } + + // Well turns out the CRC does not match so the config changed. + // Read the new config and get rid of the config memory stream. + using (var reader = new StreamReader(configStream, Encoding.UTF8)) + { + table = Toml.ReadString(reader.ReadToEnd()); + } + } + finally + { + configStream?.Dispose(); + } + + // Generate the parallax in the thread pool. + var image = await Task.Run(() => ParallaxGenerator.GenerateParallax(table, new Size(1920, 1080))); + // And load it in the main thread for safety reasons. + ParallaxTexture = Texture.LoadFromImage(image); + + // Store it and CRC so further game starts don't need to regenerate it. + using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Create)) + { + image.SaveAsPng(stream); + } + + using (var stream = _resourceCache.UserData.Open(ParallaxConfigCrcPath, FileMode.Create)) + using (var writer = new BinaryWriter(stream)) + { + writer.Write(crcValue); + } + + OnTextureLoaded?.Invoke(ParallaxTexture); + } + } +} diff --git a/Content.Client/Parallax/ParallaxOverlay.cs b/Content.Client/Parallax/ParallaxOverlay.cs new file mode 100644 index 0000000000..368de07454 --- /dev/null +++ b/Content.Client/Parallax/ParallaxOverlay.cs @@ -0,0 +1,63 @@ +using Content.Client.Interfaces.Parallax; +using SS14.Client.Graphics; +using SS14.Client.Graphics.Drawing; +using SS14.Client.Graphics.Overlays; +using SS14.Client.Graphics.Shaders; +using SS14.Client.Interfaces.Graphics.ClientEye; +using SS14.Client.Interfaces.Graphics.Overlays; +using SS14.Shared.IoC; +using SS14.Shared.Maths; +using SS14.Shared.Prototypes; + +namespace Content.Client.Parallax +{ + public class ParallaxOverlay : Overlay + { +#pragma warning disable 649 + [Dependency] private readonly IParallaxManager _parallaxManager; + [Dependency] private readonly IEyeManager _eyeManager; + [Dependency] private readonly IPrototypeManager _prototypeManager; +#pragma warning restore 649 + + public override bool AlwaysDirty => true; + private const float Slowness = 0.5f; + + private Texture _parallaxTexture; + + public override OverlaySpace Space => OverlaySpace.ScreenSpaceBelowWorld; + + public ParallaxOverlay() : base(nameof(ParallaxOverlay)) + { + IoCManager.InjectDependencies(this); + Shader = _prototypeManager.Index("unshaded").Instance(); + + if (_parallaxManager.ParallaxTexture == null) + { + _parallaxManager.OnTextureLoaded += texture => _parallaxTexture = texture; + } + else + { + _parallaxTexture = _parallaxManager.ParallaxTexture; + } + } + + protected override void Draw(DrawingHandle handle) + { + if (_parallaxTexture == null) + { + return; + } + + var (sizeX, sizeY) = _parallaxTexture.Size; + var (posX, posY) = _eyeManager.ScreenToWorld(Vector2.Zero).ToWorld().Position; + var (ox, oy) = (Vector2i) new Vector2(-posX / Slowness, posY / Slowness); + ox = MathHelper.Mod(ox, sizeX); + oy = MathHelper.Mod(oy, sizeY); + + handle.DrawTexture(_parallaxTexture, new Vector2(ox, oy)); + handle.DrawTexture(_parallaxTexture, new Vector2(ox - sizeX, oy)); + handle.DrawTexture(_parallaxTexture, new Vector2(ox, oy - sizeY)); + handle.DrawTexture(_parallaxTexture, new Vector2(ox - sizeX, oy - sizeY)); + } + } +} diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index 7af2670fd3..95f3b9189b 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -9,7 +9,7 @@ Properties Content.Server Content.Server - v4.5.1 + v4.7.1 512 ..\engine\bin\Server\Resources\Assemblies\ ..\bin\Content.Server\ @@ -159,7 +159,7 @@ - + diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index 1d625ba45f..9d2d3b124f 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -9,7 +9,7 @@ Properties Content.Shared Content.Shared - v4.5.1 + v4.7.1 512 bin\x86\Debug\ prompt diff --git a/Content.Tests/Content.Tests.csproj b/Content.Tests/Content.Tests.csproj index 3d3ccf2ec6..e16c82418c 100644 --- a/Content.Tests/Content.Tests.csproj +++ b/Content.Tests/Content.Tests.csproj @@ -12,7 +12,7 @@ Properties Content.Tests Content.Tests - v4.5.1 + v4.7.1 512 UnitTest ..\bin\Content.Tests\ @@ -124,4 +124,4 @@ --> - + \ No newline at end of file diff --git a/Resources/parallax_config.toml b/Resources/parallax_config.toml new file mode 100644 index 0000000000..b0458b29d9 --- /dev/null +++ b/Resources/parallax_config.toml @@ -0,0 +1,89 @@ +# Background nebula detail. +[[layers]] +type = "noise" +seed = 7832 +innercolor = "#A020E1" +outercolor = "#490070" +noise_type = "ridged" +frequency = "4" +octaves = 8 +power = "0.25" +threshold = "0.40" + +# Mask background nebula. +[[layers]] +type = "noise" +noise_type = "fbm" +innercolor = "#000000" +outercolor = "#000000" +destfactor = "SrcAlpha" +seed = 3551 +octaves = 4 +power = "0.35" +lacunarity = "1.5" +frequency = "3" +threshold = "0.0" + +# Dim background nebula stars. +[[layers]] +type = "points" +seed = 3909 +closecolor = "#4B5072" +count = 1500 +mask = true +masknoise_type = "fbm" +maskoctaves = 4 +maskpersistence = "0.5" +maskpower = "0.35" +masklacunarity = "1.5" +maskfrequency = "3" +maskthreshold = "0.0" +maskseed = 3551 + +# Bright background nebula stars. +[[layers]] +type = "points" +closecolor = "#7E86BF" +count = 1000 +seed = 3472 +mask = true +masknoise_type = "fbm" +maskoctaves = 4 +maskpersistence = "0.5" +maskpower = "0.35" +masklacunarity = "1.5" +maskfrequency = "3" +maskthreshold = "0.37" +maskseed = 3551 + +# Bright background nebula stars, dim edge. +[[layers]] +type = "points" +closecolor = "#3D415C" +pointsize = 2 +count = 1000 +seed = 3472 +mask = true +masknoise_type = "fbm" +maskoctaves = 4 +maskpersistence = "0.5" +maskpower = "0.35" +masklacunarity = "1.5" +maskfrequency = "3" +maskthreshold = "0.37" +maskseed = 3551 + +# Couple of odd bright yellow-ish stars. +[[layers]] +type = "points" +closecolor = "#FFD363" +count = 100 +seed = 6454 + +# And their dim edge. +[[layers]] +type = "points" +closecolor = "#43371A" +pointsize = 2 +count = 100 +seed = 6454 diff --git a/engine b/engine index 21fd3e5d96..7c4eeb136f 160000 --- a/engine +++ b/engine @@ -1 +1 @@ -Subproject commit 21fd3e5d9620f5193ab75040248d359b99557851 +Subproject commit 7c4eeb136fc61a1a39f82e6197a9bc2053c2b826