TomlDataReader, documentation updates, move ParticleSystem to Voile.Systems.Particles.

This commit is contained in:
2024-10-15 19:56:30 +02:00
parent ecd752e961
commit d5601c9dea
78 changed files with 18013 additions and 1143 deletions

View File

@@ -1,10 +1,9 @@
using System.ComponentModel;
using System.Numerics;
using Tommy;
using Voile.Resources;
using Voile.Resources.DataReaders;
using Voile.Utils;
namespace Voile.Systems;
namespace Voile.Systems.Particles;
public struct Particle
{
@@ -53,6 +52,9 @@ public class ParticleEmitterSettingsResource : Resource
}
/// <summary>
/// Loads <see cref="ParticleEmitterSettingsResource"/> from a provided TOML data file.
/// </summary>
public class ParticleEmitterSettingsResourceLoader : ResourceLoader<ParticleEmitterSettingsResource>
{
public override IEnumerable<string> SupportedExtensions => new string[] {
@@ -63,45 +65,57 @@ public class ParticleEmitterSettingsResourceLoader : ResourceLoader<ParticleEmit
{
// TODO: this is ugly, better to make some sort of wrapper API for TOML files.
var settings = new ParticleEmitterSettings();
// Parse into a node
using (StreamReader reader = File.OpenText(path))
using (var reader = new TomlDataReader("ParticleEmitterSettings"))
{
// Parse the table
TomlTable table = TOML.Parse(reader);
if (!table.HasKey("ParticleEmitterSettings"))
{
Console.WriteLine("Particle emitter settings doesnt have a header!");
}
if (table["ParticleEmitterSettings"]["MaxParticles"] is TomlInteger maxParticles)
{
settings.MaxParticles = (int)maxParticles.Value;
}
if (table["ParticleEmitterSettings"]["EmitRadius"] is TomlInteger emitRadius)
{
settings.EmitRadius = (int)emitRadius.Value;
}
if (table["ParticleEmitterSettings"]["LifeTime"] is TomlFloat lifetime)
{
settings.LifeTime = (float)lifetime.Value;
}
reader.Read(File.Open(path, FileMode.Open));
settings.MaxParticles = reader.GetInt("MaxParticles");
settings.EmitRadius = reader.GetInt("EmitRadius");
settings.LifeTime = reader.GetFloat("LifeTime", 1.0f);
settings.Explosiveness = reader.GetFloat("Explosiveness");
settings.Direction = reader.GetVector2("Direction", Vector2.Zero);
settings.LinearVelocity = reader.GetFloat("LinearVelocity", 980f);
settings.AngularVelocity = reader.GetFloat("AngularVelocity");
settings.AngularVelocityRandom = reader.GetFloat("AngularVelocityRandom", 1.0f);
settings.LinearVelocityRandom = reader.GetFloat("LinearVelocityRandom", 1.0f);
settings.Gravity = reader.GetVector2("Gravity", Vector2.UnitY * 980f);
settings.ScaleBegin = reader.GetFloat("ScaleBegin");
settings.ScaleEnd = reader.GetFloat("ScaleEnd");
settings.ColorBegin = reader.GetColor("ColorBegin", Color.White);
settings.ColorEnd = reader.GetColor("ColorEnd", Color.Black);
settings.Damping = reader.GetFloat("Damping", 1.0f);
}
return new ParticleEmitterSettingsResource(path, settings);
}
}
/// <summary>
/// Emits and simulates particles from a provided particle segment.
/// </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>
public Vector2 OriginPosition => _originPosition;
/// <summary>
/// <see cref="ParticleEmitterSettings"/> for this emitter.
/// </summary>
public ParticleEmitterSettings Settings => _settingsResource.Value.Settings;
public int ParticleArrayOffset => _particles.Offset;
/// <summary>
/// Constructs a new <see cref="ParticleEmitter"/>.
/// </summary>
/// <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)
{
_originPosition = originPosition;
@@ -115,13 +129,12 @@ public class ParticleEmitter : IUpdatableSystem
_random = new LehmerRandom();
}
/// <summary>
/// Restart this emitter.
/// </summary>
/// <param name="particles">New particle segment.</param>
public void Restart(ArraySegment<Particle> particles)
{
// foreach (var particle in _particles)
// {
// particle.LifeTimeRemaining = 0.0f;
// }
for (int i = 0; i < _particles.Count; i++)
{
var particle = _particles[i];
@@ -134,6 +147,10 @@ public class ParticleEmitter : IUpdatableSystem
_particleIndex = _maxParticles - 1;
}
/// <summary>
/// Updates this emitter's simulation.
/// </summary>
/// <param name="deltaTime"></param>
public void Update(double deltaTime)
{
var rate = (int)MathUtils.Lerp(1, _maxParticles, Settings.Explosiveness);
@@ -146,13 +163,6 @@ public class ParticleEmitter : IUpdatableSystem
for (int i = 0; i < _maxParticles; i++)
{
var particle = _particles[i];
// if (!particle.Alive) continue;
// if (particle.LifeTimeRemaining <= 0.0f)
// {
// particle.Alive = false;
// continue;
// }
particle.LifeTimeRemaining = Math.Clamp(particle.LifeTimeRemaining - (float)deltaTime, 0.0f, particle.LifeTime);
@@ -215,6 +225,9 @@ public class ParticleEmitter : IUpdatableSystem
private ResourceRef<ParticleEmitterSettingsResource> _settingsResource;
}
/// <summary>
/// CPU based particle simulator.
/// </summary>
public class ParticleSystem : IUpdatableSystem, IDisposable
{
/// <summary>
@@ -222,14 +235,26 @@ public class ParticleSystem : IUpdatableSystem, IDisposable
/// </summary>
public int ParticleLimit { get; set; } = 8192;
/// <summary>
/// List of particle emitters created for this ParticleSystem.
/// </summary>
public IReadOnlyList<ParticleEmitter> Emitters => _emitters;
/// <summary>
/// Constructs a new <see cref="ParticleSystem"/>.
/// </summary>
public ParticleSystem()
{
_particleIndex = ParticleLimit - 1;
_particles = new Particle[ParticleLimit];
}
/// <summary>
/// Creates an emitter from a <see cref="ParticleEmitterSettingsResource"/>.
/// </summary>
/// <param name="originPosition"></param>
/// <param name="settingsResource"></param>
/// <returns></returns>
public int CreateEmitter(Vector2 originPosition, ResourceRef<ParticleEmitterSettingsResource> settingsResource)
{
var settings = settingsResource.Value.Settings;
@@ -242,11 +267,6 @@ public class ParticleSystem : IUpdatableSystem, IDisposable
var particles = new ArraySegment<Particle>(_particles, _emitterSliceOffset, settings.MaxParticles);
// foreach (var particle in particles)
// {
// particle.LifeTime = settings.LifeTime;
// }
for (int i = 0; i < particles.Count; i++)
{
var particle = particles[i];
@@ -261,6 +281,11 @@ public class ParticleSystem : IUpdatableSystem, IDisposable
return _emitters.Count - 1;
}
/// <summary>
/// Restarts an emitter.
/// </summary>
/// <param name="id">Id of an emitter to restart.</param>
/// <exception cref="ArgumentException"></exception>
public void RestartEmitter(int id)
{
if (id > _emitters.Count - 1)
@@ -274,6 +299,10 @@ public class ParticleSystem : IUpdatableSystem, IDisposable
emitter.Restart(particles);
}
/// <summary>
/// Updates this particle simulation. It is recommended to use a fixed timestep for the particle simulation for performance reasons.
/// </summary>
/// <param name="deltaTime"></param>
public void Update(double deltaTime)
{
foreach (var emitter in _emitters)