ParticleEffectReaderTests.cs 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143
  1. // Copyright (c) Craftwork Games. All rights reserved.
  2. // Licensed under the MIT license.
  3. // See LICENSE file in the project root for full license information.
  4. using System;
  5. using System.IO;
  6. using System.Xml;
  7. using Microsoft.Xna.Framework;
  8. using MonoGame.Extended.Particles;
  9. using MonoGame.Extended.Particles.Modifiers;
  10. using MonoGame.Extended.Particles.Modifiers.Containers;
  11. using MonoGame.Extended.Particles.Modifiers.Interpolators;
  12. using MonoGame.Extended.Particles.Profiles;
  13. namespace MonoGame.Extended.Tests.Particles;
  14. public class ParticleEffectReaderTests
  15. {
  16. private readonly MockContentManager _mockContentManager;
  17. public ParticleEffectReaderTests()
  18. {
  19. _mockContentManager = new MockContentManager();
  20. }
  21. private ParticleEffect ReadParticleEffectFromXml(string xml)
  22. {
  23. using MemoryStream stream = new MemoryStream();
  24. using StreamWriter writer = new StreamWriter(stream);
  25. writer.Write(xml);
  26. writer.Flush();
  27. stream.Position = 0;
  28. using ParticleEffectReader reader = new ParticleEffectReader(stream, _mockContentManager);
  29. return reader.ReadParticleEffect();
  30. }
  31. [Fact]
  32. public void ReadParticleEffect_NulLStream_ThrowsArgumentNullException()
  33. {
  34. Assert.Throws<ArgumentNullException>(() => new ParticleEffectReader((Stream)null, _mockContentManager));
  35. }
  36. [Fact]
  37. public void ReadParticleEffect_NullContentManager_ThrowsArgumentNullException()
  38. {
  39. using MemoryStream stream = new MemoryStream();
  40. Assert.Throws<ArgumentNullException>(() => new ParticleEffectReader(stream, null));
  41. }
  42. [Fact]
  43. public void ReadParticleEffect_InvalidXmlRoot_ThrowsXmlException()
  44. {
  45. string xml = "<InvalidRoot />";
  46. Assert.Throws<XmlException>(() => ReadParticleEffectFromXml(xml));
  47. }
  48. [Fact]
  49. public void ReadParticleEffect_EmptyEffect_ReturnsExpected()
  50. {
  51. string xml =
  52. """
  53. <?xml version="1.0" encoding="utf-8"?>
  54. <ParticleEffect Name="EmptyEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1" />
  55. """;
  56. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  57. Assert.Equal("EmptyEffect", effect.Name);
  58. Assert.Equal(Vector2.Zero, effect.Position);
  59. Assert.Equal(0.0f, effect.Rotation);
  60. Assert.Equal(Vector2.One, effect.Scale);
  61. Assert.Empty(effect.Emitters);
  62. Assert.True(effect.AutoTrigger);
  63. Assert.Equal(effect.AutoTriggerFrequency, 1.0f);
  64. }
  65. [Fact]
  66. public void ReadParticleEffect_EmptyModifiers_ReturnsExpected()
  67. {
  68. string xml =
  69. $"""
  70. <?xml version="1.0" encoding="utf-8"?>
  71. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  72. <Emitters>
  73. <ParticleEmitter Name="EmptyModifiers" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  74. <Parameters>
  75. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  76. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  77. <Color Kind="Constant" Constant="1,1,1" />
  78. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  79. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  80. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  81. <Mass Kind="Constant" Constant="1" />
  82. </Parameters>
  83. <Profile Type="PointProfile" />
  84. </ParticleEmitter>
  85. </Emitters>
  86. </ParticleEffect>
  87. """;
  88. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  89. Assert.Single(effect.Emitters);
  90. ParticleEmitter emitter = effect.Emitters[0];
  91. Assert.Empty(emitter.Modifiers);
  92. }
  93. [Fact]
  94. public void ReadParticleEffect_EmptyInterpolators_ReturnsExpected()
  95. {
  96. string xml =
  97. $"""
  98. <?xml version="1.0" encoding="utf-8"?>
  99. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  100. <Emitters>
  101. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  102. <Parameters>
  103. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  104. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  105. <Color Kind="Constant" Constant="1,1,1" />
  106. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  107. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  108. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  109. <Mass Kind="Constant" Constant="1" />
  110. </Parameters>
  111. <Profile Type="PointProfile" />
  112. <Modifiers>
  113. <Modifier Name="AgeModifier" Frequency="60" Type="AgeModifier" />
  114. </Modifiers>
  115. </ParticleEmitter>
  116. </Emitters>
  117. </ParticleEffect>
  118. """;
  119. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  120. Assert.Single(effect.Emitters);
  121. ParticleEmitter emitter = effect.Emitters[0];
  122. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  123. Assert.Empty(modifier.Interpolators);
  124. }
  125. [Fact]
  126. public void ReadParticleEffect_BoxFillProfile_ReadsExpected()
  127. {
  128. string xml =
  129. $"""
  130. <?xml version="1.0" encoding="utf-8"?>
  131. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  132. <Emitters>
  133. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  134. <Parameters>
  135. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  136. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  137. <Color Kind="Constant" Constant="1,1,1" />
  138. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  139. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  140. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  141. <Mass Kind="Constant" Constant="1" />
  142. </Parameters>
  143. <Profile Type="BoxFillProfile" Width="1" Height="1" />
  144. </ParticleEmitter>
  145. </Emitters>
  146. </ParticleEffect>
  147. """;
  148. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  149. Assert.Single(effect.Emitters);
  150. ParticleEmitter emitter = effect.Emitters[0];
  151. BoxFillProfile profile = Assert.IsType<BoxFillProfile>(emitter.Profile);
  152. Assert.Equal(1.0f, profile.Height);
  153. Assert.Equal(1.0f, profile.Height);
  154. }
  155. [Fact]
  156. public void ReadParticleEffect_BoxProfile_ReadsExpected()
  157. {
  158. string xml =
  159. $"""
  160. <?xml version="1.0" encoding="utf-8"?>
  161. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  162. <Emitters>
  163. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  164. <Parameters>
  165. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  166. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  167. <Color Kind="Constant" Constant="1,1,1" />
  168. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  169. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  170. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  171. <Mass Kind="Constant" Constant="1" />
  172. </Parameters>
  173. <Profile Type="BoxProfile" Width="1" Height="1" />
  174. </ParticleEmitter>
  175. </Emitters>
  176. </ParticleEffect>
  177. """;
  178. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  179. Assert.Single(effect.Emitters);
  180. ParticleEmitter emitter = effect.Emitters[0];
  181. BoxProfile profile = Assert.IsType<BoxProfile>(emitter.Profile);
  182. Assert.Equal(1.0f, profile.Height);
  183. Assert.Equal(1.0f, profile.Height);
  184. }
  185. [Fact]
  186. public void ReadParticleEffect_BoxUniformProfile_ReadsExpected()
  187. {
  188. string xml =
  189. $"""
  190. <?xml version="1.0" encoding="utf-8"?>
  191. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  192. <Emitters>
  193. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  194. <Parameters>
  195. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  196. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  197. <Color Kind="Constant" Constant="1,1,1" />
  198. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  199. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  200. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  201. <Mass Kind="Constant" Constant="1" />
  202. </Parameters>
  203. <Profile Type="BoxUniformProfile" Width="1" Height="1" />
  204. </ParticleEmitter>
  205. </Emitters>
  206. </ParticleEffect>
  207. """;
  208. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  209. Assert.Single(effect.Emitters);
  210. ParticleEmitter emitter = effect.Emitters[0];
  211. BoxUniformProfile profile = Assert.IsType<BoxUniformProfile>(emitter.Profile);
  212. Assert.Equal(1.0f, profile.Height);
  213. Assert.Equal(1.0f, profile.Height);
  214. }
  215. [Fact]
  216. public void ReadParticleEffect_CircleProfile_ReadsExpected()
  217. {
  218. string xml =
  219. $"""
  220. <?xml version="1.0" encoding="utf-8"?>
  221. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  222. <Emitters>
  223. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  224. <Parameters>
  225. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  226. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  227. <Color Kind="Constant" Constant="1,1,1" />
  228. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  229. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  230. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  231. <Mass Kind="Constant" Constant="1" />
  232. </Parameters>
  233. <Profile Type="CircleProfile" Radius="1" Radiate="Out" />
  234. </ParticleEmitter>
  235. </Emitters>
  236. </ParticleEffect>
  237. """;
  238. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  239. Assert.Single(effect.Emitters);
  240. ParticleEmitter emitter = effect.Emitters[0];
  241. CircleProfile profile = Assert.IsType<CircleProfile>(emitter.Profile);
  242. Assert.Equal(1.0f, profile.Radius);
  243. Assert.Equal(CircleRadiation.Out, profile.Radiate);
  244. }
  245. [Fact]
  246. public void ReadParticleEffect_LineProfile_ReadsExpected()
  247. {
  248. string xml =
  249. $"""
  250. <?xml version="1.0" encoding="utf-8"?>
  251. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  252. <Emitters>
  253. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  254. <Parameters>
  255. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  256. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  257. <Color Kind="Constant" Constant="1,1,1" />
  258. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  259. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  260. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  261. <Mass Kind="Constant" Constant="1" />
  262. </Parameters>
  263. <Profile Type="LineProfile" Axis="1,1" Length="1" />
  264. </ParticleEmitter>
  265. </Emitters>
  266. </ParticleEffect>
  267. """;
  268. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  269. Assert.Single(effect.Emitters);
  270. ParticleEmitter emitter = effect.Emitters[0];
  271. LineProfile profile = Assert.IsType<LineProfile>(emitter.Profile);
  272. Assert.Equal(Vector2.One, profile.Axis);
  273. Assert.Equal(1.0f, profile.Length);
  274. }
  275. [Fact]
  276. public void ReadParticleEffect_PointProfile_ReadsExpected()
  277. {
  278. string xml =
  279. $"""
  280. <?xml version="1.0" encoding="utf-8"?>
  281. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  282. <Emitters>
  283. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  284. <Parameters>
  285. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  286. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  287. <Color Kind="Constant" Constant="1,1,1" />
  288. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  289. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  290. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  291. <Mass Kind="Constant" Constant="1" />
  292. </Parameters>
  293. <Profile Type="PointProfile" />
  294. </ParticleEmitter>
  295. </Emitters>
  296. </ParticleEffect>
  297. """;
  298. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  299. Assert.Single(effect.Emitters);
  300. ParticleEmitter emitter = effect.Emitters[0];
  301. PointProfile profile = Assert.IsType<PointProfile>(emitter.Profile);
  302. }
  303. [Fact]
  304. public void ReadParticleEffect_RingProfile_ReadsExpected()
  305. {
  306. string xml =
  307. $"""
  308. <?xml version="1.0" encoding="utf-8"?>
  309. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  310. <Emitters>
  311. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  312. <Parameters>
  313. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  314. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  315. <Color Kind="Constant" Constant="1,1,1" />
  316. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  317. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  318. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  319. <Mass Kind="Constant" Constant="1" />
  320. </Parameters>
  321. <Profile Type="RingProfile" Radius="1" Radiate="In" />
  322. </ParticleEmitter>
  323. </Emitters>
  324. </ParticleEffect>
  325. """;
  326. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  327. Assert.Single(effect.Emitters);
  328. ParticleEmitter emitter = effect.Emitters[0];
  329. RingProfile profile = Assert.IsType<RingProfile>(emitter.Profile);
  330. Assert.Equal(1.0f, profile.Radius);
  331. Assert.Equal(CircleRadiation.In, profile.Radiate);
  332. }
  333. [Fact]
  334. public void ReadParticleEffect_SprayProfile_ReadsExpected()
  335. {
  336. string xml =
  337. $"""
  338. <?xml version="1.0" encoding="utf-8"?>
  339. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  340. <Emitters>
  341. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  342. <Parameters>
  343. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  344. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  345. <Color Kind="Constant" Constant="1,1,1" />
  346. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  347. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  348. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  349. <Mass Kind="Constant" Constant="1" />
  350. </Parameters>
  351. <Profile Type="SprayProfile" Direction="1,1" Spread="1" />
  352. </ParticleEmitter>
  353. </Emitters>
  354. </ParticleEffect>
  355. """;
  356. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  357. Assert.Single(effect.Emitters);
  358. ParticleEmitter emitter = effect.Emitters[0];
  359. SprayProfile profile = Assert.IsType<SprayProfile>(emitter.Profile);
  360. Assert.Equal(Vector2.One, profile.Direction);
  361. Assert.Equal(1.0f, profile.Spread);
  362. }
  363. [Fact]
  364. public void ReadParticleEffect_AgeModifier_ReadsExpected()
  365. {
  366. string xml =
  367. $"""
  368. <?xml version="1.0" encoding="utf-8"?>
  369. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  370. <Emitters>
  371. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  372. <Parameters>
  373. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  374. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  375. <Color Kind="Constant" Constant="1,1,1" />
  376. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  377. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  378. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  379. <Mass Kind="Constant" Constant="1" />
  380. </Parameters>
  381. <Profile Type="PointProfile" />
  382. <Modifiers>
  383. <Modifier Name="AgeModifier" Frequency="60" Type="AgeModifier" />
  384. </Modifiers>
  385. </ParticleEmitter>
  386. </Emitters>
  387. </ParticleEffect>
  388. """;
  389. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  390. Assert.Single(effect.Emitters);
  391. ParticleEmitter emitter = effect.Emitters[0];
  392. Assert.Single(emitter.Modifiers);
  393. AgeModifier modifier = Assert.IsType<AgeModifier>(effect.Emitters[0].Modifiers[0]);
  394. Assert.Equal(60.0f, modifier.Frequency);
  395. Assert.Equal("AgeModifier", modifier.Name);
  396. }
  397. [Fact]
  398. public void ReadParticleEffect_CircleContainerModifier_ReadsExpected()
  399. {
  400. string xml =
  401. $"""
  402. <?xml version="1.0" encoding="utf-8"?>
  403. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  404. <Emitters>
  405. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  406. <Parameters>
  407. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  408. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  409. <Color Kind="Constant" Constant="1,1,1" />
  410. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  411. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  412. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  413. <Mass Kind="Constant" Constant="1" />
  414. </Parameters>
  415. <Profile Type="PointProfile" />
  416. <Modifiers>
  417. <Modifier Name="CircleContainerModifier" Frequency="60" Type="CircleContainerModifier" Radius="0" Inside="True" RestitutionCoefficient="1" />
  418. </Modifiers>
  419. </ParticleEmitter>
  420. </Emitters>
  421. </ParticleEffect>
  422. """;
  423. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  424. Assert.Single(effect.Emitters);
  425. ParticleEmitter emitter = effect.Emitters[0];
  426. Assert.Single(emitter.Modifiers);
  427. CircleContainerModifier modifier = Assert.IsType<CircleContainerModifier>(effect.Emitters[0].Modifiers[0]);
  428. Assert.Equal(60.0f, modifier.Frequency);
  429. Assert.Equal("CircleContainerModifier", modifier.Name);
  430. Assert.Equal(0.0f, modifier.Radius);
  431. Assert.True(modifier.Inside);
  432. Assert.Equal(1.0f, modifier.RestitutionCoefficient);
  433. }
  434. [Fact]
  435. public void ReadParticleEffect_DragModifier_ReadsExpected()
  436. {
  437. string xml =
  438. $"""
  439. <?xml version="1.0" encoding="utf-8"?>
  440. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  441. <Emitters>
  442. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  443. <Parameters>
  444. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  445. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  446. <Color Kind="Constant" Constant="1,1,1" />
  447. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  448. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  449. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  450. <Mass Kind="Constant" Constant="1" />
  451. </Parameters>
  452. <Profile Type="PointProfile" />
  453. <Modifiers>
  454. <Modifier Name="DragModifier" Frequency="60" Type="DragModifier" DragCoefficient="0.47" Density="0.5" />
  455. </Modifiers>
  456. </ParticleEmitter>
  457. </Emitters>
  458. </ParticleEffect>
  459. """;
  460. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  461. Assert.Single(effect.Emitters);
  462. ParticleEmitter emitter = effect.Emitters[0];
  463. Assert.Single(emitter.Modifiers);
  464. DragModifier modifier = Assert.IsType<DragModifier>(effect.Emitters[0].Modifiers[0]);
  465. Assert.Equal(60.0f, modifier.Frequency);
  466. Assert.Equal("DragModifier", modifier.Name);
  467. Assert.Equal(0.47f, modifier.DragCoefficient);
  468. Assert.Equal(0.5f, modifier.Density);
  469. }
  470. [Fact]
  471. public void ReadParticleEffect_LinearGravityModifier_ReadsExpected()
  472. {
  473. string xml =
  474. $"""
  475. <?xml version="1.0" encoding="utf-8"?>
  476. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  477. <Emitters>
  478. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  479. <Parameters>
  480. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  481. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  482. <Color Kind="Constant" Constant="1,1,1" />
  483. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  484. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  485. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  486. <Mass Kind="Constant" Constant="1" />
  487. </Parameters>
  488. <Profile Type="PointProfile" />
  489. <Modifiers>
  490. <Modifier Name="LinearGravityModifier" Frequency="60" Type="LinearGravityModifier" Direction="0,0" Strength="0" />
  491. </Modifiers>
  492. </ParticleEmitter>
  493. </Emitters>
  494. </ParticleEffect>
  495. """;
  496. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  497. Assert.Single(effect.Emitters);
  498. ParticleEmitter emitter = effect.Emitters[0];
  499. Assert.Single(emitter.Modifiers);
  500. LinearGravityModifier modifier = Assert.IsType<LinearGravityModifier>(effect.Emitters[0].Modifiers[0]);
  501. Assert.Equal(60.0f, modifier.Frequency);
  502. Assert.Equal("LinearGravityModifier", modifier.Name);
  503. Assert.Equal(Vector2.Zero, modifier.Direction);
  504. Assert.Equal(0.0f, modifier.Strength);
  505. }
  506. [Fact]
  507. public void ReadParticleEffect_OpacityFastFadeModifier_ReadsExpected()
  508. {
  509. string xml =
  510. $"""
  511. <?xml version="1.0" encoding="utf-8"?>
  512. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  513. <Emitters>
  514. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  515. <Parameters>
  516. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  517. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  518. <Color Kind="Constant" Constant="1,1,1" />
  519. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  520. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  521. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  522. <Mass Kind="Constant" Constant="1" />
  523. </Parameters>
  524. <Profile Type="PointProfile" />
  525. <Modifiers>
  526. <Modifier Name="OpacityFastFadeModifier" Frequency="60" Type="OpacityFastFadeModifier" />
  527. </Modifiers>
  528. </ParticleEmitter>
  529. </Emitters>
  530. </ParticleEffect>
  531. """;
  532. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  533. Assert.Single(effect.Emitters);
  534. ParticleEmitter emitter = effect.Emitters[0];
  535. Assert.Single(emitter.Modifiers);
  536. OpacityFastFadeModifier modifier = Assert.IsType<OpacityFastFadeModifier>(effect.Emitters[0].Modifiers[0]);
  537. Assert.Equal(60.0f, modifier.Frequency);
  538. Assert.Equal("OpacityFastFadeModifier", modifier.Name);
  539. }
  540. [Fact]
  541. public void ReadParticleEffect_RectangleContainerModifier_ReadsExpected()
  542. {
  543. string xml =
  544. $"""
  545. <?xml version="1.0" encoding="utf-8"?>
  546. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  547. <Emitters>
  548. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  549. <Parameters>
  550. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  551. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  552. <Color Kind="Constant" Constant="1,1,1" />
  553. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  554. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  555. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  556. <Mass Kind="Constant" Constant="1" />
  557. </Parameters>
  558. <Profile Type="PointProfile" />
  559. <Modifiers>
  560. <Modifier Name="RectangleContainerModifier" Frequency="60" Type="RectangleContainerModifier" Width="0" Height="0" RestitutionCoefficient="1" />
  561. </Modifiers>
  562. </ParticleEmitter>
  563. </Emitters>
  564. </ParticleEffect>
  565. """;
  566. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  567. Assert.Single(effect.Emitters);
  568. ParticleEmitter emitter = effect.Emitters[0];
  569. Assert.Single(emitter.Modifiers);
  570. RectangleContainerModifier modifier = Assert.IsType<RectangleContainerModifier>(effect.Emitters[0].Modifiers[0]);
  571. Assert.Equal(60.0f, modifier.Frequency);
  572. Assert.Equal("RectangleContainerModifier", modifier.Name);
  573. Assert.Equal(0.0f, modifier.Width);
  574. Assert.Equal(0.0f, modifier.Height);
  575. Assert.Equal(1.0f, modifier.RestitutionCoefficient);
  576. }
  577. [Fact]
  578. public void ReadParticleEffect_RectangleLoopContainerModifier_ReadsExpected()
  579. {
  580. string xml =
  581. $"""
  582. <?xml version="1.0" encoding="utf-8"?>
  583. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  584. <Emitters>
  585. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  586. <Parameters>
  587. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  588. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  589. <Color Kind="Constant" Constant="1,1,1" />
  590. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  591. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  592. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  593. <Mass Kind="Constant" Constant="1" />
  594. </Parameters>
  595. <Profile Type="PointProfile" />
  596. <Modifiers>
  597. <Modifier Name="RectangleLoopContainerModifier" Frequency="60" Type="RectangleLoopContainerModifier" Width="0" Height="0" />
  598. </Modifiers>
  599. </ParticleEmitter>
  600. </Emitters>
  601. </ParticleEffect>
  602. """;
  603. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  604. Assert.Single(effect.Emitters);
  605. ParticleEmitter emitter = effect.Emitters[0];
  606. Assert.Single(emitter.Modifiers);
  607. RectangleLoopContainerModifier modifier = Assert.IsType<RectangleLoopContainerModifier>(effect.Emitters[0].Modifiers[0]);
  608. Assert.Equal(60.0f, modifier.Frequency);
  609. Assert.Equal("RectangleLoopContainerModifier", modifier.Name);
  610. Assert.Equal(0.0f, modifier.Width);
  611. Assert.Equal(0.0f, modifier.Height);
  612. }
  613. [Fact]
  614. public void ReadParticleEffect_RotationModifier_ReadsExpected()
  615. {
  616. string xml =
  617. $"""
  618. <?xml version="1.0" encoding="utf-8"?>
  619. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  620. <Emitters>
  621. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  622. <Parameters>
  623. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  624. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  625. <Color Kind="Constant" Constant="1,1,1" />
  626. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  627. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  628. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  629. <Mass Kind="Constant" Constant="1" />
  630. </Parameters>
  631. <Profile Type="PointProfile" />
  632. <Modifiers>
  633. <Modifier Name="RotationModifier" Frequency="60" Type="RotationModifier" RotationRate="0" />
  634. </Modifiers>
  635. </ParticleEmitter>
  636. </Emitters>
  637. </ParticleEffect>
  638. """;
  639. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  640. Assert.Single(effect.Emitters);
  641. ParticleEmitter emitter = effect.Emitters[0];
  642. Assert.Single(emitter.Modifiers);
  643. RotationModifier modifier = Assert.IsType<RotationModifier>(effect.Emitters[0].Modifiers[0]);
  644. Assert.Equal(60.0f, modifier.Frequency);
  645. Assert.Equal("RotationModifier", modifier.Name);
  646. Assert.Equal(0.0f, modifier.RotationRate);
  647. }
  648. [Fact]
  649. public void ReadParticleEffect_VelocityColorModifier_ReadsExpected()
  650. {
  651. string xml =
  652. $"""
  653. <?xml version="1.0" encoding="utf-8"?>
  654. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  655. <Emitters>
  656. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  657. <Parameters>
  658. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  659. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  660. <Color Kind="Constant" Constant="1,1,1" />
  661. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  662. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  663. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  664. <Mass Kind="Constant" Constant="1" />
  665. </Parameters>
  666. <Profile Type="PointProfile" />
  667. <Modifiers>
  668. <Modifier Name="VelocityColorModifier" Frequency="60" Type="VelocityColorModifier" StationaryColor="0,0,0" VelocityColor="0,0,0" VelocityThreshold="0" />
  669. </Modifiers>
  670. </ParticleEmitter>
  671. </Emitters>
  672. </ParticleEffect>
  673. """;
  674. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  675. Assert.Single(effect.Emitters);
  676. ParticleEmitter emitter = effect.Emitters[0];
  677. Assert.Single(emitter.Modifiers);
  678. VelocityColorModifier modifier = Assert.IsType<VelocityColorModifier>(effect.Emitters[0].Modifiers[0]);
  679. Assert.Equal(60.0f, modifier.Frequency);
  680. Assert.Equal("VelocityColorModifier", modifier.Name);
  681. Assert.Equal(new HslColor(0, 0, 0), modifier.StationaryColor);
  682. Assert.Equal(new HslColor(0, 0, 0), modifier.VelocityColor);
  683. Assert.Equal(0.0f, modifier.VelocityThreshold);
  684. }
  685. [Fact]
  686. public void ReadParticleEffect_VelocityModifier_ReadsExpected()
  687. {
  688. string xml =
  689. $"""
  690. <?xml version="1.0" encoding="utf-8"?>
  691. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  692. <Emitters>
  693. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  694. <Parameters>
  695. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  696. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  697. <Color Kind="Constant" Constant="1,1,1" />
  698. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  699. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  700. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  701. <Mass Kind="Constant" Constant="1" />
  702. </Parameters>
  703. <Profile Type="PointProfile" />
  704. <Modifiers>
  705. <Modifier Name="VelocityModifier" Frequency="60" Type="VelocityModifier" VelocityThreshold="0" />
  706. </Modifiers>
  707. </ParticleEmitter>
  708. </Emitters>
  709. </ParticleEffect>
  710. """;
  711. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  712. Assert.Single(effect.Emitters);
  713. ParticleEmitter emitter = effect.Emitters[0];
  714. Assert.Single(emitter.Modifiers);
  715. VelocityModifier modifier = Assert.IsType<VelocityModifier>(effect.Emitters[0].Modifiers[0]);
  716. Assert.Equal(60.0f, modifier.Frequency);
  717. Assert.Equal("VelocityModifier", modifier.Name);
  718. Assert.Equal(0.0f, modifier.VelocityThreshold);
  719. }
  720. [Fact]
  721. public void ReadParticleEffect_VortexModifier_ReadsExpected()
  722. {
  723. string xml =
  724. $"""
  725. <?xml version="1.0" encoding="utf-8"?>
  726. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  727. <Emitters>
  728. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  729. <Parameters>
  730. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  731. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  732. <Color Kind="Constant" Constant="1,1,1" />
  733. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  734. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  735. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  736. <Mass Kind="Constant" Constant="1" />
  737. </Parameters>
  738. <Profile Type="PointProfile" />
  739. <Modifiers>
  740. <Modifier Name="VortexModifier" Frequency="60" Type="VortexModifier" Position="0,0" Strength="1" OuterRadius="2" InnerRadius="3" MaxVelocity="4" RotationAngle="5" />
  741. </Modifiers>
  742. </ParticleEmitter>
  743. </Emitters>
  744. </ParticleEffect>
  745. """;
  746. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  747. Assert.Single(effect.Emitters);
  748. ParticleEmitter emitter = effect.Emitters[0];
  749. Assert.Single(emitter.Modifiers);
  750. VortexModifier modifier = Assert.IsType<VortexModifier>(effect.Emitters[0].Modifiers[0]);
  751. Assert.Equal(60.0f, modifier.Frequency);
  752. Assert.Equal("VortexModifier", modifier.Name);
  753. Assert.Equal(Vector2.Zero, modifier.Position);
  754. Assert.Equal(1.0f, modifier.Strength);
  755. Assert.Equal(2.0f, modifier.OuterRadius);
  756. Assert.Equal(3.0f, modifier.InnerRadius);
  757. Assert.Equal(4.0f, modifier.MaxVelocity);
  758. Assert.Equal(5.0f, modifier.RotationAngle);
  759. }
  760. [Fact]
  761. public void ReadParticleEffect_ColorInterpolator_ReadsExpected()
  762. {
  763. string xml =
  764. $"""
  765. <?xml version="1.0" encoding="utf-8"?>
  766. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  767. <Emitters>
  768. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  769. <Parameters>
  770. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  771. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  772. <Color Kind="Constant" Constant="1,1,1" />
  773. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  774. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  775. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  776. <Mass Kind="Constant" Constant="1" />
  777. </Parameters>
  778. <Profile Type="PointProfile" />
  779. <Modifiers>
  780. <Modifier Name="AgeModifier" Frequency="60" Type="AgeModifier">
  781. <Interpolators>
  782. <Interpolator Name="ColorInterpolator" Type="ColorInterpolator" StartValue="0,0,0" EndValue="0,0,0" />
  783. </Interpolators>
  784. </Modifier>
  785. </Modifiers>
  786. </ParticleEmitter>
  787. </Emitters>
  788. </ParticleEffect>
  789. """;
  790. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  791. Assert.Single(effect.Emitters);
  792. ParticleEmitter emitter = effect.Emitters[0];
  793. Assert.Single(emitter.Modifiers);
  794. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  795. Assert.Single(modifier.Interpolators);
  796. ColorInterpolator interpolator = Assert.IsType<ColorInterpolator>(modifier.Interpolators[0]);
  797. Assert.Equal(new HslColor(0, 0, 0), interpolator.StartValue);
  798. Assert.Equal(new HslColor(0, 0, 0), interpolator.EndValue);
  799. }
  800. [Fact]
  801. public void ReadParticleEffect_HueInterpolator_ReadsExpected()
  802. {
  803. string xml =
  804. $"""
  805. <?xml version="1.0" encoding="utf-8"?>
  806. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  807. <Emitters>
  808. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  809. <Parameters>
  810. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  811. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  812. <Color Kind="Constant" Constant="1,1,1" />
  813. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  814. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  815. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  816. <Mass Kind="Constant" Constant="1" />
  817. </Parameters>
  818. <Profile Type="PointProfile" />
  819. <Modifiers>
  820. <Modifier Name="AgeModifier" Frequency="60" Type="AgeModifier">
  821. <Interpolators>
  822. <Interpolator Name="HueInterpolator" Type="HueInterpolator" StartValue="0" EndValue="0" />
  823. </Interpolators>
  824. </Modifier>
  825. </Modifiers>
  826. </ParticleEmitter>
  827. </Emitters>
  828. </ParticleEffect>
  829. """;
  830. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  831. Assert.Single(effect.Emitters);
  832. ParticleEmitter emitter = effect.Emitters[0];
  833. Assert.Single(emitter.Modifiers);
  834. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  835. Assert.Single(modifier.Interpolators);
  836. HueInterpolator interpolator = Assert.IsType<HueInterpolator>(modifier.Interpolators[0]);
  837. Assert.Equal(0.0f, interpolator.StartValue);
  838. Assert.Equal(0.0f, interpolator.EndValue);
  839. }
  840. [Fact]
  841. public void ReadParticleEffect_OpacityInterpolator_ReadsExpected()
  842. {
  843. string xml =
  844. $"""
  845. <?xml version="1.0" encoding="utf-8"?>
  846. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  847. <Emitters>
  848. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  849. <Parameters>
  850. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  851. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  852. <Color Kind="Constant" Constant="1,1,1" />
  853. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  854. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  855. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  856. <Mass Kind="Constant" Constant="1" />
  857. </Parameters>
  858. <Profile Type="PointProfile" />
  859. <Modifiers>
  860. <Modifier Name="AgeModifier" Frequency="60" Type="AgeModifier">
  861. <Interpolators>
  862. <Interpolator Name="OpacityInterpolator" Type="OpacityInterpolator" StartValue="0" EndValue="0" />
  863. </Interpolators>
  864. </Modifier>
  865. </Modifiers>
  866. </ParticleEmitter>
  867. </Emitters>
  868. </ParticleEffect>
  869. """;
  870. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  871. Assert.Single(effect.Emitters);
  872. ParticleEmitter emitter = effect.Emitters[0];
  873. Assert.Single(emitter.Modifiers);
  874. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  875. Assert.Single(modifier.Interpolators);
  876. OpacityInterpolator interpolator = Assert.IsType<OpacityInterpolator>(modifier.Interpolators[0]);
  877. Assert.Equal(0.0f, interpolator.StartValue);
  878. Assert.Equal(0.0f, interpolator.EndValue);
  879. }
  880. [Fact]
  881. public void ReadParticleEffect_RotationInterpolator_ReadsExpected()
  882. {
  883. string xml =
  884. $"""
  885. <?xml version="1.0" encoding="utf-8"?>
  886. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  887. <Emitters>
  888. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  889. <Parameters>
  890. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  891. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  892. <Color Kind="Constant" Constant="1,1,1" />
  893. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  894. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  895. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  896. <Mass Kind="Constant" Constant="1" />
  897. </Parameters>
  898. <Profile Type="PointProfile" />
  899. <Modifiers>
  900. <Modifier Name="AgeModifier" Frequency="60" Type="AgeModifier">
  901. <Interpolators>
  902. <Interpolator Name="RotationInterpolator" Type="RotationInterpolator" StartValue="0" EndValue="0" />
  903. </Interpolators>
  904. </Modifier>
  905. </Modifiers>
  906. </ParticleEmitter>
  907. </Emitters>
  908. </ParticleEffect>
  909. """;
  910. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  911. Assert.Single(effect.Emitters);
  912. ParticleEmitter emitter = effect.Emitters[0];
  913. Assert.Single(emitter.Modifiers);
  914. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  915. Assert.Single(modifier.Interpolators);
  916. RotationInterpolator interpolator = Assert.IsType<RotationInterpolator>(modifier.Interpolators[0]);
  917. Assert.Equal(0.0f, interpolator.StartValue);
  918. Assert.Equal(0.0f, interpolator.EndValue);
  919. }
  920. [Fact]
  921. public void ReadParticleEffect_ScaleInterpolator_ReadsExpected()
  922. {
  923. string xml =
  924. $"""
  925. <?xml version="1.0" encoding="utf-8"?>
  926. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  927. <Emitters>
  928. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  929. <Parameters>
  930. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  931. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  932. <Color Kind="Constant" Constant="1,1,1" />
  933. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  934. <Scale Kind="Random" RandomMin="0,0" RandomMax="1,1" />
  935. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  936. <Mass Kind="Constant" Constant="1" />
  937. </Parameters>
  938. <Profile Type="PointProfile" />
  939. <Modifiers>
  940. <Modifier Name="AgeModifier" Frequency="60" Type="AgeModifier">
  941. <Interpolators>
  942. <Interpolator Name="ScaleInterpolator" Type="ScaleInterpolator" StartValue="0,0" EndValue="1,1" />
  943. </Interpolators>
  944. </Modifier>
  945. </Modifiers>
  946. </ParticleEmitter>
  947. </Emitters>
  948. </ParticleEffect>
  949. """;
  950. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  951. Assert.Single(effect.Emitters);
  952. ParticleEmitter emitter = effect.Emitters[0];
  953. Assert.Single(emitter.Modifiers);
  954. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  955. Assert.Single(modifier.Interpolators);
  956. ScaleInterpolator interpolator = Assert.IsType<ScaleInterpolator>(modifier.Interpolators[0]);
  957. Assert.Equal(Vector2.Zero, interpolator.StartValue);
  958. Assert.Equal(Vector2.One, interpolator.EndValue);
  959. }
  960. [Fact]
  961. public void ReadParticleEffect_VelocityInterpolator_ReadsExpected()
  962. {
  963. string xml =
  964. $"""
  965. <?xml version="1.0" encoding="utf-8"?>
  966. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  967. <Emitters>
  968. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  969. <Parameters>
  970. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  971. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  972. <Color Kind="Constant" Constant="1,1,1" />
  973. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  974. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  975. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  976. <Mass Kind="Constant" Constant="1" />
  977. </Parameters>
  978. <Profile Type="PointProfile" />
  979. <Modifiers>
  980. <Modifier Name="AgeModifier" Frequency="60" Type="AgeModifier">
  981. <Interpolators>
  982. <Interpolator Name="VelocityInterpolator" Type="VelocityInterpolator" StartValue="0,0" EndValue="0,0" />
  983. </Interpolators>
  984. </Modifier>
  985. </Modifiers>
  986. </ParticleEmitter>
  987. </Emitters>
  988. </ParticleEffect>
  989. """;
  990. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  991. Assert.Single(effect.Emitters);
  992. ParticleEmitter emitter = effect.Emitters[0];
  993. Assert.Single(emitter.Modifiers);
  994. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  995. Assert.Single(modifier.Interpolators);
  996. VelocityInterpolator interpolator = Assert.IsType<VelocityInterpolator>(modifier.Interpolators[0]);
  997. Assert.Equal(Vector2.Zero, interpolator.StartValue);
  998. Assert.Equal(Vector2.Zero, interpolator.EndValue);
  999. }
  1000. }