billboard particle rotation

Started by
1 comment, last by football94 10 years, 4 months ago

Hi guys

Ive been experimenting with particle effects and using the particle3D sample from the xna website(link below) as a platform

for experimentation. the effect Im trying to implement in my project is an explosion ring which means laying the billboard parallel

to the terrain ,and even though Ive tried different approaches Im still not quite sure what portion of the code needs to be modified

visuals of what Im working towards

[attachment=18981:fig15.png]

below is what I have so far

a texture that expands and fades out but how do I make it parallel to the ground

[attachment=18982:11 28 2013 5 25 59 PM.jpeg]

code I have so far

main game class


#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion

namespace Particle3DSample
{
    /// <summary>
    /// Sample showing how to implement a particle system entirely
    /// on the GPU, using the vertex shader to animate point sprites.
    /// </summary>
    public class Particle3DSampleGame : Microsoft.Xna.Framework.Game
    {
        #region Fields


        GraphicsDeviceManager graphics;

        SpriteBatch spriteBatch;
        SpriteFont font;
        Model grid;


        // This sample uses five different particle systems.
        ParticleSystem explosionParticles;
        ParticleSystem explosionSmokeParticles;
        ParticleSystem projectileTrailParticles;
        ParticleSystem smokePlumeParticles;
        ParticleSystem fireParticles;


        // The sample can switch between three different visual effects.
        enum ParticleState
        {
            Explosions,
            SmokePlume,
            RingOfFire,
        };

        ParticleState currentState = ParticleState.Explosions;


        // The explosions effect works by firing projectiles up into the
        // air, so we need to keep track of all the active projectiles.
        List<Projectile> projectiles = new List<Projectile>();

        TimeSpan timeToNextProjectile = TimeSpan.Zero;


        // Random number generator for the fire effect.
        Random random = new Random();


        // Input state.
        KeyboardState currentKeyboardState;
        GamePadState currentGamePadState;

        KeyboardState lastKeyboardState;
        GamePadState lastGamePadState;


        // Camera state.
        float cameraArc = -22;
        float cameraRotation = 20;
        float cameraDistance = 200;


        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public Particle3DSampleGame()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            graphics.PreferredBackBufferWidth = 853;
            graphics.PreferredBackBufferHeight = 480;

            graphics.MinimumPixelShaderProfile = ShaderProfile.PS_2_0;

            // Construct our particle system components.
            explosionParticles = new ExplosionParticleSystem(this, Content);
            explosionSmokeParticles = new ExplosionSmokeParticleSystem(this, Content);
            projectileTrailParticles = new ProjectileTrailParticleSystem(this, Content);
            smokePlumeParticles = new SmokePlumeParticleSystem(this, Content);
            fireParticles = new FireParticleSystem(this, Content);

            // Set the draw order so the explosions and fire
            // will appear over the top of the smoke.
            smokePlumeParticles.DrawOrder = 100;
            explosionSmokeParticles.DrawOrder = 200;
            projectileTrailParticles.DrawOrder = 300;
            explosionParticles.DrawOrder = 400;
            fireParticles.DrawOrder = 500;

            // Register the particle system components.
            Components.Add(explosionParticles);
            Components.Add(explosionSmokeParticles);
            Components.Add(projectileTrailParticles);
            Components.Add(smokePlumeParticles);
            Components.Add(fireParticles);
        }


