void FreeRetiredParticles(SmokeParticleComponent particleComp)
        {
            while (particleComp.firstRetiredParticle != particleComp.firstActiveParticle)
            {
                // Has this particle been unused long enough that
                // the GPU is sure to be finished with it?
                // We multiply the retired particle index by four, because each
                // particle consists of a quad that is made up of four vertices.
                int age = particleComp.drawCounter - (int)particleComp.particles[particleComp.firstRetiredParticle * 4].Time;

                // The GPU is never supposed to get more than 2 frames behind the CPU.
                // We add 1 to that, just to be safe in case of buggy drivers that
                // might bend the rules and let the GPU get further behind.
                if (age < 3)
                {
                    break;
                }

                // Move the particle from the retired to the free queue.
                particleComp.firstRetiredParticle++;

                if (particleComp.firstRetiredParticle >= particleComp.MaxParticles)
                {
                    particleComp.firstRetiredParticle = 0;
                }
            }
        }
예제 #2
0
        void AddNewParticlesToVertexBuffer(SmokeParticleComponent particleComp)
        {
            int stride = ParticleVertex.SizeInBytes;

            if (particleComp.firstNewParticle < particleComp.firstFreeParticle)
            {
                // If the new particles are all in one consecutive range,
                // we can upload them all in a single call.
                particleComp.vertexBuffer.SetData(particleComp.firstNewParticle * stride * 4, particleComp.particles,
                                                  particleComp.firstNewParticle * 4,
                                                  (particleComp.firstFreeParticle - particleComp.firstNewParticle) * 4,
                                                  stride, SetDataOptions.NoOverwrite);
            }
            else
            {
                // If the new particle range wraps past the end of the queue
                // back to the start, we must split them over two upload calls.
                particleComp.vertexBuffer.SetData(particleComp.firstNewParticle * stride * 4, particleComp.particles,
                                                  particleComp.firstNewParticle * 4,
                                                  (particleComp.MaxParticles - particleComp.firstNewParticle) * 4,
                                                  stride, SetDataOptions.NoOverwrite);

                if (particleComp.firstFreeParticle > 0)
                {
                    particleComp.vertexBuffer.SetData(0, particleComp.particles,
                                                      0, particleComp.firstFreeParticle * 4,
                                                      stride, SetDataOptions.NoOverwrite);
                }
            }
            // Move the particles we just uploaded from the new to the active queue.
            particleComp.firstNewParticle = particleComp.firstFreeParticle;
        }
        void RetireActiveParticles(SmokeParticleComponent particleComp)
        {
            float particleDuration = (float)particleComp.Duration.TotalSeconds;

            while (particleComp.firstActiveParticle != particleComp.firstNewParticle)
            {
                // Is this particle old enough to retire?
                // We multiply the active particle index by four, because each
                // particle consists of a quad that is made up of four vertices.
                float particleAge = particleComp.currentTime - particleComp.particles[particleComp.firstActiveParticle * 4].Time;

                if (particleAge < particleDuration)
                {
                    break;
                }

                // Remember the time at which we retired this particle.
                particleComp.particles[particleComp.firstActiveParticle * 4].Time = particleComp.drawCounter;

                // Move the particle from the active to the retired queue.
                particleComp.firstActiveParticle++;

                if (particleComp.firstActiveParticle >= particleComp.MaxParticles)
                {
                    particleComp.firstActiveParticle = 0;
                }
            }
        }
        public void Update(GameTime gameTime)
        {
            if (gameTime == null)
            {
                throw new ArgumentNullException("gameTime");
            }

            List <Entity> particlesEnt = ComponentManager.Instance.GetAllEntitiesWithComponentType <SmokeParticleComponent>();

            if (particlesEnt == null)
            {
                return;
            }

            foreach (Entity e in particlesEnt)
            {
                SmokeParticleComponent pc = ComponentManager.Instance.GetEntityComponent <SmokeParticleComponent>(e);
                var transformEntities     = ComponentManager.Instance.GetAllEntitiesWithComponentType <TransformComponent>();
                var transformEntity       = ComponentManager.Instance.GetEntityWithTag("Kart", transformEntities);

                TransformComponent tc = ComponentManager.Instance.GetEntityComponent <TransformComponent>(transformEntity);

                pc.currentTime += (float)gameTime.ElapsedGameTime.TotalSeconds;

                RetireActiveParticles(pc);
                FreeRetiredParticles(pc);

                ParticleRenderSystem.AddParticle(pc, Vector3.Zero, Vector3.Zero);

                //get the position and rotation of the kart
                Matrix PosRotMatrix = Matrix.CreateTranslation(-tc.Position) * Matrix.CreateFromQuaternion(tc.Rotation) * Matrix.CreateTranslation(tc.Position);

                //Set the particles position to the models position, applying the position and offset with the Positin and rotation of the
                Vector3 newPos = Vector3.Transform(tc.Position + pc.positionOffset, PosRotMatrix);

                // If we let our timer go on increasing for ever, it would eventually
                // run out of floating point precision, at which point the particles
                // would render incorrectly. An easy way to prevent this is to notice
                // that the time value doesn't matter when no particles are being drawn,
                // so we can reset it back to zero any time the active queue is empty.
                if (pc.firstActiveParticle == pc.firstFreeParticle)
                {
                    pc.currentTime = 0;
                }

                if (pc.firstRetiredParticle == pc.firstActiveParticle)
                {
                    pc.drawCounter = 0;
                }

                pc.effect.Parameters["position"].SetValue(newPos);
            }
        }
