ParticleEffectWriter.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. using System;
  2. using System.IO;
  3. using System.Xml;
  4. using Microsoft.Xna.Framework;
  5. using MonoGame.Extended.Graphics;
  6. using MonoGame.Extended.Particles.Data;
  7. using MonoGame.Extended.Particles.Modifiers;
  8. using MonoGame.Extended.Particles.Modifiers.Containers;
  9. using MonoGame.Extended.Particles.Modifiers.Interpolators;
  10. using MonoGame.Extended.Particles.Profiles;
  11. using MonoGame.Extended.Serialization.Xml;
  12. namespace MonoGame.Extended.Particles;
  13. /// <summary>
  14. /// Represents a writer that serializes a <see cref="ParticleEffect"/> to an XML configuration.
  15. /// </summary>
  16. public class ParticleEffectWriter : IDisposable
  17. {
  18. private readonly XmlWriter _writer;
  19. /// <summary>
  20. /// Gets a value that indicates if this <see cref="ParticleEffectWriter"/> has been disposed of.
  21. /// </summary>
  22. public bool IsDisposed { get; private set; }
  23. /// <summary>
  24. /// Initializes a new instance of the <see cref="ParticleEffectWriter"/> class that writers to the writes to a file.
  25. /// </summary>
  26. /// <param name="fileName">The file path to write the XML to.</param>
  27. public ParticleEffectWriter(string fileName)
  28. {
  29. XmlWriterSettings settings = new XmlWriterSettings();
  30. settings.Indent = true;
  31. settings.IndentChars = " ";
  32. settings.CloseOutput = true;
  33. settings.NewLineChars = "\n";
  34. _writer = XmlWriter.Create(fileName, settings);
  35. }
  36. /// <summary>
  37. /// Initializes a new instance of the <see cref="ParticleEffectWriter"/> class that writes to a stream.
  38. /// </summary>
  39. /// <param name="stream">The stream to write to.</param>
  40. public ParticleEffectWriter(Stream stream)
  41. {
  42. XmlWriterSettings settings = new XmlWriterSettings();
  43. settings.Indent = true;
  44. settings.IndentChars = " ";
  45. settings.CloseOutput = false;
  46. settings.NewLineChars = "\n";
  47. _writer = XmlWriter.Create(stream, settings);
  48. }
  49. /// <summary/>
  50. ~ParticleEffectWriter()
  51. {
  52. Dispose();
  53. }
  54. /// <summary>
  55. /// Write a <see cref="ParticleEffect"/> to the XML output.
  56. /// </summary>
  57. /// <param name="effect">The <see cref="ParticleEffect"/> to serialize.</param>
  58. /// <exception cref="ArgumentNullException"><paramref name="effect"/> is <see langword="null"/>.</exception>
  59. public void WriteParticleEffect(ParticleEffect effect)
  60. {
  61. ArgumentNullException.ThrowIfNull(effect);
  62. _writer.WriteStartDocument();
  63. _writer.WriteStartElement("ParticleEffect");
  64. _writer.WriteAttributeString(nameof(ParticleEffect.Name), effect.Name);
  65. _writer.WriteAttributeVector2(nameof(ParticleEffect.Position), effect.Position);
  66. _writer.WriteAttributeFloat(nameof(ParticleEffect.Rotation), effect.Rotation);
  67. _writer.WriteAttributeVector2(nameof(ParticleEffect.Scale), effect.Scale);
  68. _writer.WriteAttributeBool(nameof(ParticleEffect.AutoTrigger), effect.AutoTrigger);
  69. _writer.WriteAttributeFloat(nameof(ParticleEffect.AutoTriggerFrequency), effect.AutoTriggerFrequency);
  70. if (effect.Emitters.Count > 0)
  71. {
  72. _writer.WriteStartElement(nameof(ParticleEffect.Emitters));
  73. foreach (ParticleEmitter emitter in effect.Emitters)
  74. {
  75. _writer.WriteStartElement(nameof(ParticleEmitter));
  76. WriteParticleEmitter(emitter);
  77. _writer.WriteEndElement();
  78. }
  79. _writer.WriteEndElement();
  80. }
  81. _writer.WriteEndElement();
  82. _writer.WriteEndDocument();
  83. _writer.Flush();
  84. }
  85. private void WriteParticleEmitter(ParticleEmitter emitter)
  86. {
  87. _writer.WriteAttributeString(nameof(ParticleEmitter.Name), emitter.Name);
  88. _writer.WriteAttributeFloat(nameof(ParticleEmitter.LifeSpan), emitter.LifeSpan);
  89. _writer.WriteAttributeVector2(nameof(ParticleEmitter.Offset), emitter.Offset);
  90. _writer.WriteAttributeFloat(nameof(ParticleEmitter.LayerDepth), emitter.LayerDepth);
  91. _writer.WriteAttributeFloat(nameof(ParticleEmitter.ReclaimFrequency), emitter.ReclaimFrequency);
  92. _writer.WriteAttributeInt(nameof(ParticleEmitter.Capacity), emitter.Capacity);
  93. _writer.WriteAttributeString(nameof(ParticleEmitter.ModifierExecutionStrategy), emitter.ModifierExecutionStrategy.ToString());
  94. _writer.WriteAttributeString(nameof(ParticleEmitter.RenderingOrder), emitter.RenderingOrder.ToString());
  95. if (emitter.TextureRegion is Texture2DRegion region)
  96. {
  97. _writer.WriteStartElement(nameof(ParticleEmitter.TextureRegion));
  98. WriteTexture2DRegion(region);
  99. _writer.WriteEndElement();
  100. }
  101. _writer.WriteStartElement(nameof(ParticleEmitter.Parameters));
  102. WriteParticleReleaseParameters(emitter.Parameters);
  103. _writer.WriteEndElement();
  104. _writer.WriteStartElement(nameof(ParticleEmitter.Profile));
  105. WriteProfile(emitter.Profile);
  106. _writer.WriteEndElement();
  107. if (emitter.Modifiers.Count > 0)
  108. {
  109. _writer.WriteStartElement(nameof(ParticleEmitter.Modifiers));
  110. foreach (Modifier modifier in emitter.Modifiers)
  111. {
  112. _writer.WriteStartElement(nameof(Modifier));
  113. WriteModifier(modifier);
  114. _writer.WriteEndElement();
  115. }
  116. _writer.WriteEndElement();
  117. }
  118. }
  119. private void WriteTexture2DRegion(Texture2DRegion region)
  120. {
  121. _writer.WriteAttributeString(nameof(Texture2DRegion.Texture.Name), region.Texture.Name);
  122. _writer.WriteAttributeRectangle(nameof(Texture2DRegion.Bounds), region.Bounds);
  123. }
  124. private void WriteParticleReleaseParameters(ParticleReleaseParameters parameters)
  125. {
  126. WriteParticleInt32Parameter(nameof(ParticleReleaseParameters.Quantity), parameters.Quantity);
  127. WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Speed), parameters.Speed);
  128. WriteParticleColorParameter(nameof(ParticleReleaseParameters.Color), parameters.Color);
  129. WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Opacity), parameters.Opacity);
  130. WriteParticleVector2Parameter(nameof(ParticleReleaseParameters.Scale), parameters.Scale);
  131. WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Rotation), parameters.Rotation);
  132. WriteParticleFloatParameter(nameof(ParticleReleaseParameters.Mass), parameters.Mass);
  133. }
  134. private void WriteParticleInt32Parameter(string name, ParticleInt32Parameter parameter)
  135. {
  136. _writer.WriteStartElement(name);
  137. _writer.WriteAttributeString(nameof(ParticleInt32Parameter.Kind), parameter.Kind.ToString());
  138. if (parameter.Kind == ParticleValueKind.Constant)
  139. {
  140. _writer.WriteAttributeInt(nameof(ParticleInt32Parameter.Constant), parameter.Constant);
  141. }
  142. else
  143. {
  144. _writer.WriteAttributeInt(nameof(ParticleInt32Parameter.RandomMin), parameter.RandomMin);
  145. _writer.WriteAttributeInt(nameof(ParticleInt32Parameter.RandomMax), parameter.RandomMax);
  146. }
  147. _writer.WriteEndElement();
  148. }
  149. private void WriteParticleFloatParameter(string name, ParticleFloatParameter parameter)
  150. {
  151. _writer.WriteStartElement(name);
  152. _writer.WriteAttributeString(nameof(ParticleFloatParameter.Kind), parameter.Kind.ToString());
  153. if (parameter.Kind == ParticleValueKind.Constant)
  154. {
  155. _writer.WriteAttributeFloat(nameof(ParticleFloatParameter.Constant), parameter.Constant);
  156. }
  157. else
  158. {
  159. _writer.WriteAttributeFloat(nameof(ParticleFloatParameter.RandomMin), parameter.RandomMin);
  160. _writer.WriteAttributeFloat(nameof(ParticleFloatParameter.RandomMax), parameter.RandomMax);
  161. }
  162. _writer.WriteEndElement();
  163. }
  164. private void WriteParticleVector2Parameter(string name, ParticleVector2Parameter parameter)
  165. {
  166. _writer.WriteStartElement(name);
  167. _writer.WriteAttributeString(nameof(ParticleVector2Parameter.Kind), parameter.Kind.ToString());
  168. if(parameter.Kind == ParticleValueKind.Constant)
  169. {
  170. _writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.Constant), parameter.Constant);
  171. }
  172. else
  173. {
  174. _writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.RandomMin), parameter.RandomMin);
  175. _writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.RandomMax), parameter.RandomMax);
  176. }
  177. _writer.WriteEndElement();
  178. }
  179. private void WriteParticleColorParameter(string name, ParticleColorParameter parameter)
  180. {
  181. _writer.WriteStartElement(name);
  182. _writer.WriteAttributeString(nameof(ParticleColorParameter.Kind), parameter.Kind.ToString());
  183. if (parameter.Kind == ParticleValueKind.Constant)
  184. {
  185. _writer.WriteAttributeVector3(nameof(ParticleColorParameter.Constant), parameter.Constant);
  186. }
  187. else
  188. {
  189. _writer.WriteAttributeVector3(nameof(ParticleColorParameter.RandomMin), parameter.RandomMin);
  190. _writer.WriteAttributeVector3(nameof(ParticleColorParameter.RandomMax), parameter.RandomMax);
  191. }
  192. _writer.WriteEndElement();
  193. }
  194. private void WriteProfile(Profile profile)
  195. {
  196. switch (profile)
  197. {
  198. case BoxFillProfile boxFillProfile:
  199. _writer.WriteAttributeString(nameof(Type), nameof(BoxFillProfile));
  200. _writer.WriteAttributeFloat(nameof(BoxFillProfile.Width), boxFillProfile.Width);
  201. _writer.WriteAttributeFloat(nameof(BoxFillProfile.Height), boxFillProfile.Height);
  202. break;
  203. case BoxProfile boxProfile:
  204. _writer.WriteAttributeString(nameof(Type), nameof(BoxProfile));
  205. _writer.WriteAttributeFloat(nameof(BoxProfile.Width), boxProfile.Width);
  206. _writer.WriteAttributeFloat(nameof(BoxProfile.Height), boxProfile.Height);
  207. break;
  208. case BoxUniformProfile boxUniformProfile:
  209. _writer.WriteAttributeString(nameof(Type), nameof(BoxUniformProfile));
  210. _writer.WriteAttributeFloat(nameof(BoxUniformProfile.Width), boxUniformProfile.Width);
  211. _writer.WriteAttributeFloat(nameof(BoxUniformProfile.Height), boxUniformProfile.Height);
  212. break;
  213. case CircleProfile circleProfile:
  214. _writer.WriteAttributeString(nameof(Type), nameof(CircleProfile));
  215. _writer.WriteAttributeFloat(nameof(CircleProfile.Radius), circleProfile.Radius);
  216. _writer.WriteAttributeString(nameof(CircleProfile.Radiate), circleProfile.Radiate.ToString());
  217. break;
  218. case LineProfile lineProfile:
  219. _writer.WriteAttributeString(nameof(Type), nameof(LineProfile));
  220. _writer.WriteAttributeVector2(nameof(LineProfile.Axis), lineProfile.Axis);
  221. _writer.WriteAttributeFloat(nameof(LineProfile.Length), lineProfile.Length);
  222. break;
  223. case PointProfile:
  224. _writer.WriteAttributeString(nameof(Type), nameof(PointProfile));
  225. break;
  226. case RingProfile ringProfile:
  227. _writer.WriteAttributeString(nameof(Type), nameof(RingProfile));
  228. _writer.WriteAttributeFloat(nameof(RingProfile.Radius), ringProfile.Radius);
  229. _writer.WriteAttributeString(nameof(RingProfile.Radiate), ringProfile.Radiate.ToString());
  230. break;
  231. case SprayProfile sprayProfile:
  232. _writer.WriteAttributeString(nameof(Type), nameof(SprayProfile));
  233. _writer.WriteAttributeVector2(nameof(SprayProfile.Direction), sprayProfile.Direction);
  234. _writer.WriteAttributeFloat(nameof(SprayProfile.Spread), sprayProfile.Spread);
  235. break;
  236. default:
  237. _writer.WriteAttributeString(nameof(Type), nameof(PointProfile));
  238. break;
  239. }
  240. }
  241. private void WriteModifier(Modifier modifier)
  242. {
  243. _writer.WriteAttributeString(nameof(Modifier.Name), modifier.Name);
  244. _writer.WriteAttributeFloat(nameof(Modifier.Frequency), modifier.Frequency);
  245. switch (modifier)
  246. {
  247. case AgeModifier ageModifier:
  248. _writer.WriteAttributeString(nameof(Type), nameof(AgeModifier));
  249. if (ageModifier.Interpolators.Count > 0)
  250. {
  251. _writer.WriteStartElement(nameof(AgeModifier.Interpolators));
  252. foreach (Interpolator interpolator in ageModifier.Interpolators)
  253. {
  254. _writer.WriteStartElement(nameof(Interpolator));
  255. WriteInterpolator(interpolator);
  256. _writer.WriteEndElement();
  257. }
  258. _writer.WriteEndElement();
  259. }
  260. break;
  261. case CircleContainerModifier circleContainerModifier:
  262. _writer.WriteAttributeString(nameof(Type), nameof(CircleContainerModifier));
  263. _writer.WriteAttributeFloat(nameof(CircleContainerModifier.Radius), circleContainerModifier.Radius);
  264. _writer.WriteAttributeBool(nameof(CircleContainerModifier.Inside), circleContainerModifier.Inside);
  265. _writer.WriteAttributeFloat(nameof(CircleContainerModifier.RestitutionCoefficient), circleContainerModifier.RestitutionCoefficient);
  266. break;
  267. case DragModifier dragModifier:
  268. _writer.WriteAttributeString(nameof(Type), nameof(DragModifier));
  269. _writer.WriteAttributeFloat(nameof(DragModifier.DragCoefficient), dragModifier.DragCoefficient);
  270. _writer.WriteAttributeFloat(nameof(DragModifier.Density), dragModifier.Density);
  271. break;
  272. case LinearGravityModifier linearGravityModifier:
  273. _writer.WriteAttributeString(nameof(Type), nameof(LinearGravityModifier));
  274. _writer.WriteAttributeVector2(nameof(LinearGravityModifier.Direction), linearGravityModifier.Direction);
  275. _writer.WriteAttributeFloat(nameof(LinearGravityModifier.Strength), linearGravityModifier.Strength);
  276. break;
  277. case OpacityFastFadeModifier:
  278. _writer.WriteAttributeString(nameof(Type), nameof(OpacityFastFadeModifier));
  279. break;
  280. case RectangleContainerModifier rectangleContainerModifier:
  281. _writer.WriteAttributeString(nameof(Type), nameof(RectangleContainerModifier));
  282. _writer.WriteAttributeInt(nameof(RectangleContainerModifier.Width), rectangleContainerModifier.Width);
  283. _writer.WriteAttributeInt(nameof(RectangleContainerModifier.Height), rectangleContainerModifier.Height);
  284. _writer.WriteAttributeFloat(nameof(RectangleContainerModifier.RestitutionCoefficient), rectangleContainerModifier.RestitutionCoefficient);
  285. break;
  286. case RectangleLoopContainerModifier rectangleLoopContainerModifier:
  287. _writer.WriteAttributeString(nameof(Type), nameof(RectangleLoopContainerModifier));
  288. _writer.WriteAttributeInt(nameof(RectangleLoopContainerModifier.Width), rectangleLoopContainerModifier.Width);
  289. _writer.WriteAttributeInt(nameof(RectangleLoopContainerModifier.Height), rectangleLoopContainerModifier.Height);
  290. break;
  291. case RotationModifier rotationModifier:
  292. _writer.WriteAttributeString(nameof(Type), nameof(RotationModifier));
  293. _writer.WriteAttributeFloat(nameof(RotationModifier.RotationRate), rotationModifier.RotationRate);
  294. break;
  295. case VelocityColorModifier velocityColorModifier:
  296. _writer.WriteAttributeString(nameof(Type), nameof(VelocityColorModifier));
  297. Vector3 stationaryColor = new(velocityColorModifier.StationaryColor.H, velocityColorModifier.StationaryColor.S, velocityColorModifier.StationaryColor.L);
  298. _writer.WriteAttributeVector3(nameof(VelocityColorModifier.StationaryColor), stationaryColor);
  299. Vector3 velocityColor = new(velocityColorModifier.VelocityColor.H, velocityColorModifier.VelocityColor.S, velocityColorModifier.VelocityColor.L);
  300. _writer.WriteAttributeVector3(nameof(VelocityColorModifier.VelocityColor), velocityColor);
  301. _writer.WriteAttributeFloat(nameof(VelocityColorModifier.VelocityThreshold), velocityColorModifier.VelocityThreshold);
  302. break;
  303. case VelocityModifier velocityModifier:
  304. _writer.WriteAttributeString(nameof(Type), nameof(VelocityModifier));
  305. _writer.WriteAttributeFloat(nameof(VelocityModifier.VelocityThreshold), velocityModifier.VelocityThreshold);
  306. if (velocityModifier.Interpolators.Count > 0)
  307. {
  308. _writer.WriteStartElement(nameof(VelocityModifier.Interpolators));
  309. foreach (Interpolator interpolator in velocityModifier.Interpolators)
  310. {
  311. _writer.WriteStartElement(nameof(Interpolator));
  312. WriteInterpolator(interpolator);
  313. _writer.WriteEndElement();
  314. }
  315. _writer.WriteEndElement();
  316. }
  317. break;
  318. case VortexModifier vortexModifier:
  319. _writer.WriteAttributeString(nameof(Type), nameof(VortexModifier));
  320. _writer.WriteAttributeVector2(nameof(VortexModifier.Position), vortexModifier.Position);
  321. _writer.WriteAttributeFloat(nameof(VortexModifier.Strength), vortexModifier.Strength);
  322. _writer.WriteAttributeFloat(nameof(VortexModifier.OuterRadius), vortexModifier.OuterRadius);
  323. _writer.WriteAttributeFloat(nameof(VortexModifier.InnerRadius), vortexModifier.InnerRadius);
  324. _writer.WriteAttributeFloat(nameof(VortexModifier.MaxVelocity), vortexModifier.MaxVelocity);
  325. _writer.WriteAttributeFloat(nameof(VortexModifier.RotationAngle), vortexModifier.RotationAngle);
  326. break;
  327. default:
  328. _writer.WriteAttributeString(nameof(Type), "Unknown");
  329. break;
  330. }
  331. }
  332. private void WriteInterpolator(Interpolator interpolator)
  333. {
  334. _writer.WriteAttributeString(nameof(interpolator.Name), interpolator.Name);
  335. switch (interpolator)
  336. {
  337. case ColorInterpolator colorInterpolator:
  338. _writer.WriteAttributeString(nameof(Type), nameof(ColorInterpolator));
  339. Vector3 startValue = new Vector3(colorInterpolator.StartValue.H, colorInterpolator.StartValue.S, colorInterpolator.StartValue.L);
  340. Vector3 endValue = new Vector3(colorInterpolator.EndValue.H, colorInterpolator.EndValue.S, colorInterpolator.EndValue.L);
  341. _writer.WriteAttributeVector3(nameof(ColorInterpolator.StartValue), startValue);
  342. _writer.WriteAttributeVector3(nameof(ColorInterpolator.EndValue), endValue);
  343. break;
  344. case HueInterpolator hueInterpolator:
  345. _writer.WriteAttributeString(nameof(Type), nameof(HueInterpolator));
  346. _writer.WriteAttributeFloat(nameof(HueInterpolator.StartValue), hueInterpolator.StartValue);
  347. _writer.WriteAttributeFloat(nameof(HueInterpolator.EndValue), hueInterpolator.EndValue);
  348. break;
  349. case OpacityInterpolator opacityInterpolator:
  350. _writer.WriteAttributeString(nameof(Type), nameof(OpacityInterpolator));
  351. _writer.WriteAttributeFloat(nameof(OpacityInterpolator.StartValue), opacityInterpolator.StartValue);
  352. _writer.WriteAttributeFloat(nameof(OpacityInterpolator.EndValue), opacityInterpolator.EndValue);
  353. break;
  354. case RotationInterpolator rotationInterpolator:
  355. _writer.WriteAttributeString(nameof(Type), nameof(RotationInterpolator));
  356. _writer.WriteAttributeFloat(nameof(RotationInterpolator.StartValue), rotationInterpolator.StartValue);
  357. _writer.WriteAttributeFloat(nameof(RotationInterpolator.EndValue), rotationInterpolator.EndValue);
  358. break;
  359. case ScaleInterpolator scaleInterpolator:
  360. _writer.WriteAttributeString(nameof(Type), nameof(ScaleInterpolator));
  361. _writer.WriteAttributeVector2(nameof(ScaleInterpolator.StartValue), scaleInterpolator.StartValue);
  362. _writer.WriteAttributeVector2(nameof(ScaleInterpolator.EndValue), scaleInterpolator.EndValue);
  363. break;
  364. case VelocityInterpolator velocityInterpolator:
  365. _writer.WriteAttributeString(nameof(Type), nameof(VelocityInterpolator));
  366. _writer.WriteAttributeVector2(nameof(VelocityInterpolator.StartValue), velocityInterpolator.StartValue);
  367. _writer.WriteAttributeVector2(nameof(VelocityInterpolator.EndValue), velocityInterpolator.EndValue);
  368. break;
  369. default:
  370. _writer.WriteAttributeString(nameof(Type), "Unknown");
  371. break;
  372. }
  373. }
  374. /// <inheritdoc/>
  375. public void Dispose()
  376. {
  377. if (IsDisposed)
  378. {
  379. return;
  380. }
  381. _writer.Dispose();
  382. GC.SuppressFinalize(this);
  383. IsDisposed = true;
  384. }
  385. }