        /// <summary>
        /// Load your graphics content.
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);

            font = Content.Load<SpriteFont>("font");
            grid = Content.Load<Model>("grid");
        }


        #endregion

        #region Update and Draw


        /// <summary>
        /// Allows the game to run logic.
        /// </summary>
        protected override void Update(GameTime gameTime)
        {
            HandleInput();

            UpdateCamera(gameTime);

            switch (currentState)
            {
                case ParticleState.Explosions:
                    UpdateExplosions(gameTime);
                    break;

                case ParticleState.SmokePlume:
                    UpdateSmokePlume();
                    break;

                case ParticleState.RingOfFire:
                    UpdateFire();
                    break;
            }

            UpdateProjectiles(gameTime);

            base.Update(gameTime);
        }


        /// <summary>
        /// Helper for updating the explosions effect.
        /// </summary>
        void UpdateExplosions(GameTime gameTime)
        {
            timeToNextProjectile -= gameTime.ElapsedGameTime;

            if (timeToNextProjectile <= TimeSpan.Zero)
            {
                // Create a new projectile once per second. The real work of moving
                // and creating particles is handled inside the Projectile class.
                projectiles.Add(new Projectile(explosionParticles,
                                               explosionSmokeParticles,
                                               projectileTrailParticles));

                timeToNextProjectile += TimeSpan.FromSeconds(1);
            }
        }


        /// <summary>
        /// Helper for updating the list of active projectiles.
        /// </summary>
        void UpdateProjectiles(GameTime gameTime)
        {
            int i = 0;

            while (i < projectiles.Count)
            {
                if (!projectiles[i].Update(gameTime))
                {
                    // Remove projectiles at the end of their life.
                    projectiles.RemoveAt(i);
                }
                else
                {
                    // Advance to the next projectile.
                    i++;
                }
            }
        }


        /// <summary>
        /// Helper for updating the smoke plume effect.
        /// </summary>
        void UpdateSmokePlume()
        {
            // This is trivial: we just create one new smoke particle per frame.
            smokePlumeParticles.AddParticle(Vector3.Zero, Vector3.Zero);
        }


        /// <summary>
        /// Helper for updating the fire effect.
        /// </summary>
        void UpdateFire()
        {
            const int fireParticlesPerFrame = 20;

            // Create a number of fire particles, randomly positioned around a circle.
            for (int i = 0; i < fireParticlesPerFrame; i++)
            {
                fireParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
            }

            // Create one smoke particle per frmae, too.
            smokePlumeParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
        }


        /// <summary>
        /// Helper used by the UpdateFire method. Chooses a random location
        /// around a circle, at which a fire particle will be created.
        /// </summary>
        Vector3 RandomPointOnCircle()
        {
            const float radius = 30;
            const float height = 40;

            double angle = random.NextDouble() * Math.PI * 2;

            float x = (float)Math.Cos(angle);
            float y = (float)Math.Sin(angle);

            return new Vector3(x * radius, y * radius + height, 0);
        }



       
        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice device = graphics.GraphicsDevice;

            device.Clear(Color.CornflowerBlue);

            // Compute camera matrices.
            float aspectRatio = (float)device.Viewport.Width /
                                (float)device.Viewport.Height;

            Matrix view = Matrix.CreateTranslation(0, -25, 0) *
                          Matrix.CreateRotationY(MathHelper.ToRadians(cameraRotation)) *
                          Matrix.CreateRotationX(MathHelper.ToRadians(cameraArc)) *
                          Matrix.CreateLookAt(new Vector3(0, 0, -cameraDistance),
                                              new Vector3(0, 0, 0), Vector3.Up);

            Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
                                                                    aspectRatio,
                                                                    1, 10000);

            // Pass camera matrices through to the particle system components.
            //explosionParticles.SetCamera(view, projection);
            //explosionSmokeParticles.SetCamera(view, projection);
            projectileTrailParticles.SetCamera( view, projection );
           // smokePlumeParticles.SetCamera(view, projection);
            //fireParticles.SetCamera(view, projection);

            // Draw our background grid and message text.
            DrawGrid(view, projection);

            DrawMessage();

            // This will draw the particle system components.
            base.Draw(gameTime);
        }


        /// <summary>
        /// Helper for drawing the background grid model.
        /// </summary>
        void DrawGrid(Matrix view, Matrix projection)
        {
            GraphicsDevice device = graphics.GraphicsDevice;

            device.RenderState.AlphaBlendEnable = false;
            device.RenderState.AlphaTestEnable = false;
            device.RenderState.DepthBufferEnable = true;

            device.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
            device.SamplerStates[0].AddressV = TextureAddressMode.Wrap;

            foreach (ModelMesh mesh in grid.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.View = view;
                    effect.Projection = projection;
                }

                mesh.Draw();
            }
        }


        /// <summary>
        /// Helper for drawing our message text.
        /// </summary>
        void DrawMessage()
        {
            string message = string.Format("Current effect: {0}!!!\n" +
                                           "Hit the A button or space bar to switch.",
                                           currentState);

            spriteBatch.Begin();
            spriteBatch.DrawString(font, message, new Vector2(50, 50), Color.White);
            spriteBatch.End();
        }


        #endregion

        #region Handle Input


        /// <summary>
        /// Handles input for quitting the game and cycling
        /// through the different particle effects.
        /// </summary>
        void HandleInput()
        {
            lastKeyboardState = currentKeyboardState;
            lastGamePadState = currentGamePadState;

            currentKeyboardState = Keyboard.GetState();
            currentGamePadState = GamePad.GetState(PlayerIndex.One);

            // Check for exit.
            if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
                currentGamePadState.Buttons.Back == ButtonState.Pressed)
            {
                Exit();
            }

            // Check for changing the active particle effect.
            if (((currentKeyboardState.IsKeyDown(Keys.Space) &&
                 (lastKeyboardState.IsKeyUp(Keys.Space))) ||
                ((currentGamePadState.Buttons.A == ButtonState.Pressed)) &&
                 (lastGamePadState.Buttons.A == ButtonState.Released)))
            {
                currentState++;

                if (currentState > ParticleState.RingOfFire)
                    currentState = 0;
            }
        }


        /// <summary>
        /// Handles input for moving the camera.
        /// </summary>
        void UpdateCamera(GameTime gameTime)
        {
            float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;

            // Check for input to rotate the camera up and down around the model.
            if (currentKeyboardState.IsKeyDown(Keys.Up) ||
                currentKeyboardState.IsKeyDown(Keys.W))
            {
                cameraArc += time * 0.025f;
            }

            if (currentKeyboardState.IsKeyDown(Keys.Down) ||
                currentKeyboardState.IsKeyDown(Keys.S))
            {
                cameraArc -= time * 0.025f;
            }

            cameraArc += currentGamePadState.ThumbSticks.Right.Y * time * 0.05f;

            // Limit the arc movement.
            if (cameraArc > 90.0f)
                cameraArc = 90.0f;
            else if (cameraArc < -90.0f)
                cameraArc = -90.0f;

            // Check for input to rotate the camera around the model.
            if (currentKeyboardState.IsKeyDown(Keys.Right) ||
                currentKeyboardState.IsKeyDown(Keys.D))
            {
                cameraRotation += time * 0.05f;
            }

            if (currentKeyboardState.IsKeyDown(Keys.Left) ||
                currentKeyboardState.IsKeyDown(Keys.A))
            {
                cameraRotation -= time * 0.05f;
            }

            cameraRotation += currentGamePadState.ThumbSticks.Right.X * time * 0.1f;

            // Check for input to zoom camera in and out.
            if (currentKeyboardState.IsKeyDown(Keys.Z))
                cameraDistance += time * 0.25f;

            if (currentKeyboardState.IsKeyDown(Keys.X))
                cameraDistance -= time * 0.25f;

            cameraDistance += currentGamePadState.Triggers.Left * time * 0.5f;
            cameraDistance -= currentGamePadState.Triggers.Right * time * 0.5f;

            // Limit the camera distance.
            if (cameraDistance > 500)
                cameraDistance = 500;
            else if (cameraDistance < 10)
                cameraDistance = 10;

            if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed ||
                currentKeyboardState.IsKeyDown(Keys.R))
            {
                cameraArc = -5;
                cameraRotation = 0;
                cameraDistance = 200;
            }
        }


        #endregion
    }

