ParticleEffectSerializer.cs 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Xml;
  5. using Microsoft.Xna.Framework;
  6. using Microsoft.Xna.Framework.Content;
  7. using Microsoft.Xna.Framework.Graphics;
  8. using MonoGame.Extended.Graphics;
  9. using MonoGame.Extended.Particles.Data;
  10. using MonoGame.Extended.Particles.Modifiers;
  11. using MonoGame.Extended.Particles.Modifiers.Containers;
  12. using MonoGame.Extended.Particles.Modifiers.Interpolators;
  13. using MonoGame.Extended.Particles.Profiles;
  14. using MonoGame.Extended.Serialization.Xml;
  15. namespace MonoGame.Extended.Particles;
  16. /// <summary>
  17. /// Provides static methods for serializing and deserializing <see cref="ParticleEffect"/> instances to and from XML.
  18. /// </summary>
  19. public static class ParticleEffectSerializer
  20. {
  21. #region Deserialize
  22. /// <summary>
  23. /// Deserializes a <see cref="ParticleEffect"/> from an XML file.
  24. /// </summary>
  25. /// <param name="fileName">The file path to read the XML from</param>
  26. /// <param name="content">The <see cref="ContentManager"/> to use for loading texture.</param>
  27. /// <returns>The deserialized <see cref="ParticleEffect"/>.</returns>
  28. /// <exception cref="ArgumentNullException">
  29. /// Thrown when <paramref name="content"/> or <paramref name="fileName"/> is <see langword="null"/>.
  30. /// </exception>
  31. /// <exception cref="ArgumentException">Thrown when <paramref name="fileName"/> is empty.</exception>
  32. /// <exception cref="XmlException">Thrown when the XMl format is invalid.</exception>
  33. public static ParticleEffect Deserialize(string fileName, ContentManager content)
  34. {
  35. ArgumentNullException.ThrowIfNull(content);
  36. ArgumentException.ThrowIfNullOrEmpty(fileName);
  37. XmlReaderSettings settings = new XmlReaderSettings();
  38. settings.CloseInput = true;
  39. settings.IgnoreComments = true;
  40. settings.IgnoreWhitespace = true;
  41. string fullPath = Path.GetFullPath(fileName);
  42. string baseDirectory = Path.GetDirectoryName(fullPath);
  43. using XmlReader reader = XmlReader.Create(fileName, settings);
  44. return Deserialize(reader, content, baseDirectory);
  45. }
  46. /// <summary>
  47. /// Deserializes a <see cref="ParticleEffect"/> from a stream containing XML data.
  48. /// </summary>
  49. /// <param name="stream">The stream to read from.</param>
  50. /// <param name="content">The <see cref="ContentManager"/> to use for loading textures.</param>
  51. /// <param name="baseDirectory">
  52. /// The base directory to use for resolving relative texture paths.
  53. /// If <see langword="null"/>, uses the <see cref="ContentManager.RootDirectory"/> property of <paramref name="content"/>.
  54. /// </param>
  55. /// <returns>The deserialized <see cref="ParticleEffect"/>.</returns>
  56. /// <exception cref="ArgumentNullException">
  57. /// Thrown when <paramref name="stream"/> or <paramref name="content"/> are <see langword="null"/>.
  58. /// </exception>
  59. /// <exception cref="XmlException">Thrown when the XMl format is invalid.</exception>
  60. public static ParticleEffect Deserialize(Stream stream, ContentManager content, string baseDirectory = null)
  61. {
  62. ArgumentNullException.ThrowIfNull(stream);
  63. ArgumentNullException.ThrowIfNull(content);
  64. XmlReaderSettings settings = new XmlReaderSettings();
  65. settings.CloseInput = false;
  66. settings.IgnoreComments = true;
  67. settings.IgnoreWhitespace = true;
  68. if (string.IsNullOrEmpty(baseDirectory))
  69. {
  70. baseDirectory = content.RootDirectory;
  71. }
  72. using XmlReader reader = XmlReader.Create(stream, settings);
  73. return Deserialize(reader, content, baseDirectory);
  74. }
  75. private static ParticleEffect Deserialize(XmlReader reader, ContentManager content, string baseDirectory)
  76. {
  77. reader.MoveToContent();
  78. if (reader.NodeType != XmlNodeType.Element || reader.LocalName != nameof(ParticleEffect))
  79. {
  80. throw new XmlException($"Expected {nameof(ParticleEffect)} root element");
  81. }
  82. string name = reader.GetAttribute(nameof(ParticleEffect.Name)) ?? nameof(ParticleEffect);
  83. ParticleEffect effect = new ParticleEffect(name);
  84. effect.Position = reader.GetAttributeVector2(nameof(ParticleEffect.Position), default);
  85. effect.Rotation = reader.GetAttributeFloat(nameof(ParticleEffect.Rotation), default);
  86. effect.Scale = reader.GetAttributeVector2(nameof(ParticleEffect.Scale), default);
  87. effect.AutoTrigger = reader.GetAttributeBool(nameof(ParticleEffect.AutoTrigger), default);
  88. effect.AutoTriggerFrequency = reader.GetAttributeFloat(nameof(ParticleEffect.AutoTriggerFrequency), default);
  89. if (reader.ReadToDescendant(nameof(ParticleEffect.Emitters)))
  90. {
  91. if (reader.ReadToDescendant(nameof(ParticleEmitter)))
  92. {
  93. do
  94. {
  95. ParticleEmitter emitter = ReadParticleEmitter(reader, content, baseDirectory);
  96. effect.Emitters.Add(emitter);
  97. } while (reader.ReadToNextSibling(nameof(ParticleEmitter)));
  98. }
  99. }
  100. return effect;
  101. }
  102. private static ParticleEmitter ReadParticleEmitter(XmlReader reader, ContentManager content, string baseDirectory)
  103. {
  104. int capacity = reader.GetAttributeInt(nameof(ParticleEmitter.Capacity), default);
  105. ParticleEmitter emitter = new ParticleEmitter(capacity);
  106. emitter.Name = reader.GetAttribute(nameof(ParticleEmitter.Name)) ?? nameof(ParticleEmitter);
  107. emitter.LifeSpan = reader.GetAttributeFloat(nameof(ParticleEmitter.LifeSpan), default);
  108. emitter.Offset = reader.GetAttributeVector2(nameof(ParticleEmitter.Offset), default);
  109. emitter.LayerDepth = reader.GetAttributeFloat(nameof(ParticleEmitter.LayerDepth), default);
  110. emitter.ReclaimFrequency = reader.GetAttributeFloat(nameof(ParticleEmitter.ReclaimFrequency), default);
  111. string strategy = reader.GetAttribute(nameof(ParticleEmitter.ModifierExecutionStrategy));
  112. if (strategy.Equals(nameof(ModifierExecutionStrategy.Serial)))
  113. {
  114. emitter.ModifierExecutionStrategy = ModifierExecutionStrategy.Serial;
  115. }
  116. else if (strategy.Equals(nameof(ModifierExecutionStrategy.Parallel)))
  117. {
  118. emitter.ModifierExecutionStrategy = ModifierExecutionStrategy.Parallel;
  119. }
  120. else
  121. {
  122. emitter.ModifierExecutionStrategy = ModifierExecutionStrategy.Serial;
  123. }
  124. emitter.RenderingOrder = reader.GetAttributeEnum<ParticleRenderingOrder>(nameof(ParticleEmitter.RenderingOrder), default);
  125. using XmlReader subtree = reader.ReadSubtree();
  126. while (subtree.Read())
  127. {
  128. if (subtree.NodeType == XmlNodeType.Element)
  129. {
  130. switch (subtree.LocalName)
  131. {
  132. case nameof(ParticleEmitter.TextureRegion):
  133. emitter.TextureRegion = ReadTexture2DRegion(subtree, content, baseDirectory);
  134. break;
  135. case nameof(ParticleEmitter.Parameters):
  136. emitter.Parameters = ReadParticleReleaseParameters(subtree);
  137. break;
  138. case nameof(ParticleEmitter.Profile):
  139. emitter.Profile = ReadProfile(subtree);
  140. break;
  141. case nameof(ParticleEmitter.Modifiers):
  142. ReadModifiers(subtree, emitter.Modifiers);
  143. break;
  144. }
  145. }
  146. }
  147. return emitter;
  148. }
  149. private static Texture2DRegion ReadTexture2DRegion(XmlReader reader, ContentManager content, string baseDirectory)
  150. {
  151. string name = reader.GetAttribute(nameof(Texture2DRegion.Texture.Name));
  152. if (string.IsNullOrEmpty(name))
  153. {
  154. return null;
  155. }
  156. string path = Path.Combine(baseDirectory, name);
  157. // Content manager will throw exception if the path given is a rooted path
  158. if (Path.IsPathRooted(path))
  159. {
  160. path = Path.GetRelativePath(baseDirectory, path);
  161. }
  162. Texture2D texture = content.Load<Texture2D>(path);
  163. if(string.IsNullOrEmpty(texture.Name))
  164. {
  165. texture.Name = Path.GetFileName(path);
  166. }
  167. Rectangle bounds = reader.GetAttributeRectangle(nameof(Texture2DRegion.Bounds), default);
  168. if (bounds.IsEmpty)
  169. {
  170. bounds = texture.Bounds;
  171. }
  172. return new Texture2DRegion(texture, bounds);
  173. }
  174. private static ParticleReleaseParameters ReadParticleReleaseParameters(XmlReader reader)
  175. {
  176. ParticleReleaseParameters parameters = new ParticleReleaseParameters();
  177. using XmlReader subtree = reader.ReadSubtree();
  178. while (subtree.Read())
  179. {
  180. if (subtree.NodeType == XmlNodeType.Element)
  181. {
  182. switch (subtree.LocalName)
  183. {
  184. case nameof(ParticleReleaseParameters.Quantity):
  185. parameters.Quantity = ReadParticleInt32Parameter(subtree);
  186. break;
  187. case nameof(ParticleReleaseParameters.Speed):
  188. parameters.Speed = ReadParticleFloatParameter(subtree);
  189. break;
  190. case nameof(ParticleReleaseParameters.Color):
  191. parameters.Color = ReadParticleColorParameter(subtree);
  192. break;
  193. case nameof(ParticleReleaseParameters.Opacity):
  194. parameters.Opacity = ReadParticleFloatParameter(subtree);
  195. break;
  196. case nameof(ParticleReleaseParameters.Scale):
  197. parameters.Scale = ReadParticleVector2Parameter(subtree);
  198. break;
  199. case nameof(ParticleReleaseParameters.Rotation):
  200. parameters.Rotation = ReadParticleFloatParameter(subtree);
  201. break;
  202. case nameof(ParticleReleaseParameters.Mass):
  203. parameters.Mass = ReadParticleFloatParameter(subtree);
  204. break;
  205. }
  206. }
  207. }
  208. return parameters;
  209. }
  210. private static ParticleInt32Parameter ReadParticleInt32Parameter(XmlReader reader)
  211. {
  212. ParticleValueKind kind = reader.GetAttributeEnum<ParticleValueKind>(nameof(ParticleInt32Parameter.Kind), default);
  213. if (kind == ParticleValueKind.Constant)
  214. {
  215. int value = reader.GetAttributeInt(nameof(ParticleInt32Parameter.Constant), default);
  216. return new ParticleInt32Parameter(value);
  217. }
  218. else if (kind == ParticleValueKind.Random)
  219. {
  220. int min = reader.GetAttributeInt(nameof(ParticleInt32Parameter.RandomMin), default);
  221. int max = reader.GetAttributeInt(nameof(ParticleInt32Parameter.RandomMax), default);
  222. return new ParticleInt32Parameter(min, max);
  223. }
  224. return new ParticleInt32Parameter(0);
  225. }
  226. private static ParticleFloatParameter ReadParticleFloatParameter(XmlReader reader)
  227. {
  228. ParticleValueKind kind = reader.GetAttributeEnum<ParticleValueKind>(nameof(ParticleFloatParameter.Kind), default);
  229. if (kind == ParticleValueKind.Constant)
  230. {
  231. float value = reader.GetAttributeFloat(nameof(ParticleFloatParameter.Constant), default);
  232. return new ParticleFloatParameter(value);
  233. }
  234. else if (kind == ParticleValueKind.Random)
  235. {
  236. float min = reader.GetAttributeFloat(nameof(ParticleFloatParameter.RandomMin), default);
  237. float max = reader.GetAttributeFloat(nameof(ParticleFloatParameter.RandomMax), default);
  238. return new ParticleFloatParameter(min, max);
  239. }
  240. return new ParticleFloatParameter(0);
  241. }
  242. private static ParticleVector2Parameter ReadParticleVector2Parameter(XmlReader reader)
  243. {
  244. ParticleValueKind kind = reader.GetAttributeEnum<ParticleValueKind>(nameof(ParticleVector2Parameter.Kind), default);
  245. if (kind == ParticleValueKind.Constant)
  246. {
  247. Vector2 value = reader.GetAttributeVector2(nameof(ParticleVector2Parameter.Constant), default);
  248. return new ParticleVector2Parameter(value);
  249. }
  250. else if (kind == ParticleValueKind.Random)
  251. {
  252. Vector2 min = reader.GetAttributeVector2(nameof(ParticleVector2Parameter.RandomMin), default);
  253. Vector2 max = reader.GetAttributeVector2(nameof(ParticleVector2Parameter.RandomMax), default);
  254. return new ParticleVector2Parameter(min, max);
  255. }
  256. return new ParticleVector2Parameter(Vector2.Zero);
  257. }
  258. private static ParticleColorParameter ReadParticleColorParameter(XmlReader reader)
  259. {
  260. ParticleValueKind kind = reader.GetAttributeEnum<ParticleValueKind>(nameof(ParticleColorParameter.Kind), default);
  261. if (kind == ParticleValueKind.Constant)
  262. {
  263. Vector3 value = reader.GetAttributeVector3(nameof(ParticleColorParameter.Constant), default);
  264. return new ParticleColorParameter(value);
  265. }
  266. else if (kind == ParticleValueKind.Random)
  267. {
  268. Vector3 min = reader.GetAttributeVector3(nameof(ParticleColorParameter.RandomMin), default);
  269. Vector3 max = reader.GetAttributeVector3(nameof(ParticleColorParameter.RandomMax), default);
  270. return new ParticleColorParameter(min, max);
  271. }
  272. return new ParticleColorParameter(Vector3.Zero);
  273. }
  274. private static Profile ReadProfile(XmlReader reader)
  275. {
  276. string type = reader.GetAttribute(nameof(Type));
  277. return type switch
  278. {
  279. nameof(BoxProfile) => ReadBoxProfile(reader),
  280. nameof(BoxFillProfile) => ReadBoxFillProfile(reader),
  281. nameof(BoxUniformProfile) => ReadBoxUniformProfile(reader),
  282. nameof(CircleProfile) => ReadCircleProfile(reader),
  283. nameof(LineProfile) => ReadLineProfile(reader),
  284. nameof(PointProfile) => ReadPointProfile(reader),
  285. nameof(RingProfile) => ReadRingProfile(reader),
  286. nameof(SprayProfile) => ReadSprayProfile(reader),
  287. _ => ReadPointProfile(reader)
  288. };
  289. }
  290. private static BoxProfile ReadBoxProfile(XmlReader reader)
  291. {
  292. float width = reader.GetAttributeFloat(nameof(BoxProfile.Width), default);
  293. float height = reader.GetAttributeFloat(nameof(BoxProfile.Height), default);
  294. return new BoxProfile { Width = width, Height = height };
  295. }
  296. private static BoxFillProfile ReadBoxFillProfile(XmlReader reader)
  297. {
  298. float width = reader.GetAttributeFloat(nameof(BoxFillProfile.Width), default);
  299. float height = reader.GetAttributeFloat(nameof(BoxFillProfile.Height), default);
  300. return new BoxFillProfile { Width = width, Height = height };
  301. }
  302. private static BoxUniformProfile ReadBoxUniformProfile(XmlReader reader)
  303. {
  304. float width = reader.GetAttributeFloat(nameof(BoxUniformProfile.Width), default);
  305. float height = reader.GetAttributeFloat(nameof(BoxUniformProfile.Height), default);
  306. return new BoxUniformProfile { Width = width, Height = height };
  307. }
  308. private static CircleProfile ReadCircleProfile(XmlReader reader)
  309. {
  310. float radius = reader.GetAttributeFloat(nameof(CircleProfile.Radius), default);
  311. CircleRadiation radiation = reader.GetAttributeEnum<CircleRadiation>(nameof(CircleProfile.Radiate), default);
  312. return new CircleProfile { Radius = radius, Radiate = radiation };
  313. }
  314. private static LineProfile ReadLineProfile(XmlReader reader)
  315. {
  316. Vector2 axis = reader.GetAttributeVector2(nameof(LineProfile.Axis), default);
  317. float length = reader.GetAttributeFloat(nameof(LineProfile.Length), default);
  318. Vector2 direction = reader.GetAttributeVector2(nameof(LineProfile.Direction), default);
  319. LineRadiation radiate = reader.GetAttributeEnum<LineRadiation>(nameof(LineProfile.Radiate), default);
  320. return new LineProfile { Axis = axis, Length = length, Direction = direction, Radiate = radiate };
  321. }
  322. private static PointProfile ReadPointProfile(XmlReader reader)
  323. {
  324. return new PointProfile();
  325. }
  326. private static RingProfile ReadRingProfile(XmlReader reader)
  327. {
  328. float radius = reader.GetAttributeFloat(nameof(RingProfile.Radius), default);
  329. CircleRadiation radiation = reader.GetAttributeEnum<CircleRadiation>(nameof(RingProfile.Radiate), default);
  330. return new RingProfile { Radius = radius, Radiate = radiation };
  331. }
  332. private static SprayProfile ReadSprayProfile(XmlReader reader)
  333. {
  334. Vector2 direction = reader.GetAttributeVector2(nameof(SprayProfile.Direction), default);
  335. float spread = reader.GetAttributeFloat(nameof(SprayProfile.Spread), default);
  336. return new SprayProfile { Direction = direction, Spread = spread };
  337. }
  338. private static void ReadModifiers(XmlReader reader, List<Modifier> modifiers)
  339. {
  340. using XmlReader subtree = reader.ReadSubtree();
  341. while (subtree.Read())
  342. {
  343. if (subtree.NodeType == XmlNodeType.Element && subtree.LocalName == nameof(Modifier))
  344. {
  345. Modifier modifier = ReadModifier(subtree);
  346. if (modifier != null)
  347. {
  348. modifiers.Add(modifier);
  349. }
  350. }
  351. }
  352. }
  353. private static Modifier ReadModifier(XmlReader reader)
  354. {
  355. string type = reader.GetAttribute(nameof(Type));
  356. string name = reader.GetAttribute(nameof(Modifier.Name));
  357. float frequency = reader.GetAttributeFloat(nameof(Modifier.Frequency), default);
  358. bool enabled = reader.GetAttributeBool(nameof(Modifier.Enabled), default);
  359. Modifier modifier = type switch
  360. {
  361. nameof(AgeModifier) => ReadAgeModifier(reader),
  362. nameof(DragModifier) => ReadDragModifier(reader),
  363. nameof(LinearGravityModifier) => ReadLinearGravityModifier(reader),
  364. nameof(OpacityFastFadeModifier) => new OpacityFastFadeModifier(),
  365. nameof(RotationModifier) => ReadRotationModifier(reader),
  366. nameof(VelocityColorModifier) => ReadVelocityColorModifier(reader),
  367. nameof(VelocityModifier) => ReadVelocityModifier(reader),
  368. nameof(VortexModifier) => ReadVortexModifier(reader),
  369. nameof(CircleContainerModifier) => ReadCircleContainerModifier(reader),
  370. nameof(RectangleContainerModifier) => ReadRectangleContainerModifier(reader),
  371. nameof(RectangleLoopContainerModifier) => ReadRectangleLoopContainerModifier(reader),
  372. _ => null
  373. };
  374. if (modifier != null)
  375. {
  376. modifier.Name = name;
  377. modifier.Frequency = frequency;
  378. modifier.Enabled = enabled;
  379. }
  380. return modifier;
  381. }
  382. private static AgeModifier ReadAgeModifier(XmlReader reader)
  383. {
  384. AgeModifier modifier = new AgeModifier();
  385. using XmlReader subtree = reader.ReadSubtree();
  386. while (subtree.Read())
  387. {
  388. if (subtree.NodeType == XmlNodeType.Element && subtree.LocalName == nameof(AgeModifier.Interpolators))
  389. {
  390. ReadInterpolators(subtree, modifier.Interpolators);
  391. }
  392. }
  393. return modifier;
  394. }
  395. private static DragModifier ReadDragModifier(XmlReader reader)
  396. {
  397. float dragCoefficient = reader.GetAttributeFloat(nameof(DragModifier.DragCoefficient), default);
  398. float density = reader.GetAttributeFloat(nameof(DragModifier.Density), default);
  399. return new DragModifier() { DragCoefficient = dragCoefficient, Density = density };
  400. }
  401. private static LinearGravityModifier ReadLinearGravityModifier(XmlReader reader)
  402. {
  403. Vector2 direction = reader.GetAttributeVector2(nameof(LinearGravityModifier.Direction), default);
  404. float strength = reader.GetAttributeFloat(nameof(LinearGravityModifier.Strength), default);
  405. return new LinearGravityModifier() { Direction = direction, Strength = strength };
  406. }
  407. private static RotationModifier ReadRotationModifier(XmlReader reader)
  408. {
  409. float rotationRate = reader.GetAttributeFloat(nameof(RotationModifier.RotationRate), default);
  410. return new RotationModifier() { RotationRate = rotationRate };
  411. }
  412. private static VelocityColorModifier ReadVelocityColorModifier(XmlReader reader)
  413. {
  414. Vector3 stationaryColorValue = reader.GetAttributeVector3(nameof(VelocityColorModifier.StationaryColor), default);
  415. Vector3 velocityColorValue = reader.GetAttributeVector3(nameof(VelocityColorModifier.VelocityColor), default);
  416. float velocityThreshold = reader.GetAttributeFloat(nameof(VelocityColorModifier.VelocityThreshold), default);
  417. HslColor stationaryColor = new HslColor(stationaryColorValue.X, stationaryColorValue.Y, stationaryColorValue.Z);
  418. HslColor velocityColor = new HslColor(velocityColorValue.X, velocityColorValue.Y, velocityColorValue.Z);
  419. return new VelocityColorModifier() { StationaryColor = stationaryColor, VelocityColor = velocityColor, VelocityThreshold = velocityThreshold };
  420. }
  421. private static VelocityModifier ReadVelocityModifier(XmlReader reader)
  422. {
  423. float velocityThreshold = reader.GetAttributeFloat(nameof(VelocityModifier.VelocityThreshold), default);
  424. VelocityModifier modifier = new VelocityModifier() { VelocityThreshold = velocityThreshold };
  425. using XmlReader subtree = reader.ReadSubtree();
  426. while (subtree.Read())
  427. {
  428. if (subtree.NodeType == XmlNodeType.Element && subtree.LocalName == nameof(VelocityModifier.Interpolators))
  429. {
  430. ReadInterpolators(subtree, modifier.Interpolators);
  431. }
  432. }
  433. return modifier;
  434. }
  435. private static VortexModifier ReadVortexModifier(XmlReader reader)
  436. {
  437. Vector2 position = reader.GetAttributeVector2(nameof(VortexModifier.Position), default);
  438. float strength = reader.GetAttributeFloat(nameof(VortexModifier.Strength), default);
  439. float outerRadius = reader.GetAttributeFloat(nameof(VortexModifier.OuterRadius), default);
  440. float innerRadius = reader.GetAttributeFloat(nameof(VortexModifier.InnerRadius), default);
  441. float maxVelocity = reader.GetAttributeFloat(nameof(VortexModifier.MaxVelocity), default);
  442. float rotationAngle = reader.GetAttributeFloat(nameof(VortexModifier.RotationAngle), default);
  443. return new VortexModifier { Position = position, Strength = strength, OuterRadius = outerRadius, InnerRadius = innerRadius, MaxVelocity = maxVelocity, RotationAngle = rotationAngle };
  444. }
  445. private static CircleContainerModifier ReadCircleContainerModifier(XmlReader reader)
  446. {
  447. float radius = reader.GetAttributeFloat(nameof(CircleContainerModifier.Radius), default);
  448. bool inside = reader.GetAttributeBool(nameof(CircleContainerModifier.Inside), default);
  449. float restitutionCoefficient = reader.GetAttributeFloat(nameof(CircleContainerModifier.RestitutionCoefficient), default);
  450. return new CircleContainerModifier() { Radius = radius, Inside = inside, RestitutionCoefficient = restitutionCoefficient };
  451. }
  452. private static RectangleContainerModifier ReadRectangleContainerModifier(XmlReader reader)
  453. {
  454. int width = reader.GetAttributeInt(nameof(RectangleContainerModifier.Width), default);
  455. int height = reader.GetAttributeInt(nameof(RectangleContainerModifier.Height), default);
  456. float restitutionCoefficient = reader.GetAttributeFloat(nameof(RectangleContainerModifier.RestitutionCoefficient), default);
  457. return new RectangleContainerModifier() { Width = width, Height = height, RestitutionCoefficient = restitutionCoefficient };
  458. }
  459. private static RectangleLoopContainerModifier ReadRectangleLoopContainerModifier(XmlReader reader)
  460. {
  461. int width = reader.GetAttributeInt(nameof(RectangleLoopContainerModifier.Width), default);
  462. int height = reader.GetAttributeInt(nameof(RectangleLoopContainerModifier.Height), default);
  463. return new RectangleLoopContainerModifier() { Width = width, Height = height };
  464. }
  465. private static void ReadInterpolators(XmlReader reader, List<Interpolator> interpolators)
  466. {
  467. using XmlReader subtree = reader.ReadSubtree();
  468. while (subtree.Read())
  469. {
  470. if (subtree.NodeType == XmlNodeType.Element && subtree.LocalName == nameof(Interpolator))
  471. {
  472. Interpolator interpolator = ReadInterpolator(subtree);
  473. if (interpolator != null)
  474. {
  475. interpolators.Add(interpolator);
  476. }
  477. }
  478. }
  479. }
  480. private static Interpolator ReadInterpolator(XmlReader reader)
  481. {
  482. string type = reader.GetAttribute(nameof(Type));
  483. string name = reader.GetAttribute(nameof(Interpolator.Name));
  484. bool enabled = reader.GetAttributeBool(nameof(Interpolator.Enabled), default);
  485. Interpolator interpolator = type switch
  486. {
  487. nameof(ColorInterpolator) => ReadColorInterpolator(reader),
  488. nameof(HueInterpolator) => ReadHueInterpolator(reader),
  489. nameof(OpacityInterpolator) => ReadOpacityInterpolator(reader),
  490. nameof(RotationInterpolator) => ReadRotationInterpolator(reader),
  491. nameof(ScaleInterpolator) => ReadScaleInterpolator(reader),
  492. nameof(VelocityInterpolator) => ReadVelocityInterpolator(reader),
  493. _ => null
  494. };
  495. if (interpolator != null)
  496. {
  497. interpolator.Name = name;
  498. }
  499. interpolator.Enabled = enabled;
  500. return interpolator;
  501. }
  502. private static ColorInterpolator ReadColorInterpolator(XmlReader reader)
  503. {
  504. Vector3 start = reader.GetAttributeVector3(nameof(ColorInterpolator.StartValue), default);
  505. Vector3 end = reader.GetAttributeVector3(nameof(ColorInterpolator.EndValue), default);
  506. HslColor startValue = new HslColor(start.X, start.Y, start.Z);
  507. HslColor endValue = new HslColor(end.X, end.Y, end.Z);
  508. return new ColorInterpolator() { StartValue = startValue, EndValue = endValue };
  509. }
  510. private static HueInterpolator ReadHueInterpolator(XmlReader reader)
  511. {
  512. float startValue = reader.GetAttributeFloat(nameof(HueInterpolator.StartValue), default);
  513. float endValue = reader.GetAttributeFloat(nameof(HueInterpolator.EndValue), default);
  514. return new HueInterpolator() { StartValue = startValue, EndValue = endValue };
  515. }
  516. private static OpacityInterpolator ReadOpacityInterpolator(XmlReader reader)
  517. {
  518. float startValue = reader.GetAttributeFloat(nameof(OpacityInterpolator.StartValue), default);
  519. float endValue = reader.GetAttributeFloat(nameof(OpacityInterpolator.EndValue), default);
  520. return new OpacityInterpolator() { StartValue = startValue, EndValue = endValue };
  521. }
  522. private static RotationInterpolator ReadRotationInterpolator(XmlReader reader)
  523. {
  524. float startValue = reader.GetAttributeFloat(nameof(RotationInterpolator.StartValue), default);
  525. float endValue = reader.GetAttributeFloat(nameof(RotationInterpolator.EndValue), default);
  526. return new RotationInterpolator() { StartValue = startValue, EndValue = endValue };
  527. }
  528. private static ScaleInterpolator ReadScaleInterpolator(XmlReader reader)
  529. {
  530. Vector2 startValue = reader.GetAttributeVector2(nameof(ScaleInterpolator.StartValue), default);
  531. Vector2 endValue = reader.GetAttributeVector2(nameof(ScaleInterpolator.EndValue), default);
  532. return new ScaleInterpolator() { StartValue = startValue, EndValue = endValue };
  533. }
  534. private static VelocityInterpolator ReadVelocityInterpolator(XmlReader reader)
  535. {
  536. Vector2 startValue = reader.GetAttributeVector2(nameof(VelocityInterpolator.StartValue), default);
  537. Vector2 endValue = reader.GetAttributeVector2(nameof(VelocityInterpolator.EndValue), default);
  538. return new VelocityInterpolator() { StartValue = startValue, EndValue = endValue };
  539. }
  540. #endregion Deserialize
  541. #region Serialize
  542. /// <summary>
  543. /// Serializes a <see cref="ParticleEffect"/> to an XML file.
  544. /// </summary>
  545. /// <param name="fileName">The file path to write the XML to.</param>
  546. /// <param name="effect">The <see cref="ParticleEffect"/> to serialize.</param>
  547. /// <exception cref="ArgumentException">Thrown when <paramref name="fileName"/> is an empty string.</exception>
  548. /// <exception cref="ArgumentNullException">
  549. /// Throw if either <paramref name="fileName"/> or <paramref name="effect"/> are <see langword="null"/>.
  550. /// </exception>
  551. public static void Serialize(string fileName, ParticleEffect effect)
  552. {
  553. ArgumentException.ThrowIfNullOrEmpty(fileName);
  554. ArgumentNullException.ThrowIfNull(effect);
  555. XmlWriterSettings settings = new XmlWriterSettings();
  556. settings.Indent = true;
  557. settings.IndentChars = " ";
  558. settings.CloseOutput = true;
  559. settings.NewLineChars = "\n";
  560. using XmlWriter writer = XmlWriter.Create(fileName, settings);
  561. Serialize(writer, effect);
  562. }
  563. /// <summary>
  564. /// Serializes a <see cref="ParticleEffect"/> to a stream as XML data.
  565. /// </summary>
  566. /// <param name="stream">The stream to write to.</param>
  567. /// <param name="effect">The <see cref="ParticleEffect"/> to serialize.</param>
  568. /// <exception cref="ArgumentNullException">
  569. /// Throw if either <paramref name="stream"/> or <paramref name="effect"/> are <see langword="null"/>.
  570. /// </exception>
  571. public static void Serialize(Stream stream, ParticleEffect effect)
  572. {
  573. ArgumentNullException.ThrowIfNull(stream);
  574. ArgumentNullException.ThrowIfNull(effect);
  575. XmlWriterSettings settings = new XmlWriterSettings();
  576. settings.Indent = true;
  577. settings.IndentChars = " ";
  578. settings.CloseOutput = false;
  579. settings.NewLineChars = "\n";
  580. using XmlWriter writer = XmlWriter.Create(stream, settings);
  581. Serialize(writer, effect);
  582. }
  583. private static void Serialize(XmlWriter writer, ParticleEffect effect)
  584. {
  585. writer.WriteStartDocument();
  586. writer.WriteStartElement(nameof(ParticleEffect));
  587. writer.WriteAttributeString(nameof(ParticleEffect.Name), effect.Name);
  588. writer.WriteAttributeVector2(nameof(ParticleEffect.Position), effect.Position);
  589. writer.WriteAttributeFloat(nameof(ParticleEffect.Rotation), effect.Rotation);
  590. writer.WriteAttributeVector2(nameof(ParticleEffect.Scale), effect.Scale);
  591. writer.WriteAttributeBool(nameof(ParticleEffect.AutoTrigger), effect.AutoTrigger);
  592. writer.WriteAttributeFloat(nameof(ParticleEffect.AutoTriggerFrequency), effect.AutoTriggerFrequency);
  593. if (effect.Emitters.Count > 0)
  594. {
  595. writer.WriteStartElement(nameof(ParticleEffect.Emitters));
  596. foreach (ParticleEmitter emitter in effect.Emitters)
  597. {
  598. writer.WriteStartElement(nameof(ParticleEmitter));
  599. WriteParticleEmitter(writer, emitter);
  600. writer.WriteEndElement();
  601. }
  602. writer.WriteEndElement();
  603. }
  604. writer.WriteEndElement();
  605. writer.WriteEndDocument();
  606. writer.Flush();
  607. }
  608. private static void WriteParticleEmitter(XmlWriter writer, ParticleEmitter emitter)
  609. {
  610. writer.WriteAttributeString(nameof(ParticleEmitter.Name), emitter.Name);
  611. writer.WriteAttributeFloat(nameof(ParticleEmitter.LifeSpan), emitter.LifeSpan);
  612. writer.WriteAttributeVector2(nameof(ParticleEmitter.Offset), emitter.Offset);
  613. writer.WriteAttributeFloat(nameof(ParticleEmitter.LayerDepth), emitter.LayerDepth);
  614. writer.WriteAttributeFloat(nameof(ParticleEmitter.ReclaimFrequency), emitter.ReclaimFrequency);
  615. writer.WriteAttributeInt(nameof(ParticleEmitter.Capacity), emitter.Capacity);
  616. writer.WriteAttributeString(nameof(ParticleEmitter.ModifierExecutionStrategy), emitter.ModifierExecutionStrategy.ToString());
  617. writer.WriteAttributeString(nameof(ParticleEmitter.RenderingOrder), emitter.RenderingOrder.ToString());
  618. if (emitter.TextureRegion is Texture2DRegion region)
  619. {
  620. writer.WriteStartElement(nameof(ParticleEmitter.TextureRegion));
  621. WriteTexture2DRegion(writer, region);
  622. writer.WriteEndElement();
  623. }
  624. writer.WriteStartElement(nameof(ParticleEmitter.Parameters));
  625. WriteParticleReleaseParameters(writer, emitter.Parameters);
  626. writer.WriteEndElement();
  627. writer.WriteStartElement(nameof(ParticleEmitter.Profile));
  628. WriteProfile(writer, emitter.Profile);
  629. writer.WriteEndElement();
  630. if (emitter.Modifiers.Count > 0)
  631. {
  632. writer.WriteStartElement(nameof(ParticleEmitter.Modifiers));
  633. foreach (Modifier modifier in emitter.Modifiers)
  634. {
  635. writer.WriteStartElement(nameof(Modifier));
  636. WriteModifier(writer, modifier);
  637. writer.WriteEndElement();
  638. }
  639. writer.WriteEndElement();
  640. }
  641. }
  642. private static void WriteTexture2DRegion(XmlWriter writer, Texture2DRegion region)
  643. {
  644. writer.WriteAttributeString(nameof(Texture2DRegion.Texture.Name), region.Texture.Name);
  645. writer.WriteAttributeRectangle(nameof(Texture2DRegion.Bounds), region.Bounds);
  646. }
  647. private static void WriteParticleReleaseParameters(XmlWriter writer, ParticleReleaseParameters parameters)
  648. {
  649. WriteParticleInt32Parameter(writer, nameof(ParticleReleaseParameters.Quantity), parameters.Quantity);
  650. WriteParticleFloatParameter(writer, nameof(ParticleReleaseParameters.Speed), parameters.Speed);
  651. WriteParticleColorParameter(writer, nameof(ParticleReleaseParameters.Color), parameters.Color);
  652. WriteParticleFloatParameter(writer, nameof(ParticleReleaseParameters.Opacity), parameters.Opacity);
  653. WriteParticleVector2Parameter(writer, nameof(ParticleReleaseParameters.Scale), parameters.Scale);
  654. WriteParticleFloatParameter(writer, nameof(ParticleReleaseParameters.Rotation), parameters.Rotation);
  655. WriteParticleFloatParameter(writer, nameof(ParticleReleaseParameters.Mass), parameters.Mass);
  656. }
  657. private static void WriteParticleInt32Parameter(XmlWriter writer, string name, ParticleInt32Parameter parameter)
  658. {
  659. writer.WriteStartElement(name);
  660. writer.WriteAttributeString(nameof(ParticleInt32Parameter.Kind), parameter.Kind.ToString());
  661. if (parameter.Kind == ParticleValueKind.Constant)
  662. {
  663. writer.WriteAttributeInt(nameof(ParticleInt32Parameter.Constant), parameter.Constant);
  664. }
  665. else
  666. {
  667. writer.WriteAttributeInt(nameof(ParticleInt32Parameter.RandomMin), parameter.RandomMin);
  668. writer.WriteAttributeInt(nameof(ParticleInt32Parameter.RandomMax), parameter.RandomMax);
  669. }
  670. writer.WriteEndElement();
  671. }
  672. private static void WriteParticleFloatParameter(XmlWriter writer, string name, ParticleFloatParameter parameter)
  673. {
  674. writer.WriteStartElement(name);
  675. writer.WriteAttributeString(nameof(ParticleFloatParameter.Kind), parameter.Kind.ToString());
  676. if (parameter.Kind == ParticleValueKind.Constant)
  677. {
  678. writer.WriteAttributeFloat(nameof(ParticleFloatParameter.Constant), parameter.Constant);
  679. }
  680. else
  681. {
  682. writer.WriteAttributeFloat(nameof(ParticleFloatParameter.RandomMin), parameter.RandomMin);
  683. writer.WriteAttributeFloat(nameof(ParticleFloatParameter.RandomMax), parameter.RandomMax);
  684. }
  685. writer.WriteEndElement();
  686. }
  687. private static void WriteParticleVector2Parameter(XmlWriter writer, string name, ParticleVector2Parameter parameter)
  688. {
  689. writer.WriteStartElement(name);
  690. writer.WriteAttributeString(nameof(ParticleVector2Parameter.Kind), parameter.Kind.ToString());
  691. if (parameter.Kind == ParticleValueKind.Constant)
  692. {
  693. writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.Constant), parameter.Constant);
  694. }
  695. else
  696. {
  697. writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.RandomMin), parameter.RandomMin);
  698. writer.WriteAttributeVector2(nameof(ParticleVector2Parameter.RandomMax), parameter.RandomMax);
  699. }
  700. writer.WriteEndElement();
  701. }
  702. private static void WriteParticleColorParameter(XmlWriter writer, string name, ParticleColorParameter parameter)
  703. {
  704. writer.WriteStartElement(name);
  705. writer.WriteAttributeString(nameof(ParticleColorParameter.Kind), parameter.Kind.ToString());
  706. if (parameter.Kind == ParticleValueKind.Constant)
  707. {
  708. writer.WriteAttributeVector3(nameof(ParticleColorParameter.Constant), parameter.Constant);
  709. }
  710. else
  711. {
  712. writer.WriteAttributeVector3(nameof(ParticleColorParameter.RandomMin), parameter.RandomMin);
  713. writer.WriteAttributeVector3(nameof(ParticleColorParameter.RandomMax), parameter.RandomMax);
  714. }
  715. writer.WriteEndElement();
  716. }
  717. private static void WriteProfile(XmlWriter writer, Profile profile)
  718. {
  719. switch (profile)
  720. {
  721. case BoxFillProfile boxFillProfile:
  722. WriteBoxFillProfile(writer, boxFillProfile);
  723. break;
  724. case BoxProfile boxProfile:
  725. WriteBoxProfile(writer, boxProfile);
  726. break;
  727. case BoxUniformProfile boxUniformProfile:
  728. WriteBoxUniformProfile(writer, boxUniformProfile);
  729. break;
  730. case CircleProfile circleProfile:
  731. WriteCircleProfile(writer, circleProfile);
  732. break;
  733. case LineProfile lineProfile:
  734. WriteLineProfile(writer, lineProfile);
  735. break;
  736. case PointProfile pointProfile:
  737. WritePointProfile(writer, pointProfile);
  738. break;
  739. case RingProfile ringProfile:
  740. WriteRingProfile(writer, ringProfile);
  741. break;
  742. case SprayProfile sprayProfile:
  743. WriteSprayProfile(writer, sprayProfile);
  744. break;
  745. default:
  746. writer.WriteAttributeString(nameof(Type), profile.GetType().ToString());
  747. break;
  748. }
  749. }
  750. private static void WriteBoxFillProfile(XmlWriter writer, BoxFillProfile profile)
  751. {
  752. writer.WriteAttributeString(nameof(Type), nameof(BoxFillProfile));
  753. writer.WriteAttributeFloat(nameof(BoxFillProfile.Width), profile.Width);
  754. writer.WriteAttributeFloat(nameof(BoxFillProfile.Height), profile.Height);
  755. }
  756. private static void WriteBoxProfile(XmlWriter writer, BoxProfile profile)
  757. {
  758. writer.WriteAttributeString(nameof(Type), nameof(BoxProfile));
  759. writer.WriteAttributeFloat(nameof(BoxProfile.Width), profile.Width);
  760. writer.WriteAttributeFloat(nameof(BoxProfile.Height), profile.Height);
  761. }
  762. private static void WriteBoxUniformProfile(XmlWriter writer, BoxUniformProfile profile)
  763. {
  764. writer.WriteAttributeString(nameof(Type), nameof(BoxUniformProfile));
  765. writer.WriteAttributeFloat(nameof(BoxUniformProfile.Width), profile.Width);
  766. writer.WriteAttributeFloat(nameof(BoxUniformProfile.Height), profile.Height);
  767. }
  768. private static void WriteCircleProfile(XmlWriter writer, CircleProfile profile)
  769. {
  770. writer.WriteAttributeString(nameof(Type), nameof(CircleProfile));
  771. writer.WriteAttributeFloat(nameof(CircleProfile.Radius), profile.Radius);
  772. writer.WriteAttributeString(nameof(CircleProfile.Radiate), profile.Radiate.ToString());
  773. }
  774. private static void WriteLineProfile(XmlWriter writer, LineProfile profile)
  775. {
  776. writer.WriteAttributeString(nameof(Type), nameof(LineProfile));
  777. writer.WriteAttributeVector2(nameof(LineProfile.Axis), profile.Axis);
  778. writer.WriteAttributeFloat(nameof(LineProfile.Length), profile.Length);
  779. writer.WriteAttributeString(nameof(LineProfile.Radiate), profile.Radiate.ToString());
  780. writer.WriteAttributeVector2(nameof(LineProfile.Direction), profile.Direction);
  781. }
  782. private static void WritePointProfile(XmlWriter writer, PointProfile profile)
  783. {
  784. writer.WriteAttributeString(nameof(Type), nameof(PointProfile));
  785. }
  786. private static void WriteRingProfile(XmlWriter writer, RingProfile profile)
  787. {
  788. writer.WriteAttributeString(nameof(Type), nameof(RingProfile));
  789. writer.WriteAttributeFloat(nameof(RingProfile.Radius), profile.Radius);
  790. writer.WriteAttributeString(nameof(RingProfile.Radiate), profile.Radiate.ToString());
  791. }
  792. private static void WriteSprayProfile(XmlWriter writer, SprayProfile profile)
  793. {
  794. writer.WriteAttributeString(nameof(Type), nameof(SprayProfile));
  795. writer.WriteAttributeVector2(nameof(SprayProfile.Direction), profile.Direction);
  796. writer.WriteAttributeFloat(nameof(SprayProfile.Spread), profile.Spread);
  797. }
  798. private static void WriteModifier(XmlWriter writer, Modifier modifier)
  799. {
  800. writer.WriteAttributeString(nameof(Modifier.Name), modifier.Name);
  801. writer.WriteAttributeBool(nameof(Modifier.Enabled), modifier.Enabled);
  802. writer.WriteAttributeFloat(nameof(Modifier.Frequency), modifier.Frequency);
  803. switch (modifier)
  804. {
  805. case AgeModifier ageModifier:
  806. WriteAgeModifier(writer, ageModifier);
  807. break;
  808. case CircleContainerModifier circleContainerModifier:
  809. WriteCircleContainerModifier(writer, circleContainerModifier);
  810. break;
  811. case DragModifier dragModifier:
  812. WriteDragModifier(writer, dragModifier);
  813. break;
  814. case LinearGravityModifier linearGravityModifier:
  815. WriteLinearGravityModifier(writer, linearGravityModifier);
  816. break;
  817. case OpacityFastFadeModifier opacityFastFadeModifier:
  818. WriteOpacityFastFadeModifier(writer, opacityFastFadeModifier);
  819. break;
  820. case RectangleContainerModifier rectangleContainerModifier:
  821. WriteRectangleContainerModifier(writer, rectangleContainerModifier);
  822. break;
  823. case RectangleLoopContainerModifier rectangleLoopContainerModifier:
  824. WriteRectangleLoopContainerModifier(writer, rectangleLoopContainerModifier);
  825. break;
  826. case RotationModifier rotationModifier:
  827. WriteRotationModifier(writer, rotationModifier);
  828. break;
  829. case VelocityColorModifier velocityColorModifier:
  830. WriteVelocityColorModifier(writer, velocityColorModifier);
  831. break;
  832. case VelocityModifier velocityModifier:
  833. WriteVelocityModifier(writer, velocityModifier);
  834. break;
  835. case VortexModifier vortexModifier:
  836. WriteVortexModifier(writer, vortexModifier);
  837. break;
  838. default:
  839. writer.WriteAttributeString(nameof(Type), modifier.GetType().Name);
  840. break;
  841. }
  842. }
  843. private static void WriteAgeModifier(XmlWriter writer, AgeModifier modifier)
  844. {
  845. writer.WriteAttributeString(nameof(Type), nameof(AgeModifier));
  846. if (modifier.Interpolators.Count > 0)
  847. {
  848. writer.WriteStartElement(nameof(AgeModifier.Interpolators));
  849. foreach (Interpolator interpolator in modifier.Interpolators)
  850. {
  851. writer.WriteStartElement(nameof(Interpolator));
  852. WriteInterpolator(writer, interpolator);
  853. writer.WriteEndElement();
  854. }
  855. writer.WriteEndElement();
  856. }
  857. }
  858. private static void WriteCircleContainerModifier(XmlWriter writer, CircleContainerModifier modifier)
  859. {
  860. writer.WriteAttributeString(nameof(Type), nameof(CircleContainerModifier));
  861. writer.WriteAttributeFloat(nameof(CircleContainerModifier.Radius), modifier.Radius);
  862. writer.WriteAttributeBool(nameof(CircleContainerModifier.Inside), modifier.Inside);
  863. writer.WriteAttributeFloat(nameof(CircleContainerModifier.RestitutionCoefficient), modifier.RestitutionCoefficient);
  864. }
  865. private static void WriteDragModifier(XmlWriter writer, DragModifier modifier)
  866. {
  867. writer.WriteAttributeString(nameof(Type), nameof(DragModifier));
  868. writer.WriteAttributeFloat(nameof(DragModifier.DragCoefficient), modifier.DragCoefficient);
  869. writer.WriteAttributeFloat(nameof(DragModifier.Density), modifier.Density);
  870. }
  871. private static void WriteLinearGravityModifier(XmlWriter writer, LinearGravityModifier modifier)
  872. {
  873. writer.WriteAttributeString(nameof(Type), nameof(LinearGravityModifier));
  874. writer.WriteAttributeVector2(nameof(LinearGravityModifier.Direction), modifier.Direction);
  875. writer.WriteAttributeFloat(nameof(LinearGravityModifier.Strength), modifier.Strength);
  876. }
  877. private static void WriteOpacityFastFadeModifier(XmlWriter writer, OpacityFastFadeModifier modifier)
  878. {
  879. writer.WriteAttributeString(nameof(Type), nameof(OpacityFastFadeModifier));
  880. }
  881. private static void WriteRectangleContainerModifier(XmlWriter writer, RectangleContainerModifier modifier)
  882. {
  883. writer.WriteAttributeString(nameof(Type), nameof(RectangleContainerModifier));
  884. writer.WriteAttributeInt(nameof(RectangleContainerModifier.Width), modifier.Width);
  885. writer.WriteAttributeInt(nameof(RectangleContainerModifier.Height), modifier.Height);
  886. writer.WriteAttributeFloat(nameof(RectangleContainerModifier.RestitutionCoefficient), modifier.RestitutionCoefficient);
  887. }
  888. private static void WriteRectangleLoopContainerModifier(XmlWriter writer, RectangleLoopContainerModifier modifier)
  889. {
  890. writer.WriteAttributeString(nameof(Type), nameof(RectangleLoopContainerModifier));
  891. writer.WriteAttributeInt(nameof(RectangleLoopContainerModifier.Width), modifier.Width);
  892. writer.WriteAttributeInt(nameof(RectangleLoopContainerModifier.Height), modifier.Height);
  893. }
  894. private static void WriteRotationModifier(XmlWriter writer, RotationModifier modifier)
  895. {
  896. writer.WriteAttributeString(nameof(Type), nameof(RotationModifier));
  897. writer.WriteAttributeFloat(nameof(RotationModifier.RotationRate), modifier.RotationRate);
  898. }
  899. private static void WriteVelocityColorModifier(XmlWriter writer, VelocityColorModifier modifier)
  900. {
  901. Vector3 stationaryColor = new Vector3(modifier.StationaryColor.H, modifier.StationaryColor.S, modifier.StationaryColor.L);
  902. Vector3 velocityColor = new Vector3(modifier.VelocityColor.H, modifier.VelocityColor.S, modifier.VelocityColor.L);
  903. writer.WriteAttributeString(nameof(Type), nameof(VelocityColorModifier));
  904. writer.WriteAttributeVector3(nameof(VelocityColorModifier.StationaryColor), stationaryColor);
  905. writer.WriteAttributeVector3(nameof(VelocityColorModifier.VelocityColor), velocityColor);
  906. writer.WriteAttributeFloat(nameof(VelocityColorModifier.VelocityThreshold), modifier.VelocityThreshold);
  907. }
  908. private static void WriteVelocityModifier(XmlWriter writer, VelocityModifier modifier)
  909. {
  910. writer.WriteAttributeString(nameof(Type), nameof(VelocityModifier));
  911. writer.WriteAttributeFloat(nameof(VelocityModifier.VelocityThreshold), modifier.VelocityThreshold);
  912. if (modifier.Interpolators.Count > 0)
  913. {
  914. writer.WriteStartElement(nameof(VelocityModifier.Interpolators));
  915. foreach (Interpolator interpolator in modifier.Interpolators)
  916. {
  917. writer.WriteStartElement(nameof(Interpolator));
  918. WriteInterpolator(writer, interpolator);
  919. writer.WriteEndElement();
  920. }
  921. writer.WriteEndElement();
  922. }
  923. }
  924. private static void WriteVortexModifier(XmlWriter writer, VortexModifier modifier)
  925. {
  926. writer.WriteAttributeString(nameof(Type), nameof(VortexModifier));
  927. writer.WriteAttributeVector2(nameof(VortexModifier.Position), modifier.Position);
  928. writer.WriteAttributeFloat(nameof(VortexModifier.Strength), modifier.Strength);
  929. writer.WriteAttributeFloat(nameof(VortexModifier.OuterRadius), modifier.OuterRadius);
  930. writer.WriteAttributeFloat(nameof(VortexModifier.InnerRadius), modifier.InnerRadius);
  931. writer.WriteAttributeFloat(nameof(VortexModifier.MaxVelocity), modifier.MaxVelocity);
  932. writer.WriteAttributeFloat(nameof(VortexModifier.RotationAngle), modifier.RotationAngle);
  933. }
  934. private static void WriteInterpolator(XmlWriter writer, Interpolator interpolator)
  935. {
  936. writer.WriteAttributeString(nameof(Interpolator.Name), interpolator.Name);
  937. writer.WriteAttributeBool(nameof(Interpolator.Enabled), interpolator.Enabled);
  938. switch (interpolator)
  939. {
  940. case ColorInterpolator colorInterpolator:
  941. WriteColorInterpolator(writer, colorInterpolator);
  942. break;
  943. case HueInterpolator hueInterpolator:
  944. WriteHueInterpolator(writer, hueInterpolator);
  945. break;
  946. case OpacityInterpolator opacityInterpolator:
  947. WriteOpacityInterpolator(writer, opacityInterpolator);
  948. break;
  949. case RotationInterpolator rotationInterpolator:
  950. WriteRotationInterpolator(writer, rotationInterpolator);
  951. break;
  952. case ScaleInterpolator scaleInterpolator:
  953. WriteScaleInterpolator(writer, scaleInterpolator);
  954. break;
  955. case VelocityInterpolator velocityInterpolator:
  956. WriteVelocityInterpolator(writer, velocityInterpolator);
  957. break;
  958. default:
  959. writer.WriteAttributeString(nameof(Type), interpolator.GetType().Name);
  960. break;
  961. }
  962. }
  963. private static void WriteColorInterpolator(XmlWriter writer, ColorInterpolator interpolator)
  964. {
  965. Vector3 startValue = new Vector3(interpolator.StartValue.H, interpolator.StartValue.S, interpolator.StartValue.L);
  966. Vector3 endValue = new Vector3(interpolator.EndValue.H, interpolator.EndValue.S, interpolator.EndValue.L);
  967. writer.WriteAttributeString(nameof(Type), nameof(ColorInterpolator));
  968. writer.WriteAttributeVector3(nameof(ColorInterpolator.StartValue), startValue);
  969. writer.WriteAttributeVector3(nameof(ColorInterpolator.EndValue), endValue);
  970. }
  971. private static void WriteHueInterpolator(XmlWriter writer, HueInterpolator interpolator)
  972. {
  973. writer.WriteAttributeString(nameof(Type), nameof(HueInterpolator));
  974. writer.WriteAttributeFloat(nameof(HueInterpolator.StartValue), interpolator.StartValue);
  975. writer.WriteAttributeFloat(nameof(HueInterpolator.EndValue), interpolator.EndValue);
  976. }
  977. private static void WriteOpacityInterpolator(XmlWriter writer, OpacityInterpolator interpolator)
  978. {
  979. writer.WriteAttributeString(nameof(Type), nameof(OpacityInterpolator));
  980. writer.WriteAttributeFloat(nameof(OpacityInterpolator.StartValue), interpolator.StartValue);
  981. writer.WriteAttributeFloat(nameof(OpacityInterpolator.EndValue), interpolator.EndValue);
  982. }
  983. private static void WriteRotationInterpolator(XmlWriter writer, RotationInterpolator interpolator)
  984. {
  985. writer.WriteAttributeString(nameof(Type), nameof(RotationInterpolator));
  986. writer.WriteAttributeFloat(nameof(RotationInterpolator.StartValue), interpolator.StartValue);
  987. writer.WriteAttributeFloat(nameof(RotationInterpolator.EndValue), interpolator.EndValue);
  988. }
  989. private static void WriteScaleInterpolator(XmlWriter writer, ScaleInterpolator interpolator)
  990. {
  991. writer.WriteAttributeString(nameof(Type), nameof(ScaleInterpolator));
  992. writer.WriteAttributeVector2(nameof(ScaleInterpolator.StartValue), interpolator.StartValue);
  993. writer.WriteAttributeVector2(nameof(ScaleInterpolator.EndValue), interpolator.EndValue);
  994. }
  995. private static void WriteVelocityInterpolator(XmlWriter writer, VelocityInterpolator interpolator)
  996. {
  997. writer.WriteAttributeString(nameof(Type), nameof(VelocityInterpolator));
  998. writer.WriteAttributeVector2(nameof(VelocityInterpolator.StartValue), interpolator.StartValue);
  999. writer.WriteAttributeVector2(nameof(VelocityInterpolator.EndValue), interpolator.EndValue);
  1000. }
  1001. #endregion Serialize
  1002. }