using System; using System.IO; using System.Xml; 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; /// /// Represents a writer that serializes a to an XML configuration. /// public class ParticleEffectWriter : IDisposable { private readonly XmlWriter _writer; /// /// Gets a value that indicates if this has been disposed of. /// public bool IsDisposed { get; private set; } /// /// Initializes a new instance of the class that writers to the writes to a file. /// /// The file path to write the XML to. public ParticleEffectWriter(string fileName) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.IndentChars = " "; settings.CloseOutput = true; settings.NewLineChars = "\n"; _writer = XmlWriter.Create(fileName, settings); } /// /// Initializes a new instance of the class that writes to a stream. /// /// The stream to write to. public ParticleEffectWriter(Stream stream) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.IndentChars = " "; settings.CloseOutput = false; settings.NewLineChars = "\n"; _writer = XmlWriter.Create(stream, settings); } /// ~ParticleEffectWriter() { Dispose(); } /// /// Write a to the XML output. /// /// The to serialize. /// is . public void WriteParticleEffect(ParticleEffect effect) { ArgumentNullException.ThrowIfNull(effect); _writer.WriteStartDocument(); _writer.WriteStartElement("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(emitter); _writer.WriteEndElement(); } _writer.WriteEndElement(); } _writer.WriteEndElement(); _writer.WriteEndDocument(); _writer.Flush(); } private void WriteParticleEmitter(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(region); _writer.WriteEndElement(); } _writer.WriteStartElement(nameof(ParticleEmitter.Parameters)); WriteParticleReleaseParameters(emitter.Parameters); _writer.WriteEndElement(); _writer.WriteStartElement(nameof(ParticleEmitter.Profile)); WriteProfile(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(modifier); _writer.WriteEndElement(); } _writer.WriteEndElement(); } } private void WriteTexture2DRegion(Texture2DRegion region) { _writer.WriteAttributeString(nameof(Texture2DRegion.Texture.Name), region.Texture.Name); _writer.WriteAttributeRectangle(nameof(Texture2DRegion.Bounds), region.Bounds); } private void WriteParticleReleaseParameters(ParticleReleaseParameters parameters) { WriteParticleInt32Parameter(nameof(ParticleReleaseParameters.Quantity), parameters.Quantity); WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Speed), parameters.Speed); WriteParticleColorParameter(nameof(ParticleReleaseParameters.Color), parameters.Color); WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Opacity), parameters.Opacity); WriteParticleVector2Parameter(nameof(ParticleReleaseParameters.Scale), parameters.Scale); WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Rotation), parameters.Rotation); WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Mass), parameters.Mass); } private void WriteParticleInt32Parameter(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 void WriteParticleFloatParameter(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 void WriteParticleVector2Parameter(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 void WriteParticleColorParameter(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 void WriteProfile(Profile profile) { switch (profile) { case BoxFillProfile boxFillProfile: _writer.WriteAttributeString(nameof(Type), nameof(BoxFillProfile)); _writer.WriteAttributeFloat(nameof(BoxFillProfile.Width), boxFillProfile.Width); _writer.WriteAttributeFloat(nameof(BoxFillProfile.Height), boxFillProfile.Height); break; case BoxProfile boxProfile: _writer.WriteAttributeString(nameof(Type), nameof(BoxProfile)); _writer.WriteAttributeFloat(nameof(BoxProfile.Width), boxProfile.Width); _writer.WriteAttributeFloat(nameof(BoxProfile.Height), boxProfile.Height); break; case BoxUniformProfile boxUniformProfile: _writer.WriteAttributeString(nameof(Type), nameof(BoxUniformProfile)); _writer.WriteAttributeFloat(nameof(BoxUniformProfile.Width), boxUniformProfile.Width); _writer.WriteAttributeFloat(nameof(BoxUniformProfile.Height), boxUniformProfile.Height); break; case CircleProfile circleProfile: _writer.WriteAttributeString(nameof(Type), nameof(CircleProfile)); _writer.WriteAttributeFloat(nameof(CircleProfile.Radius), circleProfile.Radius); _writer.WriteAttributeString(nameof(CircleProfile.Radiate), circleProfile.Radiate.ToString()); break; case LineProfile lineProfile: _writer.WriteAttributeString(nameof(Type), nameof(LineProfile)); _writer.WriteAttributeVector2(nameof(LineProfile.Axis), lineProfile.Axis); _writer.WriteAttributeFloat(nameof(LineProfile.Length), lineProfile.Length); break; case PointProfile: _writer.WriteAttributeString(nameof(Type), nameof(PointProfile)); break; case RingProfile ringProfile: _writer.WriteAttributeString(nameof(Type), nameof(RingProfile)); _writer.WriteAttributeFloat(nameof(RingProfile.Radius), ringProfile.Radius); _writer.WriteAttributeString(nameof(RingProfile.Radiate), ringProfile.Radiate.ToString()); break; case SprayProfile sprayProfile: _writer.WriteAttributeString(nameof(Type), nameof(SprayProfile)); _writer.WriteAttributeVector2(nameof(SprayProfile.Direction), sprayProfile.Direction); _writer.WriteAttributeFloat(nameof(SprayProfile.Spread), sprayProfile.Spread); break; default: _writer.WriteAttributeString(nameof(Type), nameof(PointProfile)); break; } } private void WriteModifier(Modifier modifier) { _writer.WriteAttributeString(nameof(Modifier.Name), modifier.Name); _writer.WriteAttributeFloat(nameof(Modifier.Frequency), modifier.Frequency); switch (modifier) { case AgeModifier ageModifier: _writer.WriteAttributeString(nameof(Type), nameof(AgeModifier)); if (ageModifier.Interpolators.Count > 0) { _writer.WriteStartElement(nameof(AgeModifier.Interpolators)); foreach (Interpolator interpolator in ageModifier.Interpolators) { _writer.WriteStartElement(nameof(Interpolator)); WriteInterpolator(interpolator); _writer.WriteEndElement(); } _writer.WriteEndElement(); } break; case CircleContainerModifier circleContainerModifier: _writer.WriteAttributeString(nameof(Type), nameof(CircleContainerModifier)); _writer.WriteAttributeFloat(nameof(CircleContainerModifier.Radius), circleContainerModifier.Radius); _writer.WriteAttributeBool(nameof(CircleContainerModifier.Inside), circleContainerModifier.Inside); _writer.WriteAttributeFloat(nameof(CircleContainerModifier.RestitutionCoefficient), circleContainerModifier.RestitutionCoefficient); break; case DragModifier dragModifier: _writer.WriteAttributeString(nameof(Type), nameof(DragModifier)); _writer.WriteAttributeFloat(nameof(DragModifier.DragCoefficient), dragModifier.DragCoefficient); _writer.WriteAttributeFloat(nameof(DragModifier.Density), dragModifier.Density); break; case LinearGravityModifier linearGravityModifier: _writer.WriteAttributeString(nameof(Type), nameof(LinearGravityModifier)); _writer.WriteAttributeVector2(nameof(LinearGravityModifier.Direction), linearGravityModifier.Direction); _writer.WriteAttributeFloat(nameof(LinearGravityModifier.Strength), linearGravityModifier.Strength); break; case OpacityFastFadeModifier: _writer.WriteAttributeString(nameof(Type), nameof(OpacityFastFadeModifier)); break; case RectangleContainerModifier rectangleContainerModifier: _writer.WriteAttributeString(nameof(Type), nameof(RectangleContainerModifier)); _writer.WriteAttributeInt(nameof(RectangleContainerModifier.Width), rectangleContainerModifier.Width); _writer.WriteAttributeInt(nameof(RectangleContainerModifier.Height), rectangleContainerModifier.Height); _writer.WriteAttributeFloat(nameof(RectangleContainerModifier.RestitutionCoefficient), rectangleContainerModifier.RestitutionCoefficient); break; case RectangleLoopContainerModifier rectangleLoopContainerModifier: _writer.WriteAttributeString(nameof(Type), nameof(RectangleLoopContainerModifier)); _writer.WriteAttributeInt(nameof(RectangleLoopContainerModifier.Width), rectangleLoopContainerModifier.Width); _writer.WriteAttributeInt(nameof(RectangleLoopContainerModifier.Height), rectangleLoopContainerModifier.Height); break; case RotationModifier rotationModifier: _writer.WriteAttributeString(nameof(Type), nameof(RotationModifier)); _writer.WriteAttributeFloat(nameof(RotationModifier.RotationRate), rotationModifier.RotationRate); break; case VelocityColorModifier velocityColorModifier: _writer.WriteAttributeString(nameof(Type), nameof(VelocityColorModifier)); _writer.WriteAttributeVector3(nameof(VelocityColorModifier.StationaryColor), velocityColorModifier.StationaryColor); _writer.WriteAttributeVector3(nameof(VelocityColorModifier.VelocityColor), velocityColorModifier.VelocityColor); _writer.WriteAttributeFloat(nameof(VelocityColorModifier.VelocityThreshold), velocityColorModifier.VelocityThreshold); break; case VelocityModifier velocityModifier: _writer.WriteAttributeString(nameof(Type), nameof(VelocityModifier)); _writer.WriteAttributeFloat(nameof(VelocityModifier.VelocityThreshold), velocityModifier.VelocityThreshold); if (velocityModifier.Interpolators.Count > 0) { _writer.WriteStartElement(nameof(VelocityModifier.Interpolators)); foreach (Interpolator interpolator in velocityModifier.Interpolators) { _writer.WriteStartElement(nameof(Interpolator)); WriteInterpolator(interpolator); _writer.WriteEndElement(); } _writer.WriteEndElement(); } break; case VortexModifier vortexModifier: _writer.WriteAttributeString(nameof(Type), nameof(VortexModifier)); _writer.WriteAttributeVector2(nameof(VortexModifier.Position), vortexModifier.Position); _writer.WriteAttributeFloat(nameof(VortexModifier.Strength), vortexModifier.Strength); _writer.WriteAttributeFloat(nameof(VortexModifier.OuterRadius), vortexModifier.OuterRadius); _writer.WriteAttributeFloat(nameof(VortexModifier.InnerRadius), vortexModifier.InnerRadius); _writer.WriteAttributeFloat(nameof(VortexModifier.MaxVelocity), vortexModifier.MaxVelocity); _writer.WriteAttributeFloat(nameof(VortexModifier.RotationAngle), vortexModifier.RotationAngle); break; default: _writer.WriteAttributeString(nameof(Type), "Unknown"); break; } } private void WriteInterpolator(Interpolator interpolator) { _writer.WriteAttributeString(nameof(interpolator.Name), interpolator.Name); switch (interpolator) { case ColorInterpolator colorInterpolator: _writer.WriteAttributeString(nameof(Type), nameof(ColorInterpolator)); _writer.WriteAttributeVector3(nameof(ColorInterpolator.StartValue), colorInterpolator.StartValue); _writer.WriteAttributeVector3(nameof(ColorInterpolator.EndValue), colorInterpolator.EndValue); break; case HueInterpolator hueInterpolator: _writer.WriteAttributeString(nameof(Type), nameof(HueInterpolator)); _writer.WriteAttributeFloat(nameof(HueInterpolator.StartValue), hueInterpolator.StartValue); _writer.WriteAttributeFloat(nameof(HueInterpolator.EndValue), hueInterpolator.EndValue); break; case OpacityInterpolator opacityInterpolator: _writer.WriteAttributeString(nameof(Type), nameof(OpacityInterpolator)); _writer.WriteAttributeFloat(nameof(OpacityInterpolator.StartValue), opacityInterpolator.StartValue); _writer.WriteAttributeFloat(nameof(OpacityInterpolator.EndValue), opacityInterpolator.EndValue); break; case RotationInterpolator rotationInterpolator: _writer.WriteAttributeString(nameof(Type), nameof(RotationInterpolator)); _writer.WriteAttributeFloat(nameof(RotationInterpolator.StartValue), rotationInterpolator.StartValue); _writer.WriteAttributeFloat(nameof(RotationInterpolator.EndValue), rotationInterpolator.EndValue); break; case ScaleInterpolator scaleInterpolator: _writer.WriteAttributeString(nameof(Type), nameof(ScaleInterpolator)); _writer.WriteAttributeVector2(nameof(ScaleInterpolator.StartValue), scaleInterpolator.StartValue); _writer.WriteAttributeVector2(nameof(ScaleInterpolator.EndValue), scaleInterpolator.EndValue); break; case VelocityInterpolator velocityInterpolator: _writer.WriteAttributeString(nameof(Type), nameof(VelocityInterpolator)); _writer.WriteAttributeVector2(nameof(VelocityInterpolator.StartValue), velocityInterpolator.StartValue); _writer.WriteAttributeVector2(nameof(VelocityInterpolator.EndValue), velocityInterpolator.EndValue); break; default: _writer.WriteAttributeString(nameof(Type), "Unknown"); break; } } /// public void Dispose() { if (IsDisposed) { return; } _writer.Dispose(); GC.SuppressFinalize(this); IsDisposed = true; } }