| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- #region File Description
- //-----------------------------------------------------------------------------
- // Billboard.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.Input;
- #endregion
- namespace Billboard
- {
- /// <summary>
- /// Sample showing how to efficiently render billboard sprites.
- /// </summary>
- public class BillboardGame : Microsoft.Xna.Framework.Game
- {
- #region Fields
- GraphicsDeviceManager graphics;
- KeyboardState currentKeyboardState = new KeyboardState();
- GamePadState currentGamePadState = new GamePadState();
-
- Vector3 cameraPosition = new Vector3(0, 50, 50);
- Vector3 cameraFront = new Vector3(0, 0, -1);
- Model landscape;
- #endregion
- #region Initialization
- public BillboardGame()
- {
- graphics = new GraphicsDeviceManager(this);
- Content.RootDirectory = "Content";
- }
- /// <summary>
- /// Load your graphics content.
- /// </summary>
- protected override void LoadContent()
- {
- landscape = Content.Load<Model>("landscape");
- }
- #endregion
- #region Update and Draw
- /// <summary>
- /// Allows the game to run logic.
- /// </summary>
- protected override void Update(GameTime gameTime)
- {
- HandleInput();
- UpdateCamera(gameTime);
- base.Update(gameTime);
- }
- /// <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.
- Matrix view = Matrix.CreateLookAt(cameraPosition,
- cameraPosition + cameraFront,
- Vector3.Up);
- Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4,
- device.Viewport.AspectRatio,
- 1, 10000);
- Vector3 lightDirection = Vector3.Normalize(new Vector3(3, -1, 1));
- Vector3 lightColor = new Vector3(0.3f, 0.4f, 0.2f);
- // Time is scaled down to make things wave in the wind more slowly.
- float time = (float)gameTime.TotalGameTime.TotalSeconds * 0.333f;
- // First we draw the ground geometry using BasicEffect.
- foreach (ModelMesh mesh in landscape.Meshes)
- {
- if (mesh.Name != "Billboards")
- {
- foreach (BasicEffect effect in mesh.Effects)
- {
- effect.View = view;
- effect.Projection = projection;
- effect.LightingEnabled = true;
- effect.DirectionalLight0.Enabled = true;
- effect.DirectionalLight0.Direction = lightDirection;
- effect.DirectionalLight0.DiffuseColor = lightColor;
- effect.AmbientLightColor = new Vector3(0.1f, 0.2f, 0.1f);
- }
- device.BlendState = BlendState.Opaque;
- device.DepthStencilState = DepthStencilState.Default;
- device.RasterizerState = RasterizerState.CullCounterClockwise;
- mesh.Draw();
- }
- }
- // Then we use a two-pass technique to render alpha blended billboards with
- // almost-correct depth sorting. The only way to make blending truly proper for
- // alpha objects is to draw everything in sorted order, but manually sorting all
- // our billboards would be very expensive. Instead, we draw in two passes.
- //
- // The first pass has alpha blending turned off, alpha testing set to only accept
- // ~95% or more opaque pixels, and the depth buffer turned on. Because this is only
- // rendering the solid parts of each billboard, the depth buffer works as
- // normal to give correct sorting, but obviously only part of each billboard will
- // be rendered.
- //
- // Then in the second pass we enable alpha blending, set alpha test to only accept
- // pixels with fractional alpha values, and set the depth buffer to test against
- // the existing data but not to write new depth values. This means the translucent
- // areas of each billboard will be sorted correctly against the depth buffer
- // information that was previously written while drawing the opaque parts, although
- // there can still be sorting errors between the translucent areas of different
- // billboards.
- //
- // In practice, sorting errors between translucent pixels tend not to be too
- // noticable as long as the opaque pixels are sorted correctly, so this technique
- // often looks ok, and is much faster than trying to sort everything 100%
- // correctly. It is particularly effective for organic textures like grass and
- // trees.
- foreach (ModelMesh mesh in landscape.Meshes)
- {
- if (mesh.Name == "Billboards")
- {
- // First pass renders opaque pixels.
- foreach (Effect effect in mesh.Effects)
- {
- effect.Parameters["View"].SetValue(view);
- effect.Parameters["Projection"].SetValue(projection);
- effect.Parameters["LightDirection"].SetValue(lightDirection);
- effect.Parameters["WindTime"].SetValue(time);
- effect.Parameters["AlphaTestDirection"].SetValue(1f);
- }
- device.BlendState = BlendState.Opaque;
- device.DepthStencilState = DepthStencilState.Default;
- device.RasterizerState = RasterizerState.CullNone;
- device.SamplerStates[0] = SamplerState.LinearClamp;
- mesh.Draw();
- // Second pass renders the alpha blended fringe pixels.
- foreach (Effect effect in mesh.Effects)
- {
- effect.Parameters["AlphaTestDirection"].SetValue(-1f);
- }
- device.BlendState = BlendState.NonPremultiplied;
- device.DepthStencilState = DepthStencilState.DepthRead;
- mesh.Draw();
- }
- }
- base.Draw(gameTime);
- }
- #endregion
- #region Handle Input
- /// <summary>
- /// Handles input for quitting the game.
- /// </summary>
- private void HandleInput()
- {
- currentKeyboardState = Keyboard.GetState();
- currentGamePadState = GamePad.GetState(PlayerIndex.One);
- // Check for exit.
- if (currentKeyboardState.IsKeyDown(Keys.Escape) ||
- currentGamePadState.Buttons.Back == ButtonState.Pressed)
- {
- Exit();
- }
- }
- /// <summary>
- /// Handles camera input.
- /// </summary>
- private void UpdateCamera(GameTime gameTime)
- {
- float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;
- // Check for input to rotate the camera.
- float pitch = -currentGamePadState.ThumbSticks.Right.Y * time * 0.001f;
- float turn = -currentGamePadState.ThumbSticks.Right.X * time * 0.001f;
- if (currentKeyboardState.IsKeyDown(Keys.Up))
- pitch += time * 0.001f;
- if (currentKeyboardState.IsKeyDown(Keys.Down))
- pitch -= time * 0.001f;
- if (currentKeyboardState.IsKeyDown(Keys.Left))
- turn += time * 0.001f;
- if (currentKeyboardState.IsKeyDown(Keys.Right))
- turn -= time * 0.001f;
- Vector3 cameraRight = Vector3.Cross(Vector3.Up, cameraFront);
- Vector3 flatFront = Vector3.Cross(cameraRight, Vector3.Up);
- Matrix pitchMatrix = Matrix.CreateFromAxisAngle(cameraRight, pitch);
- Matrix turnMatrix = Matrix.CreateFromAxisAngle(Vector3.Up, turn);
- Vector3 tiltedFront = Vector3.TransformNormal(cameraFront, pitchMatrix *
- turnMatrix);
- // Check angle so we cant flip over
- if (Vector3.Dot(tiltedFront, flatFront) > 0.001f)
- {
- cameraFront = Vector3.Normalize(tiltedFront);
- }
- // Check for input to move the camera around.
- if (currentKeyboardState.IsKeyDown(Keys.W))
- cameraPosition += cameraFront * time * 0.1f;
-
- if (currentKeyboardState.IsKeyDown(Keys.S))
- cameraPosition -= cameraFront * time * 0.1f;
- if (currentKeyboardState.IsKeyDown(Keys.A))
- cameraPosition += cameraRight * time * 0.1f;
- if (currentKeyboardState.IsKeyDown(Keys.D))
- cameraPosition -= cameraRight * time * 0.1f;
- cameraPosition += cameraFront *
- currentGamePadState.ThumbSticks.Left.Y * time * 0.1f;
- cameraPosition -= cameraRight *
- currentGamePadState.ThumbSticks.Left.X * time * 0.1f;
- if (currentGamePadState.Buttons.RightStick == ButtonState.Pressed ||
- currentKeyboardState.IsKeyDown(Keys.R))
- {
- cameraPosition = new Vector3(0, 50, 50);
- cameraFront = new Vector3(0, 0, -1);
- }
- }
- #endregion
- }
- #region Entry Point
- /// <summary>
- /// The main entry point for the application.
- /// </summary>
- static class Program
- {
- static void Main()
- {
- using (BillboardGame game = new BillboardGame())
- {
- game.Run();
- }
- }
- }
- #endregion
- }
|