From 7c7c61fd561fd80910c4c6775c64d03f7a63c395 Mon Sep 17 00:00:00 2001 From: dnesov Date: Wed, 16 Oct 2024 00:28:39 +0200 Subject: [PATCH] Use bytes for internal RGBA components of Color, iterate particles sequentially in ParticleEmitters, increase limit for CPU particles, reduce size of Particle struct. --- TODO.md | 11 ++-- TestGame/Resources/test_emitter.toml | 12 ++-- TestGame/TestGame.cs | 21 +++++-- Voile/Source/Rendering/RaylibRenderSystem.cs | 3 +- Voile/Source/Systems/ParticleSystem.cs | 30 ++-------- Voile/Source/Utils/Color.cs | 62 ++++++++++---------- Voile/Source/Utils/MathUtils.cs | 10 ++-- 7 files changed, 71 insertions(+), 78 deletions(-) diff --git a/TODO.md b/TODO.md index 3cdb034..b910a3b 100644 --- a/TODO.md +++ b/TODO.md @@ -13,9 +13,9 @@ - ~~Use GUIDs and string ID maps for fetching resources instead of string IDs alone.~~ - Reimplement unloading. - Finalize ResourceManager and ResourceLoader APIs for 1.0. +- Virtual file system. - (stretch goal) Streamed resource loading. - (stretch goal) Add async API for ResourceManager. -- (stretch goal) Virtual file system. ## Serialization @@ -41,14 +41,15 @@ - ~~Asset manager~~ - ~~Separate engine and game into separate projects~~ - Particle system 2.0 - - Reduce the size of Particle struct by infering most parameters through lifetime. - - Reduce cache misses by utilizing SoA. - - SIMD acceleration. + - ~~Reduce the size of Particle struct by infering most parameters through lifetime.~~ + - ~~Reduce cache misses by utilizing SoA.~~ + - ~~SIMD acceleration.~~ + - Dynamically resize particle arrays as emitters get added or removed. ## SceneGraph module -- Layers (sorting mechanism) - Full overhaul using Entity Component architecture (not to be mistaken with ECS). +- Layers (sorting mechanism) - Save/load scenes from file ## Input diff --git a/TestGame/Resources/test_emitter.toml b/TestGame/Resources/test_emitter.toml index 7d530d2..e7bdd93 100644 --- a/TestGame/Resources/test_emitter.toml +++ b/TestGame/Resources/test_emitter.toml @@ -1,14 +1,14 @@ [ParticleEmitterSettings] -MaxParticles = 8096 +MaxParticles = 1024 EmitRadius = 128 Explosiveness = 1.0 -LifeTime = 1.0 -Direction = [0.0, 1.0] +LifeTime = 2.0 +Direction = [0.0, -1.0] LinearVelocity = 200 Gravity = [0.0, 0.0] LinearVelocityRandom = 0.5 ScaleBegin = 1.0 -ScaleEnd = 0.0 -ColorBegin = [255, 0, 255, 255] -ColorEnd = [0, 0, 0, 0] +ScaleEnd = 5.0 +ColorBegin = [255, 0, 0, 0] +ColorEnd = [0, 255, 0, 255] diff --git a/TestGame/TestGame.cs b/TestGame/TestGame.cs index f59e82e..5b14554 100644 --- a/TestGame/TestGame.cs +++ b/TestGame/TestGame.cs @@ -63,6 +63,8 @@ public class TestGame : Game _particleSystem!.Update(Renderer.FrameTime); _particleSimStopwatch.Stop(); + _renderStopwatch = Stopwatch.StartNew(); + Renderer.BeginFrame(); Renderer.ClearBackground(Color.Black); foreach (var emitter in _particleSystem!.Emitters) @@ -70,13 +72,17 @@ public class TestGame : Game DrawEmitter(emitter); } - Renderer.SetTransform(Renderer.WindowSize / 2, Vector2.Zero); - Renderer.DrawTexture(_icon, Color.White); - Renderer.ResetTransform(); Renderer.DrawText(_font, $"Particle Sim: {TimeSpan.FromTicks(_particleSimStopwatch.ElapsedTicks).TotalMilliseconds} ms", Color.White); + Renderer.SetTransform(new Vector2(0.0f, 16.0f), Vector2.Zero); + Renderer.DrawText(_font, $"Render: {TimeSpan.FromTicks(_lastRenderTime).TotalMilliseconds} ms", Color.White); + Renderer.EndFrame(); + + _lastRenderTime = _renderStopwatch.ElapsedTicks; + + _renderStopwatch.Restart(); _particleSimStopwatch.Restart(); } } @@ -90,14 +96,16 @@ public class TestGame : Game { Renderer.BeginBlended(Voile.Rendering.BlendMode.BlendAlpha); - for (int i = 0; i < emitter.Settings.MaxParticles; i++) + var maxParticles = emitter.Settings.MaxParticles; + + for (int i = maxParticles - 1; i > 0; i--) { var particle = emitter.GetParticle(i); var color = new Color(particle.ColorArgb); Renderer.SetTransform(emitter.OriginPosition + particle.Position, Vector2.Zero); - Renderer.DrawCircle(16f * particle.Scale, color); + Renderer.DrawRectangle(Vector2.One * 16.0f * particle.Scale, color); } Renderer.EndBlended(); @@ -105,7 +113,8 @@ public class TestGame : Game [NotNull] private ParticleSystem _particleSystem; - private Stopwatch _particleSimStopwatch; + private Stopwatch _particleSimStopwatch, _renderStopwatch; + private long _lastRenderTime; private int _emitterId; private ResourceRef _emitterSettings; private ResourceRef _font; diff --git a/Voile/Source/Rendering/RaylibRenderSystem.cs b/Voile/Source/Rendering/RaylibRenderSystem.cs index d572fa4..280e8ce 100644 --- a/Voile/Source/Rendering/RaylibRenderSystem.cs +++ b/Voile/Source/Rendering/RaylibRenderSystem.cs @@ -219,7 +219,8 @@ namespace Voile.Rendering private Raylib_cs.Color VoileColorToRaylibColor(Color color) { - return new Raylib_cs.Color { r = (byte)Math.Round(color.R * 255f), g = (byte)Math.Round(color.G * 255f), b = (byte)Math.Round(color.B * 255f), a = (byte)Math.Round(color.A * 255f) }; + var rayColor = new Raylib_cs.Color(color.R, color.G, color.B, color.A); + return rayColor; } public override void DrawText(ResourceRef fontResource, string text, Color color) diff --git a/Voile/Source/Systems/ParticleSystem.cs b/Voile/Source/Systems/ParticleSystem.cs index ba59dac..1778322 100644 --- a/Voile/Source/Systems/ParticleSystem.cs +++ b/Voile/Source/Systems/ParticleSystem.cs @@ -14,8 +14,6 @@ public struct Particle public int EmitterIndex { get; set; } public int ColorArgb { get; set; } public Vector2 Position { get; set; } - public Vector2 Velocity { get; set; } - public float AngularVelocity { get; set; } public float LifeTime { get; set; } = 1.0f; public float Scale { get; set; } public float Rotation { get; set; } @@ -61,7 +59,6 @@ public class ParticleEmitterSettingsResourceLoader : ResourceLoader @@ -166,7 +162,6 @@ public class ParticleEmitter : IUpdatableSystem } var deltaTimeVector = new Vector2((float)deltaTime); - var lifeTimeInv = 1.0f / Settings.LifeTime; var gravityVector = Settings.Gravity * (float)deltaTime; var dampingFactor = Settings.Damping * (float)deltaTime; @@ -174,8 +169,6 @@ public class ParticleEmitter : IUpdatableSystem { _lifetimes[i] = Math.Max(0.0f, _lifetimes[i] - (float)deltaTime); - var t = _lifetimes[i] * lifeTimeInv; - _velocities[i] += gravityVector; _positions[i] += _velocities[i] * deltaTimeVector; @@ -185,8 +178,6 @@ public class ParticleEmitter : IUpdatableSystem private void Emit() { - // Particle particle = _particles[_particleIndex]; - if (_lifetimes[_particleIndex] > 0) return; _positions[_particleIndex] = GetEmitPosition(); @@ -194,12 +185,9 @@ public class ParticleEmitter : IUpdatableSystem _velocities[_particleIndex] += Vector2.One * Settings.LinearVelocityRandom * ((float)_random.NextDouble() - 0.5f); - // particle.AngularVelocity = Settings.AngularVelocity; - // particle.AngularVelocity += 1f * Settings.AngularVelocityRandom * ((float)_random.NextDouble() - 0.5f); - _lifetimes[_particleIndex] = Settings.LifeTime; - _particleIndex = --_particleIndex <= 0 ? _maxParticles - 1 : --_particleIndex; + _particleIndex = (_particleIndex + 1) % _maxParticles; } private Vector2 GetEmitPosition() @@ -233,7 +221,7 @@ public class ParticleSystem : IUpdatableSystem, IDisposable /// /// Maximum amount of particles emittable by the system. /// - public int ParticleLimit { get; set; } = 8192; + public int ParticleLimit { get; set; } = short.MaxValue; /// /// List of particle emitters created for this ParticleSystem. @@ -245,8 +233,6 @@ public class ParticleSystem : IUpdatableSystem, IDisposable /// public ParticleSystem() { - _particleIndex = ParticleLimit - 1; - _particlePositions = new Vector2[ParticleLimit]; _particleVelocities = new Vector2[ParticleLimit]; _particleLifetimes = new float[ParticleLimit]; @@ -315,20 +301,14 @@ public class ParticleSystem : IUpdatableSystem, IDisposable } public void Dispose() - { - CleanupParticles(); - } - - private void CleanupParticles() { Array.Clear(_particlePositions); Array.Clear(_particleVelocities); Array.Clear(_particleLifetimes); } - // private Particle[] _particles; + private Vector2[] _particlePositions, _particleVelocities; private float[] _particleLifetimes; - private int _particleIndex; private int _emitterSliceOffset; private List _emitters = new(); diff --git a/Voile/Source/Utils/Color.cs b/Voile/Source/Utils/Color.cs index 7ff283c..03144a6 100644 --- a/Voile/Source/Utils/Color.cs +++ b/Voile/Source/Utils/Color.cs @@ -34,25 +34,33 @@ namespace Voile public static Color Green = new(0x00FF00); public static Color Red = new(0xFF0000); - public float R { get; set; } - public float G { get; set; } - public float B { get; set; } - public float A { get; set; } = 1.0f; + public byte R { get; set; } + public byte G { get; set; } + public byte B { get; set; } + public byte A { get; set; } = 255; public int Argb { get { - int a = (int)Math.Round(A * 255f) << 24; - int r = (int)Math.Round(R * 255f) << 16; - int g = (int)Math.Round(G * 255f) << 8; - int b = (int)Math.Round(B * 255f); + int a = A << 24; + int r = R << 16; + int g = G << 8; + int b = B; return a | r | g | b; } } - public Color(float r, float g, float b, float a) + public Color(float r, float g, float b, float a = 1.0f) + { + R = (byte)Math.Clamp(r * 255, 0, 255); + G = (byte)Math.Clamp(g * 255, 0, 255); + B = (byte)Math.Clamp(b * 255, 0, 255); + A = (byte)Math.Clamp(a * 255, 0, 255); + } + + public Color(byte r, byte g, byte b, byte a = 255) { R = r; G = g; @@ -60,22 +68,16 @@ namespace Voile A = a; } - public Color(byte r, byte g, byte b, byte a) - { - R = r / 255f; - G = g / 255f; - B = b / 255f; - A = a / 255f; - } - public Color(int hex) { - A = 1.0f; - B = (hex & 0xFF) / 255.0f; - hex >>= 8; - G = (hex & 0xFF) / 255.0f; - hex >>= 8; - R = (hex & 0xFF) / 255.0f; + A = 255; // Default alpha to 255 if not provided + B = (byte)(hex & 0xFF); + G = (byte)((hex >> 8) & 0xFF); + R = (byte)((hex >> 16) & 0xFF); + if (hex > 0xFFFFFF) // If the hex value includes alpha + { + A = (byte)((hex >> 24) & 0xFF); + } } public static Color FromHexString(string hex) @@ -104,20 +106,18 @@ namespace Voile public Color Lightened(float amount) { var result = this; - result.R = result.R + (1.0f - result.R) * amount; - result.G = result.G + (1.0f - result.G) * amount; - result.B = result.B + (1.0f - result.B) * amount; - + result.R = (byte)Math.Min(255, R + (255 - R) * amount); + result.G = (byte)Math.Min(255, G + (255 - G) * amount); + result.B = (byte)Math.Min(255, B + (255 - B) * amount); return result; } public Color Darkened(float amount) { var result = this; - result.R = result.R * (1.0f - amount); - result.G = result.G * (1.0f - amount); - result.B = result.B * (1.0f - amount); - + result.R = (byte)(R * (1.0f - amount)); + result.G = (byte)(G * (1.0f - amount)); + result.B = (byte)(B * (1.0f - amount)); return result; } diff --git a/Voile/Source/Utils/MathUtils.cs b/Voile/Source/Utils/MathUtils.cs index 7f98031..551166b 100644 --- a/Voile/Source/Utils/MathUtils.cs +++ b/Voile/Source/Utils/MathUtils.cs @@ -17,10 +17,12 @@ namespace Voile public static Color LerpColor(Color colorA, Color colorB, float t) { - var r = Lerp(colorA.R, colorB.R, t); - var g = Lerp(colorA.G, colorB.G, t); - var b = Lerp(colorA.B, colorB.B, t); - var a = Lerp(colorA.A, colorB.A, t); + t = Math.Clamp(t, 0f, 1f); + + byte r = (byte)(colorA.R + (colorB.R - colorA.R) * t); + byte g = (byte)(colorA.G + (colorB.G - colorA.G) * t); + byte b = (byte)(colorA.B + (colorB.B - colorA.B) * t); + byte a = (byte)(colorA.A + (colorB.A - colorA.A) * t); return new Color(r, g, b, a); }