hlsl code


// Camera parameters.
float4x4 View;
float4x4 Projection;
float ViewportHeight;


// The current time, in seconds.
float CurrentTime;


// Parameters describing how the particles animate.
float Duration;
float DurationRandomness;
float3 Gravity;
float EndVelocity;
float4 MinColor;
float4 MaxColor;


// These float2 parameters describe the min and max of a range.
// The actual value is chosen differently for each particle,
// interpolating between x and y by some random amount.
float2 RotateSpeed;
float2 StartSize;
float2 EndSize;


// Particle texture and sampler.
texture Texture;

sampler Sampler = sampler_state
{
    Texture = (Texture);
    
    MinFilter = Linear;
    MagFilter = Linear;
    MipFilter = Point;
    
    AddressU = Clamp;
    AddressV = Clamp;
};


// Vertex shader input structure describes the start position and
// velocity of the particle, and the time at which it was created,
// along with some random values that affect its size and rotation.
struct VertexShaderInput
{
    float3 Position : POSITION0;
    float3 Velocity : NORMAL0;
    float4 Random : COLOR0;
    float Time : TEXCOORD0;
};


// Vertex shader output structure specifies the position, size, and
// color of the particle, plus a 2x2 rotation matrix (packed into
// a float4 value because we don't have enough color interpolators
// to send this directly as a float2x2).
struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float Size : PSIZE0;
    float4 Color : COLOR0;
    float4 Rotation : COLOR1;
};


// Vertex shader helper for computing the position of a particle.
float4 ComputeParticlePosition(float3 position, float3 velocity,
                               float age, float normalizedAge)
{
    float startVelocity = length(velocity);

    // Work out how fast the particle should be moving at the end of its life,
    // by applying a constant scaling factor to its starting velocity.
    float endVelocity = startVelocity * EndVelocity;
    
    // Our particles have constant acceleration, so given a starting velocity
    // S and ending velocity E, at time T their velocity should be S + (E-S)*T.
    // The particle position is the sum of this velocity over the range 0 to T.
    // To compute the position directly, we must integrate the velocity
    // equation. Integrating S + (E-S)*T for T produces S*T + (E-S)*T*T/2.

    float velocityIntegral = startVelocity * normalizedAge +
                             (endVelocity - startVelocity) * normalizedAge *
                                                             normalizedAge / 2;
     
    position += normalize(velocity) * velocityIntegral * Duration;
    
    // Apply the gravitational force.
    position += Gravity * age * normalizedAge;
    
    // Apply the camera view and projection transforms.
    return mul(mul(float4(position, 1), View), Projection);
}


