mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-25 08:22:15 +00:00
Particles data driven (#388)
* yay, a start on data driven particles * added data driven support for all particle profiles * pretty much finished all of the particle serialization code * resolved an inconsistency with the interpolators
This commit is contained in:
@@ -217,5 +217,47 @@ namespace MonoGame.Extended.Particles
|
||||
c1.S + t*(c2.S - c1.S),
|
||||
c1.L + t*(c2.L - c2.L));
|
||||
}
|
||||
|
||||
public static HslColor FromRgb(Color color)
|
||||
{
|
||||
// derived from http://www.geekymonkey.com/Programming/CSharp/RGB2HSL_HSL2RGB.htm
|
||||
var r = color.R / 255f;
|
||||
var g = color.G / 255f;
|
||||
var b = color.B / 255f;
|
||||
var h = 0f; // default to black
|
||||
var s = 0f;
|
||||
var l = 0f;
|
||||
var v = Math.Max(r, g);
|
||||
v = Math.Max(v, b);
|
||||
|
||||
var m = Math.Min(r, g);
|
||||
m = Math.Min(m, b);
|
||||
l = (m + v) / 2.0f;
|
||||
|
||||
if (l <= 0.0)
|
||||
return new HslColor(h, s, l);
|
||||
|
||||
var vm = v - m;
|
||||
s = vm;
|
||||
|
||||
if (s > 0.0)
|
||||
s /= l <= 0.5f ? v + m : 2.0f - v - m;
|
||||
else
|
||||
return new HslColor(h, s, l);
|
||||
|
||||
var r2 = (v - r) / vm;
|
||||
var g2 = (v - g) / vm;
|
||||
var b2 = (v - b) / vm;
|
||||
|
||||
if (Math.Abs(r - v) < float.Epsilon)
|
||||
h = Math.Abs(g - m) < float.Epsilon ? 5.0f + b2 : 1.0f - g2;
|
||||
else if (Math.Abs(g - v) < float.Epsilon)
|
||||
h = Math.Abs(b - m) < float.Epsilon ? 1.0f + r2 : 3.0f - b2;
|
||||
else
|
||||
h = Math.Abs(r - m) < float.Epsilon ? 3.0f + g2 : 5.0f - r2;
|
||||
|
||||
h /= 6.0f;
|
||||
return new HslColor(h, s, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,40 @@
|
||||
namespace MonoGame.Extended.Particles.Modifiers.Interpolators
|
||||
using System;
|
||||
|
||||
namespace MonoGame.Extended.Particles.Modifiers.Interpolators
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a modifier which interpolates the color of a particle over the course of its lifetime.
|
||||
/// </summary>
|
||||
public sealed class ColorInterpolator : IInterpolator
|
||||
public sealed class ColorInterpolator : IInterpolator<HslColor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the initial color of particles when they are released.
|
||||
/// </summary>
|
||||
public HslColor InitialColor { get; set; }
|
||||
public HslColor StartValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the final color of particles when they are retired.
|
||||
/// </summary>
|
||||
public HslColor FinalColor { get; set; }
|
||||
public HslColor EndValue { get; set; }
|
||||
|
||||
[Obsolete("Use StartValue instead")]
|
||||
public HslColor InitialColor
|
||||
{
|
||||
get { return StartValue; }
|
||||
set { StartValue = value; }
|
||||
}
|
||||
|
||||
[Obsolete("Use EndValue instead")]
|
||||
public HslColor FinalColor
|
||||
{
|
||||
get { return EndValue; }
|
||||
set { EndValue = value; }
|
||||
}
|
||||
|
||||
public unsafe void Update(float amount, Particle* particle)
|
||||
{
|
||||
particle->Color = HslColor.Lerp(InitialColor, FinalColor, amount);
|
||||
particle->Color = HslColor.Lerp(StartValue, EndValue, amount);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
public class HueInterpolator : IInterpolator
|
||||
{
|
||||
private float _delta;
|
||||
public float StartValue { get; set; }
|
||||
|
||||
public float StartValue { get; set; }
|
||||
public float EndValue
|
||||
{
|
||||
get { return _delta + StartValue; }
|
||||
|
||||
@@ -4,4 +4,10 @@
|
||||
{
|
||||
unsafe void Update(float amount, Particle* particle);
|
||||
}
|
||||
|
||||
public interface IInterpolator<T> : IInterpolator
|
||||
{
|
||||
T StartValue { get; set; }
|
||||
T EndValue { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
public class OpacityInterpolator : IInterpolator
|
||||
{
|
||||
private float _delta;
|
||||
|
||||
public float StartValue { get; set; }
|
||||
|
||||
public float EndValue
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
public class RotationInterpolator : IInterpolator
|
||||
{
|
||||
private float _delta;
|
||||
|
||||
public float StartValue { get; set; }
|
||||
|
||||
public float EndValue
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace MonoGame.Extended.Particles.Modifiers.Interpolators
|
||||
public class ScaleInterpolator : IInterpolator
|
||||
{
|
||||
private Vector2 _delta;
|
||||
|
||||
public Vector2 StartValue { get; set; }
|
||||
|
||||
public Vector2 EndValue
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace MonoGame.Extended.Particles.Modifiers
|
||||
{
|
||||
// Note: not the real-life one
|
||||
private const float _gravConst = 100000f;
|
||||
|
||||
public Vector2 Position { get; set; }
|
||||
public float Mass { get; set; }
|
||||
public float MaxSpeed { get; set; }
|
||||
|
||||
@@ -82,11 +82,21 @@
|
||||
<Compile Include="Profiles\RingProfile.cs" />
|
||||
<Compile Include="Profiles\SprayProfile.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Serialization\HslColorJsonConverter.cs" />
|
||||
<Compile Include="Serialization\InterpolatorJsonConverter.cs" />
|
||||
<Compile Include="Serialization\ModifierExecutionStrategyJsonConverter.cs" />
|
||||
<Compile Include="Serialization\ModifierJsonConverter.cs" />
|
||||
<Compile Include="Serialization\ParticleJsonSerializer.cs" />
|
||||
<Compile Include="Serialization\ProfileJsonConverter.cs" />
|
||||
<Compile Include="Serialization\TimeSpanJsonConverter.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="MonoGame.Framework, Version=3.6.0.1625, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MonoGame.Framework.Portable.3.6.0.1625\lib\portable-net45+win8+wpa81\MonoGame.Framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
using MonoGame.Extended.Particles.Serialization;
|
||||
using MonoGame.Extended.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MonoGame.Extended.Particles
|
||||
{
|
||||
@@ -26,6 +31,25 @@ namespace MonoGame.Extended.Particles
|
||||
}
|
||||
}
|
||||
|
||||
public static ParticleEffect FromFile(ITextureRegionService textureRegionService, string path)
|
||||
{
|
||||
using (var stream = TitleContainer.OpenStream(path))
|
||||
{
|
||||
return FromStream(textureRegionService, stream);
|
||||
}
|
||||
}
|
||||
|
||||
public static ParticleEffect FromStream(ITextureRegionService textureRegionService, Stream stream)
|
||||
{
|
||||
var skinSerializer = new ParticleJsonSerializer(textureRegionService);
|
||||
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
using (var jsonReader = new JsonTextReader(streamReader))
|
||||
{
|
||||
return skinSerializer.Deserialize<ParticleEffect>(jsonReader);
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float elapsedSeconds)
|
||||
{
|
||||
foreach (var e in Emitters)
|
||||
|
||||
@@ -15,8 +15,7 @@ namespace MonoGame.Extended.Particles
|
||||
|
||||
private float _totalSeconds;
|
||||
|
||||
public ParticleEmitter(TextureRegion2D textureRegion, int capacity, TimeSpan term, Profile profile,
|
||||
bool autoTrigger = true)
|
||||
public ParticleEmitter(TextureRegion2D textureRegion, int capacity, TimeSpan term, Profile profile, bool autoTrigger = true)
|
||||
{
|
||||
if (profile == null)
|
||||
throw new ArgumentNullException(nameof(profile));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MonoGame.Extended.Particles.Modifiers;
|
||||
|
||||
namespace MonoGame.Extended.Particles
|
||||
@@ -7,11 +8,10 @@ namespace MonoGame.Extended.Particles
|
||||
|
||||
public abstract class ParticleModifierExecutionStrategy
|
||||
{
|
||||
public static ParticleModifierExecutionStrategy Serial = new SerialModifierExecutionStrategy();
|
||||
public static ParticleModifierExecutionStrategy Parallel = new ParallelModifierExecutionStrategy();
|
||||
public static readonly ParticleModifierExecutionStrategy Serial = new SerialModifierExecutionStrategy();
|
||||
public static readonly ParticleModifierExecutionStrategy Parallel = new ParallelModifierExecutionStrategy();
|
||||
|
||||
internal abstract void ExecuteModifiers(IEnumerable<IModifier> modifiers, float elapsedSeconds,
|
||||
ParticleBuffer.ParticleIterator iterator);
|
||||
internal abstract void ExecuteModifiers(IEnumerable<IModifier> modifiers, float elapsedSeconds, ParticleBuffer.ParticleIterator iterator);
|
||||
|
||||
internal class SerialModifierExecutionStrategy : ParticleModifierExecutionStrategy
|
||||
{
|
||||
@@ -31,5 +31,16 @@ namespace MonoGame.Extended.Particles
|
||||
TPL.Parallel.ForEach(modifiers, modifier => modifier.Update(elapsedSeconds, iterator.Reset()));
|
||||
}
|
||||
}
|
||||
|
||||
public static ParticleModifierExecutionStrategy Parse(string value)
|
||||
{
|
||||
if (string.Equals(nameof(Parallel), value, StringComparison.OrdinalIgnoreCase))
|
||||
return Parallel;
|
||||
|
||||
if (string.Equals(nameof(Serial), value, StringComparison.OrdinalIgnoreCase))
|
||||
return Serial;
|
||||
|
||||
throw new InvalidOperationException($"Unknown particle modifier execution strategy '{value}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace MonoGame.Extended.Particles.Profiles
|
||||
{
|
||||
public abstract class Profile //: ICloneable
|
||||
public abstract class Profile
|
||||
{
|
||||
public enum CircleRadiation
|
||||
{
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MonoGame.Extended.Particles.Serialization
|
||||
{
|
||||
public class HslColorJsonConverter : JsonConverter
|
||||
{
|
||||
private readonly ColorJsonConverter _colorConverter = new ColorJsonConverter();
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var color = ((HslColor) value).ToRgb();
|
||||
_colorConverter.WriteJson(writer, color, serializer);
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var color = (Color)_colorConverter.ReadJson(reader, objectType, existingValue, serializer);
|
||||
return HslColor.FromRgb(color);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(HslColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MonoGame.Extended.Particles.Modifiers.Interpolators;
|
||||
using MonoGame.Extended.Serialization;
|
||||
|
||||
namespace MonoGame.Extended.Particles.Serialization
|
||||
{
|
||||
public class InterpolatorJsonConverter : BaseTypeJsonConverter<IInterpolator>
|
||||
{
|
||||
public InterpolatorJsonConverter()
|
||||
: base(GetSupportedTypes(), "Interpolator")
|
||||
{
|
||||
}
|
||||
|
||||
private static IEnumerable<TypeInfo> GetSupportedTypes()
|
||||
{
|
||||
return typeof(IInterpolator)
|
||||
.GetTypeInfo()
|
||||
.Assembly
|
||||
.DefinedTypes
|
||||
.Where(type => typeof(IInterpolator).GetTypeInfo().IsAssignableFrom(type) && !type.IsAbstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace MonoGame.Extended.Particles.Serialization
|
||||
{
|
||||
public class ModifierExecutionStrategyJsonConverter : JsonConverter
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var value = JToken.Load(reader).ToObject<string>();
|
||||
return ParticleModifierExecutionStrategy.Parse(value);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(ParticleModifierExecutionStrategy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MonoGame.Extended.Particles.Modifiers;
|
||||
using MonoGame.Extended.Serialization;
|
||||
|
||||
namespace MonoGame.Extended.Particles.Serialization
|
||||
{
|
||||
public class ModifierJsonConverter : BaseTypeJsonConverter<IModifier>
|
||||
{
|
||||
public ModifierJsonConverter()
|
||||
: base(GetSupportedTypes(), "Modifier")
|
||||
{
|
||||
}
|
||||
|
||||
private static IEnumerable<TypeInfo> GetSupportedTypes()
|
||||
{
|
||||
return typeof(IModifier)
|
||||
.GetTypeInfo()
|
||||
.Assembly
|
||||
.DefinedTypes
|
||||
.Where(type => typeof(IModifier).GetTypeInfo().IsAssignableFrom(type) && !type.IsAbstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using MonoGame.Extended.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace MonoGame.Extended.Particles.Serialization
|
||||
{
|
||||
public sealed class ParticleJsonSerializer : JsonSerializer
|
||||
{
|
||||
public ParticleJsonSerializer(ITextureRegionService textureRegionService)
|
||||
{
|
||||
Converters.Add(new Vector2JsonConverter());
|
||||
Converters.Add(new Size2JsonConverter());
|
||||
Converters.Add(new ColorJsonConverter());
|
||||
Converters.Add(new TextureRegion2DJsonConverter(textureRegionService));
|
||||
Converters.Add(new ProfileJsonConverter());
|
||||
Converters.Add(new ModifierJsonConverter());
|
||||
Converters.Add(new InterpolatorJsonConverter());
|
||||
Converters.Add(new TimeSpanJsonConverter());
|
||||
Converters.Add(new RangeJsonConverter<int>());
|
||||
Converters.Add(new RangeJsonConverter<float>());
|
||||
Converters.Add(new HslColorJsonConverter());
|
||||
Converters.Add(new ModifierExecutionStrategyJsonConverter());
|
||||
ContractResolver = new ShortNameJsonContractResolver();
|
||||
Formatting = Formatting.Indented;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MonoGame.Extended.Particles.Profiles;
|
||||
using MonoGame.Extended.Serialization;
|
||||
|
||||
namespace MonoGame.Extended.Particles.Serialization
|
||||
{
|
||||
public class ProfileJsonConverter : BaseTypeJsonConverter<Profile>
|
||||
{
|
||||
public ProfileJsonConverter()
|
||||
: base(GetSupportedTypes(), nameof(Profile))
|
||||
{
|
||||
}
|
||||
|
||||
private static IEnumerable<TypeInfo> GetSupportedTypes()
|
||||
{
|
||||
return typeof(Profile)
|
||||
.GetTypeInfo()
|
||||
.Assembly
|
||||
.DefinedTypes
|
||||
.Where(type => type.IsSubclassOf(typeof(Profile)) && !type.IsAbstract);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MonoGame.Extended.Particles.Serialization
|
||||
{
|
||||
public class TimeSpanJsonConverter : JsonConverter
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.ValueType == typeof(double))
|
||||
{
|
||||
var seconds = (double) reader.Value;
|
||||
return TimeSpan.FromSeconds(seconds);
|
||||
}
|
||||
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(TimeSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MonoGame.Framework.Portable" version="3.6.0.1625" targetFramework="portable45-net45+win8+wpa81" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="portable45-net45+win8+wpa81" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user