using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using MonoGame.Extended.Graphics;
using MonoGame.Extended.Particles.Data;
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.Xml;
namespace MonoGame.Extended.Particles;
///
/// Provides static methods for serializing and deserializing instances to and from XML.
///
public static class ParticleEffectSerializer
{
#region Deserialize
///
/// Deserializes a from an XML file.
///
/// The file path to read the XML from
/// The to use for loading texture.
/// The deserialized .
///
/// Thrown when or is .
///
/// Thrown when is empty.
/// Thrown when the XMl format is invalid.
public static ParticleEffect Deserialize(string fileName, ContentManager content)
{
ArgumentNullException.ThrowIfNull(content);
ArgumentException.ThrowIfNullOrEmpty(fileName);
XmlReaderSettings settings = new XmlReaderSettings();
settings.CloseInput = true;
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
string fullPath = Path.GetFullPath(fileName);
string baseDirectory = Path.GetDirectoryName(fullPath);
using XmlReader reader = XmlReader.Create(fileName, settings);
return Deserialize(reader, content, baseDirectory);
}
///
/// Deserializes a from a stream containing XML data.
///
/// The stream to read from.
/// The to use for loading textures.
///
/// The base directory to use for resolving relative texture paths.
/// If , uses the property of .
///
/// The deserialized .
///
/// Thrown when or are .
///
/// Thrown when the XMl format is invalid.
public static ParticleEffect Deserialize(Stream stream, ContentManager content, string baseDirectory = null)
{
ArgumentNullException.ThrowIfNull(stream);
ArgumentNullException.ThrowIfNull(content);
XmlReaderSettings settings = new XmlReaderSettings();
settings.CloseInput = false;
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
if (string.IsNullOrEmpty(baseDirectory))
{
baseDirectory = content.RootDirectory;
}
using XmlReader reader = XmlReader.Create(stream, settings);
return Deserialize(reader, content, baseDirectory);
}
private static ParticleEffect Deserialize(XmlReader reader, ContentManager content, string baseDirectory)
{
reader.MoveToContent();
if (reader.NodeType != XmlNodeType.Element || reader.LocalName != nameof(ParticleEffect))
{
throw new XmlException($"Expected {nameof(ParticleEffect)} root element");
}
string name = reader.GetAttribute(nameof(ParticleEffect.Name)) ?? nameof(ParticleEffect);
ParticleEffect effect = new ParticleEffect(name);
effect.Position = reader.GetAttributeVector2(nameof(ParticleEffect.Position), default);
effect.Rotation = reader.GetAttributeFloat(nameof(ParticleEffect.Rotation), default);
effect.Scale = reader.GetAttributeVector2(nameof(ParticleEffect.Scale), default);
effect.AutoTrigger = reader.GetAttributeBool(nameof(ParticleEffect.AutoTrigger), default);
effect.AutoTriggerFrequency = reader.GetAttributeFloat(nameof(ParticleEffect.AutoTriggerFrequency), default);
if (reader.ReadToDescendant(nameof(ParticleEffect.Emitters)))
{
if (reader.ReadToDescendant(nameof(ParticleEmitter)))
{
do
{
ParticleEmitter emitter = ReadParticleEmitter(reader, content, baseDirectory);
effect.Emitters.Add(emitter);
} while (reader.ReadToNextSibling(nameof(ParticleEmitter)));
}
}
return effect;
}
private static ParticleEmitter ReadParticleEmitter(XmlReader reader, ContentManager content, string baseDirectory)
{
int capacity = reader.GetAttributeInt(nameof(ParticleEmitter.Capacity), default);
ParticleEmitter emitter = new ParticleEmitter(capacity);
emitter.Name = reader.GetAttribute(nameof(ParticleEmitter.Name)) ?? nameof(ParticleEmitter);
emitter.LifeSpan = reader.GetAttributeFloat(nameof(ParticleEmitter.LifeSpan), default);
emitter.Offset = reader.GetAttributeVector2(nameof(ParticleEmitter.Offset), default);
emitter.LayerDepth = reader.GetAttributeFloat(nameof(ParticleEmitter.LayerDepth), default);
emitter.ReclaimFrequency = reader.GetAttributeFloat(nameof(ParticleEmitter.ReclaimFrequency), default);
string strategy = reader.GetAttribute(nameof(ParticleEmitter.ModifierExecutionStrategy));
if (strategy.Equals(nameof(ModifierExecutionStrategy.Serial)))
{
emitter.ModifierExecutionStrategy = ModifierExecutionStrategy.Serial;
}
else if (strategy.Equals(nameof(ModifierExecutionStrategy.Parallel)))
{
emitter.ModifierExecutionStrategy = ModifierExecutionStrategy.Parallel;
}
else
{
emitter.ModifierExecutionStrategy = ModifierExecutionStrategy.Serial;
}
emitter.RenderingOrder = reader.GetAttributeEnum(nameof(ParticleEmitter.RenderingOrder), default);
using XmlReader subtree = reader.ReadSubtree();
while (subtree.Read())
{
if (subtree.NodeType == XmlNodeType.Element)
{
switch (subtree.LocalName)
{
case nameof(ParticleEmitter.TextureRegion):
emitter.TextureRegion = ReadTexture2DRegion(subtree, content, baseDirectory);
break;
case nameof(ParticleEmitter.Parameters):
emitter.Parameters = ReadParticleReleaseParameters(subtree);
break;
case nameof(ParticleEmitter.Profile):
emitter.Profile = ReadProfile(subtree);
break;
case nameof(ParticleEmitter.Modifiers):
ReadModifiers(subtree, emitter.Modifiers);
break;
}
}
}
return emitter;
}
private static Texture2DRegion ReadTexture2DRegion(XmlReader reader, ContentManager content, string baseDirectory)
{
string name = reader.GetAttribute(nameof(Texture2DRegion.Texture.Name));
if (string.IsNullOrEmpty(name))
{
return null;
}
string path = Path.Combine(baseDirectory, name);
// Content manager will throw exception if the path given is a rooted path
if (Path.IsPathRooted(path))
{
path = Path.GetRelativePath(baseDirectory, path);
}
Texture2D texture = content.Load(path);
if(string.IsNullOrEmpty(texture.Name))
{
texture.Name = Path.GetFileName(path);
}
Rectangle bounds = reader.GetAttributeRectangle(nameof(Texture2DRegion.Bounds), default);
if (bounds.IsEmpty)
{
bounds = texture.Bounds;
}
return new Texture2DRegion(texture, bounds);
}
private static ParticleReleaseParameters ReadParticleReleaseParameters(XmlReader reader)
{
ParticleReleaseParameters parameters = new ParticleReleaseParameters();
using XmlReader subtree = reader.ReadSubtree();
while (subtree.Read())
{
if (subtree.NodeType == XmlNodeType.Element)
{
switch (subtree.LocalName)
{
case nameof(ParticleReleaseParameters.Quantity):
parameters.Quantity = ReadParticleInt32Parameter(subtree);
break;
case nameof(ParticleReleaseParameters.Speed):
parameters.Speed = ReadParticleFloatParameter(subtree);
break;
case nameof(ParticleReleaseParameters.Color):
parameters.Color = ReadParticleColorParameter(subtree);
break;
case nameof(ParticleReleaseParameters.Opacity):
parameters.Opacity = ReadParticleFloatParameter(subtree);
break;
case nameof(ParticleReleaseParameters.Scale):
parameters.Scale = ReadParticleVector2Parameter(subtree);
break;
case nameof(ParticleReleaseParameters.Rotation):
parameters.Rotation = ReadParticleFloatParameter(subtree);
break;
case nameof(ParticleReleaseParameters.Mass):
parameters.Mass = ReadParticleFloatParameter(subtree);
break;
}
}
}
return parameters;
}
private static ParticleInt32Parameter ReadParticleInt32Parameter(XmlReader reader)
{
ParticleValueKind kind = reader.GetAttributeEnum(nameof(ParticleInt32Parameter.Kind), default);
if (kind == ParticleValueKind.Constant)
{
int value = reader.GetAttributeInt(nameof(ParticleInt32Parameter.Constant), default);
return new ParticleInt32Parameter(value);
}
else if (kind == ParticleValueKind.Random)
{
int min = reader.GetAttributeInt(nameof(ParticleInt32Parameter.RandomMin), default);
int max = reader.GetAttributeInt(nameof(ParticleInt32Parameter.RandomMax), default);
return new ParticleInt32Parameter(min, max);
}
return new ParticleInt32Parameter(0);
}
private static ParticleFloatParameter ReadParticleFloatParameter(XmlReader reader)
{
ParticleValueKind kind = reader.GetAttributeEnum(nameof(ParticleFloatParameter.Kind), default);
if (kind == ParticleValueKind.Constant)
{
float value = reader.GetAttributeFloat(nameof(ParticleFloatParameter.Constant), default);
return new ParticleFloatParameter(value);
}
else if (kind == ParticleValueKind.Random)
{
float min = reader.GetAttributeFloat(nameof(ParticleFloatParameter.RandomMin), default);
float max = reader.GetAttributeFloat(nameof(ParticleFloatParameter.RandomMax), default);
return new ParticleFloatParameter(min, max);
}
return new ParticleFloatParameter(0);
}
private static ParticleVector2Parameter ReadParticleVector2Parameter(XmlReader reader)
{
ParticleValueKind kind = reader.GetAttributeEnum(nameof(ParticleVector2Parameter.Kind), default);
if (kind == ParticleValueKind.Constant)
{
Vector2 value = reader.GetAttributeVector2(nameof(ParticleVector2Parameter.Constant), default);
return new ParticleVector2Parameter(value);
}
else if (kind == ParticleValueKind.Random)
{
Vector2 min = reader.GetAttributeVector2(nameof(ParticleVector2Parameter.RandomMin), default);
Vector2 max = reader.GetAttributeVector2(nameof(ParticleVector2Parameter.RandomMax), default);
return new ParticleVector2Parameter(min, max);
}
return new ParticleVector2Parameter(Vector2.Zero);
}
private static ParticleColorParameter ReadParticleColorParameter(XmlReader reader)
{
ParticleValueKind kind = reader.GetAttributeEnum(nameof(ParticleColorParameter.Kind), default);
if (kind == ParticleValueKind.Constant)
{
Vector3 value = reader.GetAttributeVector3(nameof(ParticleColorParameter.Constant), default);
return new ParticleColorParameter(value);
}
else if (kind == ParticleValueKind.Random)
{
Vector3 min = reader.GetAttributeVector3(nameof(ParticleColorParameter.RandomMin), default);
Vector3 max = reader.GetAttributeVector3(nameof(ParticleColorParameter.RandomMax), default);
return new ParticleColorParameter(min, max);
}
return new ParticleColorParameter(Vector3.Zero);
}
private static Profile ReadProfile(XmlReader reader)
{
string type = reader.GetAttribute(nameof(Type));
return type switch
{
nameof(BoxProfile) => ReadBoxProfile(reader),
nameof(BoxFillProfile) => ReadBoxFillProfile(reader),
nameof(BoxUniformProfile) => ReadBoxUniformProfile(reader),
nameof(CircleProfile) => ReadCircleProfile(reader),
nameof(LineProfile) => ReadLineProfile(reader),
nameof(PointProfile) => ReadPointProfile(reader),
nameof(RingProfile) => ReadRingProfile(reader),
nameof(SprayProfile) => ReadSprayProfile(reader),
_ => ReadPointProfile(reader)
};
}
private static BoxProfile ReadBoxProfile(XmlReader reader)
{
float width = reader.GetAttributeFloat(nameof(BoxProfile.Width), default);
float height = reader.GetAttributeFloat(nameof(BoxProfile.Height), default);
return new BoxProfile { Width = width, Height = height };
}
private static BoxFillProfile ReadBoxFillProfile(XmlReader reader)
{
float width = reader.GetAttributeFloat(nameof(BoxFillProfile.Width), default);
float height = reader.GetAttributeFloat(nameof(BoxFillProfile.Height), default);
return new BoxFillProfile { Width = width, Height = height };
}
private static BoxUniformProfile ReadBoxUniformProfile(XmlReader reader)
{
float width = reader.GetAttributeFloat(nameof(BoxUniformProfile.Width), default);
float height = reader.GetAttributeFloat(nameof(BoxUniformProfile.Height), default);
return new BoxUniformProfile { Width = width, Height = height };
}
private static CircleProfile ReadCircleProfile(XmlReader reader)
{
float radius = reader.GetAttributeFloat(nameof(CircleProfile.Radius), default);
CircleRadiation radiation = reader.GetAttributeEnum(nameof(CircleProfile.Radiate), default);
return new CircleProfile { Radius = radius, Radiate = radiation };
}
private static LineProfile ReadLineProfile(XmlReader reader)
{
Vector2 axis = reader.GetAttributeVector2(nameof(LineProfile.Axis), default);
float length = reader.GetAttributeFloat(nameof(LineProfile.Length), default);
Vector2 direction = reader.GetAttributeVector2(nameof(LineProfile.Direction), default);
LineRadiation radiate = reader.GetAttributeEnum(nameof(LineProfile.Radiate), default);
return new LineProfile { Axis = axis, Length = length, Direction = direction, Radiate = radiate };
}
private static PointProfile ReadPointProfile(XmlReader reader)
{
return new PointProfile();
}
private static RingProfile ReadRingProfile(XmlReader reader)
{
float radius = reader.GetAttributeFloat(nameof(RingProfile.Radius), default);
CircleRadiation radiation = reader.GetAttributeEnum(nameof(RingProfile.Radiate), default);
return new RingProfile { Radius = radius, Radiate = radiation };
}
private static SprayProfile ReadSprayProfile(XmlReader reader)
{
Vector2 direction = reader.GetAttributeVector2(nameof(SprayProfile.Direction), default);
float spread = reader.GetAttributeFloat(nameof(SprayProfile.Spread), default);
return new SprayProfile { Direction = direction, Spread = spread };
}
private static void ReadModifiers(XmlReader reader, List modifiers)
{
using XmlReader subtree = reader.ReadSubtree();
while (subtree.Read())
{
if (subtree.NodeType == XmlNodeType.Element && subtree.LocalName == nameof(Modifier))
{
Modifier modifier = ReadModifier(subtree);
if (modifier != null)
{
modifiers.Add(modifier);
}
}
}
}
private static Modifier ReadModifier(XmlReader reader)
{
string type = reader.GetAttribute(nameof(Type));
string name = reader.GetAttribute(nameof(Modifier.Name));
float frequency = reader.GetAttributeFloat(nameof(Modifier.Frequency), default);
bool enabled = reader.GetAttributeBool(nameof(Modifier.Enabled), default);
Modifier modifier = type switch
{
nameof(AgeModifier) => ReadAgeModifier(reader),
nameof(DragModifier) => ReadDragModifier(reader),
nameof(LinearGravityModifier) => ReadLinearGravityModifier(reader),
nameof(OpacityFastFadeModifier) => new OpacityFastFadeModifier(),
nameof(RotationModifier) => ReadRotationModifier(reader),
nameof(VelocityColorModifier) => ReadVelocityColorModifier(reader),
nameof(VelocityModifier) => ReadVelocityModifier(reader),
nameof(VortexModifier) => ReadVortexModifier(reader),
nameof(CircleContainerModifier) => ReadCircleContainerModifier(reader),
nameof(RectangleContainerModifier) => ReadRectangleContainerModifier(reader),
nameof(RectangleLoopContainerModifier) => ReadRectangleLoopContainerModifier(reader),
_ => null
};
if (modifier != null)
{
modifier.Name = name;
modifier.Frequency = frequency;
modifier.Enabled = enabled;
}
return modifier;
}
private static AgeModifier ReadAgeModifier(XmlReader reader)
{
AgeModifier modifier = new AgeModifier();
using XmlReader subtree = reader.ReadSubtree();
while (subtree.Read())
{
if (subtree.NodeType == XmlNodeType.Element && subtree.LocalName == nameof(AgeModifier.Interpolators))
{
ReadInterpolators(subtree, modifier.Interpolators);
}
}
return modifier;
}
private static DragModifier ReadDragModifier(XmlReader reader)
{
float dragCoefficient = reader.GetAttributeFloat(nameof(DragModifier.DragCoefficient), default);
float density = reader.GetAttributeFloat(nameof(DragModifier.Density), default);
return new DragModifier() { DragCoefficient = dragCoefficient, Density = density };
}
private static LinearGravityModifier ReadLinearGravityModifier(XmlReader reader)
{
Vector2 direction = reader.GetAttributeVector2(nameof(LinearGravityModifier.Direction), default);
float strength = reader.GetAttributeFloat(nameof(LinearGravityModifier.Strength), default);
return new LinearGravityModifier() { Direction = direction, Strength = strength };
}
private static RotationModifier ReadRotationModifier(XmlReader reader)
{
float rotationRate = reader.GetAttributeFloat(nameof(RotationModifier.RotationRate), default);
return new RotationModifier() { RotationRate = rotationRate };
}
private static VelocityColorModifier ReadVelocityColorModifier(XmlReader reader)
{
Vector3 stationaryColorValue = reader.GetAttributeVector3(nameof(VelocityColorModifier.StationaryColor), default);
Vector3 velocityColorValue = reader.GetAttributeVector3(nameof(VelocityColorModifier.VelocityColor), default);
float velocityThreshold = reader.GetAttributeFloat(nameof(VelocityColorModifier.VelocityThreshold), default);
HslColor stationaryColor = new HslColor(stationaryColorValue.X, stationaryColorValue.Y, stationaryColorValue.Z);
HslColor velocityColor = new HslColor(velocityColorValue.X, velocityColorValue.Y, velocityColorValue.Z);
return new VelocityColorModifier() { StationaryColor = stationaryColor, VelocityColor = velocityColor, VelocityThreshold = velocityThreshold };
}
private static VelocityModifier ReadVelocityModifier(XmlReader reader)
{
float velocityThreshold = reader.GetAttributeFloat(nameof(VelocityModifier.VelocityThreshold), default);
VelocityModifier modifier = new VelocityModifier() { VelocityThreshold = velocityThreshold };
using XmlReader subtree = reader.ReadSubtree();
while (subtree.Read())
{
if (subtree.NodeType == XmlNodeType.Element && subtree.LocalName == nameof(VelocityModifier.Interpolators))
{
ReadInterpolators(subtree, modifier.Interpolators);
}
}
return modifier;
}
private static VortexModifier ReadVortexModifier(XmlReader reader)
{
Vector2 position = reader.GetAttributeVector2(nameof(VortexModifier.Position), default);
float strength = reader.GetAttributeFloat(nameof(VortexModifier.Strength), default);
float outerRadius = reader.GetAttributeFloat(nameof(VortexModifier.OuterRadius), default);
float innerRadius = reader.GetAttributeFloat(nameof(VortexModifier.InnerRadius), default);
float maxVelocity = reader.GetAttributeFloat(nameof(VortexModifier.MaxVelocity), default);
float rotationAngle = reader.GetAttributeFloat(nameof(VortexModifier.RotationAngle), default);
return new VortexModifier { Position = position, Strength = strength, OuterRadius = outerRadius, InnerRadius = innerRadius, MaxVelocity = maxVelocity, RotationAngle = rotationAngle };
}
private static CircleContainerModifier ReadCircleContainerModifier(XmlReader reader)
{
float radius = reader.GetAttributeFloat(nameof(CircleContainerModifier.Radius), default);
bool inside = reader.GetAttributeBool(nameof(CircleContainerModifier.Inside), default);
float restitutionCoefficient = reader.GetAttributeFloat(nameof(CircleContainerModifier.RestitutionCoefficient), default);
return new CircleContainerModifier() { Radius = radius, Inside = inside, RestitutionCoefficient = restitutionCoefficient };
}
private static RectangleContainerModifier ReadRectangleContainerModifier(XmlReader reader)
{
int width = reader.GetAttributeInt(nameof(RectangleContainerModifier.Width), default);
int height = reader.GetAttributeInt(nameof(RectangleContainerModifier.Height), default);
float restitutionCoefficient = reader.GetAttributeFloat(nameof(RectangleContainerModifier.RestitutionCoefficient), default);
return new RectangleContainerModifier() { Width = width, Height = height, RestitutionCoefficient = restitutionCoefficient };
}
private static RectangleLoopContainerModifier ReadRectangleLoopContainerModifier(XmlReader reader)
{
int width = reader.GetAttributeInt(nameof(RectangleLoopContainerModifier.Width), default);
int height = reader.GetAttributeInt(nameof(RectangleLoopContainerModifier.Height), default);
return new RectangleLoopContainerModifier() { Width = width, Height = height };
}
private static void ReadInterpolators(XmlReader reader, List interpolators)
{
using XmlReader subtree = reader.ReadSubtree();
while (subtree.Read())
{
if (subtree.NodeType == XmlNodeType.Element && subtree.LocalName == nameof(Interpolator))
{
Interpolator interpolator = ReadInterpolator(subtree);
if (interpolator != null)
{
interpolators.Add(interpolator);
}
}
}
}
private static Interpolator ReadInterpolator(XmlReader reader)
{
string type = reader.GetAttribute(nameof(Type));
string name = reader.GetAttribute(nameof(Interpolator.Name));
bool enabled = reader.GetAttributeBool(nameof(Interpolator.Enabled), default);
Interpolator interpolator = type switch
{
nameof(ColorInterpolator) => ReadColorInterpolator(reader),
nameof(HueInterpolator) => ReadHueInterpolator(reader),
nameof(OpacityInterpolator) => ReadOpacityInterpolator(reader),
nameof(RotationInterpolator) => ReadRotationInterpolator(reader),
nameof(ScaleInterpolator) => ReadScaleInterpolator(reader),
nameof(VelocityInterpolator) => ReadVelocityInterpolator(reader),
_ => null
};
if (interpolator != null)
{
interpolator.Name = name;
}
interpolator.Enabled = enabled;
return interpolator;
}
private static ColorInterpolator ReadColorInterpolator(XmlReader reader)
{
Vector3 start = reader.GetAttributeVector3(nameof(ColorInterpolator.StartValue), default);
Vector3 end = reader.GetAttributeVector3(nameof(ColorInterpolator.EndValue), default);
HslColor startValue = new HslColor(start.X, start.Y, start.Z);
HslColor endValue = new HslColor(end.X, end.Y, end.Z);
return new ColorInterpolator() { StartValue = startValue, EndValue = endValue };
}
private static HueInterpolator ReadHueInterpolator(XmlReader reader)
{
float startValue = reader.GetAttributeFloat(nameof(HueInterpolator.StartValue), default);
float endValue = reader.GetAttributeFloat(nameof(HueInterpolator.EndValue), default);
return new HueInterpolator() { StartValue = startValue, EndValue = endValue };
}
private static OpacityInterpolator ReadOpacityInterpolator(XmlReader reader)
{
float startValue = reader.GetAttributeFloat(nameof(OpacityInterpolator.StartValue), default);
float endValue = reader.GetAttributeFloat(nameof(OpacityInterpolator.EndValue), default);
return new OpacityInterpolator() { StartValue = startValue, EndValue = endValue };
}
private static RotationInterpolator ReadRotationInterpolator(XmlReader reader)
{
float startValue = reader.GetAttributeFloat(nameof(RotationInterpolator.StartValue), default);
float endValue = reader.GetAttributeFloat(nameof(RotationInterpolator.EndValue), default);
return new RotationInterpolator() { StartValue = startValue, EndValue = endValue };
}
private static ScaleInterpolator ReadScaleInterpolator(XmlReader reader)
{
Vector2 startValue = reader.GetAttributeVector2(nameof(ScaleInterpolator.StartValue), default);
Vector2 endValue = reader.GetAttributeVector2(nameof(ScaleInterpolator.EndValue), default);
return new ScaleInterpolator() { StartValue = startValue, EndValue = endValue };
}
private static VelocityInterpolator ReadVelocityInterpolator(XmlReader reader)
{
Vector2 startValue = reader.GetAttributeVector2(nameof(VelocityInterpolator.StartValue), default);
Vector2 endValue = reader.GetAttributeVector2(nameof(VelocityInterpolator.EndValue), default);
return new VelocityInterpolator() { StartValue = startValue, EndValue = endValue };
}
#endregion Deserialize
#region Serialize
///
/// Serializes a to an XML file.
///
/// The file path to write the XML to.
/// The to serialize.
/// Thrown when is an empty string.
///
/// Throw if either or are .
///
public static void Serialize(string fileName, ParticleEffect effect)
{
ArgumentException.ThrowIfNullOrEmpty(fileName);
ArgumentNullException.ThrowIfNull(effect);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = " ";
settings.CloseOutput = true;
settings.NewLineChars = "\n";
using XmlWriter writer = XmlWriter.Create(fileName, settings);
Serialize(writer, effect);
}
///
/// Serializes a to a stream as XML data.
///
/// The stream to write to.
/// The to serialize.
///
/// Throw if either or are .
///
public static void Serialize(Stream stream, ParticleEffect effect)
{
ArgumentNullException.ThrowIfNull(stream);
ArgumentNullException.ThrowIfNull(effect);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = " ";
settings.CloseOutput = false;
settings.NewLineChars = "\n";
using XmlWriter writer = XmlWriter.Create(stream, settings);
Serialize(writer, effect);
}
private static void Serialize(XmlWriter writer, ParticleEffect effect)
{
writer.WriteStartDocument();
writer.WriteStartElement(nameof(ParticleEffect));
writer.WriteAttributeString(nameof(ParticleEffect.Name), effect.Name);
writer.WriteAttributeVector2(nameof(ParticleEffect.Position), effect.Position);
writer.WriteAttributeFloat(nameof(ParticleEffect.Rotation), effect.Rotation);
writer.WriteAttributeVector2(nameof(ParticleEffect.Scale), effect.Scale);
writer.WriteAttributeBool(nameof(ParticleEffect.AutoTrigger), effect.AutoTrigger);
writer.WriteAttributeFloat(nameof(ParticleEffect.AutoTriggerFrequency), effect.AutoTriggerFrequency);
if (effect.Emitters.Count > 0)
{
writer.WriteStartElement(nameof(ParticleEffect.Emitters));
foreach (ParticleEmitter emitter in effect.Emitters)
{
writer.WriteStartElement(nameof(ParticleEmitter));
WriteParticleEmitter(writer, emitter);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
private static void WriteParticleEmitter(XmlWriter writer, ParticleEmitter emitter)
{
writer.WriteAttributeString(nameof(ParticleEmitter.Name), emitter.Name);
writer.WriteAttributeFloat(nameof(ParticleEmitter.LifeSpan), emitter.LifeSpan);
writer.WriteAttributeVector2(nameof(ParticleEmitter.Offset), emitter.Offset);
writer.WriteAttributeFloat(nameof(ParticleEmitter.LayerDepth), emitter.LayerDepth);
writer.WriteAttributeFloat(nameof(ParticleEmitter.ReclaimFrequency), emitter.ReclaimFrequency);
writer.WriteAttributeInt(nameof(ParticleEmitter.Capacity), emitter.Capacity);
writer.WriteAttributeString(nameof(ParticleEmitter.ModifierExecutionStrategy), emitter.ModifierExecutionStrategy.ToString());
writer.WriteAttributeString(nameof(ParticleEmitter.RenderingOrder), emitter.RenderingOrder.ToString());
if (emitter.TextureRegion is Texture2DRegion region)
{
writer.WriteStartElement(nameof(ParticleEmitter.TextureRegion));
WriteTexture2DRegion(writer, region);
writer.WriteEndElement();
}
writer.WriteStartElement(nameof(ParticleEmitter.Parameters));
WriteParticleReleaseParameters(writer, emitter.Parameters);
writer.WriteEndElement();
writer.WriteStartElement(nameof(ParticleEmitter.Profile));
WriteProfile(writer, emitter.Profile);
writer.WriteEndElement();
if (emitter.Modifiers.Count > 0)
{
writer.WriteStartElement(nameof(ParticleEmitter.Modifiers));
foreach (Modifier modifier in emitter.Modifiers)
{
writer.WriteStartElement(nameof(Modifier));
WriteModifier(writer, modifier);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
private static void WriteTexture2DRegion(XmlWriter writer, Texture2DRegion region)
{
writer.WriteAttributeString(nameof(Texture2DRegion.Texture.Name), region.Texture.Name);
writer.WriteAttributeRectangle(nameof(Texture2DRegion.Bounds), region.Bounds);
}
private static void WriteParticleReleaseParameters(XmlWriter writer, ParticleReleaseParameters parameters)
{
WriteParticleInt32Parameter(writer, nameof(ParticleReleaseParameters.Quantity), parameters.Quantity);
WriteParticleFloatParameter(writer, nameof(ParticleReleaseParameters.Speed), parameters.Speed);
WriteParticleColorParameter(writer, nameof(ParticleReleaseParameters.Color), parameters.Color);
WriteParticleFloatParameter(writer, nameof(ParticleReleaseParameters.Opacity), parameters.Opacity);
WriteParticleVector2Parameter(writer, nameof(ParticleReleaseParameters.Scale), parameters.Scale);
WriteParticleFloatParameter(writer, nameof(ParticleReleaseParameters.Rotation), parameters.Rotation);
WriteParticleFloatParameter(writer, nameof(ParticleReleaseParameters.Mass), parameters.Mass);
}
private static void WriteParticleInt32Parameter(XmlWriter writer, string name, ParticleInt32Parameter parameter)
{
writer.WriteStartElement(name);
writer.WriteAttributeString(nameof(ParticleInt32Parameter.Kind), parameter.Kind.ToString());
if (parameter.Kind == ParticleValueKind.Constant)
{
writer.WriteAttributeInt(nameof(ParticleInt32Parameter.Constant), parameter.Constant);
}
else
{
writer.WriteAttributeInt(nameof(ParticleInt32Parameter.RandomMin), parameter.RandomMin);
writer.WriteAttributeInt(nameof(ParticleInt32Parameter.RandomMax), parameter.RandomMax);
}
writer.WriteEndElement();
}
private static void WriteParticleFloatParameter(XmlWriter writer, string name, ParticleFloatParameter parameter)
{
writer.WriteStartElement(name);
writer.WriteAttributeString(nameof(ParticleFloatParameter.Kind), parameter.Kind.ToString());
if (parameter.Kind == ParticleValueKind.Constant)
{
writer.WriteAttributeFloat(nameof(ParticleFloatParameter.Constant), parameter.Constant);
}
else
{
writer.WriteAttributeFloat(nameof(ParticleFloatParameter.RandomMin), parameter.RandomMin);
writer.WriteAttributeFloat(nameof(ParticleFloatParameter.RandomMax), parameter.RandomMax);
}
writer.WriteEndElement();
}
private static void WriteParticleVector2Parameter(XmlWriter writer, string name, ParticleVector2Parameter parameter)
{
writer.WriteStartElement(name);
writer.WriteAttributeString(nameof(ParticleVector2Parameter.Kind), parameter.Kind.ToString());
if (parameter.Kind == ParticleValueKind.Constant)
{
writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.Constant), parameter.Constant);
}
else
{
writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.RandomMin), parameter.RandomMin);
writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.RandomMax), parameter.RandomMax);
}
writer.WriteEndElement();
}
private static void WriteParticleColorParameter(XmlWriter writer, string name, ParticleColorParameter parameter)
{
writer.WriteStartElement(name);
writer.WriteAttributeString(nameof(ParticleColorParameter.Kind), parameter.Kind.ToString());
if (parameter.Kind == ParticleValueKind.Constant)
{
writer.WriteAttributeVector3(nameof(ParticleColorParameter.Constant), parameter.Constant);
}
else
{
writer.WriteAttributeVector3(nameof(ParticleColorParameter.RandomMin), parameter.RandomMin);
writer.WriteAttributeVector3(nameof(ParticleColorParameter.RandomMax), parameter.RandomMax);
}
writer.WriteEndElement();
}
private static void WriteProfile(XmlWriter writer, Profile profile)
{
switch (profile)
{
case BoxFillProfile boxFillProfile:
WriteBoxFillProfile(writer, boxFillProfile);
break;
case BoxProfile boxProfile:
WriteBoxProfile(writer, boxProfile);
break;
case BoxUniformProfile boxUniformProfile:
WriteBoxUniformProfile(writer, boxUniformProfile);
break;
case CircleProfile circleProfile:
WriteCircleProfile(writer, circleProfile);
break;
case LineProfile lineProfile:
WriteLineProfile(writer, lineProfile);
break;
case PointProfile pointProfile:
WritePointProfile(writer, pointProfile);
break;
case RingProfile ringProfile:
WriteRingProfile(writer, ringProfile);
break;
case SprayProfile sprayProfile:
WriteSprayProfile(writer, sprayProfile);
break;
default:
writer.WriteAttributeString(nameof(Type), profile.GetType().ToString());
break;
}
}
private static void WriteBoxFillProfile(XmlWriter writer, BoxFillProfile profile)
{
writer.WriteAttributeString(nameof(Type), nameof(BoxFillProfile));
writer.WriteAttributeFloat(nameof(BoxFillProfile.Width), profile.Width);
writer.WriteAttributeFloat(nameof(BoxFillProfile.Height), profile.Height);
}
private static void WriteBoxProfile(XmlWriter writer, BoxProfile profile)
{
writer.WriteAttributeString(nameof(Type), nameof(BoxProfile));
writer.WriteAttributeFloat(nameof(BoxProfile.Width), profile.Width);
writer.WriteAttributeFloat(nameof(BoxProfile.Height), profile.Height);
}
private static void WriteBoxUniformProfile(XmlWriter writer, BoxUniformProfile profile)
{
writer.WriteAttributeString(nameof(Type), nameof(BoxUniformProfile));
writer.WriteAttributeFloat(nameof(BoxUniformProfile.Width), profile.Width);
writer.WriteAttributeFloat(nameof(BoxUniformProfile.Height), profile.Height);
}
private static void WriteCircleProfile(XmlWriter writer, CircleProfile profile)
{
writer.WriteAttributeString(nameof(Type), nameof(CircleProfile));
writer.WriteAttributeFloat(nameof(CircleProfile.Radius), profile.Radius);
writer.WriteAttributeString(nameof(CircleProfile.Radiate), profile.Radiate.ToString());
}
private static void WriteLineProfile(XmlWriter writer, LineProfile profile)
{
writer.WriteAttributeString(nameof(Type), nameof(LineProfile));
writer.WriteAttributeVector2(nameof(LineProfile.Axis), profile.Axis);
writer.WriteAttributeFloat(nameof(LineProfile.Length), profile.Length);
writer.WriteAttributeString(nameof(LineProfile.Radiate), profile.Radiate.ToString());
writer.WriteAttributeVector2(nameof(LineProfile.Direction), profile.Direction);
}
private static void WritePointProfile(XmlWriter writer, PointProfile profile)
{
writer.WriteAttributeString(nameof(Type), nameof(PointProfile));
}
private static void WriteRingProfile(XmlWriter writer, RingProfile profile)
{
writer.WriteAttributeString(nameof(Type), nameof(RingProfile));
writer.WriteAttributeFloat(nameof(RingProfile.Radius), profile.Radius);
writer.WriteAttributeString(nameof(RingProfile.Radiate), profile.Radiate.ToString());
}
private static void WriteSprayProfile(XmlWriter writer, SprayProfile profile)
{
writer.WriteAttributeString(nameof(Type), nameof(SprayProfile));
writer.WriteAttributeVector2(nameof(SprayProfile.Direction), profile.Direction);
writer.WriteAttributeFloat(nameof(SprayProfile.Spread), profile.Spread);
}
private static void WriteModifier(XmlWriter writer, Modifier modifier)
{
writer.WriteAttributeString(nameof(Modifier.Name), modifier.Name);
writer.WriteAttributeBool(nameof(Modifier.Enabled), modifier.Enabled);
writer.WriteAttributeFloat(nameof(Modifier.Frequency), modifier.Frequency);
switch (modifier)
{
case AgeModifier ageModifier:
WriteAgeModifier(writer, ageModifier);
break;
case CircleContainerModifier circleContainerModifier:
WriteCircleContainerModifier(writer, circleContainerModifier);
break;
case DragModifier dragModifier:
WriteDragModifier(writer, dragModifier);
break;
case LinearGravityModifier linearGravityModifier:
WriteLinearGravityModifier(writer, linearGravityModifier);
break;
case OpacityFastFadeModifier opacityFastFadeModifier:
WriteOpacityFastFadeModifier(writer, opacityFastFadeModifier);
break;
case RectangleContainerModifier rectangleContainerModifier:
WriteRectangleContainerModifier(writer, rectangleContainerModifier);
break;
case RectangleLoopContainerModifier rectangleLoopContainerModifier:
WriteRectangleLoopContainerModifier(writer, rectangleLoopContainerModifier);
break;
case RotationModifier rotationModifier:
WriteRotationModifier(writer, rotationModifier);
break;
case VelocityColorModifier velocityColorModifier:
WriteVelocityColorModifier(writer, velocityColorModifier);
break;
case VelocityModifier velocityModifier:
WriteVelocityModifier(writer, velocityModifier);
break;
case VortexModifier vortexModifier:
WriteVortexModifier(writer, vortexModifier);
break;
default:
writer.WriteAttributeString(nameof(Type), modifier.GetType().Name);
break;
}
}
private static void WriteAgeModifier(XmlWriter writer, AgeModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(AgeModifier));
if (modifier.Interpolators.Count > 0)
{
writer.WriteStartElement(nameof(AgeModifier.Interpolators));
foreach (Interpolator interpolator in modifier.Interpolators)
{
writer.WriteStartElement(nameof(Interpolator));
WriteInterpolator(writer, interpolator);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
private static void WriteCircleContainerModifier(XmlWriter writer, CircleContainerModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(CircleContainerModifier));
writer.WriteAttributeFloat(nameof(CircleContainerModifier.Radius), modifier.Radius);
writer.WriteAttributeBool(nameof(CircleContainerModifier.Inside), modifier.Inside);
writer.WriteAttributeFloat(nameof(CircleContainerModifier.RestitutionCoefficient), modifier.RestitutionCoefficient);
}
private static void WriteDragModifier(XmlWriter writer, DragModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(DragModifier));
writer.WriteAttributeFloat(nameof(DragModifier.DragCoefficient), modifier.DragCoefficient);
writer.WriteAttributeFloat(nameof(DragModifier.Density), modifier.Density);
}
private static void WriteLinearGravityModifier(XmlWriter writer, LinearGravityModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(LinearGravityModifier));
writer.WriteAttributeVector2(nameof(LinearGravityModifier.Direction), modifier.Direction);
writer.WriteAttributeFloat(nameof(LinearGravityModifier.Strength), modifier.Strength);
}
private static void WriteOpacityFastFadeModifier(XmlWriter writer, OpacityFastFadeModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(OpacityFastFadeModifier));
}
private static void WriteRectangleContainerModifier(XmlWriter writer, RectangleContainerModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(RectangleContainerModifier));
writer.WriteAttributeInt(nameof(RectangleContainerModifier.Width), modifier.Width);
writer.WriteAttributeInt(nameof(RectangleContainerModifier.Height), modifier.Height);
writer.WriteAttributeFloat(nameof(RectangleContainerModifier.RestitutionCoefficient), modifier.RestitutionCoefficient);
}
private static void WriteRectangleLoopContainerModifier(XmlWriter writer, RectangleLoopContainerModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(RectangleLoopContainerModifier));
writer.WriteAttributeInt(nameof(RectangleLoopContainerModifier.Width), modifier.Width);
writer.WriteAttributeInt(nameof(RectangleLoopContainerModifier.Height), modifier.Height);
}
private static void WriteRotationModifier(XmlWriter writer, RotationModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(RotationModifier));
writer.WriteAttributeFloat(nameof(RotationModifier.RotationRate), modifier.RotationRate);
}
private static void WriteVelocityColorModifier(XmlWriter writer, VelocityColorModifier modifier)
{
Vector3 stationaryColor = new Vector3(modifier.StationaryColor.H, modifier.StationaryColor.S, modifier.StationaryColor.L);
Vector3 velocityColor = new Vector3(modifier.VelocityColor.H, modifier.VelocityColor.S, modifier.VelocityColor.L);
writer.WriteAttributeString(nameof(Type), nameof(VelocityColorModifier));
writer.WriteAttributeVector3(nameof(VelocityColorModifier.StationaryColor), stationaryColor);
writer.WriteAttributeVector3(nameof(VelocityColorModifier.VelocityColor), velocityColor);
writer.WriteAttributeFloat(nameof(VelocityColorModifier.VelocityThreshold), modifier.VelocityThreshold);
}
private static void WriteVelocityModifier(XmlWriter writer, VelocityModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(VelocityModifier));
writer.WriteAttributeFloat(nameof(VelocityModifier.VelocityThreshold), modifier.VelocityThreshold);
if (modifier.Interpolators.Count > 0)
{
writer.WriteStartElement(nameof(VelocityModifier.Interpolators));
foreach (Interpolator interpolator in modifier.Interpolators)
{
writer.WriteStartElement(nameof(Interpolator));
WriteInterpolator(writer, interpolator);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
private static void WriteVortexModifier(XmlWriter writer, VortexModifier modifier)
{
writer.WriteAttributeString(nameof(Type), nameof(VortexModifier));
writer.WriteAttributeVector2(nameof(VortexModifier.Position), modifier.Position);
writer.WriteAttributeFloat(nameof(VortexModifier.Strength), modifier.Strength);
writer.WriteAttributeFloat(nameof(VortexModifier.OuterRadius), modifier.OuterRadius);
writer.WriteAttributeFloat(nameof(VortexModifier.InnerRadius), modifier.InnerRadius);
writer.WriteAttributeFloat(nameof(VortexModifier.MaxVelocity), modifier.MaxVelocity);
writer.WriteAttributeFloat(nameof(VortexModifier.RotationAngle), modifier.RotationAngle);
}
private static void WriteInterpolator(XmlWriter writer, Interpolator interpolator)
{
writer.WriteAttributeString(nameof(Interpolator.Name), interpolator.Name);
writer.WriteAttributeBool(nameof(Interpolator.Enabled), interpolator.Enabled);
switch (interpolator)
{
case ColorInterpolator colorInterpolator:
WriteColorInterpolator(writer, colorInterpolator);
break;
case HueInterpolator hueInterpolator:
WriteHueInterpolator(writer, hueInterpolator);
break;
case OpacityInterpolator opacityInterpolator:
WriteOpacityInterpolator(writer, opacityInterpolator);
break;
case RotationInterpolator rotationInterpolator:
WriteRotationInterpolator(writer, rotationInterpolator);
break;
case ScaleInterpolator scaleInterpolator:
WriteScaleInterpolator(writer, scaleInterpolator);
break;
case VelocityInterpolator velocityInterpolator:
WriteVelocityInterpolator(writer, velocityInterpolator);
break;
default:
writer.WriteAttributeString(nameof(Type), interpolator.GetType().Name);
break;
}
}
private static void WriteColorInterpolator(XmlWriter writer, ColorInterpolator interpolator)
{
Vector3 startValue = new Vector3(interpolator.StartValue.H, interpolator.StartValue.S, interpolator.StartValue.L);
Vector3 endValue = new Vector3(interpolator.EndValue.H, interpolator.EndValue.S, interpolator.EndValue.L);
writer.WriteAttributeString(nameof(Type), nameof(ColorInterpolator));
writer.WriteAttributeVector3(nameof(ColorInterpolator.StartValue), startValue);
writer.WriteAttributeVector3(nameof(ColorInterpolator.EndValue), endValue);
}
private static void WriteHueInterpolator(XmlWriter writer, HueInterpolator interpolator)
{
writer.WriteAttributeString(nameof(Type), nameof(HueInterpolator));
writer.WriteAttributeFloat(nameof(HueInterpolator.StartValue), interpolator.StartValue);
writer.WriteAttributeFloat(nameof(HueInterpolator.EndValue), interpolator.EndValue);
}
private static void WriteOpacityInterpolator(XmlWriter writer, OpacityInterpolator interpolator)
{
writer.WriteAttributeString(nameof(Type), nameof(OpacityInterpolator));
writer.WriteAttributeFloat(nameof(OpacityInterpolator.StartValue), interpolator.StartValue);
writer.WriteAttributeFloat(nameof(OpacityInterpolator.EndValue), interpolator.EndValue);
}
private static void WriteRotationInterpolator(XmlWriter writer, RotationInterpolator interpolator)
{
writer.WriteAttributeString(nameof(Type), nameof(RotationInterpolator));
writer.WriteAttributeFloat(nameof(RotationInterpolator.StartValue), interpolator.StartValue);
writer.WriteAttributeFloat(nameof(RotationInterpolator.EndValue), interpolator.EndValue);
}
private static void WriteScaleInterpolator(XmlWriter writer, ScaleInterpolator interpolator)
{
writer.WriteAttributeString(nameof(Type), nameof(ScaleInterpolator));
writer.WriteAttributeVector2(nameof(ScaleInterpolator.StartValue), interpolator.StartValue);
writer.WriteAttributeVector2(nameof(ScaleInterpolator.EndValue), interpolator.EndValue);
}
private static void WriteVelocityInterpolator(XmlWriter writer, VelocityInterpolator interpolator)
{
writer.WriteAttributeString(nameof(Type), nameof(VelocityInterpolator));
writer.WriteAttributeVector2(nameof(VelocityInterpolator.StartValue), interpolator.StartValue);
writer.WriteAttributeVector2(nameof(VelocityInterpolator.EndValue), interpolator.EndValue);
}
#endregion Serialize
}