// Vertex shader helper for computing the size of a particle.
float ComputeParticleSize(float4 projectedPosition,
                          float randomValue, float normalizedAge)
{
    // Apply a random factor to make each particle a slightly different size.
    float startSize = lerp(StartSize.x, StartSize.y, randomValue);
    float endSize = lerp(EndSize.x, EndSize.y, randomValue);
    
    // Compute the actual size based on the age of the particle.
    float size = lerp(startSize, endSize, normalizedAge);
    
    // Project the size into screen coordinates.
    return size * Projection._m11 / projectedPosition.w * ViewportHeight / 2;
}


// Vertex shader helper for computing the color of a particle.
float4 ComputeParticleColor(float4 projectedPosition,
                            float randomValue, float normalizedAge)
{
    // Apply a random factor to make each particle a slightly different color.
    float4 color = lerp(MinColor, MaxColor, randomValue);
    
    // Fade the alpha based on the age of the particle. This curve is hard coded
    // to make the particle fade in fairly quickly, then fade out more slowly:
    // plot x*(1-x)*(1-x) for x=0:1 in a graphing program if you want to see what
    // this looks like. The 6.7 scaling factor normalizes the curve so the alpha
    // will reach all the way up to fully solid.
    
    color.a *= normalizedAge * (1-normalizedAge) * (1-normalizedAge) * 6.7;
   
    return color;
}


// Vertex shader helper for computing the rotation of a particle.
float4 ComputeParticleRotation(float randomValue, float age)
{    
    // Apply a random factor to make each particle rotate at a different speed.
    float rotateSpeed = lerp(RotateSpeed.x, RotateSpeed.y, randomValue);
    
    float rotation = rotateSpeed * age;

    // Compute a 2x2 rotation matrix.
    float c = cos(rotation);
    float s = sin(rotation);
    
    float4 rotationMatrix = float4(c, -s, s, c);
    
    // Normally we would output this matrix using a texture coordinate interpolator,
    // but texture coordinates are generated directly by the hardware when drawing
    // point sprites. So we have to use a color interpolator instead. Only trouble
    // is, color interpolators are clamped to the range 0 to 1. Our rotation values
    // range from -1 to 1, so we have to scale them to avoid unwanted clamping.
    
    rotationMatrix *= 0.5;
    rotationMatrix += 0.5;
    
    return rotationMatrix;
}


// Custom vertex shader animates particles entirely on the GPU.
VertexShaderOutput VertexShader(VertexShaderInput input)
{
    VertexShaderOutput output;
    
    // Compute the age of the particle.
    float age = CurrentTime - input.Time;
    
    // Apply a random factor to make different particles age at different rates.
    age *= 1 + input.Random.x * DurationRandomness;
    
    // Normalize the age into the range zero to one.
    float normalizedAge = saturate(age / Duration);

    // Compute the particle position, size, color, and rotation.
    output.Position = ComputeParticlePosition(input.Position, input.Velocity,
                                              age, normalizedAge);
    
    output.Size = ComputeParticleSize(output.Position, input.Random.y, normalizedAge);
    output.Color = ComputeParticleColor(output.Position, input.Random.z, normalizedAge);
    output.Rotation = ComputeParticleRotation(input.Random.w, age);
    
    return output;
}


// Pixel shader input structure for particles that do not rotate.
struct NonRotatingPixelShaderInput
{
    float4 Color : COLOR0;
    
#ifdef XBOX
    float2 TextureCoordinate : SPRITETEXCOORD;
#else
    float2 TextureCoordinate : TEXCOORD0;
#endif
};


// Pixel shader for drawing particles that do not rotate.
float4 NonRotatingPixelShader(NonRotatingPixelShaderInput input) : COLOR0
{
    return tex2D(Sampler, input.TextureCoordinate) * input.Color;
}


// Pixel shader input structure for particles that can rotate.
struct RotatingPixelShaderInput
{
    float4 Color : COLOR0;
    float4 Rotation : COLOR1;
    
#ifdef XBOX
    float2 TextureCoordinate : SPRITETEXCOORD;
#else
    float2 TextureCoordinate : TEXCOORD0;
#endif
};


