diff --git a/Source/MonoGame.Extended.Gui/MonoGame.Extended.Gui.csproj b/Source/MonoGame.Extended.Gui/MonoGame.Extended.Gui.csproj
index 8437bc6d..df64a51d 100644
--- a/Source/MonoGame.Extended.Gui/MonoGame.Extended.Gui.csproj
+++ b/Source/MonoGame.Extended.Gui/MonoGame.Extended.Gui.csproj
@@ -61,7 +61,6 @@
-
diff --git a/Source/MonoGame.Extended.Gui/Serialization/GuiControlJsonConverter.cs b/Source/MonoGame.Extended.Gui/Serialization/GuiControlJsonConverter.cs
index b88c5740..eb509c8f 100644
--- a/Source/MonoGame.Extended.Gui/Serialization/GuiControlJsonConverter.cs
+++ b/Source/MonoGame.Extended.Gui/Serialization/GuiControlJsonConverter.cs
@@ -23,7 +23,7 @@ namespace MonoGame.Extended.Gui.Serialization
{
var skin = _guiSkinService.Skin;
var style = (GuiControlStyle) _styleConverter.ReadJson(reader, objectType, existingValue, serializer);
- var template = GetControlTempalte(style, _styleProperty);
+ var template = GetControlTemplate(style);
var control = skin.Create(style.TargetType, template);
object childControls;
@@ -48,7 +48,7 @@ namespace MonoGame.Extended.Gui.Serialization
return objectType == typeof(GuiControl);
}
- private string GetControlTempalte(GuiControlStyle style, string styleProperty)
+ private static string GetControlTemplate(GuiControlStyle style)
{
object template;
diff --git a/Source/MonoGame.Extended.Gui/Serialization/GuiJsonSerializer.cs b/Source/MonoGame.Extended.Gui/Serialization/GuiJsonSerializer.cs
index b64ca0a9..477d244a 100644
--- a/Source/MonoGame.Extended.Gui/Serialization/GuiJsonSerializer.cs
+++ b/Source/MonoGame.Extended.Gui/Serialization/GuiJsonSerializer.cs
@@ -1,11 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
using Microsoft.Xna.Framework.Content;
using MonoGame.Extended.BitmapFonts;
using MonoGame.Extended.Serialization;
using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
namespace MonoGame.Extended.Gui.Serialization
{
@@ -23,7 +19,7 @@ namespace MonoGame.Extended.Gui.Serialization
Converters.Add(new GuiTextureAtlasJsonConverter(contentManager, textureRegionService));
Converters.Add(new GuiNinePatchRegion2DJsonConverter(textureRegionService));
Converters.Add(new TextureRegion2DJsonConverter(textureRegionService));
- ContractResolver = new GuiJsonContractResolver();
+ ContractResolver = new ShortNameJsonContractResolver();
Formatting = Formatting.Indented;
}
}
diff --git a/Source/MonoGame.Extended.Gui/Serialization/GuiTextureRegionService.cs b/Source/MonoGame.Extended.Gui/Serialization/GuiTextureRegionService.cs
index 86262186..83fe8382 100644
--- a/Source/MonoGame.Extended.Gui/Serialization/GuiTextureRegionService.cs
+++ b/Source/MonoGame.Extended.Gui/Serialization/GuiTextureRegionService.cs
@@ -7,31 +7,9 @@ namespace MonoGame.Extended.Gui.Serialization
{
public interface IGuiTextureRegionService : ITextureRegionService
{
- IList TextureAtlases { get; }
- IList NinePatches { get; }
}
- public class GuiTextureRegionService : IGuiTextureRegionService
+ public class GuiTextureRegionService : TextureRegionService, IGuiTextureRegionService
{
- public GuiTextureRegionService()
- {
- TextureAtlases = new List();
- NinePatches = new List();
- }
-
- public IList TextureAtlases { get; }
- public IList NinePatches { get; }
-
- public TextureRegion2D GetTextureRegion(string name)
- {
- var ninePatch = NinePatches.FirstOrDefault(p => p.Name == name);
-
- if (ninePatch != null)
- return ninePatch;
-
- return TextureAtlases
- .Select(textureAtlas => textureAtlas.GetRegion(name))
- .FirstOrDefault(region => region != null);
- }
}
}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/HslColor.cs b/Source/MonoGame.Extended.Particles/HslColor.cs
index f9343727..f21b335e 100644
--- a/Source/MonoGame.Extended.Particles/HslColor.cs
+++ b/Source/MonoGame.Extended.Particles/HslColor.cs
@@ -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);
+ }
}
}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/ColorInterpolator.cs b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/ColorInterpolator.cs
index 20e46f0b..76821508 100644
--- a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/ColorInterpolator.cs
+++ b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/ColorInterpolator.cs
@@ -1,23 +1,40 @@
-namespace MonoGame.Extended.Particles.Modifiers.Interpolators
+using System;
+
+namespace MonoGame.Extended.Particles.Modifiers.Interpolators
{
///
/// Defines a modifier which interpolates the color of a particle over the course of its lifetime.
///
- public sealed class ColorInterpolator : IInterpolator
+ public sealed class ColorInterpolator : IInterpolator
{
///
/// Gets or sets the initial color of particles when they are released.
///
- public HslColor InitialColor { get; set; }
+ public HslColor StartValue { get; set; }
///
/// Gets or sets the final color of particles when they are retired.
///
- 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);
}
+
}
}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/HueInterpolator.cs b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/HueInterpolator.cs
index b9950bd1..49721622 100644
--- a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/HueInterpolator.cs
+++ b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/HueInterpolator.cs
@@ -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; }
diff --git a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/IInterpolator.cs b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/IInterpolator.cs
index 617f1ff5..7d8ded13 100644
--- a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/IInterpolator.cs
+++ b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/IInterpolator.cs
@@ -4,4 +4,10 @@
{
unsafe void Update(float amount, Particle* particle);
}
+
+ public interface IInterpolator : IInterpolator
+ {
+ T StartValue { get; set; }
+ T EndValue { get; set; }
+ }
}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/OpacityInterpolator.cs b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/OpacityInterpolator.cs
index 0a10f6ab..6442e004 100644
--- a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/OpacityInterpolator.cs
+++ b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/OpacityInterpolator.cs
@@ -3,6 +3,7 @@
public class OpacityInterpolator : IInterpolator
{
private float _delta;
+
public float StartValue { get; set; }
public float EndValue
diff --git a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/RotationInterpolator.cs b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/RotationInterpolator.cs
index 8d7d0c74..bfb161df 100644
--- a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/RotationInterpolator.cs
+++ b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/RotationInterpolator.cs
@@ -3,6 +3,7 @@
public class RotationInterpolator : IInterpolator
{
private float _delta;
+
public float StartValue { get; set; }
public float EndValue
diff --git a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/ScaleInterpolator.cs b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/ScaleInterpolator.cs
index 95e4ab4e..6595cc30 100644
--- a/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/ScaleInterpolator.cs
+++ b/Source/MonoGame.Extended.Particles/Modifiers/Interpolators/ScaleInterpolator.cs
@@ -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
diff --git a/Source/MonoGame.Extended.Particles/Modifiers/VortexModifier.cs b/Source/MonoGame.Extended.Particles/Modifiers/VortexModifier.cs
index 7816e6a6..9f40a625 100644
--- a/Source/MonoGame.Extended.Particles/Modifiers/VortexModifier.cs
+++ b/Source/MonoGame.Extended.Particles/Modifiers/VortexModifier.cs
@@ -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; }
diff --git a/Source/MonoGame.Extended.Particles/MonoGame.Extended.Particles.csproj b/Source/MonoGame.Extended.Particles/MonoGame.Extended.Particles.csproj
index b577ed99..92dc981b 100644
--- a/Source/MonoGame.Extended.Particles/MonoGame.Extended.Particles.csproj
+++ b/Source/MonoGame.Extended.Particles/MonoGame.Extended.Particles.csproj
@@ -82,11 +82,21 @@
+
+
+
+
+
+
+
..\packages\MonoGame.Framework.Portable.3.6.0.1625\lib\portable-net45+win8+wpa81\MonoGame.Framework.dll
+
+ ..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll
+
diff --git a/Source/MonoGame.Extended.Particles/ParticleEffect.cs b/Source/MonoGame.Extended.Particles/ParticleEffect.cs
index 71edcfe2..9bc8b459 100644
--- a/Source/MonoGame.Extended.Particles/ParticleEffect.cs
+++ b/Source/MonoGame.Extended.Particles/ParticleEffect.cs
@@ -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(jsonReader);
+ }
+ }
+
public void Update(float elapsedSeconds)
{
foreach (var e in Emitters)
diff --git a/Source/MonoGame.Extended.Particles/ParticleEmitter.cs b/Source/MonoGame.Extended.Particles/ParticleEmitter.cs
index 74d4eba3..9f04fb51 100644
--- a/Source/MonoGame.Extended.Particles/ParticleEmitter.cs
+++ b/Source/MonoGame.Extended.Particles/ParticleEmitter.cs
@@ -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));
diff --git a/Source/MonoGame.Extended.Particles/ParticleModifierExecutionStrategy.cs b/Source/MonoGame.Extended.Particles/ParticleModifierExecutionStrategy.cs
index 7c80efdf..a57ef039 100644
--- a/Source/MonoGame.Extended.Particles/ParticleModifierExecutionStrategy.cs
+++ b/Source/MonoGame.Extended.Particles/ParticleModifierExecutionStrategy.cs
@@ -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 modifiers, float elapsedSeconds,
- ParticleBuffer.ParticleIterator iterator);
+ internal abstract void ExecuteModifiers(IEnumerable 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}'");
+ }
}
}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/Profiles/Profile.cs b/Source/MonoGame.Extended.Particles/Profiles/Profile.cs
index f3a2da85..bfde371b 100644
--- a/Source/MonoGame.Extended.Particles/Profiles/Profile.cs
+++ b/Source/MonoGame.Extended.Particles/Profiles/Profile.cs
@@ -2,7 +2,7 @@
namespace MonoGame.Extended.Particles.Profiles
{
- public abstract class Profile //: ICloneable
+ public abstract class Profile
{
public enum CircleRadiation
{
diff --git a/Source/MonoGame.Extended.Particles/Serialization/HslColorJsonConverter.cs b/Source/MonoGame.Extended.Particles/Serialization/HslColorJsonConverter.cs
new file mode 100644
index 00000000..4b65c117
--- /dev/null
+++ b/Source/MonoGame.Extended.Particles/Serialization/HslColorJsonConverter.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/Serialization/InterpolatorJsonConverter.cs b/Source/MonoGame.Extended.Particles/Serialization/InterpolatorJsonConverter.cs
new file mode 100644
index 00000000..44422d5b
--- /dev/null
+++ b/Source/MonoGame.Extended.Particles/Serialization/InterpolatorJsonConverter.cs
@@ -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
+ {
+ public InterpolatorJsonConverter()
+ : base(GetSupportedTypes(), "Interpolator")
+ {
+ }
+
+ private static IEnumerable GetSupportedTypes()
+ {
+ return typeof(IInterpolator)
+ .GetTypeInfo()
+ .Assembly
+ .DefinedTypes
+ .Where(type => typeof(IInterpolator).GetTypeInfo().IsAssignableFrom(type) && !type.IsAbstract);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/Serialization/ModifierExecutionStrategyJsonConverter.cs b/Source/MonoGame.Extended.Particles/Serialization/ModifierExecutionStrategyJsonConverter.cs
new file mode 100644
index 00000000..da66a4c5
--- /dev/null
+++ b/Source/MonoGame.Extended.Particles/Serialization/ModifierExecutionStrategyJsonConverter.cs
@@ -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();
+ return ParticleModifierExecutionStrategy.Parse(value);
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(ParticleModifierExecutionStrategy);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/Serialization/ModifierJsonConverter.cs b/Source/MonoGame.Extended.Particles/Serialization/ModifierJsonConverter.cs
new file mode 100644
index 00000000..ab3bc670
--- /dev/null
+++ b/Source/MonoGame.Extended.Particles/Serialization/ModifierJsonConverter.cs
@@ -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
+ {
+ public ModifierJsonConverter()
+ : base(GetSupportedTypes(), "Modifier")
+ {
+ }
+
+ private static IEnumerable GetSupportedTypes()
+ {
+ return typeof(IModifier)
+ .GetTypeInfo()
+ .Assembly
+ .DefinedTypes
+ .Where(type => typeof(IModifier).GetTypeInfo().IsAssignableFrom(type) && !type.IsAbstract);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/Serialization/ParticleJsonSerializer.cs b/Source/MonoGame.Extended.Particles/Serialization/ParticleJsonSerializer.cs
new file mode 100644
index 00000000..ed15965c
--- /dev/null
+++ b/Source/MonoGame.Extended.Particles/Serialization/ParticleJsonSerializer.cs
@@ -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());
+ Converters.Add(new RangeJsonConverter());
+ Converters.Add(new HslColorJsonConverter());
+ Converters.Add(new ModifierExecutionStrategyJsonConverter());
+ ContractResolver = new ShortNameJsonContractResolver();
+ Formatting = Formatting.Indented;
+ }
+ }
+}
diff --git a/Source/MonoGame.Extended.Particles/Serialization/ProfileJsonConverter.cs b/Source/MonoGame.Extended.Particles/Serialization/ProfileJsonConverter.cs
new file mode 100644
index 00000000..8d980791
--- /dev/null
+++ b/Source/MonoGame.Extended.Particles/Serialization/ProfileJsonConverter.cs
@@ -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
+ {
+ public ProfileJsonConverter()
+ : base(GetSupportedTypes(), nameof(Profile))
+ {
+ }
+
+ private static IEnumerable GetSupportedTypes()
+ {
+ return typeof(Profile)
+ .GetTypeInfo()
+ .Assembly
+ .DefinedTypes
+ .Where(type => type.IsSubclassOf(typeof(Profile)) && !type.IsAbstract);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/Serialization/TimeSpanJsonConverter.cs b/Source/MonoGame.Extended.Particles/Serialization/TimeSpanJsonConverter.cs
new file mode 100644
index 00000000..e6029152
--- /dev/null
+++ b/Source/MonoGame.Extended.Particles/Serialization/TimeSpanJsonConverter.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Particles/packages.config b/Source/MonoGame.Extended.Particles/packages.config
index 28c01444..43f6cc68 100644
--- a/Source/MonoGame.Extended.Particles/packages.config
+++ b/Source/MonoGame.Extended.Particles/packages.config
@@ -1,4 +1,5 @@
+
\ No newline at end of file
diff --git a/Source/MonoGame.Extended/MonoGame.Extended.csproj b/Source/MonoGame.Extended/MonoGame.Extended.csproj
index bf1c3198..f0b4d031 100644
--- a/Source/MonoGame.Extended/MonoGame.Extended.csproj
+++ b/Source/MonoGame.Extended/MonoGame.Extended.csproj
@@ -42,6 +42,10 @@
+
+
+
+
@@ -53,7 +57,7 @@
-
+
diff --git a/Source/MonoGame.Extended/Serialization/BaseTypeJsonConverter.cs b/Source/MonoGame.Extended/Serialization/BaseTypeJsonConverter.cs
new file mode 100644
index 00000000..24fbf3e7
--- /dev/null
+++ b/Source/MonoGame.Extended/Serialization/BaseTypeJsonConverter.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace MonoGame.Extended.Serialization
+{
+ public abstract class BaseTypeJsonConverter : JsonConverter
+ {
+ private readonly string _suffix;
+ private readonly Dictionary _baseTypes;
+
+ protected BaseTypeJsonConverter(IEnumerable supportedTypes, string suffix)
+ {
+ _suffix = suffix;
+ _baseTypes = supportedTypes
+ .ToDictionary(t => TrimSuffix(t.Name, suffix), t => t.AsType(), StringComparer.OrdinalIgnoreCase);
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var jObject = JObject.Load(reader);
+ var key = jObject.GetValue("type", StringComparison.OrdinalIgnoreCase).ToObject();
+ Type type;
+
+ if (_baseTypes.TryGetValue(key, out type))
+ return jObject.ToObject(type, serializer);
+
+ throw new InvalidOperationException($"Unknown {_suffix} type '{key}'");
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(T);
+ }
+
+ private static string TrimSuffix(string input, string suffix)
+ {
+ if (input.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
+ return input.Substring(0, input.Length - suffix.Length);
+
+ return input;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended/Serialization/ITextureRegionService.cs b/Source/MonoGame.Extended/Serialization/ITextureRegionService.cs
deleted file mode 100644
index 8617f778..00000000
--- a/Source/MonoGame.Extended/Serialization/ITextureRegionService.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using MonoGame.Extended.TextureAtlases;
-
-namespace MonoGame.Extended.Serialization
-{
- public interface ITextureRegionService
- {
- TextureRegion2D GetTextureRegion(string name);
- }
-}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended/Serialization/JsonReaderExtensions.cs b/Source/MonoGame.Extended/Serialization/JsonReaderExtensions.cs
new file mode 100644
index 00000000..e48b5c60
--- /dev/null
+++ b/Source/MonoGame.Extended/Serialization/JsonReaderExtensions.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace MonoGame.Extended.Serialization
+{
+ public static class JsonReaderExtensions
+ {
+ private static readonly Dictionary> _stringParsers = new Dictionary>
+ {
+ {typeof(int), s => int.Parse(s, CultureInfo.InvariantCulture.NumberFormat)},
+ {typeof(float), s => float.Parse(s, CultureInfo.InvariantCulture.NumberFormat)},
+ };
+
+ public static T[] ReadAsMultiDimensional(this JsonReader reader)
+ {
+ var tokenType = reader.TokenType;
+
+ switch (tokenType)
+ {
+ case JsonToken.StartArray:
+ var jArray = JArray.Load(reader);
+ return jArray
+ .Select(i => i.Value())
+ .ToArray();
+
+ case JsonToken.String:
+ var value = (string)reader.Value;
+ var parser = _stringParsers[typeof(T)];
+ return value.Split(' ')
+ .Select(i => parser(i))
+ .Cast()
+ .ToArray();
+
+ case JsonToken.Integer:
+ case JsonToken.Float:
+ return new []{ JToken.Load(reader).ToObject() };
+
+ default:
+ throw new NotSupportedException($"{tokenType} is not currently supported in the multi dimensional parser");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended/Serialization/RangeJsonConverter.cs b/Source/MonoGame.Extended/Serialization/RangeJsonConverter.cs
new file mode 100644
index 00000000..ddf0cc36
--- /dev/null
+++ b/Source/MonoGame.Extended/Serialization/RangeJsonConverter.cs
@@ -0,0 +1,30 @@
+using System;
+using Newtonsoft.Json;
+
+namespace MonoGame.Extended.Serialization
+{
+ public class RangeJsonConverter : JsonConverter where T : IComparable
+ {
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var values = reader.ReadAsMultiDimensional();
+
+ if (values.Length == 2)
+ return new Range(values[0], values[1]);
+
+ if (values.Length == 1)
+ return new Range(values[0], values[0]);
+
+ throw new InvalidOperationException("Invalid range");
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(Range);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended.Gui/Serialization/GuiJsonContractResolver.cs b/Source/MonoGame.Extended/Serialization/ShortNameJsonContractResolver.cs
similarity index 85%
rename from Source/MonoGame.Extended.Gui/Serialization/GuiJsonContractResolver.cs
rename to Source/MonoGame.Extended/Serialization/ShortNameJsonContractResolver.cs
index 3b2fe01d..38708a37 100644
--- a/Source/MonoGame.Extended.Gui/Serialization/GuiJsonContractResolver.cs
+++ b/Source/MonoGame.Extended/Serialization/ShortNameJsonContractResolver.cs
@@ -4,15 +4,15 @@ using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
-namespace MonoGame.Extended.Gui.Serialization
+namespace MonoGame.Extended.Serialization
{
- public class GuiJsonContractResolver : DefaultContractResolver
+ public class ShortNameJsonContractResolver : DefaultContractResolver
{
protected override IList CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
- if (type.GetTypeInfo().IsAbstract) //.IsSubclassOf(typeof(GuiControl)))
+ if (type.GetTypeInfo().IsAbstract)
{
properties.Insert(0, new JsonProperty
{
diff --git a/Source/MonoGame.Extended/Serialization/Size2JsonConverter.cs b/Source/MonoGame.Extended/Serialization/Size2JsonConverter.cs
index e23fc6bd..78701999 100644
--- a/Source/MonoGame.Extended/Serialization/Size2JsonConverter.cs
+++ b/Source/MonoGame.Extended/Serialization/Size2JsonConverter.cs
@@ -1,5 +1,4 @@
using System;
-using System.Globalization;
using Newtonsoft.Json;
namespace MonoGame.Extended.Serialization
@@ -15,11 +14,15 @@ namespace MonoGame.Extended.Serialization
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
- var value = (string) reader.Value;
- var fields = value.Split(' ');
- var width = float.Parse(fields[0], CultureInfo.InvariantCulture.NumberFormat);
- var height = float.Parse(fields[1], CultureInfo.InvariantCulture.NumberFormat);
- return new Size2(width, height);
+ var values = reader.ReadAsMultiDimensional();
+
+ if(values.Length == 2)
+ return new Size2(values[0], values[1]);
+
+ if (values.Length == 1)
+ return new Size2(values[0], values[1]);
+
+ throw new InvalidOperationException("Invalid size");
}
public override bool CanConvert(Type objectType)
diff --git a/Source/MonoGame.Extended/Serialization/TextureRegionService.cs b/Source/MonoGame.Extended/Serialization/TextureRegionService.cs
new file mode 100644
index 00000000..66ef6faf
--- /dev/null
+++ b/Source/MonoGame.Extended/Serialization/TextureRegionService.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using System.Linq;
+using MonoGame.Extended.TextureAtlases;
+
+namespace MonoGame.Extended.Serialization
+{
+ public interface ITextureRegionService
+ {
+ IList TextureAtlases { get; }
+ IList NinePatches { get; }
+
+ TextureRegion2D GetTextureRegion(string name);
+ }
+
+ public class TextureRegionService : ITextureRegionService
+ {
+ public TextureRegionService()
+ {
+ TextureAtlases = new List();
+ NinePatches = new List();
+ }
+
+ public IList TextureAtlases { get; }
+ public IList NinePatches { get; }
+
+ public TextureRegion2D GetTextureRegion(string name)
+ {
+ var ninePatch = NinePatches.FirstOrDefault(p => p.Name == name);
+
+ if (ninePatch != null)
+ return ninePatch;
+
+ return TextureAtlases
+ .Select(textureAtlas => textureAtlas.GetRegion(name))
+ .FirstOrDefault(region => region != null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/MonoGame.Extended/Serialization/Vector2JsonConverter.cs b/Source/MonoGame.Extended/Serialization/Vector2JsonConverter.cs
index d536520e..0f4f9cda 100644
--- a/Source/MonoGame.Extended/Serialization/Vector2JsonConverter.cs
+++ b/Source/MonoGame.Extended/Serialization/Vector2JsonConverter.cs
@@ -1,5 +1,4 @@
using System;
-using System.Globalization;
using Microsoft.Xna.Framework;
using Newtonsoft.Json;
@@ -16,11 +15,15 @@ namespace MonoGame.Extended.Serialization
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
- var value = (string) reader.Value;
- var fields = value.Split(' ');
- var x = float.Parse(fields[0], CultureInfo.InvariantCulture.NumberFormat);
- var y = float.Parse(fields[1], CultureInfo.InvariantCulture.NumberFormat);
- return new Vector2(x, y);
+ var values = reader.ReadAsMultiDimensional();
+
+ if(values.Length == 2)
+ return new Vector2(values[0], values[1]);
+
+ if (values.Length == 1)
+ return new Vector2(values[0]);
+
+ throw new InvalidOperationException("Invalid Vector2");
}
public override bool CanConvert(Type objectType)
diff --git a/Source/Tests/Sandbox/Content/particle-effect.json b/Source/Tests/Sandbox/Content/particle-effect.json
new file mode 100644
index 00000000..74027a9a
--- /dev/null
+++ b/Source/Tests/Sandbox/Content/particle-effect.json
@@ -0,0 +1,40 @@
+{
+ "name": "test",
+ "emitters": [
+ {
+ "textureRegion": "checkBox_beige_unchecked",
+ "capacity": 500,
+ "term": 2.5,
+ "ModifierExecutionStrategy": "Parallel",
+ "profile": {
+ "type": "box",
+ "width": 700,
+ "height": 380
+ },
+ "parameters": {
+ "quantity": [ 3, 4 ],
+ "speed": [ 0, 10 ],
+ "rotation": [ -1, 1 ],
+ "scale": [ 13, 14 ]
+ },
+ "modifiers": [
+ {
+ "type": "linearGravity",
+ "direction": [ 0, 1 ],
+ "strength": 10
+ },
+ { "type": "opacityFastFade" },
+ {
+ "type": "age",
+ "interpolators": [
+ {
+ "type": "Color",
+ "startValue": "#ff0000",
+ "endValue": "#00ff00"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/Source/Tests/Sandbox/Game1.cs b/Source/Tests/Sandbox/Game1.cs
index d9e3e279..0901bd6b 100644
--- a/Source/Tests/Sandbox/Game1.cs
+++ b/Source/Tests/Sandbox/Game1.cs
@@ -1,9 +1,19 @@
-using Microsoft.Xna.Framework;
+using System;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using MonoGame.Extended;
using MonoGame.Extended.Gui;
using MonoGame.Extended.Gui.Controls;
+using MonoGame.Extended.Particles;
+using MonoGame.Extended.Particles.Modifiers;
+using MonoGame.Extended.Particles.Modifiers.Containers;
+using MonoGame.Extended.Particles.Modifiers.Interpolators;
+using MonoGame.Extended.Particles.Profiles;
+using MonoGame.Extended.Serialization;
+using MonoGame.Extended.TextureAtlases;
using MonoGame.Extended.ViewportAdapters;
+using Newtonsoft.Json;
// The Sandbox project is used for experiementing outside the normal demos.
// Any code found here should be considered experimental work in progress.
@@ -17,6 +27,9 @@ namespace Sandbox
private Camera2D _camera;
private GuiSystem _guiSystem;
+ private SpriteBatch _spriteBatch;
+ private ParticleEffect _particleEffect;
+
public Game1()
{
_graphicsDeviceManager = new GraphicsDeviceManager(this);
@@ -39,28 +52,90 @@ namespace Sandbox
{
Controls =
{
- new GuiLabel { Text = "Hello World" }
+ //new GuiLabel { Text = "Hello World" }
}
}
};
+ _spriteBatch = new SpriteBatch(GraphicsDevice);
+
+ var particleTexture = new Texture2D(GraphicsDevice, 1, 1);
+ particleTexture.SetData(new[] { Color.White });
+
+ var textureRegionService = new TextureRegionService();
+ textureRegionService.TextureAtlases.Add(Content.Load("adventure-gui-atlas"));
+
+ _particleEffect = ParticleEffect.FromFile(textureRegionService, @"Content/particle-effect.json");
+
+ //ParticleInit(new TextureRegion2D(particleTexture));
}
protected override void Update(GameTime gameTime)
{
+ var deltaTime = gameTime.GetElapsedSeconds();
var keyboardState = Keyboard.GetState();
+ var mouseState = Mouse.GetState();
+ var p = _camera.ScreenToWorld(mouseState.X, mouseState.Y);
if (keyboardState.IsKeyDown(Keys.Escape))
Exit();
+ _particleEffect.Update(deltaTime);
+
+ if (mouseState.LeftButton == ButtonState.Pressed)
+ _particleEffect.Trigger(new Vector2(p.X, p.Y));
+
+ _particleEffect.Trigger(new Vector2(400, 240));
+
_guiSystem.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
- GraphicsDevice.Clear(Color.CornflowerBlue);
+ GraphicsDevice.Clear(Color.Black);
+
+ _spriteBatch.Begin(blendState: BlendState.AlphaBlend, transformMatrix: _camera.GetViewMatrix());
+ _spriteBatch.Draw(_particleEffect);
+ _spriteBatch.End();
_guiSystem.Draw(gameTime);
}
+
+ private ParticleEffect ParticleInit(TextureRegion2D textureRegion)
+ {
+ return _particleEffect = new ParticleEffect
+ {
+ Emitters = new[]
+ {
+ new ParticleEmitter(textureRegion, 500, TimeSpan.FromSeconds(2.5), Profile.Ring(150f, Profile.CircleRadiation.In))
+ {
+ Parameters = new ParticleReleaseParameters
+ {
+ Speed = new Range(0f, 50f),
+ Quantity = 3,
+ Rotation = new Range(-1f, 1f),
+ Scale = new Range(3.0f, 4.0f)
+ },
+ Modifiers = new IModifier[]
+ {
+ new AgeModifier
+ {
+ Interpolators = new IInterpolator[]
+ {
+ new ColorInterpolator
+ {
+ InitialColor = new HslColor(0.33f, 0.5f, 0.5f),
+ FinalColor = new HslColor(0.5f, 0.9f, 1.0f)
+ }
+ }
+ },
+ new RotationModifier {RotationRate = -2.1f},
+ new RectangleContainerModifier {Width = 800, Height = 480},
+ new LinearGravityModifier {Direction = -Vector2.UnitY, Strength = 30f}
+ }
+ }
+ }
+ };
+ }
}
}
diff --git a/Source/Tests/Sandbox/Sandbox.csproj b/Source/Tests/Sandbox/Sandbox.csproj
index 6c8134e8..a5071b4b 100644
--- a/Source/Tests/Sandbox/Sandbox.csproj
+++ b/Source/Tests/Sandbox/Sandbox.csproj
@@ -50,6 +50,9 @@
$(MonoGameInstallDirectory)\MonoGame\v3.0\Assemblies\DesktopGL\MonoGame.Framework.dll
+
+ ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
+
@@ -108,13 +111,21 @@
PreserveNewest
+
+ PreserveNewest
+
+
{8e425575-378a-4f83-88c9-f8bebad227c9}
MonoGame.Extended.Gui
+
+ {6c8b9e29-d09b-4901-80fd-45aaa35882c6}
+ MonoGame.Extended.Particles
+
{41724c52-3d50-45bb-81eb-3c8a247eafd1}
MonoGame.Extended
diff --git a/Source/Tests/Sandbox/packages.config b/Source/Tests/Sandbox/packages.config
new file mode 100644
index 00000000..e1fae9c6
--- /dev/null
+++ b/Source/Tests/Sandbox/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file