예제 #5
0
        private void InitParticles(ECSEngine engine)
        {
            SystemManager.Instance.RegisterSystem("Game", new ParticleRenderSystem(engine.GetGraphicsDevice()));
            SystemManager.Instance.RegisterSystem("Game", new ParticleUpdateSystem());
            Entity SmokehParticle        = EntityFactory.Instance.NewEntityWithTag("smokeh");
            SmokeParticleComponent pComp = new SmokeParticleComponent();

            ComponentManager.Instance.AddComponentToEntity(SmokehParticle, pComp);
            ParticleRenderSystem.LoadParticleEffect(engine.GetGraphicsDevice(), engine.LoadContent <Effect>("Effects/ParticleEffect"), engine.LoadContent <Texture2D>("smoke"), ref pComp);
            ParticleRenderSystem.setParticleOffsetPosition(ref pComp, new Vector3(0, 0, 10f));
            SceneManager.Instance.AddEntityToSceneOnLayer("Game", 2, SmokehParticle);
        }
예제 #6
0
        public static void AddParticle(SmokeParticleComponent particleComp, Vector3 position, Vector3 velocity)
        {
            // Figure out where in the circular queue to allocate the new particle.
            int nextFreeParticle = particleComp.firstFreeParticle + 1;

            if (nextFreeParticle >= particleComp.MaxParticles)
            {
                nextFreeParticle = 0;
            }

            // If there are no free particles, we just have to give up.
            if (nextFreeParticle == particleComp.firstRetiredParticle)
            {
                return;
            }

            // Adjust the input velocity based on how much
            // this particle system wants to be affected by it.
            velocity *= particleComp.EmitterVelocitySensitivity;

            // Add in some random amount of horizontal velocity.
            float horizontalVelocity = MathHelper.Lerp(particleComp.MinHorizontalVelocity,
                                                       particleComp.MaxHorizontalVelocity,
                                                       (float)random.NextDouble());

            double horizontalAngle = random.NextDouble() * MathHelper.TwoPi;

            velocity.X += horizontalVelocity * (float)Math.Cos(horizontalAngle);
            velocity.Z += horizontalVelocity * (float)Math.Sin(horizontalAngle);

            // Add in some random amount of vertical velocity.
            velocity.Y += MathHelper.Lerp(particleComp.MinVerticalVelocity,
                                          particleComp.MaxVerticalVelocity,
                                          (float)random.NextDouble());

            // Choose four random control values. These will be used by the vertex
            // shader to give each particle a different size, rotation, and color.
            Color randomValues = new Color((byte)random.Next(255),
                                           (byte)random.Next(255),
                                           (byte)random.Next(255),
                                           (byte)random.Next(255));

            // Fill in the particle vertex structure.
            for (int i = 0; i < 4; i++)
            {
                particleComp.particles[particleComp.firstFreeParticle * 4 + i].Position = position;
                particleComp.particles[particleComp.firstFreeParticle * 4 + i].Velocity = velocity;
                particleComp.particles[particleComp.firstFreeParticle * 4 + i].Random   = randomValues;
                particleComp.particles[particleComp.firstFreeParticle * 4 + i].Time     = particleComp.currentTime;
            }

            particleComp.firstFreeParticle = nextFreeParticle;
        }
예제 #7
0
 public ParticleEmitter(SmokeParticleComponent particleComp, float particlesPerSecond, Vector3 initialPosition)
 {
     this.particleComp    = particleComp;
     timeBetweenParticles = 1.0f / particlesPerSecond;
     previousPosition     = initialPosition;
 }