// Pixel shader for drawing particles that can rotate. It is not actually
// possible to rotate a point sprite, so instead we rotate our texture
// coordinates. Leaving the sprite the regular way up but rotating the
// texture has the exact same effect as if we were able to rotate the
// point sprite itself.
float4 RotatingPixelShader(RotatingPixelShaderInput input) : COLOR0
{
    float2 textureCoordinate = input.TextureCoordinate;

    // We want to rotate around the middle of the particle, not the origin,
    // so we offset the texture coordinate accordingly.
    textureCoordinate -= 0.5;
    
    // Apply the rotation matrix, after rescaling it back from the packed
    // color interpolator format into a full -1 to 1 range.
    float4 rotation = input.Rotation * 2 - 1;
    
    textureCoordinate = mul(textureCoordinate, float2x2(rotation));
    
    // Point sprites are squares. So are textures. When we rotate one square
    // inside another square, the corners of the texture will go past the
    // edge of the point sprite and get clipped. To avoid this, we scale
    // our texture coordinates to make sure the entire square can be rotated
    // inside the point sprite without any clipping.
    textureCoordinate *= sqrt(2);
    
    // Undo the offset used to control the rotation origin.
    textureCoordinate += 0.5;

    return tex2D(Sampler, textureCoordinate) * input.Color;
}


// Effect technique for drawing particles that do not rotate. Works with shader 1.1.
technique NonRotatingParticles
{
    pass P0
    {
        VertexShader = compile vs_1_1 VertexShader();
        PixelShader = compile ps_1_1 NonRotatingPixelShader();
    }
}


// Effect technique for drawing particles that can rotate. Requires shader 2.0.
technique RotatingParticles
{
    pass P0
    {
        VertexShader = compile vs_1_1 VertexShader();
        PixelShader = compile ps_2_0 RotatingPixelShader();
    }
}

link to original xna project

http://xbox.create.msdn.com/en-US/education/catalog/sample/particle_3d

Thanks Guys

Advertisement

hello.

I think you may need to make it a world space quad and not a screen aligned quad.

Thanks for replying ankhd

got it working (kind of sloppy but should work),code and pics below if anyones interested

[attachment=19051:12 2 2013 4 40 42 PM.jpeg]

circle expands and vanishes with spacebar press

main game code


