#region usings using System; using System.Collections.Generic; using OpenTK; using SM.Base.Scene; using SM.Base.Time; using SM.Base.Window; #endregion namespace SM.Base.Drawing.Particles { /// /// The (drawing) basis for particles /// public abstract class ParticleDrawingBasis : DrawingBasis, IScriptable where TTransform : GenericTransformation, new() where TDirection : struct { private float? _continuesIntervalSeconds = null; private Interval _continuesInterval; /// /// The stopwatch of the particles. /// protected Timer timer; /// /// This contains the different instances for the particles. /// protected List> instances; /// /// The amount of particles /// public int Amount = 32; /// /// The base lifetime for particles in seconds. /// public float Lifetime; /// /// Ranomizes the lifetime for particles. /// public float LifetimeRandomize = 0; /// /// If set to any value (except null), it will create the particles continuously. /// public float? ContinuousInterval { get => _continuesIntervalSeconds; set { if (value.HasValue) { _continuesInterval.Target = value.Value; } _continuesIntervalSeconds = value; } } public bool DetachedParticles; /// /// The maximum speed of the particles /// Default: 25 /// public float MaxSpeed = 25; /// /// Sets up the timer. /// /// Duration how long the particles should live protected ParticleDrawingBasis(TimeSpan duration) { timer = new Timer(duration); _continuesInterval = new Interval(0); _continuesInterval.End += CreateContinuesParticles; Lifetime = (float) duration.TotalSeconds; } /// /// Get/Sets the state of pausing. /// public bool Paused { get => timer.Paused; set => timer.Paused = value; } /// /// Controls the movement of each particles. /// public abstract Func, TDirection> MovementCalculation { get; set; } /// public bool UpdateActive { get => timer.Active || _continuesInterval.Active; set { return; } } /// public void Update(UpdateContext context) { for (int i = 0; i < instances.Count; i++) { instances[i].Lifetime -= context.Deltatime; if (instances[i].Lifetime <= 0) { instances.Remove(instances[i]); break; } instances[i].ModelMatrix = CreateMatrix(instances[i], MovementCalculation(instances[i])); } } /// /// Triggers the particles. /// public void Trigger() { instances = new List>(); if (_continuesIntervalSeconds.HasValue) { _continuesInterval.Target = _continuesIntervalSeconds.Value; _continuesInterval.Start(); return; } timer.Start(); CreateParticles(); } /// /// Stops the particles. /// public void Stop() { if (_continuesInterval.Active) { _continuesInterval.Stop(); } timer.Stop(); } public override void OnRemoved(object sender) { base.OnRemoved(sender); Stop(); } /// protected override void DrawContext(ref DrawContext context) { if (!timer.Active && _continuesInterval != null && !_continuesInterval.Active) return; base.DrawContext(ref context); if (DetachedParticles) context.ModelMatrix = Matrix4.Identity; context.Instances = instances.ConvertAll(a => (Instance)a); context.Shader.Draw(context); } /// /// Creates the particles. /// protected virtual void CreateParticles() { for (int i = 0; i < Amount; i++) { instances.Add(CreateObject(i)); } } private void CreateContinuesParticles(Timer arg1, UpdateContext arg2) { instances.Add(CreateObject(instances.Count)); } /// /// Creates a particle. /// protected abstract ParticleInstance CreateObject(int index); /// /// Generates the desired matrix for drawing. /// protected abstract Matrix4 CreateMatrix(ParticleInstance Struct, TDirection relativePosition); } }