Add ParticleSystem, fix incorrect Argb conversion in Color, remove byte casting in LerpColor, update TestGame to demostrate particle system.
This commit is contained in:
@@ -4,6 +4,8 @@ using Voile.Resources;
|
|||||||
using Voile.SceneGraph;
|
using Voile.SceneGraph;
|
||||||
using Voile.Utils;
|
using Voile.Utils;
|
||||||
using Voile.Input;
|
using Voile.Input;
|
||||||
|
using Voile.Systems;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
public class TestGame : Game
|
public class TestGame : Game
|
||||||
{
|
{
|
||||||
@@ -12,9 +14,7 @@ public class TestGame : Game
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
InitializeDefault();
|
InitializeDefault();
|
||||||
|
_particleSystem = new ParticleSystem();
|
||||||
_audioSystem = new FmodAudioSystem();
|
|
||||||
_audioSystem.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadResources()
|
protected override void LoadResources()
|
||||||
@@ -32,62 +32,50 @@ public class TestGame : Game
|
|||||||
|
|
||||||
protected override void Ready()
|
protected override void Ready()
|
||||||
{
|
{
|
||||||
_scene = new Scene(new SceneSettings()
|
_particleSystem!.CreateEmitter(Renderer.WindowSize / 2, new ParticleEmitterSettings()
|
||||||
{
|
{
|
||||||
Renderer = Renderer,
|
ColorBegin = Color.Green,
|
||||||
AudioBackend = _audioSystem!,
|
ColorEnd = Color.Red,
|
||||||
InputHandler = Input,
|
EmitRadius = 128,
|
||||||
ResourceManager = ResourceManager
|
MaxParticles = 256
|
||||||
});
|
});
|
||||||
|
|
||||||
_uiLayer = new UiLayer();
|
|
||||||
_worldLayer = new EntityLayer();
|
|
||||||
|
|
||||||
_testSoundInstance = _audioSystem!.CreateInstance(_testSound!);
|
|
||||||
|
|
||||||
Input.AddInputMapping("play", new InputAction[] { new KeyInputAction(KeyboardKey.Spacebar) });
|
|
||||||
Input.AddInputMapping("sprint", new InputAction[] { new KeyInputAction(KeyboardKey.LeftShift) });
|
|
||||||
Input.AddInputMapping("toggle_fullscreen", new InputAction[] { new KeyInputAction(KeyboardKey.F11) });
|
|
||||||
|
|
||||||
_scene!.AddLayer("World", _worldLayer!);
|
|
||||||
|
|
||||||
_worldLayer!.AddEntity(new World());
|
|
||||||
_worldLayer.AddEntity(new TestPlayer());
|
|
||||||
|
|
||||||
_scene.AddLayer("UI", _uiLayer!);
|
|
||||||
_scene.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Run()
|
protected override void Run()
|
||||||
{
|
{
|
||||||
while (Renderer.ShouldRun)
|
while (Renderer.ShouldRun)
|
||||||
{
|
{
|
||||||
if (Input.IsActionPressed("play"))
|
_particleSystem!.Update(Renderer.FrameTime);
|
||||||
{
|
|
||||||
_testSoundInstance!.PitchVariation(0.9f, 1.1f)
|
|
||||||
.VolumeVariation(0.98f, 1.02f);
|
|
||||||
|
|
||||||
_testSoundInstance!.Play();
|
Renderer.BeginFrame();
|
||||||
|
Renderer.ClearBackground(Color.Black);
|
||||||
|
foreach (var emitter in _particleSystem!.Emitters)
|
||||||
|
{
|
||||||
|
DrawEmitter(emitter);
|
||||||
}
|
}
|
||||||
_scene.Update();
|
|
||||||
_scene.BeginDraw();
|
Renderer.EndFrame();
|
||||||
_scene.EndDraw();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
ShutdownDefault();
|
ShutdownDefault();
|
||||||
_audioSystem!.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Sound? _testSound;
|
private void DrawEmitter(ParticleEmitter emitter)
|
||||||
private SoundInstance? _testSoundInstance;
|
{
|
||||||
private Font? _font;
|
for (int i = 0; i < emitter.Particles.Length; i++)
|
||||||
private FmodAudioSystem? _audioSystem;
|
{
|
||||||
private Scene? _scene;
|
var particle = emitter.Particles[i];
|
||||||
|
|
||||||
private UiLayer? _uiLayer;
|
var color = new Color(particle.ColorArgb);
|
||||||
private EntityLayer? _worldLayer;
|
|
||||||
|
Renderer.SetTransform(emitter.OriginPosition + particle.Position, Vector2.Zero);
|
||||||
|
Renderer.DrawCircle(16f * particle.Scale, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParticleSystem? _particleSystem;
|
||||||
private Logger _logger = new(nameof(TestGame));
|
private Logger _logger = new(nameof(TestGame));
|
||||||
}
|
}
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
using System.Drawing;
|
|
||||||
using System.Numerics;
|
|
||||||
using Voile.Rendering;
|
|
||||||
|
|
||||||
namespace Voile.SceneGraph
|
|
||||||
{
|
|
||||||
// TODO: add oneshot parameter.
|
|
||||||
public class Particles2d : Drawable2d
|
|
||||||
{
|
|
||||||
public int MaxParticles => _maxParticles;
|
|
||||||
public ParticleSettings Settings => _settings;
|
|
||||||
|
|
||||||
public Particles2d(ParticleSettings settings)
|
|
||||||
{
|
|
||||||
_settings = settings;
|
|
||||||
_maxParticles = _settings.MaxParticles;
|
|
||||||
_particleIndex = _maxParticles - 1;
|
|
||||||
|
|
||||||
_particles = new Particle[_maxParticles];
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Restart()
|
|
||||||
{
|
|
||||||
CleanupParticles();
|
|
||||||
|
|
||||||
// Allocate a new particle array if max particles property got changed.
|
|
||||||
if (_maxParticles != _settings.MaxParticles)
|
|
||||||
{
|
|
||||||
_particles = new Particle[_maxParticles];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnDraw(RenderSystem renderer)
|
|
||||||
{
|
|
||||||
foreach (var particle in _particles)
|
|
||||||
{
|
|
||||||
if (!particle.Alive) continue;
|
|
||||||
|
|
||||||
var t = particle.LifeTimeRemaining / particle.LifeTime;
|
|
||||||
var scale = MathUtils.Lerp(_settings.ScaleEnd, _settings.ScaleBegin, t);
|
|
||||||
var color = MathUtils.LerpColor(_settings.ColorEnd, _settings.ColorBegin, t);
|
|
||||||
|
|
||||||
renderer.SetTransform(particle.Position, Vector2.Zero, particle.Rotation);
|
|
||||||
renderer.DrawRectangle(Vector2.One * scale, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected override void OnStart()
|
|
||||||
{
|
|
||||||
base.OnStart();
|
|
||||||
// Emit();
|
|
||||||
}
|
|
||||||
protected override void OnUpdate(double dt)
|
|
||||||
{
|
|
||||||
base.OnUpdate(dt);
|
|
||||||
|
|
||||||
var rate = (int)MathUtils.Lerp(1, _maxParticles, _settings.Explosiveness);
|
|
||||||
for (int i = 0; i < rate; i++)
|
|
||||||
{
|
|
||||||
Emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 -= (float)dt;
|
|
||||||
particle.Velocity += _settings.Gravity * (float)dt;
|
|
||||||
particle.Position += particle.Velocity * (float)dt;
|
|
||||||
particle.Rotation += particle.AngularVelocity * (float)dt;
|
|
||||||
|
|
||||||
particle.Velocity -= particle.Velocity * _settings.Damping * (float)dt;
|
|
||||||
|
|
||||||
_particles[i] = particle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Emit()
|
|
||||||
{
|
|
||||||
Particle particle = _particles[_particleIndex];
|
|
||||||
if (!(particle.LifeTimeRemaining <= 0)) return;
|
|
||||||
particle.Alive = true;
|
|
||||||
particle.Position = GetEmitPosition();
|
|
||||||
particle.Velocity = _settings.Direction * _settings.LinearVelocity;
|
|
||||||
|
|
||||||
particle.Velocity += Vector2.One * _settings.LinearVelocityRandom * ((float)_random.NextDouble() - 0.5f);
|
|
||||||
|
|
||||||
particle.AngularVelocity = _settings.AngularVelocity;
|
|
||||||
particle.AngularVelocity += 1f * _settings.AngularVelocityRandom * ((float)_random.NextDouble() - 0.5f);
|
|
||||||
|
|
||||||
particle.LifeTime = _settings.LifeTime;
|
|
||||||
particle.LifeTimeRemaining = particle.LifeTime;
|
|
||||||
|
|
||||||
_particles[_particleIndex] = particle;
|
|
||||||
_particleIndex = --_particleIndex <= 0 ? _maxParticles - 1 : --_particleIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CleanupParticles() => Array.Clear(_particles);
|
|
||||||
private Vector2 GetEmitPosition()
|
|
||||||
{
|
|
||||||
// https://gamedev.stackexchange.com/questions/26713/calculate-random-points-pixel-within-a-circle-image
|
|
||||||
var angle = _random.NextDouble() * Math.PI * 2;
|
|
||||||
float radius = (float)Math.Sqrt(_random.NextDouble()) * _settings.EmitRadius;
|
|
||||||
|
|
||||||
float x = Position.X + radius * (float)Math.Cos(angle);
|
|
||||||
float y = Position.Y + radius * (float)Math.Sin(angle);
|
|
||||||
|
|
||||||
return new Vector2(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ParticleSettings _settings;
|
|
||||||
private int _maxParticles;
|
|
||||||
private Particle[] _particles;
|
|
||||||
private int _particleIndex;
|
|
||||||
|
|
||||||
// TODO: replace a random function for better distribution and performance.
|
|
||||||
private LehmerRandom _random = new LehmerRandom();
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct ParticleSettings
|
|
||||||
{
|
|
||||||
public ParticleSettings()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public float EmitRadius;
|
|
||||||
public float LifeTime;
|
|
||||||
public float Explosiveness;
|
|
||||||
public int MaxParticles;
|
|
||||||
public Vector2 Direction;
|
|
||||||
public float LinearVelocity;
|
|
||||||
public float AngularVelocity = 0.0f;
|
|
||||||
public float AngularVelocityRandom;
|
|
||||||
public float LinearVelocityRandom;
|
|
||||||
public Vector2 Gravity;
|
|
||||||
public float ScaleBegin = 16f;
|
|
||||||
public float ScaleEnd = 0.0f;
|
|
||||||
public Color ColorBegin = Color.White;
|
|
||||||
public Color ColorEnd = Color.Black;
|
|
||||||
public float Damping = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Particle
|
|
||||||
{
|
|
||||||
public Particle()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public Vector2 Position;
|
|
||||||
public Vector2 Velocity;
|
|
||||||
public float AngularVelocity;
|
|
||||||
public float LifeTime;
|
|
||||||
public float LifeTimeRemaining;
|
|
||||||
public float Scale;
|
|
||||||
public float Rotation;
|
|
||||||
public bool Alive = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,9 @@ namespace Voile;
|
|||||||
|
|
||||||
public interface IStartableSystem
|
public interface IStartableSystem
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Starts this system.
|
||||||
|
/// </summary>
|
||||||
void Start();
|
void Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
205
Voile/Source/Systems/ParticleSystem.cs
Normal file
205
Voile/Source/Systems/ParticleSystem.cs
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Voile.Rendering;
|
||||||
|
using Voile.Utils;
|
||||||
|
|
||||||
|
namespace Voile.Systems;
|
||||||
|
|
||||||
|
public struct Particle
|
||||||
|
{
|
||||||
|
public 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 LifeTimeRemaining { get; set; }
|
||||||
|
public float Scale { get; set; }
|
||||||
|
public float Rotation { get; set; }
|
||||||
|
public bool Alive { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParticleEmitterSettings
|
||||||
|
{
|
||||||
|
public int MaxParticles { get; set; } = 16;
|
||||||
|
public float EmitRadius { get; set; }
|
||||||
|
public float LifeTime { get; set; } = 1.0f;
|
||||||
|
public float Explosiveness { get; set; }
|
||||||
|
public Vector2 Direction { get; set; } = -Vector2.UnitY;
|
||||||
|
public float LinearVelocity { get; set; } = 980.0f;
|
||||||
|
public float AngularVelocity { get; set; }
|
||||||
|
public float AngularVelocityRandom { get; set; }
|
||||||
|
public float LinearVelocityRandom { get; set; } = 0.5f;
|
||||||
|
public Vector2 Gravity { get; set; } = Vector2.UnitY * 980f;
|
||||||
|
public float ScaleBegin { get; set; } = 1.0f;
|
||||||
|
public float ScaleEnd { get; set; } = 0.0f;
|
||||||
|
public Color ColorBegin { get; set; } = Color.White;
|
||||||
|
public Color ColorEnd { get; set; } = Color.Black;
|
||||||
|
public float Damping { get; set; } = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParticleEmitter : IUpdatableSystem
|
||||||
|
{
|
||||||
|
public ReadOnlySpan<Particle> Particles => _particles.AsSpan();
|
||||||
|
public Vector2 OriginPosition => _originPosition;
|
||||||
|
public ParticleEmitterSettings Settings => _settings;
|
||||||
|
|
||||||
|
public ParticleEmitter(Vector2 originPosition, ParticleEmitterSettings settings, ArraySegment<Particle> particles)
|
||||||
|
{
|
||||||
|
_originPosition = originPosition;
|
||||||
|
|
||||||
|
_settings = settings;
|
||||||
|
_maxParticles = _settings.MaxParticles;
|
||||||
|
_particleIndex = _maxParticles - 1;
|
||||||
|
|
||||||
|
_particles = particles;
|
||||||
|
|
||||||
|
_random = new LehmerRandom();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
var rate = (int)MathUtils.Lerp(1, _maxParticles, _settings.Explosiveness);
|
||||||
|
for (int i = 0; i < rate; i++)
|
||||||
|
{
|
||||||
|
Emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
var t = particle.LifeTimeRemaining / particle.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);
|
||||||
|
|
||||||
|
var color = MathUtils.LerpColor(Settings.ColorEnd, Settings.ColorBegin, t);
|
||||||
|
particle.ColorArgb = color.Argb;
|
||||||
|
|
||||||
|
particle.Velocity -= particle.Velocity * _settings.Damping * (float)deltaTime;
|
||||||
|
|
||||||
|
_particles[i] = particle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Emit()
|
||||||
|
{
|
||||||
|
Particle particle = _particles[_particleIndex];
|
||||||
|
if (!(particle.LifeTimeRemaining <= 0)) return;
|
||||||
|
particle.Alive = true;
|
||||||
|
particle.Position = GetEmitPosition();
|
||||||
|
particle.Velocity = _settings.Direction * _settings.LinearVelocity;
|
||||||
|
|
||||||
|
particle.Velocity += Vector2.One * _settings.LinearVelocityRandom * ((float)_random.NextDouble() - 0.5f);
|
||||||
|
|
||||||
|
particle.AngularVelocity = _settings.AngularVelocity;
|
||||||
|
particle.AngularVelocity += 1f * _settings.AngularVelocityRandom * ((float)_random.NextDouble() - 0.5f);
|
||||||
|
|
||||||
|
particle.LifeTime = _settings.LifeTime;
|
||||||
|
particle.LifeTimeRemaining = particle.LifeTime;
|
||||||
|
|
||||||
|
_particles[_particleIndex] = particle;
|
||||||
|
_particleIndex = --_particleIndex <= 0 ? _maxParticles - 1 : --_particleIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 GetEmitPosition()
|
||||||
|
{
|
||||||
|
// https://gamedev.stackexchange.com/questions/26713/calculate-random-points-pixel-within-a-circle-image
|
||||||
|
var angle = _random.NextDouble() * Math.PI * 2;
|
||||||
|
float radius = (float)Math.Sqrt(_random.NextDouble()) * _settings.EmitRadius;
|
||||||
|
|
||||||
|
float x = radius * (float)Math.Cos(angle);
|
||||||
|
float y = radius * (float)Math.Sin(angle);
|
||||||
|
|
||||||
|
return new Vector2(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LehmerRandom _random;
|
||||||
|
private int _maxParticles;
|
||||||
|
private int _particleIndex;
|
||||||
|
private Vector2 _originPosition = Vector2.Zero;
|
||||||
|
private ArraySegment<Particle> _particles;
|
||||||
|
private ParticleEmitterSettings _settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParticleSystem : IUpdatableSystem, IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum amount of particles emittable by the system.
|
||||||
|
/// </summary>
|
||||||
|
public int ParticleLimit { get; set; } = 8192;
|
||||||
|
|
||||||
|
public IReadOnlyList<ParticleEmitter> Emitters => _emitters;
|
||||||
|
|
||||||
|
public ParticleSystem()
|
||||||
|
{
|
||||||
|
_particleIndex = ParticleLimit - 1;
|
||||||
|
_particles = new Particle[ParticleLimit];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateEmitter(Vector2 originPosition, ParticleEmitterSettings settings)
|
||||||
|
{
|
||||||
|
if (_emitterSliceOffset + settings.MaxParticles >= ParticleLimit - 1)
|
||||||
|
{
|
||||||
|
_logger.Error("Cannot create an emitter! Reached particle limit.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
particle.LifeTime = settings.LifeTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
var emitter = new ParticleEmitter(originPosition, settings, particles);
|
||||||
|
|
||||||
|
_emitters.Add(emitter);
|
||||||
|
|
||||||
|
_emitterSliceOffset += settings.MaxParticles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
foreach (var emitter in _emitters)
|
||||||
|
{
|
||||||
|
emitter.Update(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
CleanupParticles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanupParticles() => Array.Clear(_particles);
|
||||||
|
private Particle[] _particles;
|
||||||
|
private int _particleIndex;
|
||||||
|
|
||||||
|
private int _emitterSliceOffset;
|
||||||
|
|
||||||
|
private List<ParticleEmitter> _emitters = new();
|
||||||
|
|
||||||
|
private Logger _logger = new(nameof(ParticleSystem));
|
||||||
|
}
|
||||||
@@ -37,21 +37,18 @@ namespace Voile
|
|||||||
public float R { get; set; }
|
public float R { get; set; }
|
||||||
public float G { get; set; }
|
public float G { get; set; }
|
||||||
public float B { get; set; }
|
public float B { get; set; }
|
||||||
public float A { get; set; }
|
public float A { get; set; } = 1.0f;
|
||||||
|
|
||||||
public int Argb
|
public int Argb
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
int c = (ushort)Math.Round(A * 255f);
|
int a = (int)Math.Round(A * 255f) << 24;
|
||||||
c <<= 8;
|
int r = (int)Math.Round(R * 255f) << 16;
|
||||||
c |= (ushort)Math.Round(R * 255f);
|
int g = (int)Math.Round(G * 255f) << 8;
|
||||||
c <<= 8;
|
int b = (int)Math.Round(B * 255f);
|
||||||
c |= (ushort)Math.Round(G * 255f);
|
|
||||||
c <<= 8;
|
|
||||||
c |= (ushort)Math.Round(B * 255f);
|
|
||||||
|
|
||||||
return c;
|
return a | r | g | b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ namespace Voile
|
|||||||
|
|
||||||
public static Color LerpColor(Color colorA, Color colorB, double t)
|
public static Color LerpColor(Color colorA, Color colorB, double t)
|
||||||
{
|
{
|
||||||
var r = (byte)Lerp(colorA.R, colorB.R, t);
|
var r = Lerp(colorA.R, colorB.R, t);
|
||||||
var g = (byte)Lerp(colorA.G, colorB.G, t);
|
var g = Lerp(colorA.G, colorB.G, t);
|
||||||
var b = (byte)Lerp(colorA.B, colorB.B, t);
|
var b = Lerp(colorA.B, colorB.B, t);
|
||||||
var a = (byte)Lerp(colorA.A, colorB.A, t);
|
var a = Lerp(colorA.A, colorB.A, t);
|
||||||
|
|
||||||
return new Color(r, g, b, a);
|
return new Color(r, g, b, a);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user