using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace BookCode
{
    public struct VertexExplosion
    {
        public Vector3 Position;
        public Vector4 TexCoord;
        public Vector4 AdditionalInfo;
        public VertexExplosion(Vector3 Position, Vector4 TexCoord, Vector4 AdditionalInfo)
        {
            this.Position = Position;
            this.TexCoord = TexCoord;
            this.AdditionalInfo = AdditionalInfo;
        }
        public static readonly VertexElement[] VertexElements = new VertexElement[]
            {
                new VertexElement(0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0),
                new VertexElement(0, 12, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0),
                new VertexElement(0, 28, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 1),
            };
        public static readonly int SizeInBytes = sizeof(float) * (3 + 4 + 4);
    }

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        GraphicsDevice device;
        BasicEffect basicEffect;
        SpriteBatch spriteBatch;
        SpriteFont spriteFont;
        CoordCross cCross;
        QuatCam quatCam;

        Texture2D myTexture;
        VertexExplosion[] explosionVertices;
        VertexDeclaration myVertexDeclaration;
        Effect expEffect;
        float time = 0;
        Random rand;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            quatCam = new QuatCam(GraphicsDevice.Viewport);
            rand = new Random();
            base.Initialize();
        }

        protected override void LoadContent()
        {
            device = graphics.GraphicsDevice;
            basicEffect = new BasicEffect(device, null);
            cCross = new CoordCross(device);

            myTexture = Content.Load<Texture2D>("shockwave");
            expEffect = Content.Load<Effect>("explosionEffect");

            myVertexDeclaration = new VertexDeclaration(device, VertexExplosion.VertexElements);

            //only used to render one line of text!
            spriteBatch = new SpriteBatch(device);
            spriteFont = Content.Load<SpriteFont>("arial");
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
            if (gamePadState.Buttons.Back == ButtonState.Pressed)
                this.Exit();

            MouseState mouseState = Mouse.GetState();
            KeyboardState keyState = Keyboard.GetState();
            if (keyState.IsKeyDown(Keys.Escape))
                this.Exit(); // Exit with ESC key

            quatCam.Update(mouseState, keyState, gamePadState);

            if ((keyState.IsKeyDown(Keys.Space)) || (gamePadState.Buttons.A == ButtonState.Pressed))
                CreateExplosionVertices((float)gameTime.TotalGameTime.TotalMilliseconds);

            base.Update(gameTime);
        }

        private void CreateExplosionVertices(float time)
        {
            int particles = 1;
            explosionVertices = new VertexExplosion[particles * 6];

            int i = 0;
            for (int partnr = 0; partnr < particles; partnr++)
            {
                Vector3 startingPos = new Vector3(0, 0, 0);

               // float r1 = (float)rand.NextDouble() - 0.5f;
                //float r2 = (float)rand.NextDouble() - 0.5f;
                //float r3 = (float)rand.NextDouble() - 0.5f;
                //Vector3 moveDirection = new Vector3(-0.1f, 10.105f, 0.1f);
                Vector3 moveDirection = new Vector3(0.0f, 0.0f, 0.0f);
                moveDirection.Normalize();

                //float r4 = (float)rand.NextDouble();
                //r4 = r4 / 4.0f * 3.0f + 0.25f;

                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(1, 1, time, 1000), new Vector4(moveDirection, 0.0f));
                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(0, 0, time, 1000), new Vector4(moveDirection, 0.0f));
                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(1, 0, time, 1000), new Vector4(moveDirection, 0.0f));

                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(1, 1, time, 1000), new Vector4(moveDirection, 0.0f));
                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(0, 1, time, 1000), new Vector4(moveDirection, 0.0f));
                explosionVertices[i++] = new VertexExplosion(startingPos, new Vector4(0, 0, time, 1000), new Vector4(moveDirection, 0.0f));
            }
        }
        
        protected override void Draw(GameTime gameTime)
        {
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1, 0);

            cCross.Draw(quatCam.ViewMatrix, quatCam.ProjectionMatrix);

            Vector3 rotAxis = new Vector3(4 ,  70 ,10);

            float angle = 5.0f;
            Matrix worldMatrix = Matrix.CreateRotationZ(angle);
            Matrix worldMatrix2 = Matrix.CreateRotationX(angle);
            Matrix worldMatrix1 = Matrix.CreateTranslation(rotAxis);



            if (explosionVertices != null)
            {
                //draw billboards
                expEffect.CurrentTechnique = expEffect.Techniques["Explosion"];
                expEffect.Parameters["xWorld"].SetValue(worldMatrix );
                expEffect.Parameters["xProjection"].SetValue(quatCam.ProjectionMatrix);
                expEffect.Parameters["xView"].SetValue(quatCam.ViewMatrix);

                expEffect.Parameters["xCamPos"].SetValue(rotAxis);
                expEffect.Parameters["xExplosionTexture"].SetValue(myTexture);
                expEffect.Parameters["xCamUp"].SetValue(quatCam.UpVector);
                expEffect.Parameters["xTime"].SetValue((float)gameTime.TotalGameTime.TotalMilliseconds);

                device.RenderState.AlphaBlendEnable = true;
                device.RenderState.SourceBlend = Blend.SourceAlpha;
                device.RenderState.DestinationBlend = Blend.One;
                device.RenderState.DepthBufferWriteEnable = false;
            
                expEffect.Begin();
                foreach (EffectPass pass in expEffect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    device.VertexDeclaration = myVertexDeclaration;
                    device.DrawUserPrimitives<VertexExplosion>(PrimitiveType.TriangleList, explosionVertices, 0, explosionVertices.Length / 3);
                    pass.End();
                }
                expEffect.End();

                device.RenderState.DepthBufferWriteEnable = true;                
            }

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.BackToFront, SaveStateMode.None);
            spriteBatch.DrawString(spriteFont, "Hit Space to initiate an explosion!", new Vector2(20, 20), Color.Red);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

camera code


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

namespace BookCode
{
#if XBOX
    //When compiled for the XBox, Mouse and MouseState are unknown.
    //Define dummy bodies for them, so a MouseState object
    //can be passed to the Update method of the camera.
    public class MouseState
    {
    }
    public static class Mouse
    {
        public static MouseState GetState()
        {
            return new MouseState();
        }
    }
#endif

    class QuatCam
    {
        Viewport viewPort;
        Matrix viewMatrix;
        Matrix projectionMatrix;

        Vector3 cameraPosition;
        Quaternion cameraRotation;
        MouseState originalMouseState;

        public QuatCam(Viewport viewPort)
        {
            this.viewPort = viewPort;

            float viewAngle = MathHelper.PiOver4;
            float nearPlane = 0.5f;
            float farPlane = 1000.0f;
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(viewAngle, viewPort.AspectRatio, nearPlane, farPlane);

            cameraPosition = new Vector3(0, 0, 20);
            cameraRotation = Quaternion.CreateFromAxisAngle(Vector3.Up, 0);
            UpdateViewMatrix();

#if XBOX
#else
            Mouse.SetPosition(viewPort.Width / 2, viewPort.Height / 2);
            originalMouseState = Mouse.GetState();
#endif
        }

        public void Update(MouseState currentMouseState, KeyboardState keyState, GamePadState gamePadState)
        {            
#if XBOX        
            float rotationSpeed = 0.1f;
            float xDifference = - rotationSpeed * gamePadState.ThumbSticks.Left.X * 5.0f;
            float yDifference = rotationSpeed * gamePadState.ThumbSticks.Left.Y * 5.0f;
            float leftrightRot = rotationSpeed * xDifference;
            float updownRot = rotationSpeed * yDifference;

            Quaternion additionalRotation = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), updownRot) * Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), leftrightRot);
            cameraRotation = cameraRotation * additionalRotation;

            UpdateViewMatrix();

            float moveUp = gamePadState.Triggers.Right - gamePadState.Triggers.Left;
            AddToCameraPosition(new Vector3(gamePadState.ThumbSticks.Right.X, moveUp, -gamePadState.ThumbSticks.Right.Y));
