// Project: XnaGraphicEngineVs2005, File: BaseGame.cs
// Namespace: XnaGraphicEngine.Game, Class: BaseGame
// Path: C:\code\XnaBook\XnaGraphicEngine\Game, Author: Abi
// Code lines: 728, Size of file: 21,72 KB
// Creation date: 26.11.2006 12:22
// Last modified: 27.11.2006 07:11
// Generated with Commenter by abi.exDream.com
#region Using directives
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Text;
using XnaGraphicEngine.Graphics;
using XnaGraphicEngine.Helpers;
using XnaGraphicEngine.Shaders;
#endregion
namespace XnaGraphicEngine.Game
{
///
/// Base game class for all the basic game support.
/// Connects all our helper classes together and makes our live easier!
///
public class BaseGame : Microsoft.Xna.Framework.Game
{
#region Constants
///
/// Background color
///
protected static readonly Color BackgroundColor = Color.Black;
///
/// Field of view and near and far plane distances for the
/// ProjectionMatrix creation.
///
private const float FieldOfView = (float)Math.PI / 2,
NearPlane = 1.0f,
FarPlane = 500.0f;
#endregion
#region Variables
///
/// Graphics
///
protected GraphicsDeviceManager graphics;
///
/// Content
///
protected static ContentManager content;
///
/// Remember graphics device and allow the useage wherever needed via
/// the Device property.
///
private static GraphicsDevice device;
///
/// Resolution of our game.
///
protected static int width, height;
///
/// Aspect ratio of our current resolution
///
private static float aspectRatio = 1.0f;
///
/// Render delegate for rendering methods, also used for many other
/// methods.
///
public delegate void RenderDelegate();
///
/// Matrices for shaders. Used in a similar way than in Rocket Commander,
/// but since we don't have a fixed function pipeline here we just use
/// these values in the shader. Make sure to set all matrices before
/// calling a shader. Inside a shader you have to update the shader
/// parameter too, just setting the WorldMatrix alone does not work.
///
private static Matrix worldMatrix,
viewMatrix,
projectionMatrix;
///
/// Line manager 2D
///
private static LineManager2D lineManager2D = null;
///
/// Line manager 3D
///
private static LineManager3D lineManager3D = null;
///
/// Texture font to render text on the screen.
///
private static TextureFont font;
#endregion
#region Properties
#region Calc rectangle helpers
///
/// XToRes helper method to convert 1024x640 to the current
/// screen resolution. Used to position UI elements.
///
/// X in 1024px width resolution
/// Int
public static int XToRes(int xIn1024px)
{
return (int)Math.Round(xIn1024px * BaseGame.Width / 1024.0f);
} // XToRes(xIn1024px)
///
/// YToRes helper method to convert 1024x640 to the current
/// screen resolution. Used to position UI elements.
///
/// Y in 640px height
/// Int
public static int YToRes(int yIn640px)
{
return (int)Math.Round(yIn640px * BaseGame.Height / 640.0f);
} // YToRes(yIn768px)
///
/// YTo res 768
///
/// Y in 768px
/// Int
public static int YToRes768(int yIn768px)
{
return (int)Math.Round(yIn768px * BaseGame.Height / 768.0f);
} // YToRes768(yIn768px)
///
/// XTo res 1600
///
/// X in 1600px
/// Int
public static int XToRes1600(int xIn1600px)
{
return (int)Math.Round(xIn1600px * BaseGame.Width / 1600.0f);
} // XToRes1600(xIn1600px)
///
/// YTo res 1200
///
/// Y in 1200px
/// Int
public static int YToRes1200(int yIn1200px)
{
return (int)Math.Round(yIn1200px * BaseGame.Height / 1200.0f);
} // YToRes1200(yIn1200px)
///
/// XTo res 1400
///
/// X in 1400px
/// Int
public static int XToRes1400(int xIn1400px)
{
return (int)Math.Round(xIn1400px * BaseGame.Width / 1400.0f);
} // XToRes1400(xIn1400px)
///
/// YTo res 1200
///
/// Y in 1050px
/// Int
public static int YToRes1050(int yIn1050px)
{
return (int)Math.Round(yIn1050px * BaseGame.Height / 1050.0f);
} // YToRes1050(yIn1050px)
///
/// Calc rectangle, helper method to convert from our images (1024)
/// to the current resolution. Everything will stay in the 16/9
/// format of the textures.
///
/// X
/// Y
/// Width
/// Height
/// Rectangle
public static Rectangle CalcRectangle(
int relX, int relY, int relWidth, int relHeight)
{
float widthFactor = width / 1024.0f;
float heightFactor = height / 640.0f;
return new Rectangle(
(int)Math.Round(relX * widthFactor),
(int)Math.Round(relY * heightFactor),
(int)Math.Round(relWidth * widthFactor),
(int)Math.Round(relHeight * heightFactor));
} // CalcRectangle(x, y, width)
///
/// Calc rectangle with bounce effect, same as CalcRectangle, but sizes
/// the resulting rect up and down depending on the bounceEffect value.
///
/// Rel x
/// Rel y
/// Rel width
/// Rel height
/// Bounce effect
/// Rectangle
public static Rectangle CalcRectangleWithBounce(
int relX, int relY, int relWidth, int relHeight, float bounceEffect)
{
float widthFactor = width / 1024.0f;
float heightFactor = height / 640.0f;
float middleX = (relX + relWidth / 2) * widthFactor;
float middleY = (relY + relHeight / 2) * heightFactor;
float retWidth = relWidth * widthFactor * bounceEffect;
float retHeight = relHeight * heightFactor * bounceEffect;
return new Rectangle(
(int)Math.Round(middleX - retWidth / 2),
(int)Math.Round(middleY - retHeight / 2),
(int)Math.Round(retWidth),
(int)Math.Round(retHeight));
} // CalcRectangleWithBounce(relX, relY, relWidth)
///
/// Calc rectangle, same method as CalcRectangle, but keep the 4 to 3
/// ratio for the image. The Rect will take same screen space in
/// 16:9 and 4:3 modes. E.g. Buttons should be displayed this way.
/// Should be used for 1024px width graphics.
///
/// Rel x
/// Rel y
/// Rel width
/// Rel height
/// Rectangle
public static Rectangle CalcRectangleKeep4To3(
int relX, int relY, int relWidth, int relHeight)
{
float widthFactor = width / 1024.0f;
float heightFactor = height / 768.0f;
return new Rectangle(
(int)Math.Round(relX * widthFactor),
(int)Math.Round(relY * heightFactor),
(int)Math.Round(relWidth * widthFactor),
(int)Math.Round(relHeight * heightFactor));
} // CalcRectangleKeep4To3(relX, relY, relWidth)
///
/// Calc rectangle, same method as CalcRectangle, but keep the 4 to 3
/// ratio for the image. The Rect will take same screen space in
/// 16:9 and 4:3 modes. E.g. Buttons should be displayed this way.
/// Should be used for 1024px width graphics.
///
/// Gfx rectangle
/// Rectangle
public static Rectangle CalcRectangleKeep4To3(
Rectangle gfxRect)
{
float widthFactor = width / 1024.0f;
float heightFactor = height / 768.0f;
return new Rectangle(
(int)Math.Round(gfxRect.X * widthFactor),
(int)Math.Round(gfxRect.Y * heightFactor),
(int)Math.Round(gfxRect.Width * widthFactor),
(int)Math.Round(gfxRect.Height * heightFactor));
} // CalcRectangleKeep4To3(gfxRect)
///
/// Calc rectangle for 1600px width graphics.
///
/// Rel x
/// Rel y
/// Rel width
/// Rel height
/// Rectangle
public static Rectangle CalcRectangle1600(
int relX, int relY, int relWidth, int relHeight)
{
float widthFactor = width / 1600.0f;
// keep height factor: float heightFactor = height / 1200.0f;
float heightFactor = (height / 1200.0f);// / (aspectRatio / (16 / 9));
return new Rectangle(
(int)Math.Round(relX * widthFactor),
(int)Math.Round(relY * heightFactor),
(int)Math.Round(relWidth * widthFactor),
(int)Math.Round(relHeight * heightFactor));
} // CalcRectangle1600(relX, relY, relWidth)
///
/// Calc rectangle 2000px, just a helper to scale stuff down
///
/// Rel x
/// Rel y
/// Rel width
/// Rel height
/// Rectangle
public static Rectangle CalcRectangle2000(
int relX, int relY, int relWidth, int relHeight)
{
float widthFactor = width / 2000.0f;
float heightFactor = (height / 1500.0f);
return new Rectangle(
(int)Math.Round(relX * widthFactor),
(int)Math.Round(relY * heightFactor),
(int)Math.Round(relWidth * widthFactor),
(int)Math.Round(relHeight * heightFactor));
} // CalcRectangle2000(relX, relY, relWidth)
/*unused
///
/// Calc rectangle for 1920px width graphics.
///
/// Rel x
/// Rel y
/// Rel width
/// Rel height
/// Rectangle
public static Rectangle CalcRectangle1920(
int relX, int relY, int relWidth, int relHeight)
{
float widthFactor = width / 1920.0f;
// keep height factor: float heightFactor = height / 1200.0f;
float heightFactor = (height / 1200.0f);// / (aspectRatio / (16 / 9));
return new Rectangle(
(int)Math.Round(relX * widthFactor),
(int)Math.Round(relY * heightFactor),
(int)Math.Round(relWidth * widthFactor),
(int)Math.Round(relHeight * heightFactor));
} // CalcRectangle1920(relX, relY, relWidth)
*/
///
/// Calc rectangle keep 4 to 3 align bottom
///
/// Rel x
/// Rel y
/// Rel width
/// Rel height
/// Rectangle
public static Rectangle CalcRectangleKeep4To3AlignBottom(
int relX, int relY, int relWidth, int relHeight)
{
float widthFactor = width / 1024.0f;
float heightFactor16To9 = height / 640.0f;
float heightFactor4To3 = height / 768.0f;
return new Rectangle(
(int)(relX * widthFactor),
(int)(relY * heightFactor16To9) -
(int)Math.Round(relHeight * heightFactor4To3),
(int)Math.Round(relWidth * widthFactor),
(int)Math.Round(relHeight * heightFactor4To3));
} // CalcRectangleKeep4To3AlignBottom(relX, relY, relWidth)
///
/// Calc rectangle keep 4 to 3 align bottom right
///
/// Rel x
/// Rel y
/// Rel width
/// Rel height
/// Rectangle
public static Rectangle CalcRectangleKeep4To3AlignBottomRight(
int relX, int relY, int relWidth, int relHeight)
{
float widthFactor = width / 1024.0f;
float heightFactor16To9 = height / 640.0f;
float heightFactor4To3 = height / 768.0f;
return new Rectangle(
(int)(relX * widthFactor) -
(int)Math.Round(relWidth * widthFactor),
(int)(relY * heightFactor16To9) -
(int)Math.Round(relHeight * heightFactor4To3),
(int)Math.Round(relWidth * widthFactor),
(int)Math.Round(relHeight * heightFactor4To3));
} // CalcRectangleKeep4To3AlignBottomRight(relX, relY, relWidth)
///
/// Calc rectangle centered with given height.
/// This one uses relX and relY points as the center for our rect.
/// The relHeight is then calculated and we align everything
/// with help of gfxRect (determinating the width).
/// Very useful for buttons, logos and other centered UI textures.
///
/// Rel x
/// Rel y
/// Rel height
/// Gfx rectangle
/// Rectangle
public static Rectangle CalcRectangleCenteredWithGivenHeight(
int relX, int relY, int relHeight, Rectangle gfxRect)
{
float widthFactor = width / 1024.0f;
float heightFactor = height / 640.0f;
int rectHeight = (int)Math.Round(relHeight * heightFactor);
// Keep aspect ratio
int rectWidth = (int)Math.Round(
gfxRect.Width * rectHeight / (float)gfxRect.Height);
return new Rectangle(
Math.Max(0, (int)Math.Round(relX * widthFactor) - rectWidth / 2),
Math.Max(0, (int)Math.Round(relY * heightFactor) - rectHeight / 2),
rectWidth, rectHeight);
} // CalcRectangleCenteredWithGivenHeight(relX, relY, relHeight)
#endregion
///
/// Graphics device access for the whole the engine.
///
/// Graphics device
public static GraphicsDevice Device
{
get
{
return device;
} // get
} // Device
///
/// Content manager access for the whole the engine.
///
/// Content manager
public static ContentManager Content
{
get
{
return content;
} // get
} // Content
public static bool UsePostScreenShaders
{
get
{
return true;
} // get
} // UsePostScreenShaders
public static bool CanUsePS20
{
get
{
return true;
} // get
} // CanUsePS20
public static bool CanUsePS30
{
get
{
return true;
} // get
} // CanUsePS30
public static bool HighDetail
{
get
{
return true;
} // get
} // HighDetail
///
/// Width of our backbuffer we render into.
///
/// Int
public static int Width
{
get
{
return width;
} // get
} // Width
///
/// Height of our backbuffer we render into.
///
/// Int
public static int Height
{
get
{
return height;
} // get
} // Height
///
/// Resolution rectangle
///
/// Rectangle
public static Rectangle ResolutionRect
{
get
{
return new Rectangle(0, 0, width, height);
} // get
} // ResolutionRect
///
/// Alpha blending
///
/// Bool
public static bool AlphaBlending
{
set
{
if (value)
{
device.RenderState.AlphaBlendEnable = true;
device.RenderState.SourceBlend = Blend.SourceAlpha;
device.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
} // if (value)
else
device.RenderState.AlphaBlendEnable = false;
} // set
} // AlphaBlending
///
/// World matrix
///
/// Matrix
public static Matrix WorldMatrix
{
get
{
return worldMatrix;
} // get
set
{
worldMatrix = value;
// Update worldViewProj here?
} // set
} // WorldMatrix
///
/// View matrix
///
/// Matrix
public static Matrix ViewMatrix
{
get
{
return viewMatrix;
} // get
set
{
// Set view matrix, usually only done in ChaseCamera.Update!
viewMatrix = value;
// Update camera pos and rotation, used all over the game!
invViewMatrix = Matrix.Invert(ViewMatrix);
CameraPos = invViewMatrix.Translation;
cameraRotation = Vector3.TransformNormal(
new Vector3(0, 0, 1), invViewMatrix);
} // set
} // ViewMatrix
///
/// Projection matrix
///
/// Matrix
public static Matrix ProjectionMatrix
{
get
{
return projectionMatrix;
} // get
set
{
projectionMatrix = value;
// Update worldViewProj here?
} // set
} // ProjectionMatrix
///
/// Camera pos, updated each loop in Update()!
/// Public to allow easy access from everywhere, will be called a lot each
/// frame, for example Model.Render uses this for distance checks.
///
public static Vector3 CameraPos;
///
/// Camera rotation, used to compare objects for visibility.
///
private static Vector3 cameraRotation = new Vector3(0, 0, 1);
///
/// Camera rotation
///
/// Vector 3
public static Vector3 CameraRotation
{
get
{
return cameraRotation;
} // get
} // CameraRotation
///
/// Remember inverse view matrix.
///
private static Matrix invViewMatrix;
///
/// Inverse view matrix
///
/// Matrix
public static Matrix InverseViewMatrix
{
get
{
return invViewMatrix;//Matrix.Invert(ViewMatrix);
} // get
} // InverseViewMatrix
///
/// View projection matrix
///
/// Matrix
public static Matrix ViewProjectionMatrix
{
get
{
return ViewMatrix * ProjectionMatrix;
} // get
} // ViewProjectionMatrix
///
/// World view projection matrix
///
/// Matrix
public static Matrix WorldViewProjectionMatrix
{
get
{
return WorldMatrix * ViewMatrix * ProjectionMatrix;
} // get
} // WorldViewProjectionMatrix
public static Vector3 LightDirection
{
get
{
// Note: Not really used here yet, only for the Model.Render method!
// Note2: Because we have no valid tangent data, the model will
// not look right yet, read Chapter 7 on how to fix that.
return new Vector3(0, 0, 1);
} // get
} // LightDirection
///
/// Move factor per second
///
public static float MoveFactorPerSecond
{
get
{
// Not really implemented here. This is just a quick hack.
// See later chapters for the full implementation!
return 1.0f / FpsCounter.Fps;
} // get
} // MoveFactorPerSecond
#endregion
#region Constructor
///
/// Create base game
///
public BaseGame()
{
graphics = new GraphicsDeviceManager(this);
content = new ContentManager(Services);
#if !XBOX360
this.Components.Add(new ScreenshotCapturer(this));
#endif
} // BaseGame()
///
/// Initialize
///
protected override void Initialize()
{
// Remember device
device = graphics.GraphicsDevice;
// Remember resolution
width = graphics.GraphicsDevice.Viewport.Width;
height = graphics.GraphicsDevice.Viewport.Height;
// Create matrices for our shaders, this makes it much easier to manage
// all the required matrices and we have to do this ourselfs since there
// is no fixed function support and theirfore no Device.Transform class.
WorldMatrix = Matrix.Identity;
aspectRatio = (float)width / (float)height;
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
FieldOfView, aspectRatio, NearPlane, FarPlane);
// ViewMatrix is updated in camera class
ViewMatrix = Matrix.CreateLookAt(
new Vector3(0, 0, 15), Vector3.Zero, Vector3.Up);
// Init global manager classes, which will be used all over the place ^^
lineManager2D = new LineManager2D();
lineManager3D = new LineManager3D();
// Create font
font = new TextureFont();
base.Initialize();
} // Initialize()
///
/// Graphic content container, just holds a link to every resource we
/// create.
///
static List graphicContent =
new List();
///
/// Register a IGraphicContent object to our internal graphicContent
/// container in case we need to recreate the graphic content later.
///
/// Graphic object
internal static void RegisterGraphicContentObject(IGraphicContent obj)
{
graphicContent.Add(obj);
} // RegisterGraphicContentObject(obj)
///
/// Load all graphics content (just our background texture).
/// Use this method to make sure a device reset event is handled correctly.
///
/// Load everything?
protected override void LoadGraphicsContent(bool loadAllContent)
{
if (loadAllContent)
{
// Remember device
device = graphics.GraphicsDevice;
// Recreate all content files
foreach (IGraphicContent contentItem in
// Create a new temporary list to prevent direct modification
new List(graphicContent))
contentItem.Load();
} // if
base.LoadGraphicsContent(loadAllContent);
} // LoadGraphicsContent(loadAllContent)
///
/// Unload graphic content if the device gets lost.
///
/// Unload everything?
protected override void UnloadGraphicsContent(bool unloadAllContent)
{
if (unloadAllContent == true)
{
// Dispose everything, but start with the stuff we create.
foreach (IGraphicContent contentItem in graphicContent)
contentItem.Dispose();
SpriteHelper.Dispose();
// Make sure there is nothing left
content.Unload();
} // if
base.UnloadGraphicsContent(unloadAllContent);
} // UnloadGraphicsContent(loadAllContent)
#endregion
#region Update
///
/// Update
///
/// Game time
protected override void Update(GameTime gameTime)
{
//unused: Sound.Update();
Input.Update();
if (Input.KeyboardEscapeJustPressed ||
Input.GamePadBackJustPressed)
this.Exit();
base.Update(gameTime);
} // Update(gameTime)
#endregion
#region Draw
///
/// Draw
///
/// Game time
protected override void Draw(GameTime gameTime)
{
// Draw all sprites and fonts
SpriteHelper.DrawSprites(width, height);
font.WriteAll();
lineManager2D.Render();
lineManager3D.Render();
base.Draw(gameTime);
// Allow clearing background next frame
clearedYet = false;
} // Draw(gameTime)
#endregion
#region ClearBackground
private bool clearedYet = false;
///
/// Clear background, will only be executed once per frame, so
/// it is save to call it multiple times.
///
public void ClearBackground()
{
if (clearedYet == false)
graphics.GraphicsDevice.Clear(BackgroundColor);
clearedYet = true;
} // ClearBackground()
#endregion
#region Line helper methods
///
/// Draw line
///
/// Start point
/// End point
/// Color
public static void DrawLine(Point startPoint, Point endPoint, Color color)
{
lineManager2D.AddLine(startPoint, endPoint, color);
} // DrawLine(startPoint, endPoint, color)
///
/// Draw line
///
/// Start point
/// End point
public static void DrawLine(Point startPoint, Point endPoint)
{
lineManager2D.AddLine(startPoint, endPoint, Color.White);
} // DrawLine(startPoint, endPoint)
///
/// Draw line
///
/// Start position
/// End position
/// Color
public static void DrawLine(Vector3 startPos, Vector3 endPos, Color color)
{
lineManager3D.AddLine(startPos, endPos, color);
} // DrawLine(startPos, endPos, color)
///
/// Draw line
///
/// Start position
/// End position
/// Start color
/// End color
public static void DrawLine(Vector3 startPos, Vector3 endPos,
Color startColor, Color endColor)
{
lineManager3D.AddLine(startPos, startColor, endPos, endColor);
} // DrawLine(startPos, endPos, startColor)
///
/// Draw line
///
/// Start position
/// End position
public static void DrawLine(Vector3 startPos, Vector3 endPos)
{
lineManager3D.AddLine(startPos, endPos, Color.White);
} // DrawLine(startPos, endPos)
///
/// Flush line manager 2D. Renders all lines and allows more lines
/// to be rendered. Used to render lines into textures and stuff.
///
public static void FlushLineManager2D()
{
lineManager2D.Render();
} // FlushLineManager2D()
///
/// Flush line manager 3D. Renders all lines and allows more lines
/// to be rendered.
///
public static void FlushLineManager3D()
{
lineManager3D.Render();
} // FlushLineManager3D()
#endregion
#region Set and reset render targets
///
/// Remember scene render target. This is very important because
/// for our post screen shaders we have to render our whole scene
/// to this render target. But in the process we will use many other
/// shaders and they might set their own render targets and then
/// reset it, but we need to have this scene still to be set.
/// Don't reset to the back buffer (with SetRenderTarget(0, null), this
/// would stop rendering to our scene render target and the post screen
/// shader will not be able to process our screen.
/// The whole reason for this is that we can't use StrechRectangle
/// like in Rocket Commander because XNA does not provide that function
/// (the reason for that is cross platform compatibility with the XBox360).
/// Instead we could use ResolveBackBuffer, but that method is VERY SLOW.
/// Our framerate would drop from 600 fps down to 20, not good.
/// However, multisampling will not work, so we will disable it anyway!
///
static RenderTarget2D remSceneRenderTarget = null;
///
/// Remember the last render target we set, this way we can check
/// if the rendertarget was set before calling resolve!
///
static RenderTarget2D lastSetRenderTarget = null;
///
/// Remember render to texture instances to allow recreating them all
/// when DeviceReset is called.
///
static List remRenderToTextures =
new List();
///
/// Add render to texture instance to allow recreating them all
/// when DeviceReset is called with help of the remRenderToTextures list.
///
public static void AddRemRenderToTexture(RenderToTexture renderToTexture)
{
remRenderToTextures.Add(renderToTexture);
} // AddRemToTexture(renderToTexture)
///
/// Current render target we have set, null if it is just the back buffer.
///
public static RenderTarget2D CurrentRenderTarget
{
get
{
return lastSetRenderTarget;
} // get
} // CurrentRenderTarget
///
/// Set render target
///
/// Is scene render target
internal static void SetRenderTarget(RenderTarget2D renderTarget,
bool isSceneRenderTarget)
{
//Log.Write("SetRenderTarget: "+renderTarget.Name+
// ", isSceneRenderTarget="+isSceneRenderTarget);
Device.SetRenderTarget(0, renderTarget);
if (isSceneRenderTarget)
remSceneRenderTarget = renderTarget;
lastSetRenderTarget = renderTarget;
} // SetRenderTarget(isSceneRenderTarget)
///
/// Reset render target
///
/// Full reset to back buffer
internal static void ResetRenderTarget(bool fullResetToBackBuffer)
{
//Log.Write("ResetRenderTarget: "+fullResetToBackBuffer);
if (remSceneRenderTarget == null ||
fullResetToBackBuffer)
{
remSceneRenderTarget = null;
lastSetRenderTarget = null;
Device.SetRenderTarget(0, null);
} // if (remSceneRenderTarget)
else
{
Device.SetRenderTarget(0, remSceneRenderTarget);
lastSetRenderTarget = remSceneRenderTarget;
} // else
} // ResetRenderTarget(fullResetToBackBuffer)
#endregion
} // class BaseGame
} // namespace XnaGraphicEngine.Game