WIP: Use SoA in ParticleSystem
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user