#else
            if (currentMouseState != originalMouseState)
            {
                float rotationSpeed = 0.005f;    
                float xDifference = originalMouseState.X - currentMouseState.X;
                float yDifference = originalMouseState.Y - currentMouseState.Y;
                float leftrightRot = rotationSpeed * xDifference;
                float updownRot = rotationSpeed * yDifference;
                Mouse.SetPosition(viewPort.Width / 2, viewPort.Height / 2);

                Quaternion additionalRotation = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), updownRot) * Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), leftrightRot);
                cameraRotation = cameraRotation * additionalRotation;

                UpdateViewMatrix();
            }            
         
            if (keyState.IsKeyDown(Keys.Up) || keyState.IsKeyDown(Keys.W))      //Forward
                AddToCameraPosition(new Vector3(0, 0, -1));
            if (keyState.IsKeyDown(Keys.Down) || keyState.IsKeyDown(Keys.S))    //Backward
                AddToCameraPosition(new Vector3(0, 0, 1));
            if (keyState.IsKeyDown(Keys.Right) || keyState.IsKeyDown(Keys.D))   //Right
                AddToCameraPosition(new Vector3(1, 0, 0));
            if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A))    //Left
                AddToCameraPosition(new Vector3(-1, 0, 0));
            if (keyState.IsKeyDown(Keys.Q))                                     //Up
                AddToCameraPosition(new Vector3(0, 1, 0));
            if (keyState.IsKeyDown(Keys.Z))                                     //Down
                AddToCameraPosition(new Vector3(0, -1, 0));
#endif
        }

        public void AddToCameraPosition(Vector3 vectorToAdd)
        {
            float moveSpeed = 0.5f;
            Vector3 rotatedVector = Vector3.Transform(vectorToAdd, cameraRotation);
            cameraPosition += moveSpeed * rotatedVector;
            UpdateViewMatrix();
        }

        public void UpdateViewMatrix()
        {
            Vector3 cameraOriginalTarget = new Vector3(0,0, -1);
            Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);

            Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
            Vector3 cameraFinalTarget = cameraPosition + cameraRotatedTarget;

            Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);

            viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraFinalTarget, cameraRotatedUpVector);
        }

        public Matrix ProjectionMatrix
        {
            get{ return projectionMatrix;}
        }

        public Matrix ViewMatrix { get { return viewMatrix; } }

        public Vector3 Position { get { return cameraPosition; } }

        public Vector3 Forward
        {
            get 
            {
                Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
                Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
                return cameraRotatedTarget;
            }
        }

        public Vector3 UpVector
        {
            get
            {
                Vector3 cameraOriginalUp = new Vector3(0, 1, 0);
                Vector3 cameraRotatedUp = Vector3.Transform(cameraOriginalUp, cameraRotation);
                return cameraRotatedUp;
            }
        }
    }
}

hlsl code


//------- XNA interface --------
float4x4 xView;
float4x4 xProjection;
float4x4 xWorld;
float3 xCamPos;
float3 xAllowedRotDir;
float3 xCamUp;
float xTime;

//------- Texture Samplers --------
Texture xExplosionTexture;
sampler textureSampler = sampler_state { texture = <xExplosionTexture> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = CLAMP; AddressV = CLAMP;};

struct ExpVertexToPixel
{
	float4 Position : POSITION;
	float2 TexCoord	: TEXCOORD0;
	float4 Color	: COLOR0;
};
struct ExpPixelToFrame
{
    float4 Color 	: COLOR0;
};

//------- Technique: Explosion --------
float3 BillboardVertex(float3 billboardCenter, float2 cornerID, float size)
{
	float3 eyeVector = billboardCenter - xCamPos;		
	
	float3 sideVector = cross(eyeVector,xCamUp);
	sideVector = normalize(sideVector);
	float3 upVector = cross(sideVector,eyeVector);
	upVector = normalize(upVector);
	
	float3 finalPosition = billboardCenter;
	finalPosition += (cornerID.x-0.5f)*sideVector*size;
	finalPosition += (0.5f-cornerID.y)*upVector*size;	
	
	return finalPosition;
}

