WIP: Use SoA in ParticleSystem

This commit is contained in:
2024-10-15 22:52:20 +02:00
parent 8f28a62817
commit 40ff986312
2 changed files with 62 additions and 50 deletions

View File

@@ -90,9 +90,9 @@ public class TestGame : Game
{
Renderer.BeginBlended(Voile.Rendering.BlendMode.BlendAlpha);
for (int i = 0; i < emitter.Particles.Length; i++)
for (int i = 0; i < emitter.Settings.MaxParticles; i++)
{
var particle = emitter.Particles[i];
var particle = emitter.GetParticle(i);
var color = new Color(particle.ColorArgb);

View File

@@ -17,10 +17,8 @@ public struct Particle
public Vector2 Velocity { get; set; }
public float AngularVelocity { get; set; }
public float LifeTime { get; set; } = 1.0f;
public float LifeTimeRemaining { get; set; }
public float Scale { get; set; }
public float Rotation { get; set; }
public bool Alive { get; set; } = true;
}
public class ParticleEmitterSettings
@@ -96,10 +94,6 @@ public class ParticleEmitterSettingsResourceLoader : ResourceLoader<ParticleEmit
/// </summary>
public class ParticleEmitter : IUpdatableSystem
{
/// <summary>
/// A segment of particles this emitter simulates.
/// </summary>
public ReadOnlySpan<Particle> Particles => _particles.AsSpan();
/// <summary>
/// Origin position in the world of this emitter.
/// </summary>
@@ -108,7 +102,7 @@ public class ParticleEmitter : IUpdatableSystem
/// <see cref="ParticleEmitterSettings"/> for this emitter.
/// </summary>
public ParticleEmitterSettings Settings => _settingsResource.Value.Settings;
public int ParticleArrayOffset => _particles.Offset;
public int ArrayOffset => _lifetimes.Offset;
/// <summary>
/// Constructs a new <see cref="ParticleEmitter"/>.
@@ -116,7 +110,7 @@ public class ParticleEmitter : IUpdatableSystem
/// <param name="originPosition">World origin position.</param>
/// <param name="settingsResource">Emitter settings resource.</param>
/// <param name="particles">Particle segment that this emitter will simulate.</param>
public ParticleEmitter(Vector2 originPosition, ResourceRef<ParticleEmitterSettingsResource> settingsResource, ArraySegment<Particle> particles)
public ParticleEmitter(Vector2 originPosition, ResourceRef<ParticleEmitterSettingsResource> settingsResource, ArraySegment<Vector2> positionsSlice, ArraySegment<Vector2> velocitiesSlice, ArraySegment<float> lifetimesSlice)
{
_originPosition = originPosition;
@@ -124,25 +118,36 @@ public class ParticleEmitter : IUpdatableSystem
_maxParticles = _settingsResource.Value.Settings.MaxParticles;
_particleIndex = _maxParticles - 1;
_particles = particles;
_positions = positionsSlice;
_velocities = velocitiesSlice;
_lifetimes = lifetimesSlice;
_random = new LehmerRandom();
}
public Particle GetParticle(int idx)
{
var t = _lifetimes[idx] / Settings.LifeTime;
return new Particle()
{
Position = _positions[idx],
Velocity = _velocities[idx],
Scale = MathUtils.Lerp(Settings.ScaleEnd, Settings.ScaleBegin, t),
ColorArgb = MathUtils.LerpColor(Settings.ColorEnd, Settings.ColorBegin, t).Argb
};
}
/// <summary>
/// Restart this emitter.
/// </summary>
/// <param name="particles">New particle segment.</param>
public void Restart(ArraySegment<Particle> particles)
public void Restart()
{
for (int i = 0; i < _particles.Count; i++)
for (int i = 0; i < _lifetimes.Count; i++)
{
var particle = _particles[i];
particle.LifeTimeRemaining = 0.0f;
_lifetimes[i] = 0.0f;
}
_particles = particles;
_maxParticles = Settings.MaxParticles;
_particleIndex = _maxParticles - 1;
}
@@ -162,44 +167,40 @@ public class ParticleEmitter : IUpdatableSystem
for (int i = 0; i < _maxParticles; i++)
{
var particle = _particles[i];
// var particle = _particles[i];
particle.LifeTimeRemaining = Math.Clamp(particle.LifeTimeRemaining - (float)deltaTime, 0.0f, particle.LifeTime);
_lifetimes[i] = Math.Max(0.0f, _lifetimes[i] - (float)deltaTime);
var t = particle.LifeTimeRemaining / particle.LifeTime;
var t = _lifetimes[i] / Settings.LifeTime;
particle.Velocity += Settings.Gravity * (float)deltaTime;
particle.Position += particle.Velocity * (float)deltaTime;
particle.Rotation += particle.AngularVelocity * (float)deltaTime;
particle.Scale = MathUtils.Lerp(Settings.ScaleEnd, Settings.ScaleBegin, t);
_velocities[i] += Settings.Gravity * (float)deltaTime;
_positions[i] += _velocities[i] * (float)deltaTime;
// particle.Rotation += particle.AngularVelocity * (float)deltaTime;
// particle.Scale = MathUtils.Lerp(Settings.ScaleEnd, Settings.ScaleBegin, t);
var color = MathUtils.LerpColor(Settings.ColorEnd, Settings.ColorBegin, t);
particle.ColorArgb = color.Argb;
// particle.ColorArgb = color.Argb;
particle.Velocity -= particle.Velocity * Settings.Damping * (float)deltaTime;
_particles[i] = particle;
_velocities[i] -= _velocities[i] * Settings.Damping * (float)deltaTime;
}
}
private void Emit()
{
Particle particle = _particles[_particleIndex];
if (!(particle.LifeTimeRemaining <= 0)) return;
// Particle particle = _particles[_particleIndex];
// particle.Alive = true;
particle.Position = GetEmitPosition();
particle.Velocity = Settings.Direction * Settings.LinearVelocity;
if (_lifetimes[_particleIndex] > 0) return;
particle.Velocity += Vector2.One * Settings.LinearVelocityRandom * ((float)_random.NextDouble() - 0.5f);
_positions[_particleIndex] = GetEmitPosition();
_velocities[_particleIndex] = Settings.Direction * Settings.LinearVelocity;
particle.AngularVelocity = Settings.AngularVelocity;
particle.AngularVelocity += 1f * Settings.AngularVelocityRandom * ((float)_random.NextDouble() - 0.5f);
_velocities[_particleIndex] += Vector2.One * Settings.LinearVelocityRandom * ((float)_random.NextDouble() - 0.5f);
particle.LifeTime = Settings.LifeTime;
particle.LifeTimeRemaining = particle.LifeTime;
// particle.AngularVelocity = Settings.AngularVelocity;
// particle.AngularVelocity += 1f * Settings.AngularVelocityRandom * ((float)_random.NextDouble() - 0.5f);
_lifetimes[_particleIndex] = Settings.LifeTime;
_particles[_particleIndex] = particle;
_particleIndex = --_particleIndex <= 0 ? _maxParticles - 1 : --_particleIndex;
}
@@ -221,7 +222,8 @@ public class ParticleEmitter : IUpdatableSystem
private int _maxParticles;
private int _particleIndex;
private Vector2 _originPosition = Vector2.Zero;
private ArraySegment<Particle> _particles;
private ArraySegment<Vector2> _positions, _velocities;
private ArraySegment<float> _lifetimes;
private ResourceRef<ParticleEmitterSettingsResource> _settingsResource;
}
@@ -246,7 +248,10 @@ public class ParticleSystem : IUpdatableSystem, IDisposable
public ParticleSystem()
{
_particleIndex = ParticleLimit - 1;
_particles = new Particle[ParticleLimit];
_particlePositions = new Vector2[ParticleLimit];
_particleVelocities = new Vector2[ParticleLimit];
_particleLifetimes = new float[ParticleLimit];
}
/// <summary>
@@ -265,15 +270,16 @@ public class ParticleSystem : IUpdatableSystem, IDisposable
return -1;
}
var particles = new ArraySegment<Particle>(_particles, _emitterSliceOffset, settings.MaxParticles);
var positionsSlice = new ArraySegment<Vector2>(_particlePositions, _emitterSliceOffset, settings.MaxParticles);
var velocitiesSlice = new ArraySegment<Vector2>(_particleVelocities, _emitterSliceOffset, settings.MaxParticles);
var lifetimesSlice = new ArraySegment<float>(_particleLifetimes, _emitterSliceOffset, settings.MaxParticles);
for (int i = 0; i < particles.Count; i++)
for (int i = 0; i < lifetimesSlice.Count; i++)
{
var particle = particles[i];
particle.LifeTime = settings.LifeTime;
lifetimesSlice[i] = settings.LifeTime;
}
var emitter = new ParticleEmitter(originPosition, settingsResource, particles);
var emitter = new ParticleEmitter(originPosition, settingsResource, positionsSlice, velocitiesSlice, lifetimesSlice);
_emitters.Add(emitter);
_emitterSliceOffset += settings.MaxParticles;
@@ -294,9 +300,8 @@ public class ParticleSystem : IUpdatableSystem, IDisposable
}
var emitter = _emitters[id];
var particles = new ArraySegment<Particle>(_particles, emitter.ParticleArrayOffset, emitter.Settings.MaxParticles);
emitter.Restart(particles);
emitter.Restart();
}
/// <summary>
@@ -316,8 +321,15 @@ public class ParticleSystem : IUpdatableSystem, IDisposable
CleanupParticles();
}
private void CleanupParticles() => Array.Clear(_particles);
private Particle[] _particles;
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;