#region File Description
//-----------------------------------------------------------------------------
// SpriteEffects.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 SpriteEffects
{
///
/// Sample demonstrating how shaders can be used to
/// apply special effects to sprite rendering.
///
public class SpriteEffectsGame : Microsoft.Xna.Framework.Game
{
#region Fields
GraphicsDeviceManager graphics;
KeyboardState lastKeyboardState = new KeyboardState();
GamePadState lastGamePadState = new GamePadState();
KeyboardState currentKeyboardState = new KeyboardState();
GamePadState currentGamePadState = new GamePadState();
// Enum describes all the rendering techniques used in this demo.
enum DemoEffect
{
Desaturate,
Disappear,
RefractCat,
RefractGlacier,
Normalmap,
}
DemoEffect currentEffect = 0;
// Effects used by this sample.
Effect desaturateEffect;
Effect disappearEffect;
Effect normalmapEffect;
Effect refractionEffect;
// Textures used by this sample.
Texture2D catTexture;
Texture2D catNormalmapTexture;
Texture2D glacierTexture;
Texture2D waterfallTexture;
// SpriteBatch instance used to render all the effects.
SpriteBatch spriteBatch;
#endregion
#region Initialization
public SpriteEffectsGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
///
/// Loads graphics content.
///
protected override void LoadContent()
{
desaturateEffect = Content.Load("desaturate");
disappearEffect = Content.Load("disappear");
normalmapEffect = Content.Load("normalmap");
refractionEffect = Content.Load("refraction");
catTexture = Content.Load("cat");
catNormalmapTexture = Content.Load("cat_normalmap");
glacierTexture = Content.Load("glacier");
waterfallTexture = Content.Load("waterfall");
spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
}
#endregion
#region Update and Draw
///
/// Allows the game to run logic.
///
protected override void Update(GameTime gameTime)
{
HandleInput();
if (NextButtonPressed())
{
currentEffect++;
if (currentEffect > DemoEffect.Normalmap)
currentEffect = 0;
}
base.Update(gameTime);
}
///
/// This is called when the game should draw itself.
///
protected override void Draw(GameTime gameTime)
{
switch (currentEffect)
{
case DemoEffect.Desaturate:
DrawDesaturate(gameTime);
break;
case DemoEffect.Disappear:
DrawDisappear(gameTime);
break;
case DemoEffect.Normalmap:
DrawNormalmap(gameTime);
break;
case DemoEffect.RefractCat:
DrawRefractCat(gameTime);
break;
case DemoEffect.RefractGlacier:
DrawRefractGlacier(gameTime);
break;
}
base.Draw(gameTime);
}
///
/// Effect dynamically changes color saturation.
///
void DrawDesaturate(GameTime gameTime)
{
// Begin the sprite batch, using our custom effect.
spriteBatch.Begin(0, null, null, null, null, desaturateEffect);
// Draw four copies of the same sprite with different saturation levels.
// The saturation amount is passed into the effect using the alpha of the
// SpriteBatch.Draw color parameter. This isn't as flexible as using a
// regular effect parameter, but makes it easy to draw many sprites with
// a different saturation amount for each. If we had used an effect
// parameter for this, we would have to end the sprite batch, then begin
// a new one, each time we wanted to change the saturation setting.
byte pulsate = (byte)Pulsate(gameTime, 4, 0, 255);
spriteBatch.Draw(glacierTexture,
QuarterOfScreen(0, 0),
new Color(255, 255, 255, 0));
spriteBatch.Draw(glacierTexture,
QuarterOfScreen(1, 0),
new Color(255, 255, 255, 64));
spriteBatch.Draw(glacierTexture,
QuarterOfScreen(0, 1),
new Color(255, 255, 255, 255));
spriteBatch.Draw(glacierTexture,
QuarterOfScreen(1, 1),
new Color(255, 255, 255, pulsate));
// End the sprite batch.
spriteBatch.End();
}
///
/// Effect uses a scrolling overlay texture to make different parts of
/// an image fade in or out at different speeds.
///
void DrawDisappear(GameTime gameTime)
{
// Draw the background image.
spriteBatch.Begin();
spriteBatch.Draw(glacierTexture, GraphicsDevice.Viewport.Bounds, Color.White);
spriteBatch.End();
// Set an effect parameter to make our overlay
// texture scroll in a giant circle.
disappearEffect.Parameters["OverlayScroll"].SetValue(
MoveInCircle(gameTime, 0.8f) * 0.25f);
// Set the overlay texture (as texture 1,
// because 0 will be the main sprite texture).
graphics.GraphicsDevice.Textures[1] = waterfallTexture;
// Begin the sprite batch.
spriteBatch.Begin(0, null, null, null, null, disappearEffect);
// Draw the sprite, passing the fade amount as the
// alpha of the SpriteBatch.Draw color parameter.
byte fade = (byte)Pulsate(gameTime, 2, 0, 255);
spriteBatch.Draw(catTexture,
MoveInCircle(gameTime, catTexture, 1),
new Color(255, 255, 255, fade));
// End the sprite batch.
spriteBatch.End();
}
///
// Effect uses a scrolling displacement texture to offset the position of
// the main texture.
///
void DrawRefractCat(GameTime gameTime)
{
// Draw the background image.
spriteBatch.Begin();
spriteBatch.Draw(glacierTexture, GraphicsDevice.Viewport.Bounds, Color.White);
spriteBatch.End();
// Set an effect parameter to make the
// displacement texture scroll in a giant circle.
refractionEffect.Parameters["DisplacementScroll"].SetValue(
MoveInCircle(gameTime, 0.1f));
// Set the displacement texture.
graphics.GraphicsDevice.Textures[1] = waterfallTexture;
// Begin the sprite batch.
spriteBatch.Begin(0, null, null, null, null, refractionEffect);
// Draw the sprite.
spriteBatch.Draw(catTexture,
MoveInCircle(gameTime, catTexture, 1),
Color.White);
// End the sprite batch.
spriteBatch.End();
}
///
// Effect uses a scrolling displacement texture to offset the position of
// the main texture.
///
void DrawRefractGlacier(GameTime gameTime)
{
// Set an effect parameter to make the
// displacement texture scroll in a giant circle.
refractionEffect.Parameters["DisplacementScroll"].SetValue(
MoveInCircle(gameTime, 0.2f));
// Set the displacement texture.
graphics.GraphicsDevice.Textures[1] = waterfallTexture;
// Begin the sprite batch.
spriteBatch.Begin(0, null, null, null, null, refractionEffect);
// Because the effect will displace the texture coordinates before
// sampling the main texture, the coordinates could sometimes go right
// off the edges of the texture, which looks ugly. To prevent this, we
// adjust our sprite source region to leave a little border around the
// edge of the texture. The displacement effect will then just move the
// texture coordinates into this border region, without ever hitting
// the edge of the texture.
Rectangle croppedGlacier = new Rectangle(32, 32,
glacierTexture.Width - 64,
glacierTexture.Height - 64);
spriteBatch.Draw(glacierTexture,
GraphicsDevice.Viewport.Bounds,
croppedGlacier,
Color.White);
// End the sprite batch.
spriteBatch.End();
}
///
/// Effect uses a normalmap texture to apply realtime lighting while
/// drawing a 2D sprite.
///
void DrawNormalmap(GameTime gameTime)
{
// Draw the background image.
spriteBatch.Begin();
spriteBatch.Draw(glacierTexture, GraphicsDevice.Viewport.Bounds, Color.White);
spriteBatch.End();
// Animate the light direction.
Vector2 spinningLight = MoveInCircle(gameTime, 1.5f);
double time = gameTime.TotalGameTime.TotalSeconds;
float tiltUpAndDown = 0.5f + (float)Math.Cos(time * 0.75) * 0.1f;
Vector3 lightDirection = new Vector3(spinningLight * tiltUpAndDown,
1 - tiltUpAndDown);
lightDirection.Normalize();
normalmapEffect.Parameters["LightDirection"].SetValue(lightDirection);
// Set the normalmap texture.
graphics.GraphicsDevice.Textures[1] = catNormalmapTexture;
// Begin the sprite batch.
spriteBatch.Begin(0, null, null, null, null, normalmapEffect);
// Draw the sprite.
spriteBatch.Draw(catTexture, CenterOnScreen(catTexture), Color.White);
// End the sprite batch.
spriteBatch.End();
}
///
/// Helper calculates the destination rectangle needed
/// to draw a sprite to one quarter of the screen.
///
Rectangle QuarterOfScreen(int x, int y)
{
Viewport viewport = graphics.GraphicsDevice.Viewport;
int w = viewport.Width / 2;
int h = viewport.Height / 2;
return new Rectangle(w * x, h * y, w, h);
}
///
/// Helper calculates the destination position needed
/// to center a sprite in the middle of the screen.
///
Vector2 CenterOnScreen(Texture2D texture)
{
Viewport viewport = graphics.GraphicsDevice.Viewport;
int x = (viewport.Width - texture.Width) / 2;
int y = (viewport.Height - texture.Height) / 2;
return new Vector2(x, y);
}
///
/// Helper computes a value that oscillates over time.
///
static float Pulsate(GameTime gameTime, float speed, float min, float max)
{
double time = gameTime.TotalGameTime.TotalSeconds * speed;
return min + ((float)Math.Sin(time) + 1) / 2 * (max - min);
}
///
/// Helper for moving a value around in a circle.
///
static Vector2 MoveInCircle(GameTime gameTime, float speed)
{
double time = gameTime.TotalGameTime.TotalSeconds * speed;
float x = (float)Math.Cos(time);
float y = (float)Math.Sin(time);
return new Vector2(x, y);
}
///
/// Helper for moving a sprite around in a circle.
///
Vector2 MoveInCircle(GameTime gameTime, Texture2D texture, float speed)
{
Viewport viewport = graphics.GraphicsDevice.Viewport;
float x = (viewport.Width - texture.Width) / 2;
float y = (viewport.Height - texture.Height) / 2;
return MoveInCircle(gameTime, speed) * 128 + new Vector2(x, y);
}
#endregion
#region Handle Input
///
/// Handles input for quitting the game.
///
private 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();
}
}
///
/// Checks whether the user wants to advance to the next rendering effect.
///
bool NextButtonPressed()
{
// Have they pressed the space bar?
if (currentKeyboardState.IsKeyDown(Keys.Space) &&
!lastKeyboardState.IsKeyDown(Keys.Space))
return true;
// Have they pressed the gamepad A button?
if (currentGamePadState.Buttons.A == ButtonState.Pressed &&
lastGamePadState.Buttons.A == ButtonState.Released)
return true;
return false;
}
#endregion
}
#region Entry Point
///
/// The main entry point for the application.
///
static class Program
{
static void Main()
{
using (SpriteEffectsGame game = new SpriteEffectsGame())
{
game.Run();
}
}
}
#endregion
}