ExpVertexToPixel ExplosionVS(float3 inPos: POSITION0, float4 inTexCoord: TEXCOORD0, float4 inExtra: TEXCOORD1)
{
	ExpVertexToPixel Output = (ExpVertexToPixel)0;	

	float3 startingPosition = mul(inPos, xWorld);
	
	float2 texCoords = inTexCoord.xy;
	float birthTime = inTexCoord.z;
	float maxAge = inTexCoord.w;
	float3 moveDirection = inExtra.xyz;
	float random = inExtra.w;	
	
	float age = xTime - birthTime;	
	float relAge = age/maxAge;	
	
	float sizer = saturate(1-relAge*relAge/ 2.0f);
	float size = 5.0f/sizer;
    
	float totalDisplacement = sin(relAge*6.28f/4.0f)*3.0f*random;
	float3 billboardCenter = startingPosition ;	
	billboardCenter += age*float3(0,0,0)/1000.0f;		
	
	float3 finalPosition = BillboardVertex(billboardCenter, texCoords, size);
	float4 finalPosition4 = float4(finalPosition, 1);
		
	float4x4 preViewProjection = mul (xView, xProjection);
	Output.Position = mul(finalPosition4, preViewProjection);
	
	float alpha = 1-relAge*relAge;
	Output.Color = float4(0.5f,0.5f,0.5f,alpha);	
	
	Output.TexCoord = texCoords;	
	
	return Output;
}

ExpPixelToFrame ExplosionPS(ExpVertexToPixel PSIn) : COLOR0
{
	ExpPixelToFrame Output = (ExpPixelToFrame)0;		
	Output.Color = tex2D(textureSampler, PSIn.TexCoord)*PSIn.Color;
	return Output;
}

technique Explosion
{
	pass Pass0
    {          
    	VertexShader = compile vs_1_1 ExplosionVS();
        PixelShader  = compile ps_1_1 ExplosionPS();        
    }
}

coordcross code


using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

namespace BookCode
{
    class CoordCross
    {
        private VertexPositionColor[] vertices;
        private GraphicsDevice device;
        private BasicEffect basicEffect;
        private VertexDeclaration vertDeclaration;

        public CoordCross(GraphicsDevice device)
        {
            this.device = device;
            basicEffect = new BasicEffect(device, null);

            InitVertices();
            vertDeclaration = new VertexDeclaration(device, VertexPositionColor.VertexElements);
        }

        private void InitVertices()
        {
            vertices = new VertexPositionColor[30];

            vertices[0] = new VertexPositionColor(new Vector3(0, 0, 0), Color.White);
            vertices[1] = new VertexPositionColor(Vector3.Right * 5, Color.White);
            vertices[2] = new VertexPositionColor(new Vector3(5, 0, 0), Color.White);
            vertices[3] = new VertexPositionColor(new Vector3(4.5f, 0.5f, 0), Color.White);
            vertices[4] = new VertexPositionColor(new Vector3(5, 0, 0), Color.White);
            vertices[5] = new VertexPositionColor(new Vector3(4.5f, -0.5f, 0), Color.White);

            vertices[6] = new VertexPositionColor(new Vector3(0, 0, 0), Color.White);
            vertices[7] = new VertexPositionColor(Vector3.Up * 5, Color.White);
            vertices[8] = new VertexPositionColor(new Vector3(0, 5, 0), Color.White);
            vertices[9] = new VertexPositionColor(new Vector3(0.5f, 4.5f, 0), Color.White);
            vertices[10] = new VertexPositionColor(new Vector3(0, 5, 0), Color.White);
            vertices[11] = new VertexPositionColor(new Vector3(-0.5f, 4.5f, 0), Color.White);

            vertices[12] = new VertexPositionColor(new Vector3(0, 0, 0), Color.White);
            vertices[13] = new VertexPositionColor(Vector3.Forward * 5, Color.White);
            vertices[14] = new VertexPositionColor(new Vector3(0, 0, -5), Color.White);
            vertices[15] = new VertexPositionColor(new Vector3(0, 0.5f, -4.5f), Color.White);
            vertices[16] = new VertexPositionColor(new Vector3(0, 0, -5), Color.White);
            vertices[17] = new VertexPositionColor(new Vector3(0, -0.5f, -4.5f), Color.White);
        }

        public void Draw(Matrix viewMatrix, Matrix projectionMatrix)
        {
            basicEffect.World = Matrix.Identity;
            basicEffect.View = viewMatrix;
            basicEffect.Projection = projectionMatrix;
            basicEffect.VertexColorEnabled = true;
            basicEffect.Begin();
            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Begin();

                device.VertexDeclaration = vertDeclaration;
                device.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, vertices, 0, 9);           

                pass.End();
            }
            basicEffect.End();            
        }

        public void DrawUsingPresetEffect()
        {
            device.VertexDeclaration = new VertexDeclaration(device, VertexPositionColor.VertexElements);
            device.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, vertices, 0, 9);           
        }
    }
}

This topic is closed to new replies.

Advertisement