예제 #8
0
        public void Render(GraphicsDevice graphicsDevice, GameTime gameTime)
        {
            List <Entity> particleEnts = ComponentManager.Instance.GetAllEntitiesWithComponentType <SmokeParticleComponent>();
            Entity        cameraEnt    = ComponentManager.Instance.GetFirstEntityOfType <CameraComponent>();

            CameraComponent c = ComponentManager.Instance.GetEntityComponent <CameraComponent>(cameraEnt);

            foreach (Entity e in particleEnts)
            {
                SmokeParticleComponent pc = ComponentManager.Instance.GetEntityComponent <SmokeParticleComponent>(e);


                //// Restore the vertex buffer if context is lost
                //if (pc.vertexBuffer.IsContentLost)
                //{
                //    pc.vertexBuffer.SetData(pc.particles);
                //}

                // If there are any particles waiting in the newly added queue,
                // we'd better upload them to the GPU ready for drawing.
                if (pc.firstNewParticle != pc.firstFreeParticle)
                {
                    AddNewParticlesToVertexBuffer(pc);
                }

                // If there are any active particles, draw them now!
                if (pc.firstActiveParticle != pc.firstFreeParticle)
                {
                    graphicsDevice.BlendState        = pc.BlendState;
                    graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;


                    // Set an effect parameter describing the current time. All the vertex
                    // shader particle animation is keyed off this value.
                    effectTimeParameter.SetValue(pc.currentTime);

                    // Set the particle vertex and index buffer.
                    graphicsDevice.SetVertexBuffer(pc.vertexBuffer);
                    graphicsDevice.Indices = pc.indexBuffer;

                    effectViewParameter.SetValue(c.viewMatrix);
                    effectProjectionParameter.SetValue(c.projectionMatrix);
                    effectViewportScaleParameter.SetValue(new Vector2(0.5f / device.Viewport.AspectRatio, -0.5f));

                    // Activate the particle effect.
                    foreach (EffectPass pass in pc.effect.CurrentTechnique.Passes)
                    {
                        pass.Apply();

                        if (pc.firstActiveParticle < pc.firstFreeParticle)
                        {
                            // If the active particles are all in one consecutive range,
                            // we can draw them all in a single call.
                            graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,
                                                                 pc.firstActiveParticle * 4, (pc.firstFreeParticle - pc.firstActiveParticle) * 4,
                                                                 pc.firstActiveParticle * 6, (pc.firstFreeParticle - pc.firstActiveParticle) * 2);
                        }
                        else
                        {
                            // If the active particle range wraps past the end of the queue
                            // back to the start, we must split them over two draw calls.
                            graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,
                                                                 pc.firstActiveParticle * 4, (pc.MaxParticles - pc.firstActiveParticle) * 4,
                                                                 pc.firstActiveParticle * 6, (pc.MaxParticles - pc.firstActiveParticle) * 2);

                            if (pc.firstFreeParticle > 0)
                            {
                                graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0,
                                                                     0, pc.firstFreeParticle * 4,
                                                                     0, pc.firstFreeParticle * 2);
                            }
                        }
                    }

                    // Reset some of the renderstates that we changed,
                    // so as not to mess up any other subsequent drawing.
                    graphicsDevice.DepthStencilState = DepthStencilState.Default;
                }

                pc.drawCounter++;
            }
        }
예제 #9
0
        public static void LoadParticleEffect(GraphicsDevice device, Effect effect, Texture2D particleTexture, ref SmokeParticleComponent particleComp)
        {
            // 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.

            particleComp.effect = effect.Clone();

            EffectParameterCollection parameters = particleComp.effect.Parameters;

            effectViewParameter          = parameters["View"];
            effectProjectionParameter    = parameters["Projection"];
            effectViewportScaleParameter = parameters["ViewportScale"];

            // Look up shortcuts for parameters that change every frame.
            effectTimeParameter = parameters["CurrentTime"];

            // Set the values of parameters that do not change.
            parameters["Duration"].SetValue((float)particleComp.Duration.TotalSeconds);
            parameters["DurationRandomness"].SetValue(particleComp.LifeDurationRandomness);
            parameters["Gravity"].SetValue(particleComp.Gravity);
            parameters["EndVelocity"].SetValue(particleComp.EndVelocity);
            parameters["MinColor"].SetValue(particleComp.MinColor.ToVector4());
            parameters["MaxColor"].SetValue(particleComp.MaxColor.ToVector4());

            parameters["RotateSpeed"].SetValue(
                new Vector2(particleComp.MinRotateSpeed, particleComp.MaxRotateSpeed));

            parameters["StartSize"].SetValue(
                new Vector2(particleComp.MinStartSize, particleComp.MaxStartSize));

            parameters["EndSize"].SetValue(
                new Vector2(particleComp.MinEndSize, particleComp.MaxEndSize));
            parameters["Texture"].SetValue(particleTexture);

            // Create a dynamic vertex buffer.
            particleComp.vertexBuffer = new DynamicVertexBuffer(device, ParticleVertex.VertexDeclaration,
                                                                particleComp.MaxParticles * 4, BufferUsage.WriteOnly);
            // Create and populate the index buffer.
            ushort[] indices = new ushort[particleComp.MaxParticles * 6];

            for (int i = 0; i < particleComp.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);
            }

            particleComp.indexBuffer = new IndexBuffer(device, typeof(ushort), indices.Length, BufferUsage.WriteOnly);

            particleComp.indexBuffer.SetData(indices);
        }
예제 #10
0
 public static void setParticleOffsetPosition(ref SmokeParticleComponent particleComp, Vector3 positionOffset)
 {
     particleComp.positionOffset = positionOffset;
 }