context: #region File Description
  //-----------------------------------------------------------------------------
  // ParticleSystem.cs
  //
  // Microsoft XNA Community Game Platform
  // Copyright (C) Microsoft Corporation. All rights reserved.
  //-----------------------------------------------------------------------------
  #endregion
  
  #region Using Statements
  using System;
  using Microsoft.Xna.Framework;
  using Microsoft.Xna.Framework.Content;
  using Microsoft.Xna.Framework.Graphics;
  using Microsoft.Xna.Framework.Graphics.PackedVector;
  #endregion
  
  namespace Particle3DSample
  {
      /// <summary>
      /// The main component in charge of displaying particles.
      /// </summary>
      public abstract class ParticleSystem : DrawableGameComponent
      {
          #region Fields
  
  
          // Settings class controls the appearance and animation of this particle system.
          ParticleSettings settings = new ParticleSettings();
  
  
          // For loading the effect and particle texture.
          ContentManager content;
  
  
          // Custom effect for drawing particles. This computes the particle
          // animation entirely in the vertex shader: no per-particle CPU work required!
          Effect particleEffect;
  
  
          // Shortcuts for accessing frequently changed effect parameters.
          EffectParameter effectViewParameter;
          EffectParameter effectProjectionParameter;
          EffectParameter effectViewportScaleParameter;
          EffectParameter effectTimeParameter;
  
  
          // An array of particles, treated as a circular queue.
          ParticleVertex[] particles;
  
  
          // A vertex buffer holding our particles. This contains the same data as
          // the particles array, but copied across to where the GPU can access it.
          DynamicVertexBuffer vertexBuffer;
  
  
          // Index buffer turns sets of four vertices into particle quads (pairs of triangles).
          IndexBuffer indexBuffer;
  
  
          // The particles array and vertex buffer are treated as a circular queue.
          // Initially, the entire contents of the array are free, because no particles
          // are in use. When a new particle is created, this is allocated from the
          // beginning of the array. If more than one particle is created, these will
          // always be stored in a consecutive block of array elements. Because all
          // particles last for the same amount of time, old particles will always be
          // removed in order from the start of this active particle region, so the
          // active and free regions will never be intermingled. Because the queue is
          // circular, there can be times when the active particle region wraps from the
          // end of the array back to the start. The queue uses modulo arithmetic to
          // handle these cases. For instance with a four entry queue we could have:
          //
          //      0
          //      1 - first active particle
          //      2 
          //      3 - first free particle
          //
          // In this case, particles 1 and 2 are active, while 3 and 4 are free.
          // Using modulo arithmetic we could also have:
          //
          //      0
          //      1 - first free particle
          //      2 
          //      3 - first active particle
          //
          // Here, 3 and 0 are active, while 1 and 2 are free.
          //
          // But wait! The full story is even more complex.
          //
          // When we create a new particle, we add them to our managed particles array.
          // We also need to copy this new data into the GPU vertex buffer, but we don't
          // want to do that straight away, because setting new data into a vertex buffer
          // can be an expensive operation. If we are going to be adding several particles
          // in a single frame, it is faster to initially just store them in our managed
          // array, and then later upload them all to the GPU in one single call. So our
          // queue also needs a region for storing new particles that have been added to
          // the managed array but not yet uploaded to the vertex buffer.
          //
          // Another issue occurs when old particles are retired. The CPU and GPU run
          // asynchronously, so the GPU will often still be busy drawing the previous
          // frame while the CPU is working on the next frame. This can cause a
          // synchronization problem if an old particle is retired, and then immediately
          // overwritten by a new one, because the CPU might try to change the contents
          // of the vertex buffer while the GPU is still busy drawing the old data from
          // it. Normally the graphics driver will take care of this by waiting until
          // the GPU has finished drawing inside the VertexBuffer.SetData call, but we
          // don't want to waste time waiting around every time we try to add a new
          // particle! To avoid this delay, we can specify the SetDataOptions.NoOverwrite
          // flag when we write to the vertex buffer. This basically means "I promise I
          // will never try to overwrite any data that the GPU might still be using, so
          // you can just go ahead and update the buffer straight away". To keep this
          // promise, we must avoid reusing vertices immediately after they are drawn.
          //
          // So in total, our queue contains four different regions:
          //
          // Vertices between firstActiveParticle and firstNewParticle are actively
          // being drawn, and exist in both the managed particles array and the GPU
          // vertex buffer.
          //
          // Vertices between firstNewParticle and firstFreeParticle are newly created,
          // and exist only in the managed particles array. These need to be uploaded
          // to the GPU at the start of the next draw call.
          //
          // Vertices between firstFreeParticle and firstRetiredParticle are free and
          // waiting to be allocated.
          //
          // Vertices between firstRetiredParticle and firstActiveParticle are no longer
          // being drawn, but were drawn recently enough that the GPU could still be
          // using them. These need to be kept around for a few more frames before they
          // can be reallocated.
  
          int firstActiveParticle;
          int firstNewParticle;
          int firstFreeParticle;
          int firstRetiredParticle;
  
  
          // Store the current time, in seconds.
          float currentTime;
  
  
          // Count how many times Draw has been called. This is used to know
          // when it is safe to retire old particles back into the free list.
          int drawCounter;
  
  
          // Shared random number generator.
          static Random random = new Random();
  
  
          #endregion
  
          #region Initialization
  
  
          /// <summary>
          /// Constructor.
          /// </summary>
          protected ParticleSystem(Game game, ContentManager content)
              : base(game)
          {
              this.content = content;
          }
  
  
          /// <summary>
          /// Initializes the component.
          /// </summary>
          public override void Initialize()
          {
              InitializeSettings(settings);
  
              // Allocate the particle array, and fill in the corner fields (which never change).
              particles = new ParticleVertex[settings.MaxParticles * 4];
  
              for (int i = 0; i < settings.MaxParticles; i++)
              {
                  particles[i * 4 + 0].Corner = new Short2(-1, -1);
                  particles[i * 4 + 1].Corner = new Short2(1, -1);
                  particles[i * 4 + 2].Corner = new Short2(1, 1);
                  particles[i * 4 + 3].Corner = new Short2(-1, 1);
              }
  
              base.Initialize();
          }
  
  
          /// <summary>
          /// Derived particle system classes should override this method
          /// and use it to initalize their tweakable settings.
          /// </summary>
          protected abstract void InitializeSettings(ParticleSettings settings);
  
  
          /// <summary>
          /// Loads graphics for the particle system.
          /// </summary>
          protected override void LoadContent()
          {
              LoadParticleEffect();
  
              // Create a dynamic vertex buffer.
              vertexBuffer = new DynamicVertexBuffer(GraphicsDevice, ParticleVertex.VertexDeclaration,
                                                     settings.MaxParticles * 4, BufferUsage.WriteOnly);
  
              // Create and populate the index buffer.
              ushort[] indices = new ushort[settings.MaxParticles * 6];
  
              for (int i = 0; i < settings.MaxParticles; i++)
              {
                  indices[i * 6 + 0] = (ushort)(i * 4 + 0);
                  indices[i * 6 + 1] = (ushort)(i * 4 + 1);
                  indices[i * 6 + 2] = (ushort)(i * 4 + 2);
  
                  indices[i * 6 + 3] = (ushort)(i * 4 + 0);
                  indices[i * 6 + 4] = (ushort)(i * 4 + 2);
                  indices[i * 6 + 5] = (ushort)(i * 4 + 3);
              }
  
              indexBuffer = new IndexBuffer(GraphicsDevice, typeof(ushort), indices.Length, BufferUsage.WriteOnly);
  
              indexBuffer.SetData(indices);
          }
  
  
          /// <summary>
          /// Helper for loading and initializing the particle effect.
          /// </summary>
          void LoadParticleEffect()
          {
              Effect effect = content.Load<Effect>("particle/ParticleEffect");
  
              // If we have several particle systems, the content manager will return
              // a single shared effect instance to them all. But we want to preconfigure
              // the effect with parameters that are specific to this particular
              // particle system. By cloning the effect, we prevent one particle system
              // from stomping over the parameter settings of another.
              
              particleEffect = effect.Clone();
  
              EffectParameterCollection parameters = particleEffect.Parameters;
  
              // Look up shortcuts for parameters that change every frame.
              effectViewParameter = parameters["View"];
              effectProjectionParameter = parameters["Projection"];
              effectViewportScaleParameter = parameters["ViewportScale"];
              effectTimeParameter = parameters["CurrentTime"];
  
              // Set the values of parameters that do not change.
              parameters["Duration"].SetValue((float)settings.Duration.TotalSeconds);
              parameters["DurationRandomness"].SetValue(settings.DurationRandomness);
              parameters["Gravity"].SetValue(settings.Gravity);
              parameters["EndVelocity"].SetValue(settings.EndVelocity);
              parameters["MinColor"].SetValue(settings.MinColor.ToVector4());
              parameters["MaxColor"].SetValue(settings.MaxColor.ToVector4());
  
              parameters["RotateSpeed"].SetValue(
  
gt:                 new Vector2(settings.MinRotateSpeed, settings.MaxRotateSpeed));