SpriteBatchExtensions.ParticleEfffect.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. using System;
  2. using Microsoft.Xna.Framework;
  3. using Microsoft.Xna.Framework.Graphics;
  4. using MonoGame.Extended.Graphics;
  5. using MonoGame.Extended.Particles.Data;
  6. namespace MonoGame.Extended.Particles;
  7. /// <summary>
  8. /// Provides extension methods for <see cref="SpriteBatch"/> to draw particle effects and emitters.
  9. /// </summary>
  10. public static class SpriteBatchExtensions
  11. {
  12. /// <summary>
  13. /// Draws a particle effect by rendering all of its active emitters.
  14. /// </summary>
  15. /// <param name="spriteBatch">The <see cref="SpriteBatch"/> used for drawing.</param>
  16. /// <param name="effect">The <see cref="ParticleEffect"/> to draw.</param>
  17. /// <exception cref="ArgumentNullException">Thrown when <paramref name="effect"/> is null.</exception>
  18. /// <exception cref="ObjectDisposedException">Thrown when <paramref name="effect"/> is disposed.</exception>
  19. public static void Draw(this SpriteBatch spriteBatch, ParticleEffect effect)
  20. {
  21. ArgumentNullException.ThrowIfNull(effect);
  22. ObjectDisposedException.ThrowIf(effect.IsDisposed, effect);
  23. for (int i = 0; i < effect.Emitters.Count; i++)
  24. {
  25. UnsafeDraw(spriteBatch, effect.Emitters[i]);
  26. }
  27. }
  28. /// <summary>
  29. /// Draws a particle emitter by rendering all of its active particles.
  30. /// </summary>
  31. /// <param name="spriteBatch">The <see cref="SpriteBatch"/> used for drawing.</param>
  32. /// <param name="emitter">The <see cref="ParticleEmitter"/> to draw.</param>
  33. /// <exception cref="ArgumentNullException">Thrown when <paramref name="emitter"/> is null.</exception>
  34. /// <exception cref="ObjectDisposedException">Thrown when <paramref name="emitter"/> is disposed.</exception>
  35. public static void Draw(this SpriteBatch spriteBatch, ParticleEmitter emitter)
  36. {
  37. ArgumentNullException.ThrowIfNull(emitter);
  38. ObjectDisposedException.ThrowIf(emitter.IsDisposed, emitter);
  39. UnsafeDraw(spriteBatch, emitter);
  40. }
  41. private static unsafe void UnsafeDraw(SpriteBatch spriteBatch, ParticleEmitter emitter)
  42. {
  43. ArgumentNullException.ThrowIfNull(spriteBatch);
  44. // Early exit if no texture region assigned
  45. if (emitter.TextureRegion == null)
  46. {
  47. return;
  48. }
  49. // Early exit if there are no active particles
  50. if (emitter.ActiveParticles == 0)
  51. {
  52. return;
  53. }
  54. // Early exit if the emitter is not visible
  55. if (!emitter.Visible)
  56. {
  57. return;
  58. }
  59. Texture2DRegion region = emitter.TextureRegion;
  60. Texture2D texture = region.Texture;
  61. Rectangle sourceRect = region.Bounds;
  62. Vector2 origin = new Vector2(region.Width, region.Height) * 0.5f;
  63. if (emitter.RenderingOrder == ParticleRenderingOrder.FrontToBack)
  64. {
  65. int count = emitter.ActiveParticles;
  66. Span<IntPtr> particlePtrs = count <= 1024 ?
  67. stackalloc IntPtr[count] :
  68. new IntPtr[count];
  69. ParticleIterator iterator = emitter.Buffer.Iterator;
  70. int index = 0;
  71. while (iterator.HasNext)
  72. {
  73. particlePtrs[index++] = (IntPtr)iterator.Next();
  74. }
  75. for (int i = count - 1; i >= 0; i--)
  76. {
  77. RenderParticle(spriteBatch, (Particle*)particlePtrs[i], texture, sourceRect, origin);
  78. }
  79. }
  80. else
  81. {
  82. ParticleIterator iterator = emitter.Buffer.Iterator;
  83. while (iterator.HasNext)
  84. {
  85. Particle* particle = iterator.Next();
  86. RenderParticle(spriteBatch, particle, texture, sourceRect, origin);
  87. }
  88. }
  89. }
  90. private static unsafe void RenderParticle(SpriteBatch spriteBatch, Particle* particle, Texture2D texture, Rectangle sourceRect, Vector2 origin)
  91. {
  92. HslColor hsl = new HslColor(particle->Color[0], particle->Color[1], particle->Color[2]);
  93. Color color = HslColor.ToRgb(hsl);
  94. if (spriteBatch.GraphicsDevice.BlendState == BlendState.AlphaBlend)
  95. {
  96. color *= particle->Opacity;
  97. }
  98. else
  99. {
  100. color.A = (byte)MathHelper.Clamp(particle->Opacity * 255, 0, 255);
  101. }
  102. Vector2 position = new Vector2(particle->Position[0], particle->Position[1]);
  103. Vector2 scale = new Vector2(particle->Scale[0], particle->Scale[1]);
  104. float rotation = particle->Rotation;
  105. float layerDepth = particle->LayerDepth;
  106. spriteBatch.Draw(
  107. texture,
  108. position,
  109. sourceRect,
  110. color,
  111. rotation,
  112. origin,
  113. scale,
  114. SpriteEffects.None,
  115. layerDepth
  116. );
  117. }
  118. }