ParticleEffectSerializerTests.cs 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191
  1. using System;
  2. using System.IO;
  3. using System.Xml;
  4. using Microsoft.Xna.Framework;
  5. using MonoGame.Extended.Particles;
  6. using MonoGame.Extended.Particles.Modifiers;
  7. using MonoGame.Extended.Particles.Modifiers.Containers;
  8. using MonoGame.Extended.Particles.Modifiers.Interpolators;
  9. using MonoGame.Extended.Particles.Profiles;
  10. namespace MonoGame.Extended.Tests.Particles;
  11. public sealed class ParticleEffectSerializerTests
  12. {
  13. private readonly MockContentManager _mockContentManager;
  14. public ParticleEffectSerializerTests()
  15. {
  16. _mockContentManager = new MockContentManager();
  17. }
  18. private ParticleEffect ReadParticleEffectFromXml(string xml)
  19. {
  20. using MemoryStream stream = new MemoryStream();
  21. using StreamWriter writer = new StreamWriter(stream);
  22. writer.Write(xml);
  23. writer.Flush();
  24. stream.Position = 0;
  25. return ParticleEffectSerializer.Deserialize(stream, _mockContentManager);
  26. }
  27. [Fact]
  28. public void Deserialize_NullStream_ThrowsArgumentNullException()
  29. {
  30. Assert.Throws<ArgumentNullException>(() => ParticleEffectSerializer.Deserialize((Stream)null!, _mockContentManager));
  31. }
  32. [Fact]
  33. public void Deserialize_NullContentManager_ThrowsArgumentNullException()
  34. {
  35. using MemoryStream stream = new MemoryStream();
  36. Assert.Throws<ArgumentNullException>(() => ParticleEffectSerializer.Deserialize(stream, null));
  37. }
  38. [Fact]
  39. public void Deserialize_InvalidXmlRoot_ThrowsXmlException()
  40. {
  41. string xml = "<InvalidRoot />";
  42. Assert.Throws<XmlException>(() => ReadParticleEffectFromXml(xml));
  43. }
  44. [Fact]
  45. public void Deserialize_EmptyEffect_ReturnsExpected()
  46. {
  47. string xml =
  48. """
  49. <?xml version="1.0" encoding="utf-8"?>
  50. <ParticleEffect Name="EmptyEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1" />
  51. """;
  52. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  53. Assert.Equal("EmptyEffect", effect.Name);
  54. Assert.Equal(Vector2.Zero, effect.Position);
  55. Assert.Equal(0.0f, effect.Rotation);
  56. Assert.Equal(Vector2.One, effect.Scale);
  57. Assert.Empty(effect.Emitters);
  58. Assert.True(effect.AutoTrigger);
  59. Assert.Equal(1.0f, effect.AutoTriggerFrequency);
  60. }
  61. [Fact]
  62. public void Deserialize_EmptyModifiers_ReturnsExpected()
  63. {
  64. string xml =
  65. $"""
  66. <?xml version="1.0" encoding="utf-8"?>
  67. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  68. <Emitters>
  69. <ParticleEmitter Name="EmptyModifiers" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  70. <Parameters>
  71. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  72. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  73. <Color Kind="Constant" Constant="1,1,1" />
  74. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  75. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  76. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  77. <Mass Kind="Constant" Constant="1" />
  78. </Parameters>
  79. <Profile Type="PointProfile" />
  80. </ParticleEmitter>
  81. </Emitters>
  82. </ParticleEffect>
  83. """;
  84. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  85. Assert.Single(effect.Emitters);
  86. ParticleEmitter emitter = effect.Emitters[0];
  87. Assert.Empty(emitter.Modifiers);
  88. }
  89. [Fact]
  90. public void Deserialize_BoxFillProfile_ReadsExpected()
  91. {
  92. string xml =
  93. $"""
  94. <?xml version="1.0" encoding="utf-8"?>
  95. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  96. <Emitters>
  97. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  98. <Parameters>
  99. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  100. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  101. <Color Kind="Constant" Constant="1,1,1" />
  102. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  103. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  104. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  105. <Mass Kind="Constant" Constant="1" />
  106. </Parameters>
  107. <Profile Type="BoxFillProfile" Width="1" Height="1" />
  108. </ParticleEmitter>
  109. </Emitters>
  110. </ParticleEffect>
  111. """;
  112. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  113. Assert.Single(effect.Emitters);
  114. ParticleEmitter emitter = effect.Emitters[0];
  115. BoxFillProfile profile = Assert.IsType<BoxFillProfile>(emitter.Profile);
  116. Assert.Equal(1.0f, profile.Height);
  117. Assert.Equal(1.0f, profile.Height);
  118. }
  119. [Fact]
  120. public void Deserialize_BoxProfile_ReadsExpected()
  121. {
  122. string xml =
  123. $"""
  124. <?xml version="1.0" encoding="utf-8"?>
  125. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  126. <Emitters>
  127. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  128. <Parameters>
  129. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  130. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  131. <Color Kind="Constant" Constant="1,1,1" />
  132. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  133. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  134. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  135. <Mass Kind="Constant" Constant="1" />
  136. </Parameters>
  137. <Profile Type="BoxProfile" Width="1" Height="1" />
  138. </ParticleEmitter>
  139. </Emitters>
  140. </ParticleEffect>
  141. """;
  142. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  143. Assert.Single(effect.Emitters);
  144. ParticleEmitter emitter = effect.Emitters[0];
  145. BoxProfile profile = Assert.IsType<BoxProfile>(emitter.Profile);
  146. Assert.Equal(1.0f, profile.Height);
  147. Assert.Equal(1.0f, profile.Height);
  148. }
  149. [Fact]
  150. public void DeserializeBoxUniformProfile_ReadsExpected()
  151. {
  152. string xml =
  153. $"""
  154. <?xml version="1.0" encoding="utf-8"?>
  155. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  156. <Emitters>
  157. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  158. <Parameters>
  159. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  160. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  161. <Color Kind="Constant" Constant="1,1,1" />
  162. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  163. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  164. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  165. <Mass Kind="Constant" Constant="1" />
  166. </Parameters>
  167. <Profile Type="BoxUniformProfile" Width="1" Height="1" />
  168. </ParticleEmitter>
  169. </Emitters>
  170. </ParticleEffect>
  171. """;
  172. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  173. Assert.Single(effect.Emitters);
  174. ParticleEmitter emitter = effect.Emitters[0];
  175. BoxUniformProfile profile = Assert.IsType<BoxUniformProfile>(emitter.Profile);
  176. Assert.Equal(1.0f, profile.Height);
  177. Assert.Equal(1.0f, profile.Height);
  178. }
  179. [Fact]
  180. public void DeserializeCircleProfile_ReadsExpected()
  181. {
  182. string xml =
  183. $"""
  184. <?xml version="1.0" encoding="utf-8"?>
  185. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  186. <Emitters>
  187. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  188. <Parameters>
  189. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  190. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  191. <Color Kind="Constant" Constant="1,1,1" />
  192. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  193. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  194. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  195. <Mass Kind="Constant" Constant="1" />
  196. </Parameters>
  197. <Profile Type="CircleProfile" Radius="1" Radiate="Out" />
  198. </ParticleEmitter>
  199. </Emitters>
  200. </ParticleEffect>
  201. """;
  202. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  203. Assert.Single(effect.Emitters);
  204. ParticleEmitter emitter = effect.Emitters[0];
  205. CircleProfile profile = Assert.IsType<CircleProfile>(emitter.Profile);
  206. Assert.Equal(1.0f, profile.Radius);
  207. Assert.Equal(CircleRadiation.Out, profile.Radiate);
  208. }
  209. [Fact]
  210. public void DeserializeLineProfile_ReadsExpected()
  211. {
  212. string xml =
  213. $"""
  214. <?xml version="1.0" encoding="utf-8"?>
  215. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  216. <Emitters>
  217. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  218. <Parameters>
  219. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  220. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  221. <Color Kind="Constant" Constant="1,1,1" />
  222. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  223. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  224. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  225. <Mass Kind="Constant" Constant="1" />
  226. </Parameters>
  227. <Profile Type="LineProfile" Axis="1,1" Length="1" Direction="0,0" Radiate="None" />
  228. </ParticleEmitter>
  229. </Emitters>
  230. </ParticleEffect>
  231. """;
  232. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  233. Assert.Single(effect.Emitters);
  234. ParticleEmitter emitter = effect.Emitters[0];
  235. LineProfile profile = Assert.IsType<LineProfile>(emitter.Profile);
  236. Assert.Equal(Vector2.One, profile.Axis);
  237. Assert.Equal(1.0f, profile.Length);
  238. }
  239. [Fact]
  240. public void DeserializePointProfile_ReadsExpected()
  241. {
  242. string xml =
  243. $"""
  244. <?xml version="1.0" encoding="utf-8"?>
  245. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  246. <Emitters>
  247. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  248. <Parameters>
  249. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  250. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  251. <Color Kind="Constant" Constant="1,1,1" />
  252. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  253. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  254. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  255. <Mass Kind="Constant" Constant="1" />
  256. </Parameters>
  257. <Profile Type="PointProfile" />
  258. </ParticleEmitter>
  259. </Emitters>
  260. </ParticleEffect>
  261. """;
  262. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  263. Assert.Single(effect.Emitters);
  264. ParticleEmitter emitter = effect.Emitters[0];
  265. Assert.IsType<PointProfile>(emitter.Profile);
  266. }
  267. [Fact]
  268. public void DeserializeRingProfile_ReadsExpected()
  269. {
  270. string xml =
  271. $"""
  272. <?xml version="1.0" encoding="utf-8"?>
  273. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  274. <Emitters>
  275. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  276. <Parameters>
  277. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  278. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  279. <Color Kind="Constant" Constant="1,1,1" />
  280. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  281. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  282. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  283. <Mass Kind="Constant" Constant="1" />
  284. </Parameters>
  285. <Profile Type="RingProfile" Radius="1" Radiate="In" />
  286. </ParticleEmitter>
  287. </Emitters>
  288. </ParticleEffect>
  289. """;
  290. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  291. Assert.Single(effect.Emitters);
  292. ParticleEmitter emitter = effect.Emitters[0];
  293. RingProfile profile = Assert.IsType<RingProfile>(emitter.Profile);
  294. Assert.Equal(1.0f, profile.Radius);
  295. Assert.Equal(CircleRadiation.In, profile.Radiate);
  296. }
  297. [Fact]
  298. public void DeserializeSprayProfile_ReadsExpected()
  299. {
  300. string xml =
  301. $"""
  302. <?xml version="1.0" encoding="utf-8"?>
  303. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  304. <Emitters>
  305. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  306. <Parameters>
  307. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  308. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  309. <Color Kind="Constant" Constant="1,1,1" />
  310. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  311. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  312. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  313. <Mass Kind="Constant" Constant="1" />
  314. </Parameters>
  315. <Profile Type="SprayProfile" Direction="1,1" Spread="1" />
  316. </ParticleEmitter>
  317. </Emitters>
  318. </ParticleEffect>
  319. """;
  320. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  321. Assert.Single(effect.Emitters);
  322. ParticleEmitter emitter = effect.Emitters[0];
  323. SprayProfile profile = Assert.IsType<SprayProfile>(emitter.Profile);
  324. Assert.Equal(Vector2.One, profile.Direction);
  325. Assert.Equal(1.0f, profile.Spread);
  326. }
  327. [Fact]
  328. public void DeserializeAgeModifier_ReadsExpected()
  329. {
  330. string xml =
  331. $"""
  332. <?xml version="1.0" encoding="utf-8"?>
  333. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  334. <Emitters>
  335. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  336. <Parameters>
  337. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  338. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  339. <Color Kind="Constant" Constant="1,1,1" />
  340. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  341. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  342. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  343. <Mass Kind="Constant" Constant="1" />
  344. </Parameters>
  345. <Profile Type="PointProfile" />
  346. <Modifiers>
  347. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier" />
  348. </Modifiers>
  349. </ParticleEmitter>
  350. </Emitters>
  351. </ParticleEffect>
  352. """;
  353. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  354. Assert.Single(effect.Emitters);
  355. ParticleEmitter emitter = effect.Emitters[0];
  356. Assert.Single(emitter.Modifiers);
  357. AgeModifier modifier = Assert.IsType<AgeModifier>(effect.Emitters[0].Modifiers[0]);
  358. Assert.True(modifier.Enabled);
  359. Assert.Equal(60.0f, modifier.Frequency);
  360. Assert.Equal(nameof(AgeModifier), modifier.Name);
  361. }
  362. [Fact]
  363. public void DeserializeCircleContainerModifier_ReadsExpected()
  364. {
  365. string xml =
  366. $"""
  367. <?xml version="1.0" encoding="utf-8"?>
  368. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  369. <Emitters>
  370. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  371. <Parameters>
  372. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  373. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  374. <Color Kind="Constant" Constant="1,1,1" />
  375. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  376. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  377. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  378. <Mass Kind="Constant" Constant="1" />
  379. </Parameters>
  380. <Profile Type="PointProfile" />
  381. <Modifiers>
  382. <Modifier Name="CircleContainerModifier" Enabled="True" Frequency="60" Type="CircleContainerModifier" Radius="0" Inside="True" RestitutionCoefficient="1" />
  383. </Modifiers>
  384. </ParticleEmitter>
  385. </Emitters>
  386. </ParticleEffect>
  387. """;
  388. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  389. Assert.Single(effect.Emitters);
  390. ParticleEmitter emitter = effect.Emitters[0];
  391. Assert.Single(emitter.Modifiers);
  392. CircleContainerModifier modifier = Assert.IsType<CircleContainerModifier>(effect.Emitters[0].Modifiers[0]);
  393. Assert.Equal(60.0f, modifier.Frequency);
  394. Assert.True(modifier.Enabled);
  395. Assert.Equal(nameof(CircleContainerModifier), modifier.Name);
  396. Assert.Equal(0.0f, modifier.Radius);
  397. Assert.True(modifier.Inside);
  398. Assert.Equal(1.0f, modifier.RestitutionCoefficient);
  399. }
  400. [Fact]
  401. public void DeserializeDragModifier_ReadsExpected()
  402. {
  403. string xml =
  404. $"""
  405. <?xml version="1.0" encoding="utf-8"?>
  406. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  407. <Emitters>
  408. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  409. <Parameters>
  410. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  411. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  412. <Color Kind="Constant" Constant="1,1,1" />
  413. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  414. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  415. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  416. <Mass Kind="Constant" Constant="1" />
  417. </Parameters>
  418. <Profile Type="PointProfile" />
  419. <Modifiers>
  420. <Modifier Name="DragModifier" Enabled="True" Frequency="60" Type="DragModifier" DragCoefficient="0.47" Density="0.5" />
  421. </Modifiers>
  422. </ParticleEmitter>
  423. </Emitters>
  424. </ParticleEffect>
  425. """;
  426. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  427. Assert.Single(effect.Emitters);
  428. ParticleEmitter emitter = effect.Emitters[0];
  429. Assert.Single(emitter.Modifiers);
  430. DragModifier modifier = Assert.IsType<DragModifier>(effect.Emitters[0].Modifiers[0]);
  431. Assert.Equal(60.0f, modifier.Frequency);
  432. Assert.True(modifier.Enabled);
  433. Assert.Equal(nameof(DragModifier), modifier.Name);
  434. Assert.Equal(0.47f, modifier.DragCoefficient);
  435. Assert.Equal(0.5f, modifier.Density);
  436. }
  437. [Fact]
  438. public void DeserializeLinearGravityModifier_ReadsExpected()
  439. {
  440. string xml =
  441. $"""
  442. <?xml version="1.0" encoding="utf-8"?>
  443. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  444. <Emitters>
  445. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  446. <Parameters>
  447. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  448. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  449. <Color Kind="Constant" Constant="1,1,1" />
  450. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  451. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  452. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  453. <Mass Kind="Constant" Constant="1" />
  454. </Parameters>
  455. <Profile Type="PointProfile" />
  456. <Modifiers>
  457. <Modifier Name="LinearGravityModifier" Enabled="True" Frequency="60" Type="LinearGravityModifier" Direction="0,0" Strength="0" />
  458. </Modifiers>
  459. </ParticleEmitter>
  460. </Emitters>
  461. </ParticleEffect>
  462. """;
  463. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  464. Assert.Single(effect.Emitters);
  465. ParticleEmitter emitter = effect.Emitters[0];
  466. Assert.Single(emitter.Modifiers);
  467. LinearGravityModifier modifier = Assert.IsType<LinearGravityModifier>(effect.Emitters[0].Modifiers[0]);
  468. Assert.Equal(60.0f, modifier.Frequency);
  469. Assert.True(modifier.Enabled);
  470. Assert.Equal(nameof(LinearGravityModifier), modifier.Name);
  471. Assert.Equal(Vector2.Zero, modifier.Direction);
  472. Assert.Equal(0.0f, modifier.Strength);
  473. }
  474. [Fact]
  475. public void DeserializeOpacityFastFadeModifier_ReadsExpected()
  476. {
  477. string xml =
  478. $"""
  479. <?xml version="1.0" encoding="utf-8"?>
  480. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  481. <Emitters>
  482. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  483. <Parameters>
  484. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  485. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  486. <Color Kind="Constant" Constant="1,1,1" />
  487. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  488. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  489. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  490. <Mass Kind="Constant" Constant="1" />
  491. </Parameters>
  492. <Profile Type="PointProfile" />
  493. <Modifiers>
  494. <Modifier Name="OpacityFastFadeModifier" Enabled="True" Frequency="60" Type="OpacityFastFadeModifier" />
  495. </Modifiers>
  496. </ParticleEmitter>
  497. </Emitters>
  498. </ParticleEffect>
  499. """;
  500. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  501. Assert.Single(effect.Emitters);
  502. ParticleEmitter emitter = effect.Emitters[0];
  503. Assert.Single(emitter.Modifiers);
  504. OpacityFastFadeModifier modifier = Assert.IsType<OpacityFastFadeModifier>(effect.Emitters[0].Modifiers[0]);
  505. Assert.Equal(60.0f, modifier.Frequency);
  506. Assert.True(modifier.Enabled);
  507. Assert.Equal(nameof(OpacityFastFadeModifier), modifier.Name);
  508. }
  509. [Fact]
  510. public void DeserializeRectangleContainerModifier_ReadsExpected()
  511. {
  512. string xml =
  513. $"""
  514. <?xml version="1.0" encoding="utf-8"?>
  515. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  516. <Emitters>
  517. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  518. <Parameters>
  519. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  520. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  521. <Color Kind="Constant" Constant="1,1,1" />
  522. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  523. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  524. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  525. <Mass Kind="Constant" Constant="1" />
  526. </Parameters>
  527. <Profile Type="PointProfile" />
  528. <Modifiers>
  529. <Modifier Name="RectangleContainerModifier" Enabled="True" Frequency="60" Type="RectangleContainerModifier" Width="0" Height="0" RestitutionCoefficient="1" />
  530. </Modifiers>
  531. </ParticleEmitter>
  532. </Emitters>
  533. </ParticleEffect>
  534. """;
  535. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  536. Assert.Single(effect.Emitters);
  537. ParticleEmitter emitter = effect.Emitters[0];
  538. Assert.Single(emitter.Modifiers);
  539. RectangleContainerModifier modifier = Assert.IsType<RectangleContainerModifier>(effect.Emitters[0].Modifiers[0]);
  540. Assert.Equal(60.0f, modifier.Frequency);
  541. Assert.True(modifier.Enabled);
  542. Assert.Equal(nameof(RectangleContainerModifier), modifier.Name);
  543. Assert.Equal(0.0f, modifier.Width);
  544. Assert.Equal(0.0f, modifier.Height);
  545. Assert.Equal(1.0f, modifier.RestitutionCoefficient);
  546. }
  547. [Fact]
  548. public void DeserializeRectangleLoopContainerModifier_ReadsExpected()
  549. {
  550. string xml =
  551. $"""
  552. <?xml version="1.0" encoding="utf-8"?>
  553. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  554. <Emitters>
  555. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  556. <Parameters>
  557. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  558. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  559. <Color Kind="Constant" Constant="1,1,1" />
  560. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  561. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  562. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  563. <Mass Kind="Constant" Constant="1" />
  564. </Parameters>
  565. <Profile Type="PointProfile" />
  566. <Modifiers>
  567. <Modifier Name="RectangleLoopContainerModifier" Enabled="True" Frequency="60" Type="RectangleLoopContainerModifier" Width="0" Height="0" />
  568. </Modifiers>
  569. </ParticleEmitter>
  570. </Emitters>
  571. </ParticleEffect>
  572. """;
  573. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  574. Assert.Single(effect.Emitters);
  575. ParticleEmitter emitter = effect.Emitters[0];
  576. Assert.Single(emitter.Modifiers);
  577. RectangleLoopContainerModifier modifier = Assert.IsType<RectangleLoopContainerModifier>(effect.Emitters[0].Modifiers[0]);
  578. Assert.Equal(60.0f, modifier.Frequency);
  579. Assert.True(modifier.Enabled);
  580. Assert.Equal(nameof(RectangleLoopContainerModifier), modifier.Name);
  581. Assert.Equal(0.0f, modifier.Width);
  582. Assert.Equal(0.0f, modifier.Height);
  583. }
  584. [Fact]
  585. public void DeserializeRotationModifier_ReadsExpected()
  586. {
  587. string xml =
  588. $"""
  589. <?xml version="1.0" encoding="utf-8"?>
  590. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  591. <Emitters>
  592. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  593. <Parameters>
  594. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  595. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  596. <Color Kind="Constant" Constant="1,1,1" />
  597. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  598. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  599. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  600. <Mass Kind="Constant" Constant="1" />
  601. </Parameters>
  602. <Profile Type="PointProfile" />
  603. <Modifiers>
  604. <Modifier Name="RotationModifier" Enabled="True" Frequency="60" Type="RotationModifier" RotationRate="0" />
  605. </Modifiers>
  606. </ParticleEmitter>
  607. </Emitters>
  608. </ParticleEffect>
  609. """;
  610. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  611. Assert.Single(effect.Emitters);
  612. ParticleEmitter emitter = effect.Emitters[0];
  613. Assert.Single(emitter.Modifiers);
  614. RotationModifier modifier = Assert.IsType<RotationModifier>(effect.Emitters[0].Modifiers[0]);
  615. Assert.Equal(60.0f, modifier.Frequency);
  616. Assert.True(modifier.Enabled);
  617. Assert.Equal(nameof(RotationModifier), modifier.Name);
  618. Assert.Equal(0.0f, modifier.RotationRate);
  619. }
  620. [Fact]
  621. public void DeserializeVelocityColorModifier_ReadsExpected()
  622. {
  623. string xml =
  624. $"""
  625. <?xml version="1.0" encoding="utf-8"?>
  626. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  627. <Emitters>
  628. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  629. <Parameters>
  630. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  631. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  632. <Color Kind="Constant" Constant="1,1,1" />
  633. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  634. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  635. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  636. <Mass Kind="Constant" Constant="1" />
  637. </Parameters>
  638. <Profile Type="PointProfile" />
  639. <Modifiers>
  640. <Modifier Name="VelocityColorModifier" Enabled="True" Frequency="60" Type="VelocityColorModifier" StationaryColor="0,0,0" VelocityColor="0,0,0" VelocityThreshold="0" />
  641. </Modifiers>
  642. </ParticleEmitter>
  643. </Emitters>
  644. </ParticleEffect>
  645. """;
  646. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  647. Assert.Single(effect.Emitters);
  648. ParticleEmitter emitter = effect.Emitters[0];
  649. Assert.Single(emitter.Modifiers);
  650. VelocityColorModifier modifier = Assert.IsType<VelocityColorModifier>(effect.Emitters[0].Modifiers[0]);
  651. Assert.Equal(60.0f, modifier.Frequency);
  652. Assert.True(modifier.Enabled);
  653. Assert.Equal(nameof(VelocityColorModifier), modifier.Name);
  654. Assert.Equal(new HslColor(0, 0, 0), modifier.StationaryColor);
  655. Assert.Equal(new HslColor(0, 0, 0), modifier.VelocityColor);
  656. Assert.Equal(0.0f, modifier.VelocityThreshold);
  657. }
  658. [Fact]
  659. public void DeserializeVelocityModifier_ReadsExpected()
  660. {
  661. string xml =
  662. $"""
  663. <?xml version="1.0" encoding="utf-8"?>
  664. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  665. <Emitters>
  666. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  667. <Parameters>
  668. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  669. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  670. <Color Kind="Constant" Constant="1,1,1" />
  671. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  672. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  673. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  674. <Mass Kind="Constant" Constant="1" />
  675. </Parameters>
  676. <Profile Type="PointProfile" />
  677. <Modifiers>
  678. <Modifier Name="VelocityModifier" Enabled="True" Frequency="60" Type="VelocityModifier" VelocityThreshold="0" />
  679. </Modifiers>
  680. </ParticleEmitter>
  681. </Emitters>
  682. </ParticleEffect>
  683. """;
  684. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  685. Assert.Single(effect.Emitters);
  686. ParticleEmitter emitter = effect.Emitters[0];
  687. Assert.Single(emitter.Modifiers);
  688. VelocityModifier modifier = Assert.IsType<VelocityModifier>(effect.Emitters[0].Modifiers[0]);
  689. Assert.Equal(60.0f, modifier.Frequency);
  690. Assert.Equal(nameof(VelocityModifier), modifier.Name);
  691. Assert.Equal(0.0f, modifier.VelocityThreshold);
  692. }
  693. [Fact]
  694. public void DeserializeVortexModifier_ReadsExpected()
  695. {
  696. string xml =
  697. $"""
  698. <?xml version="1.0" encoding="utf-8"?>
  699. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  700. <Emitters>
  701. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  702. <Parameters>
  703. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  704. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  705. <Color Kind="Constant" Constant="1,1,1" />
  706. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  707. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  708. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  709. <Mass Kind="Constant" Constant="1" />
  710. </Parameters>
  711. <Profile Type="PointProfile" />
  712. <Modifiers>
  713. <Modifier Name="VortexModifier" Enabled="True" Frequency="60" Type="VortexModifier" Position="0,0" Strength="1" OuterRadius="2" InnerRadius="3" MaxVelocity="4" RotationAngle="5" />
  714. </Modifiers>
  715. </ParticleEmitter>
  716. </Emitters>
  717. </ParticleEffect>
  718. """;
  719. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  720. Assert.Single(effect.Emitters);
  721. ParticleEmitter emitter = effect.Emitters[0];
  722. Assert.Single(emitter.Modifiers);
  723. VortexModifier modifier = Assert.IsType<VortexModifier>(effect.Emitters[0].Modifiers[0]);
  724. Assert.Equal(60.0f, modifier.Frequency);
  725. Assert.Equal(nameof(VortexModifier), modifier.Name);
  726. Assert.Equal(Vector2.Zero, modifier.Position);
  727. Assert.Equal(1.0f, modifier.Strength);
  728. Assert.Equal(2.0f, modifier.OuterRadius);
  729. Assert.Equal(3.0f, modifier.InnerRadius);
  730. Assert.Equal(4.0f, modifier.MaxVelocity);
  731. Assert.Equal(5.0f, modifier.RotationAngle);
  732. }
  733. [Fact]
  734. public void DeserializeColorInterpolator_ReadsExpected()
  735. {
  736. string xml =
  737. $"""
  738. <?xml version="1.0" encoding="utf-8"?>
  739. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  740. <Emitters>
  741. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  742. <Parameters>
  743. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  744. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  745. <Color Kind="Constant" Constant="1,1,1" />
  746. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  747. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  748. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  749. <Mass Kind="Constant" Constant="1" />
  750. </Parameters>
  751. <Profile Type="PointProfile" />
  752. <Modifiers>
  753. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  754. <Interpolators>
  755. <Interpolator Name="ColorInterpolator" Type="ColorInterpolator" Enabled="True" StartValue="0,0,0" EndValue="0,0,0" />
  756. </Interpolators>
  757. </Modifier>
  758. </Modifiers>
  759. </ParticleEmitter>
  760. </Emitters>
  761. </ParticleEffect>
  762. """;
  763. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  764. Assert.Single(effect.Emitters);
  765. ParticleEmitter emitter = effect.Emitters[0];
  766. Assert.Single(emitter.Modifiers);
  767. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  768. Assert.Single(modifier.Interpolators);
  769. ColorInterpolator interpolator = Assert.IsType<ColorInterpolator>(modifier.Interpolators[0]);
  770. Assert.True(interpolator.Enabled);
  771. Assert.Equal(new HslColor(0, 0, 0), interpolator.StartValue);
  772. Assert.Equal(new HslColor(0, 0, 0), interpolator.EndValue);
  773. }
  774. [Fact]
  775. public void DeserializeHueInterpolator_ReadsExpected()
  776. {
  777. string xml =
  778. $"""
  779. <?xml version="1.0" encoding="utf-8"?>
  780. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  781. <Emitters>
  782. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  783. <Parameters>
  784. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  785. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  786. <Color Kind="Constant" Constant="1,1,1" />
  787. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  788. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  789. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  790. <Mass Kind="Constant" Constant="1" />
  791. </Parameters>
  792. <Profile Type="PointProfile" />
  793. <Modifiers>
  794. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  795. <Interpolators>
  796. <Interpolator Name="HueInterpolator" Type="HueInterpolator" Enabled="True" StartValue="0" EndValue="0" />
  797. </Interpolators>
  798. </Modifier>
  799. </Modifiers>
  800. </ParticleEmitter>
  801. </Emitters>
  802. </ParticleEffect>
  803. """;
  804. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  805. Assert.Single(effect.Emitters);
  806. ParticleEmitter emitter = effect.Emitters[0];
  807. Assert.Single(emitter.Modifiers);
  808. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  809. Assert.Single(modifier.Interpolators);
  810. HueInterpolator interpolator = Assert.IsType<HueInterpolator>(modifier.Interpolators[0]);
  811. Assert.True(interpolator.Enabled);
  812. Assert.Equal(0.0f, interpolator.StartValue);
  813. Assert.Equal(0.0f, interpolator.EndValue);
  814. }
  815. [Fact]
  816. public void DeserializeOpacityInterpolator_ReadsExpected()
  817. {
  818. string xml =
  819. $"""
  820. <?xml version="1.0" encoding="utf-8"?>
  821. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  822. <Emitters>
  823. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  824. <Parameters>
  825. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  826. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  827. <Color Kind="Constant" Constant="1,1,1" />
  828. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  829. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  830. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  831. <Mass Kind="Constant" Constant="1" />
  832. </Parameters>
  833. <Profile Type="PointProfile" />
  834. <Modifiers>
  835. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  836. <Interpolators>
  837. <Interpolator Name="OpacityInterpolator" Type="OpacityInterpolator" Enabled="True" StartValue="0" EndValue="0" />
  838. </Interpolators>
  839. </Modifier>
  840. </Modifiers>
  841. </ParticleEmitter>
  842. </Emitters>
  843. </ParticleEffect>
  844. """;
  845. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  846. Assert.Single(effect.Emitters);
  847. ParticleEmitter emitter = effect.Emitters[0];
  848. Assert.Single(emitter.Modifiers);
  849. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  850. Assert.Single(modifier.Interpolators);
  851. OpacityInterpolator interpolator = Assert.IsType<OpacityInterpolator>(modifier.Interpolators[0]);
  852. Assert.True(interpolator.Enabled);
  853. Assert.Equal(0.0f, interpolator.StartValue);
  854. Assert.Equal(0.0f, interpolator.EndValue);
  855. }
  856. [Fact]
  857. public void DeserializeRotationInterpolator_ReadsExpected()
  858. {
  859. string xml =
  860. $"""
  861. <?xml version="1.0" encoding="utf-8"?>
  862. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  863. <Emitters>
  864. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  865. <Parameters>
  866. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  867. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  868. <Color Kind="Constant" Constant="1,1,1" />
  869. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  870. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  871. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  872. <Mass Kind="Constant" Constant="1" />
  873. </Parameters>
  874. <Profile Type="PointProfile" />
  875. <Modifiers>
  876. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  877. <Interpolators>
  878. <Interpolator Name="RotationInterpolator" Type="RotationInterpolator" Enabled="True" StartValue="0" EndValue="0" />
  879. </Interpolators>
  880. </Modifier>
  881. </Modifiers>
  882. </ParticleEmitter>
  883. </Emitters>
  884. </ParticleEffect>
  885. """;
  886. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  887. Assert.Single(effect.Emitters);
  888. ParticleEmitter emitter = effect.Emitters[0];
  889. Assert.Single(emitter.Modifiers);
  890. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  891. Assert.Single(modifier.Interpolators);
  892. RotationInterpolator interpolator = Assert.IsType<RotationInterpolator>(modifier.Interpolators[0]);
  893. Assert.True(interpolator.Enabled);
  894. Assert.Equal(0.0f, interpolator.StartValue);
  895. Assert.Equal(0.0f, interpolator.EndValue);
  896. }
  897. [Fact]
  898. public void DeserializeScaleInterpolator_ReadsExpected()
  899. {
  900. string xml =
  901. $"""
  902. <?xml version="1.0" encoding="utf-8"?>
  903. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  904. <Emitters>
  905. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  906. <Parameters>
  907. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  908. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  909. <Color Kind="Constant" Constant="1,1,1" />
  910. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  911. <Scale Kind="Random" RandomMin="0,0" RandomMax="1,1" />
  912. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  913. <Mass Kind="Constant" Constant="1" />
  914. </Parameters>
  915. <Profile Type="PointProfile" />
  916. <Modifiers>
  917. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  918. <Interpolators>
  919. <Interpolator Name="ScaleInterpolator" Type="ScaleInterpolator" Enabled="True" StartValue="0,0" EndValue="1,1" />
  920. </Interpolators>
  921. </Modifier>
  922. </Modifiers>
  923. </ParticleEmitter>
  924. </Emitters>
  925. </ParticleEffect>
  926. """;
  927. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  928. Assert.Single(effect.Emitters);
  929. ParticleEmitter emitter = effect.Emitters[0];
  930. Assert.Single(emitter.Modifiers);
  931. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  932. Assert.Single(modifier.Interpolators);
  933. ScaleInterpolator interpolator = Assert.IsType<ScaleInterpolator>(modifier.Interpolators[0]);
  934. Assert.True(interpolator.Enabled);
  935. Assert.Equal(Vector2.Zero, interpolator.StartValue);
  936. Assert.Equal(Vector2.One, interpolator.EndValue);
  937. }
  938. [Fact]
  939. public void DeserializeVelocityInterpolator_ReadsExpected()
  940. {
  941. string xml =
  942. $"""
  943. <?xml version="1.0" encoding="utf-8"?>
  944. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  945. <Emitters>
  946. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  947. <Parameters>
  948. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  949. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  950. <Color Kind="Constant" Constant="1,1,1" />
  951. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  952. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  953. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  954. <Mass Kind="Constant" Constant="1" />
  955. </Parameters>
  956. <Profile Type="PointProfile" />
  957. <Modifiers>
  958. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  959. <Interpolators>
  960. <Interpolator Name="VelocityInterpolator" Type="VelocityInterpolator" Enabled="True" StartValue="0,0" EndValue="0,0" />
  961. </Interpolators>
  962. </Modifier>
  963. </Modifiers>
  964. </ParticleEmitter>
  965. </Emitters>
  966. </ParticleEffect>
  967. """;
  968. ParticleEffect effect = ReadParticleEffectFromXml(xml);
  969. Assert.Single(effect.Emitters);
  970. ParticleEmitter emitter = effect.Emitters[0];
  971. Assert.Single(emitter.Modifiers);
  972. AgeModifier modifier = Assert.IsType<AgeModifier>(emitter.Modifiers[0]);
  973. Assert.Single(modifier.Interpolators);
  974. VelocityInterpolator interpolator = Assert.IsType<VelocityInterpolator>(modifier.Interpolators[0]);
  975. Assert.True(interpolator.Enabled);
  976. Assert.Equal(Vector2.Zero, interpolator.StartValue);
  977. Assert.Equal(Vector2.Zero, interpolator.EndValue);
  978. }
  979. [Fact]
  980. public void SerializeNulLEffect_ThrowsArgumentNullException()
  981. {
  982. using MemoryStream stream = new MemoryStream();
  983. using ParticleEffectWriter writer = new ParticleEffectWriter(stream);
  984. Assert.Throws<ArgumentNullException>(() => writer.WriteParticleEffect(null));
  985. }
  986. [Fact]
  987. public void SerializeEmptyEffect_WritesMinimalXml()
  988. {
  989. ParticleEffect effect = new ParticleEffect("EmptyEffect");
  990. string expected =
  991. $"""
  992. <?xml version="1.0" encoding="utf-8"?>
  993. <ParticleEffect Name="EmptyEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1" />
  994. """;
  995. AssertParticleEffect(effect, expected);
  996. }
  997. [Fact]
  998. public void SerializeEmptyModifiers_WritesMinimalXml()
  999. {
  1000. ParticleEffect effect = new ParticleEffect("TestEffect");
  1001. ParticleEmitter emitter = new ParticleEmitter(1);
  1002. emitter.Name = "EmptyModifiers";
  1003. effect.Emitters.Add(emitter);
  1004. string expected =
  1005. $"""
  1006. <?xml version="1.0" encoding="utf-8"?>
  1007. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1008. <Emitters>
  1009. <ParticleEmitter Name="EmptyModifiers" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1010. <Parameters>
  1011. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1012. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1013. <Color Kind="Constant" Constant="1,1,1" />
  1014. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1015. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1016. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1017. <Mass Kind="Constant" Constant="1" />
  1018. </Parameters>
  1019. <Profile Type="PointProfile" />
  1020. </ParticleEmitter>
  1021. </Emitters>
  1022. </ParticleEffect>
  1023. """;
  1024. AssertParticleEffect(effect, expected);
  1025. }
  1026. [Fact]
  1027. public void SerializeEmptyInterpolators_WritesMinimalXml()
  1028. {
  1029. ParticleEffect effect = new ParticleEffect("TestEffect");
  1030. ParticleEmitter emitter = new ParticleEmitter(1);
  1031. emitter.Name = "TestEmitter";
  1032. effect.Emitters.Add(emitter);
  1033. AgeModifier ageModifier = new AgeModifier();
  1034. emitter.Modifiers.Add(ageModifier);
  1035. string expected =
  1036. $"""
  1037. <?xml version="1.0" encoding="utf-8"?>
  1038. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1039. <Emitters>
  1040. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1041. <Parameters>
  1042. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1043. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1044. <Color Kind="Constant" Constant="1,1,1" />
  1045. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1046. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1047. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1048. <Mass Kind="Constant" Constant="1" />
  1049. </Parameters>
  1050. <Profile Type="PointProfile" />
  1051. <Modifiers>
  1052. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier" />
  1053. </Modifiers>
  1054. </ParticleEmitter>
  1055. </Emitters>
  1056. </ParticleEffect>
  1057. """;
  1058. AssertParticleEffect(effect, expected);
  1059. }
  1060. [Fact]
  1061. public void SerializeBoxFillProfile_WritesExpected()
  1062. {
  1063. ParticleEffect effect = new ParticleEffect("TestEffect");
  1064. ParticleEmitter emitter = new ParticleEmitter(1);
  1065. emitter.Name = "TestEmitter";
  1066. emitter.Profile = Profile.BoxFill(1.0f, 2.0f);
  1067. effect.Emitters.Add(emitter);
  1068. string expected =
  1069. $"""
  1070. <?xml version="1.0" encoding="utf-8"?>
  1071. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1072. <Emitters>
  1073. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1074. <Parameters>
  1075. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1076. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1077. <Color Kind="Constant" Constant="1,1,1" />
  1078. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1079. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1080. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1081. <Mass Kind="Constant" Constant="1" />
  1082. </Parameters>
  1083. <Profile Type="BoxFillProfile" Width="1" Height="2" />
  1084. </ParticleEmitter>
  1085. </Emitters>
  1086. </ParticleEffect>
  1087. """;
  1088. AssertParticleEffect(effect, expected);
  1089. }
  1090. [Fact]
  1091. public void SerializeBoxProfile_WritesExpected()
  1092. {
  1093. ParticleEffect effect = new ParticleEffect("TestEffect");
  1094. ParticleEmitter emitter = new ParticleEmitter(1);
  1095. emitter.Name = "TestEmitter";
  1096. emitter.Profile = Profile.Box(1.0f, 2.0f);
  1097. effect.Emitters.Add(emitter);
  1098. string expected =
  1099. $"""
  1100. <?xml version="1.0" encoding="utf-8"?>
  1101. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1102. <Emitters>
  1103. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1104. <Parameters>
  1105. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1106. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1107. <Color Kind="Constant" Constant="1,1,1" />
  1108. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1109. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1110. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1111. <Mass Kind="Constant" Constant="1" />
  1112. </Parameters>
  1113. <Profile Type="BoxProfile" Width="1" Height="2" />
  1114. </ParticleEmitter>
  1115. </Emitters>
  1116. </ParticleEffect>
  1117. """;
  1118. AssertParticleEffect(effect, expected);
  1119. }
  1120. [Fact]
  1121. public void SerializeBoxUniformProfile_WritesExpected()
  1122. {
  1123. ParticleEffect effect = new ParticleEffect("TestEffect");
  1124. ParticleEmitter emitter = new ParticleEmitter(1);
  1125. emitter.Name = "TestEmitter";
  1126. emitter.Profile = Profile.BoxUniform(1.0f, 2.0f);
  1127. effect.Emitters.Add(emitter);
  1128. string expected =
  1129. $"""
  1130. <?xml version="1.0" encoding="utf-8"?>
  1131. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1132. <Emitters>
  1133. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1134. <Parameters>
  1135. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1136. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1137. <Color Kind="Constant" Constant="1,1,1" />
  1138. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1139. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1140. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1141. <Mass Kind="Constant" Constant="1" />
  1142. </Parameters>
  1143. <Profile Type="BoxUniformProfile" Width="1" Height="2" />
  1144. </ParticleEmitter>
  1145. </Emitters>
  1146. </ParticleEffect>
  1147. """;
  1148. AssertParticleEffect(effect, expected);
  1149. }
  1150. [Fact]
  1151. public void SerializeCircleProfile_WritesExpected()
  1152. {
  1153. ParticleEffect effect = new ParticleEffect("TestEffect");
  1154. ParticleEmitter emitter = new ParticleEmitter(1);
  1155. emitter.Name = "TestEmitter";
  1156. emitter.Profile = Profile.Circle(1.0f, CircleRadiation.Out);
  1157. effect.Emitters.Add(emitter);
  1158. string expected =
  1159. $"""
  1160. <?xml version="1.0" encoding="utf-8"?>
  1161. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1162. <Emitters>
  1163. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1164. <Parameters>
  1165. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1166. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1167. <Color Kind="Constant" Constant="1,1,1" />
  1168. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1169. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1170. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1171. <Mass Kind="Constant" Constant="1" />
  1172. </Parameters>
  1173. <Profile Type="CircleProfile" Radius="1" Radiate="Out" />
  1174. </ParticleEmitter>
  1175. </Emitters>
  1176. </ParticleEffect>
  1177. """;
  1178. AssertParticleEffect(effect, expected);
  1179. }
  1180. [Fact]
  1181. public void SerializeLineProfile_WritesExpected()
  1182. {
  1183. ParticleEffect effect = new ParticleEffect("TestEffect");
  1184. ParticleEmitter emitter = new ParticleEmitter(1);
  1185. emitter.Name = "TestEmitter";
  1186. emitter.Profile = Profile.Line(Vector2.One, 1.0f);
  1187. effect.Emitters.Add(emitter);
  1188. string expected =
  1189. $"""
  1190. <?xml version="1.0" encoding="utf-8"?>
  1191. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1192. <Emitters>
  1193. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1194. <Parameters>
  1195. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1196. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1197. <Color Kind="Constant" Constant="1,1,1" />
  1198. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1199. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1200. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1201. <Mass Kind="Constant" Constant="1" />
  1202. </Parameters>
  1203. <Profile Type="LineProfile" Axis="1,1" Length="1" Radiate="None" Direction="0,1" />
  1204. </ParticleEmitter>
  1205. </Emitters>
  1206. </ParticleEffect>
  1207. """;
  1208. AssertParticleEffect(effect, expected);
  1209. }
  1210. [Fact]
  1211. public void SerializePointProfile_WritesExpected()
  1212. {
  1213. ParticleEffect effect = new ParticleEffect("TestEffect");
  1214. ParticleEmitter emitter = new ParticleEmitter(1);
  1215. emitter.Name = "TestEmitter";
  1216. emitter.Profile = Profile.Point();
  1217. effect.Emitters.Add(emitter);
  1218. string expected =
  1219. $"""
  1220. <?xml version="1.0" encoding="utf-8"?>
  1221. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1222. <Emitters>
  1223. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1224. <Parameters>
  1225. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1226. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1227. <Color Kind="Constant" Constant="1,1,1" />
  1228. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1229. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1230. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1231. <Mass Kind="Constant" Constant="1" />
  1232. </Parameters>
  1233. <Profile Type="PointProfile" />
  1234. </ParticleEmitter>
  1235. </Emitters>
  1236. </ParticleEffect>
  1237. """;
  1238. AssertParticleEffect(effect, expected);
  1239. }
  1240. [Fact]
  1241. public void SerializeRingProfile_WritesExpected()
  1242. {
  1243. ParticleEffect effect = new ParticleEffect("TestEffect");
  1244. ParticleEmitter emitter = new ParticleEmitter(1);
  1245. emitter.Name = "TestEmitter";
  1246. emitter.Profile = Profile.Ring(1.0f, CircleRadiation.In);
  1247. effect.Emitters.Add(emitter);
  1248. string expected =
  1249. $"""
  1250. <?xml version="1.0" encoding="utf-8"?>
  1251. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1252. <Emitters>
  1253. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1254. <Parameters>
  1255. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1256. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1257. <Color Kind="Constant" Constant="1,1,1" />
  1258. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1259. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1260. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1261. <Mass Kind="Constant" Constant="1" />
  1262. </Parameters>
  1263. <Profile Type="RingProfile" Radius="1" Radiate="In" />
  1264. </ParticleEmitter>
  1265. </Emitters>
  1266. </ParticleEffect>
  1267. """;
  1268. AssertParticleEffect(effect, expected);
  1269. }
  1270. [Fact]
  1271. public void SerializeSprayProfile_WritesExpected()
  1272. {
  1273. ParticleEffect effect = new ParticleEffect("TestEffect");
  1274. ParticleEmitter emitter = new ParticleEmitter(1);
  1275. emitter.Name = "TestEmitter";
  1276. emitter.Profile = Profile.Spray(Vector2.One, 1.0f);
  1277. effect.Emitters.Add(emitter);
  1278. string expected =
  1279. $"""
  1280. <?xml version="1.0" encoding="utf-8"?>
  1281. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1282. <Emitters>
  1283. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1284. <Parameters>
  1285. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1286. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1287. <Color Kind="Constant" Constant="1,1,1" />
  1288. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1289. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1290. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1291. <Mass Kind="Constant" Constant="1" />
  1292. </Parameters>
  1293. <Profile Type="SprayProfile" Direction="1,1" Spread="1" />
  1294. </ParticleEmitter>
  1295. </Emitters>
  1296. </ParticleEffect>
  1297. """;
  1298. AssertParticleEffect(effect, expected);
  1299. }
  1300. [Fact]
  1301. public void SerializeAgeModifier_WritesExpected()
  1302. {
  1303. ParticleEffect effect = new ParticleEffect("TestEffect");
  1304. ParticleEmitter emitter = new ParticleEmitter(1);
  1305. emitter.Name = "TestEmitter";
  1306. emitter.Profile = Profile.Point();
  1307. AgeModifier modifier = new AgeModifier();
  1308. emitter.Modifiers.Add(modifier);
  1309. effect.Emitters.Add(emitter);
  1310. string expected =
  1311. $"""
  1312. <?xml version="1.0" encoding="utf-8"?>
  1313. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1314. <Emitters>
  1315. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1316. <Parameters>
  1317. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1318. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1319. <Color Kind="Constant" Constant="1,1,1" />
  1320. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1321. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1322. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1323. <Mass Kind="Constant" Constant="1" />
  1324. </Parameters>
  1325. <Profile Type="PointProfile" />
  1326. <Modifiers>
  1327. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier" />
  1328. </Modifiers>
  1329. </ParticleEmitter>
  1330. </Emitters>
  1331. </ParticleEffect>
  1332. """;
  1333. AssertParticleEffect(effect, expected);
  1334. }
  1335. [Fact]
  1336. public void SerializeCircleContainerModifier_WritesExpected()
  1337. {
  1338. ParticleEffect effect = new ParticleEffect("TestEffect");
  1339. ParticleEmitter emitter = new ParticleEmitter(1);
  1340. emitter.Name = "TestEmitter";
  1341. emitter.Profile = Profile.Point();
  1342. CircleContainerModifier modifier = new CircleContainerModifier();
  1343. emitter.Modifiers.Add(modifier);
  1344. effect.Emitters.Add(emitter);
  1345. string expected =
  1346. $"""
  1347. <?xml version="1.0" encoding="utf-8"?>
  1348. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1349. <Emitters>
  1350. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1351. <Parameters>
  1352. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1353. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1354. <Color Kind="Constant" Constant="1,1,1" />
  1355. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1356. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1357. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1358. <Mass Kind="Constant" Constant="1" />
  1359. </Parameters>
  1360. <Profile Type="PointProfile" />
  1361. <Modifiers>
  1362. <Modifier Name="CircleContainerModifier" Enabled="True" Frequency="60" Type="CircleContainerModifier" Radius="0" Inside="True" RestitutionCoefficient="1" />
  1363. </Modifiers>
  1364. </ParticleEmitter>
  1365. </Emitters>
  1366. </ParticleEffect>
  1367. """;
  1368. AssertParticleEffect(effect, expected);
  1369. }
  1370. [Fact]
  1371. public void SerializeDragModifier_WritesExpected()
  1372. {
  1373. ParticleEffect effect = new ParticleEffect("TestEffect");
  1374. ParticleEmitter emitter = new ParticleEmitter(1);
  1375. emitter.Name = "TestEmitter";
  1376. emitter.Profile = Profile.Point();
  1377. DragModifier modifier = new DragModifier();
  1378. emitter.Modifiers.Add(modifier);
  1379. effect.Emitters.Add(emitter);
  1380. string expected =
  1381. $"""
  1382. <?xml version="1.0" encoding="utf-8"?>
  1383. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1384. <Emitters>
  1385. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1386. <Parameters>
  1387. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1388. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1389. <Color Kind="Constant" Constant="1,1,1" />
  1390. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1391. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1392. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1393. <Mass Kind="Constant" Constant="1" />
  1394. </Parameters>
  1395. <Profile Type="PointProfile" />
  1396. <Modifiers>
  1397. <Modifier Name="DragModifier" Enabled="True" Frequency="60" Type="DragModifier" DragCoefficient="0.47" Density="0.5" />
  1398. </Modifiers>
  1399. </ParticleEmitter>
  1400. </Emitters>
  1401. </ParticleEffect>
  1402. """;
  1403. AssertParticleEffect(effect, expected);
  1404. }
  1405. [Fact]
  1406. public void SerializeLinearGravityModifier_WritesExpected()
  1407. {
  1408. ParticleEffect effect = new ParticleEffect("TestEffect");
  1409. ParticleEmitter emitter = new ParticleEmitter(1);
  1410. emitter.Name = "TestEmitter";
  1411. emitter.Profile = Profile.Point();
  1412. LinearGravityModifier modifier = new LinearGravityModifier();
  1413. emitter.Modifiers.Add(modifier);
  1414. effect.Emitters.Add(emitter);
  1415. string expected =
  1416. $"""
  1417. <?xml version="1.0" encoding="utf-8"?>
  1418. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1419. <Emitters>
  1420. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1421. <Parameters>
  1422. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1423. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1424. <Color Kind="Constant" Constant="1,1,1" />
  1425. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1426. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1427. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1428. <Mass Kind="Constant" Constant="1" />
  1429. </Parameters>
  1430. <Profile Type="PointProfile" />
  1431. <Modifiers>
  1432. <Modifier Name="LinearGravityModifier" Enabled="True" Frequency="60" Type="LinearGravityModifier" Direction="0,0" Strength="0" />
  1433. </Modifiers>
  1434. </ParticleEmitter>
  1435. </Emitters>
  1436. </ParticleEffect>
  1437. """;
  1438. AssertParticleEffect(effect, expected);
  1439. }
  1440. [Fact]
  1441. public void SerializeOpacityFastFadeModifier_WritesExpected()
  1442. {
  1443. ParticleEffect effect = new ParticleEffect("TestEffect");
  1444. ParticleEmitter emitter = new ParticleEmitter(1);
  1445. emitter.Name = "TestEmitter";
  1446. emitter.Profile = Profile.Point();
  1447. OpacityFastFadeModifier modifier = new OpacityFastFadeModifier();
  1448. emitter.Modifiers.Add(modifier);
  1449. effect.Emitters.Add(emitter);
  1450. string expected =
  1451. $"""
  1452. <?xml version="1.0" encoding="utf-8"?>
  1453. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1454. <Emitters>
  1455. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1456. <Parameters>
  1457. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1458. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1459. <Color Kind="Constant" Constant="1,1,1" />
  1460. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1461. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1462. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1463. <Mass Kind="Constant" Constant="1" />
  1464. </Parameters>
  1465. <Profile Type="PointProfile" />
  1466. <Modifiers>
  1467. <Modifier Name="OpacityFastFadeModifier" Enabled="True" Frequency="60" Type="OpacityFastFadeModifier" />
  1468. </Modifiers>
  1469. </ParticleEmitter>
  1470. </Emitters>
  1471. </ParticleEffect>
  1472. """;
  1473. AssertParticleEffect(effect, expected);
  1474. }
  1475. [Fact]
  1476. public void SerializeRectangleContainerModifier_WritesExpected()
  1477. {
  1478. ParticleEffect effect = new ParticleEffect("TestEffect");
  1479. ParticleEmitter emitter = new ParticleEmitter(1);
  1480. emitter.Name = "TestEmitter";
  1481. emitter.Profile = Profile.Point();
  1482. RectangleContainerModifier modifier = new RectangleContainerModifier();
  1483. emitter.Modifiers.Add(modifier);
  1484. effect.Emitters.Add(emitter);
  1485. string expected =
  1486. $"""
  1487. <?xml version="1.0" encoding="utf-8"?>
  1488. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1489. <Emitters>
  1490. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1491. <Parameters>
  1492. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1493. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1494. <Color Kind="Constant" Constant="1,1,1" />
  1495. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1496. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1497. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1498. <Mass Kind="Constant" Constant="1" />
  1499. </Parameters>
  1500. <Profile Type="PointProfile" />
  1501. <Modifiers>
  1502. <Modifier Name="RectangleContainerModifier" Enabled="True" Frequency="60" Type="RectangleContainerModifier" Width="0" Height="0" RestitutionCoefficient="1" />
  1503. </Modifiers>
  1504. </ParticleEmitter>
  1505. </Emitters>
  1506. </ParticleEffect>
  1507. """;
  1508. AssertParticleEffect(effect, expected);
  1509. }
  1510. [Fact]
  1511. public void SerializeRectangleLoopContainerModifier_WritesExpected()
  1512. {
  1513. ParticleEffect effect = new ParticleEffect("TestEffect");
  1514. ParticleEmitter emitter = new ParticleEmitter(1);
  1515. emitter.Name = "TestEmitter";
  1516. emitter.Profile = Profile.Point();
  1517. RectangleLoopContainerModifier modifier = new RectangleLoopContainerModifier();
  1518. emitter.Modifiers.Add(modifier);
  1519. effect.Emitters.Add(emitter);
  1520. string expected =
  1521. $"""
  1522. <?xml version="1.0" encoding="utf-8"?>
  1523. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1524. <Emitters>
  1525. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1526. <Parameters>
  1527. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1528. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1529. <Color Kind="Constant" Constant="1,1,1" />
  1530. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1531. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1532. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1533. <Mass Kind="Constant" Constant="1" />
  1534. </Parameters>
  1535. <Profile Type="PointProfile" />
  1536. <Modifiers>
  1537. <Modifier Name="RectangleLoopContainerModifier" Enabled="True" Frequency="60" Type="RectangleLoopContainerModifier" Width="0" Height="0" />
  1538. </Modifiers>
  1539. </ParticleEmitter>
  1540. </Emitters>
  1541. </ParticleEffect>
  1542. """;
  1543. AssertParticleEffect(effect, expected);
  1544. }
  1545. [Fact]
  1546. public void SerializeRotationModifier_WritesExpected()
  1547. {
  1548. ParticleEffect effect = new ParticleEffect("TestEffect");
  1549. ParticleEmitter emitter = new ParticleEmitter(1);
  1550. emitter.Name = "TestEmitter";
  1551. emitter.Profile = Profile.Point();
  1552. RotationModifier modifier = new RotationModifier();
  1553. emitter.Modifiers.Add(modifier);
  1554. effect.Emitters.Add(emitter);
  1555. string expected =
  1556. $"""
  1557. <?xml version="1.0" encoding="utf-8"?>
  1558. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1559. <Emitters>
  1560. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1561. <Parameters>
  1562. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1563. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1564. <Color Kind="Constant" Constant="1,1,1" />
  1565. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1566. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1567. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1568. <Mass Kind="Constant" Constant="1" />
  1569. </Parameters>
  1570. <Profile Type="PointProfile" />
  1571. <Modifiers>
  1572. <Modifier Name="RotationModifier" Enabled="True" Frequency="60" Type="RotationModifier" RotationRate="0" />
  1573. </Modifiers>
  1574. </ParticleEmitter>
  1575. </Emitters>
  1576. </ParticleEffect>
  1577. """;
  1578. AssertParticleEffect(effect, expected);
  1579. }
  1580. [Fact]
  1581. public void SerializeVelocityColorModifier_WritesExpected()
  1582. {
  1583. ParticleEffect effect = new ParticleEffect("TestEffect");
  1584. ParticleEmitter emitter = new ParticleEmitter(1);
  1585. emitter.Name = "TestEmitter";
  1586. emitter.Profile = Profile.Point();
  1587. VelocityColorModifier modifier = new VelocityColorModifier();
  1588. emitter.Modifiers.Add(modifier);
  1589. effect.Emitters.Add(emitter);
  1590. string expected =
  1591. $"""
  1592. <?xml version="1.0" encoding="utf-8"?>
  1593. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1594. <Emitters>
  1595. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1596. <Parameters>
  1597. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1598. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1599. <Color Kind="Constant" Constant="1,1,1" />
  1600. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1601. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1602. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1603. <Mass Kind="Constant" Constant="1" />
  1604. </Parameters>
  1605. <Profile Type="PointProfile" />
  1606. <Modifiers>
  1607. <Modifier Name="VelocityColorModifier" Enabled="True" Frequency="60" Type="VelocityColorModifier" StationaryColor="0,0,0" VelocityColor="0,0,0" VelocityThreshold="0" />
  1608. </Modifiers>
  1609. </ParticleEmitter>
  1610. </Emitters>
  1611. </ParticleEffect>
  1612. """;
  1613. AssertParticleEffect(effect, expected);
  1614. }
  1615. [Fact]
  1616. public void SerializeVelocityModifier_WritesExpected()
  1617. {
  1618. ParticleEffect effect = new ParticleEffect("TestEffect");
  1619. ParticleEmitter emitter = new ParticleEmitter(1);
  1620. emitter.Name = "TestEmitter";
  1621. emitter.Profile = Profile.Point();
  1622. VelocityModifier modifier = new VelocityModifier();
  1623. emitter.Modifiers.Add(modifier);
  1624. effect.Emitters.Add(emitter);
  1625. string expected =
  1626. $"""
  1627. <?xml version="1.0" encoding="utf-8"?>
  1628. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1629. <Emitters>
  1630. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1631. <Parameters>
  1632. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1633. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1634. <Color Kind="Constant" Constant="1,1,1" />
  1635. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1636. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1637. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1638. <Mass Kind="Constant" Constant="1" />
  1639. </Parameters>
  1640. <Profile Type="PointProfile" />
  1641. <Modifiers>
  1642. <Modifier Name="VelocityModifier" Enabled="True" Frequency="60" Type="VelocityModifier" VelocityThreshold="0" />
  1643. </Modifiers>
  1644. </ParticleEmitter>
  1645. </Emitters>
  1646. </ParticleEffect>
  1647. """;
  1648. AssertParticleEffect(effect, expected);
  1649. }
  1650. [Fact]
  1651. public void SerializeVortexModifier_WritesExpected()
  1652. {
  1653. ParticleEffect effect = new ParticleEffect("TestEffect");
  1654. ParticleEmitter emitter = new ParticleEmitter(1);
  1655. emitter.Name = "TestEmitter";
  1656. emitter.Profile = Profile.Point();
  1657. VortexModifier modifier = new VortexModifier();
  1658. emitter.Modifiers.Add(modifier);
  1659. effect.Emitters.Add(emitter);
  1660. string expected =
  1661. $"""
  1662. <?xml version="1.0" encoding="utf-8"?>
  1663. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1664. <Emitters>
  1665. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1666. <Parameters>
  1667. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1668. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1669. <Color Kind="Constant" Constant="1,1,1" />
  1670. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1671. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1672. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1673. <Mass Kind="Constant" Constant="1" />
  1674. </Parameters>
  1675. <Profile Type="PointProfile" />
  1676. <Modifiers>
  1677. <Modifier Name="VortexModifier" Enabled="True" Frequency="60" Type="VortexModifier" Position="0,0" Strength="0" OuterRadius="0" InnerRadius="0" MaxVelocity="0" RotationAngle="0" />
  1678. </Modifiers>
  1679. </ParticleEmitter>
  1680. </Emitters>
  1681. </ParticleEffect>
  1682. """;
  1683. AssertParticleEffect(effect, expected);
  1684. }
  1685. [Fact]
  1686. public void SerializeColorInterpolator_WritesExpected()
  1687. {
  1688. ParticleEffect effect = new ParticleEffect("TestEffect");
  1689. ParticleEmitter emitter = new ParticleEmitter(1);
  1690. emitter.Name = "TestEmitter";
  1691. emitter.Profile = Profile.Point();
  1692. AgeModifier modifier = new AgeModifier();
  1693. ColorInterpolator interpolator = new ColorInterpolator();
  1694. modifier.Interpolators.Add(interpolator);
  1695. emitter.Modifiers.Add(modifier);
  1696. effect.Emitters.Add(emitter);
  1697. string expected =
  1698. $"""
  1699. <?xml version="1.0" encoding="utf-8"?>
  1700. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1701. <Emitters>
  1702. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1703. <Parameters>
  1704. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1705. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1706. <Color Kind="Constant" Constant="1,1,1" />
  1707. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1708. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1709. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1710. <Mass Kind="Constant" Constant="1" />
  1711. </Parameters>
  1712. <Profile Type="PointProfile" />
  1713. <Modifiers>
  1714. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  1715. <Interpolators>
  1716. <Interpolator Name="ColorInterpolator" Enabled="True" Type="ColorInterpolator" StartValue="0,0,0" EndValue="0,0,0" />
  1717. </Interpolators>
  1718. </Modifier>
  1719. </Modifiers>
  1720. </ParticleEmitter>
  1721. </Emitters>
  1722. </ParticleEffect>
  1723. """;
  1724. AssertParticleEffect(effect, expected);
  1725. }
  1726. [Fact]
  1727. public void SerializeHueInterpolator_WritesExpected()
  1728. {
  1729. ParticleEffect effect = new ParticleEffect("TestEffect");
  1730. ParticleEmitter emitter = new ParticleEmitter(1);
  1731. emitter.Name = "TestEmitter";
  1732. emitter.Profile = Profile.Point();
  1733. AgeModifier modifier = new AgeModifier();
  1734. HueInterpolator interpolator = new HueInterpolator();
  1735. modifier.Interpolators.Add(interpolator);
  1736. emitter.Modifiers.Add(modifier);
  1737. effect.Emitters.Add(emitter);
  1738. string expected =
  1739. $"""
  1740. <?xml version="1.0" encoding="utf-8"?>
  1741. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1742. <Emitters>
  1743. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1744. <Parameters>
  1745. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1746. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1747. <Color Kind="Constant" Constant="1,1,1" />
  1748. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1749. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1750. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1751. <Mass Kind="Constant" Constant="1" />
  1752. </Parameters>
  1753. <Profile Type="PointProfile" />
  1754. <Modifiers>
  1755. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  1756. <Interpolators>
  1757. <Interpolator Name="HueInterpolator" Enabled="True" Type="HueInterpolator" StartValue="0" EndValue="0" />
  1758. </Interpolators>
  1759. </Modifier>
  1760. </Modifiers>
  1761. </ParticleEmitter>
  1762. </Emitters>
  1763. </ParticleEffect>
  1764. """;
  1765. AssertParticleEffect(effect, expected);
  1766. }
  1767. [Fact]
  1768. public void SerializeOpacityInterpolator_WritesExpected()
  1769. {
  1770. ParticleEffect effect = new ParticleEffect("TestEffect");
  1771. ParticleEmitter emitter = new ParticleEmitter(1);
  1772. emitter.Name = "TestEmitter";
  1773. emitter.Profile = Profile.Point();
  1774. AgeModifier modifier = new AgeModifier();
  1775. OpacityInterpolator interpolator = new OpacityInterpolator();
  1776. modifier.Interpolators.Add(interpolator);
  1777. emitter.Modifiers.Add(modifier);
  1778. effect.Emitters.Add(emitter);
  1779. string expected =
  1780. $"""
  1781. <?xml version="1.0" encoding="utf-8"?>
  1782. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1783. <Emitters>
  1784. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1785. <Parameters>
  1786. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1787. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1788. <Color Kind="Constant" Constant="1,1,1" />
  1789. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1790. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1791. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1792. <Mass Kind="Constant" Constant="1" />
  1793. </Parameters>
  1794. <Profile Type="PointProfile" />
  1795. <Modifiers>
  1796. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  1797. <Interpolators>
  1798. <Interpolator Name="OpacityInterpolator" Enabled="True" Type="OpacityInterpolator" StartValue="0" EndValue="0" />
  1799. </Interpolators>
  1800. </Modifier>
  1801. </Modifiers>
  1802. </ParticleEmitter>
  1803. </Emitters>
  1804. </ParticleEffect>
  1805. """;
  1806. AssertParticleEffect(effect, expected);
  1807. }
  1808. [Fact]
  1809. public void SerializeRotationInterpolator_WritesExpected()
  1810. {
  1811. ParticleEffect effect = new ParticleEffect("TestEffect");
  1812. ParticleEmitter emitter = new ParticleEmitter(1);
  1813. emitter.Name = "TestEmitter";
  1814. emitter.Profile = Profile.Point();
  1815. AgeModifier modifier = new AgeModifier();
  1816. RotationInterpolator interpolator = new RotationInterpolator();
  1817. modifier.Interpolators.Add(interpolator);
  1818. emitter.Modifiers.Add(modifier);
  1819. effect.Emitters.Add(emitter);
  1820. string expected =
  1821. $"""
  1822. <?xml version="1.0" encoding="utf-8"?>
  1823. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1824. <Emitters>
  1825. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1826. <Parameters>
  1827. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1828. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1829. <Color Kind="Constant" Constant="1,1,1" />
  1830. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1831. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1832. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1833. <Mass Kind="Constant" Constant="1" />
  1834. </Parameters>
  1835. <Profile Type="PointProfile" />
  1836. <Modifiers>
  1837. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  1838. <Interpolators>
  1839. <Interpolator Name="RotationInterpolator" Enabled="True" Type="RotationInterpolator" StartValue="0" EndValue="0" />
  1840. </Interpolators>
  1841. </Modifier>
  1842. </Modifiers>
  1843. </ParticleEmitter>
  1844. </Emitters>
  1845. </ParticleEffect>
  1846. """;
  1847. AssertParticleEffect(effect, expected);
  1848. }
  1849. [Fact]
  1850. public void SerializeScaleInterpolator_WritesExpected()
  1851. {
  1852. ParticleEffect effect = new ParticleEffect("TestEffect");
  1853. ParticleEmitter emitter = new ParticleEmitter(1);
  1854. emitter.Name = "TestEmitter";
  1855. emitter.Profile = Profile.Point();
  1856. AgeModifier modifier = new AgeModifier();
  1857. ScaleInterpolator interpolator = new ScaleInterpolator();
  1858. modifier.Interpolators.Add(interpolator);
  1859. emitter.Modifiers.Add(modifier);
  1860. effect.Emitters.Add(emitter);
  1861. string expected =
  1862. $"""
  1863. <?xml version="1.0" encoding="utf-8"?>
  1864. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1865. <Emitters>
  1866. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1867. <Parameters>
  1868. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1869. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1870. <Color Kind="Constant" Constant="1,1,1" />
  1871. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1872. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1873. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1874. <Mass Kind="Constant" Constant="1" />
  1875. </Parameters>
  1876. <Profile Type="PointProfile" />
  1877. <Modifiers>
  1878. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  1879. <Interpolators>
  1880. <Interpolator Name="ScaleInterpolator" Enabled="True" Type="ScaleInterpolator" StartValue="0,0" EndValue="0,0" />
  1881. </Interpolators>
  1882. </Modifier>
  1883. </Modifiers>
  1884. </ParticleEmitter>
  1885. </Emitters>
  1886. </ParticleEffect>
  1887. """;
  1888. AssertParticleEffect(effect, expected);
  1889. }
  1890. [Fact]
  1891. public void SerializeVelocityInterpolator_WritesExpected()
  1892. {
  1893. ParticleEffect effect = new ParticleEffect("TestEffect");
  1894. ParticleEmitter emitter = new ParticleEmitter(1);
  1895. emitter.Name = "TestEmitter";
  1896. emitter.Profile = Profile.Point();
  1897. AgeModifier modifier = new AgeModifier();
  1898. VelocityInterpolator interpolator = new VelocityInterpolator();
  1899. modifier.Interpolators.Add(interpolator);
  1900. emitter.Modifiers.Add(modifier);
  1901. effect.Emitters.Add(emitter);
  1902. string expected =
  1903. $"""
  1904. <?xml version="1.0" encoding="utf-8"?>
  1905. <ParticleEffect Name="TestEffect" Position="0,0" Rotation="0" Scale="1,1" AutoTrigger="True" AutoTriggerFrequency="1">
  1906. <Emitters>
  1907. <ParticleEmitter Name="TestEmitter" LifeSpan="1" Offset="0,0" LayerDepth="0" ReclaimFrequency="60" Capacity="1" ModifierExecutionStrategy="Serial" RenderingOrder="FrontToBack">
  1908. <Parameters>
  1909. <Quantity Kind="Random" RandomMin="5" RandomMax="100" />
  1910. <Speed Kind="Random" RandomMin="50" RandomMax="100" />
  1911. <Color Kind="Constant" Constant="1,1,1" />
  1912. <Opacity Kind="Random" RandomMin="0" RandomMax="1" />
  1913. <Scale Kind="Random" RandomMin="0.5,0.5" RandomMax="1,1" />
  1914. <Rotation Kind="Random" RandomMin="{-MathF.PI}" RandomMax="{MathF.PI}" />
  1915. <Mass Kind="Constant" Constant="1" />
  1916. </Parameters>
  1917. <Profile Type="PointProfile" />
  1918. <Modifiers>
  1919. <Modifier Name="AgeModifier" Enabled="True" Frequency="60" Type="AgeModifier">
  1920. <Interpolators>
  1921. <Interpolator Name="VelocityInterpolator" Enabled="True" Type="VelocityInterpolator" StartValue="0,0" EndValue="0,0" />
  1922. </Interpolators>
  1923. </Modifier>
  1924. </Modifiers>
  1925. </ParticleEmitter>
  1926. </Emitters>
  1927. </ParticleEffect>
  1928. """;
  1929. AssertParticleEffect(effect, expected);
  1930. }
  1931. private static void AssertParticleEffect(ParticleEffect effect, string expected)
  1932. {
  1933. using MemoryStream stream = new MemoryStream();
  1934. ParticleEffectSerializer.Serialize(stream, effect);
  1935. stream.Position = 0;
  1936. using StreamReader reader = new StreamReader(stream);
  1937. string actual = reader.ReadToEnd();
  1938. Assert.Equal(expected, actual);
  1939. }
  1940. }