// Project: XnaGraphicEngine, File: BaseGame.cs
// Namespace: XnaGraphicEngine.Game, Class: BaseGame
// Path: C:\code\XnaBook\XnaGraphicEngine\Game, Author: abi
// Code lines: 1448, Size of file: 42,61 KB
// Creation date: 07.12.2006 18:22
// Last modified: 07.12.2006 23:44
// 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 Microsoft.Xna.Framework.Input;
using XnaGraphicEngine.Graphics;
using XnaGraphicEngine.Helpers;
using XnaGraphicEngine.Properties;
using XnaGraphicEngine.Shaders;
using XnaGraphicEngine.Sounds;
using System;
using System.Collections.Generic;
using System.Text;
using Texture = XnaGraphicEngine.Graphics.Texture;
using Model = XnaGraphicEngine.Graphics.Model;
#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.
/// Use a standard field of view of 90 degrees, and use 1 for near plane
/// and 1000 for far plane. Field of view might be changed for speed items.
///
public const float FieldOfView = (float)Math.PI / 1.8f,//1.75f,//2.0f,
NearPlane = 1,
FarPlane = 250;//150;
#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;
///
/// BaseGame of our game.
///
protected static int width, height;
///
/// Back buffer depth format
///
static DepthFormat backBufferDepthFormat = DepthFormat.Depth32;
///
/// Remember multi sample type in Initialize for later use
/// in RenderToTexture!
///
static MultiSampleType remMultiSampleType = MultiSampleType.None;
///
/// Remember multi sample quality.
///
static int remMultiSampleQuality = 0;
///
/// Quit game, used to check if other threads have to be quitted too.
///
private static bool quit = false;
///
/// 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;
///
/// Precalculated view projection matrix for the Convert3DPointTo2D,
/// IsInFrontOfCamera and IsVisible methods. Calculated in ViewMatrix.
///
private static Matrix viewProjMatrix;
///
/// Line manager 2D
///
private static LineManager2D lineManager2D = null;
///
/// Line manager 3D
///
private static LineManager3D lineManager3D = null;
///
/// Mesh render manager to render meshes of models in a highly
/// optimized manner. We don't really have anything stored in here
/// except for a sorted list on how to render everything based on the
/// techniques and the materials and links to the renderable meshes.
///
private static MeshRenderManager meshRenderManager =
new MeshRenderManager();
///
/// Texture font to render text on the screen.
///
private static TextureFont font;
///
/// Numbers texture font for score, time, etc.
///
private static NumbersTextureFont numbersFont;
///
/// Camera for everything, used also for the rocket input.
///
public static SimpleCamera camera = null;
///
/// Post screen effect: Bloom and motion blur effect, every cool :)
///
protected static PostScreenMenu glowShader = null;
///
/// Glow shader
///
public static PostScreenMenu GlowShader
{
get
{
return glowShader;
} // get
} // GlowShader
///
/// Use this color to colorize the whole scene, light will affect
/// everything, this way we can easily lighten up everything or turn
/// the color from light blue to dark red. Use the LightColor property!
///
private static Color lightColor = Color.White;
///
/// Remember the lens flare color and sky background color.
///
protected static Color remLensFlareColor = Color.White,
remSkyBackgroundColor = Color.White;
///
/// Lens flare color
///
/// Color
public static Color LensFlareColor
{
set
{
remLensFlareColor = value;
} // set
} // LensFlareColor
///
/// Sky background color
///
/// Color
public static Color SkyBackgroundColor
{
get
{
return remSkyBackgroundColor;
} // get
set
{
remSkyBackgroundColor = value;
} // set
} // SkyBackgroundColor
///
/// Light color, used for shader color multiplication, for
/// non shader lights and for the lens flare effect.
///
/// Color
public static Color LightColor
{
get
{
return lightColor;
} // get
set
{
lightColor = value;
} // set
} // LightColor
#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 1024x768 to the current
/// screen resolution. Used to position UI elements.
///
/// Y in 768px
/// Int
public static int YToRes(int yIn768px)
{
return (int)Math.Round(yIn768px * BaseGame.Height / 768.0f);
} // YToRes(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;
float heightFactor = (height / 1200.0f);
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)
///
/// 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
#region Other properties
///
/// Graphics device access for the whole the engine.
///
/// Graphics device
public static GraphicsDevice Device
{
get
{
return device;
} // get
} // Device
///
/// Quit game, used to check if other threads have to be quitted too.
///
public static bool Quit
{
get
{
return quit;
} // get
} // Quit
///
/// Content manager access for the whole the engine.
///
/// Content manager
public static ContentManager Content
{
get
{
return content;
} // get
} // Content
///
/// 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
///
/// BaseGame 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
private static Vector3 lightDir = new Vector3(0, 0, 1);
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);
if (Input.KeyboardSpaceJustPressed)
lightDir = new Vector3(1, 0, 0);
return lightDir;
} // get
set
{
lightDir = value;
lightDir.Normalize();
} // set
} // LightDirection
#endregion
#region Matrices
///
/// 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);
// Precalculate viewProjMatrix for 3d helper methods below.
viewProjMatrix = viewMatrix * projectionMatrix;
} // 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 viewProjMatrix;// ViewMatrix* ProjectionMatrix;
} // get
} // ViewProjectionMatrix
///
/// World view projection matrix
///
/// Matrix
public static Matrix WorldViewProjectionMatrix
{
get
{
return WorldMatrix * viewProjMatrix;// ViewMatrix* ProjectionMatrix;
} // get
} // WorldViewProjectionMatrix
#endregion
#region Frames per second
///
/// Elapsed time this frame in ms. Always have something valid here
/// in case we devide through this values!
///
private static float elapsedTimeThisFrameInMs = 0.001f, totalTimeMs = 0;//,
//lastFrameTotalTimeMs = 0;
///
/// Helper for calculating frames per second.
///
private static float startTimeThisSecond = 0;
///
/// For more accurate frames per second calculations,
/// just count for one second, then fpsLastSecond is updated.
/// Btw: Start with 1 to help some tests avoid the devide through zero
/// problem.
///
private static int
frameCountThisSecond = 0,
totalFrameCount = 0,
fpsLastSecond = 60;
///
/// Fps
///
/// Int
public static int Fps
{
get
{
return fpsLastSecond;
} // get
} // Fps
/*suxx, just use fpsInterpolated, it more accurate than overall checks
///
/// Fps after 10 seconds
///
/// Int
public static int FpsAfter10Seconds
{
get
{
// Return 100 if 10 seconds have not passed yet.
return TotalTime < 10 ? 100 :
// Else return total frames devided by the game time in sec.
(int)(totalFrameCount / TotalTime);
} // get
} // FpsAfter10Seconds
*/
///
/// Interpolated fps over the last 10 seconds.
/// Obviously goes down if our framerate is low.
///
private static float fpsInterpolated = 100.0f;
///
/// Total frames
///
/// Int
public static int TotalFrames
{
get
{
return totalFrameCount;
} // get
} // TotalFrames
#endregion
#region Timing stuff
///
/// Elapsed time this frame in ms
///
/// Int
public static float ElapsedTimeThisFrameInMs
{
get
{
return elapsedTimeThisFrameInMs;
} // get
} // ElapsedTimeThisFrameInMs
///
/// Total time in seconds
///
/// Int
public static float TotalTime
{
get
{
return totalTimeMs / 1000.0f;
} // get
} // TotalTime
///
/// Total time ms
///
/// Float
public static float TotalTimeMs
{
get
{
return totalTimeMs;
} // get
} // TotalTimeMs
///
/// Move factor per second, when we got 1 fps, this will be 1.0f,
/// when we got 100 fps, this will be 0.01f.
///
public static float MoveFactorPerSecond
{
get
{
return elapsedTimeThisFrameInMs / 1000.0f;
} // get
} // MoveFactorPerSecond
#endregion
#region Settings
///
/// Performance setting
///
public enum PerformanceSetting
{
Fastest,
Medium,
Quality,
} // enum PerformanceSetting
public static PerformanceSetting GraphicsPerformanceSetting
{
get
{
return GameSettings.Default.PerformanceSettings == 0 ?
PerformanceSetting.Fastest :
GameSettings.Default.PerformanceSettings == 1 ?
PerformanceSetting.Medium :
PerformanceSetting.Quality;
} // get
set
{
int newSetting = value == PerformanceSetting.Fastest ? 0 :
value == PerformanceSetting.Medium ? 1 : 2;
GameSettings.Default.PerformanceSettings = newSetting;
} // set
} // GraphicsPerformanceSetting
#endregion
#region Enable or disable alpha blending
///
/// Enable alpha blending with default SourceAlpha - InvSourceAlpha
///
public static void EnableAlphaBlending()
{
device.RenderState.AlphaBlendEnable = true;
device.RenderState.SourceBlend = Blend.SourceAlpha;
device.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
} // EnableAlphaBlending()
///
/// Disable alpha blending
///
public static void DisableAlphaBlending()
{
device.RenderState.AlphaBlendEnable = false;
} // DisableAlphaBlending()
#endregion
#region Enable or disable linear texture filtering
///
/// Enable linear texture filtering
///
public static void EnableLinearTextureFiltering()
{
device.SamplerStates[0].MinFilter = TextureFilter.Linear;
device.SamplerStates[0].MagFilter = TextureFilter.Linear;
device.SamplerStates[0].MipFilter = TextureFilter.Linear;
} // EnableLinearTextureFiltering()
///
/// Disable linear texture filtering
///
public static void DisableLinearTextureFiltering()
{
device.SamplerStates[0].MinFilter = TextureFilter.Point;
device.SamplerStates[0].MagFilter = TextureFilter.Point;
device.SamplerStates[0].MipFilter = TextureFilter.Point;
} // DisableLinearTextureFiltering()
#endregion
#region MeshRenderManager
///
/// Mesh render manager to render meshes of models in a highly
/// optimized manner.
///
/// Mesh render manager
public static MeshRenderManager MeshRenderManager
{
get
{
return meshRenderManager;
} // get
} // MeshRenderManager
#endregion
#region Other properties
///
/// Back buffer depth format
///
/// Surface format
public static DepthFormat BackBufferDepthFormat
{
get
{
return backBufferDepthFormat;
} // get
} // BackBufferDepthFormat
///
/// MultiSampleType
///
public static MultiSampleType MultiSampleType
{
get
{
return remMultiSampleType;
} // get
} // MultiSampleType
///
/// Multi sample quality
///
public static int MultiSampleQuality
{
get
{
return remMultiSampleQuality;
} // get
} // MultiSampleQuality
///
/// Game font
///
/// Texture font
public static TextureFont GameFont
{
get
{
return font;
} // get
} // GameFont
///
/// Numbers font
///
public static NumbersTextureFont NumbersFont
{
get
{
return numbersFont;
} // get
} // NumbersFont
///
/// Use PS
///
/// Bool
public static bool UsePS
{
get
{
return GameSettings.Default.PerformanceSettings > 0;
} // get
} // UsePS
///
/// Use PS 2.0
///
/// Bool
public static bool UsePS20
{
get
{
return GameSettings.Default.PerformanceSettings > 1;
} // get
} // UsePS20
///
/// Use PS 2.0
///
/// Bool
public static bool CanUsePS20
{
get
{
return GameSettings.Default.PerformanceSettings > 1;
} // get
} // UsePS20
///
/// Use post screen shaders
///
/// Bool
public static bool UsePostScreenShaders
{
get
{
return true;
} // get
} // UsePostScreenShaders
///
/// High detail
///
/// Bool
public static bool HighDetail
{
get
{
return true;
} // get
} // HighDetail
#endregion
#region Is active?
private static bool isWindowActive = false;
///
/// Is active?
///
public static bool IsWindowActive
{
get
{
return isWindowActive;
} // get
} // IsActive
///
/// On activated event.
///
///
///
protected override void OnActivated(object sender, EventArgs args)
{
base.OnActivated(sender, args);
isWindowActive = true;
} // OnActivated
///
/// On deactivated event
///
///
///
protected override void OnDeactivated(object sender, EventArgs args)
{
base.OnDeactivated(sender, args);
isWindowActive = false;
} // OnDeactivated
#endregion
#endregion
#region Helper methods for 3d-calculations
///
/// Epsilon (1/1000000) for comparing stuff which is nearly equal.
///
public const float Epsilon = 0.000001f;
///
/// Convert 3D vector to 2D vector, this is kinda the oposite of
/// GetScreenPlaneVector (not shown here). This can be useful for user
/// input/output, because we will often need the actual position on screen
/// of an object in 3D space from the users view to handle it the right
/// way. Used for lens flare and asteroid optimizations.
///
/// 3D world position
/// Resulting 2D screen position
public static Point Convert3DPointTo2D(Vector3 point)
{
/*doesn't work, Vector3.Transform does not behave like
* MDX Vector3.TransformCoordinate!
Log.Write("point=" + point);
Log.Write("ViewMatrix=" + ViewMatrix);
Log.Write("ProjectionMatrix=" + ProjectionMatrix);
Log.Write("ViewProjectionMatrix=" + ViewProjectionMatrix);
Vector3 result = Vector3.Transform(point, ViewProjectionMatrix);
Log.Write("result=" + result);
*/
// Fix:
Vector4 result4 = Vector4.Transform(point,
ViewProjectionMatrix);
//Log.Write("result4=" + result4);
if (result4.W == 0)
result4.W = BaseGame.Epsilon;
Vector3 result = new Vector3(
result4.X / result4.W,
result4.Y / result4.W,
result4.Z / result4.W);
//Log.Write("result=" + result);
// Output result from 3D to 2D
return new Point(
(int)Math.Round(+result.X * (width / 2)) + (width / 2),
(int)Math.Round(-result.Y * (height / 2)) + (height / 2));
} // Convert3DPointTo2D(point)
///
/// Is point in front of camera?
///
/// Position to check.
/// Bool
public static bool IsInFrontOfCamera(Vector3 point)
{
Vector4 result = Vector4.Transform(
new Vector4(point.X, point.Y, point.Z, 1),
viewProjMatrix);
// Is result in front?
return result.Z > result.W - NearPlane;
} // IsInFrontOfCamera(point)
///
/// Helper to check if a 3d-point is visible on the screen.
/// Will basically do the same as IsInFrontOfCamera and Convert3DPointTo2D,
/// but requires less code and is faster. Also returns just an bool.
/// Will return true if point is visble on screen, false otherwise.
/// Use the offset parameter to include points into the screen that are
/// only a couple of pixel outside of it.
///
/// Point
/// Check offset in percent of total
/// screen
/// Bool
public static bool IsVisible(Vector3 point, float checkOffset)
{
Vector4 result = Vector4.Transform(
new Vector4(point.X, point.Y, point.Z, 1),
viewProjMatrix);
// Point must be in front of camera, else just skip everything.
if (result.Z > result.W - NearPlane)
{
Vector2 screenPoint = new Vector2(
result.X / result.W, result.Y / result.W);
// Change checkOffset depending on how depth we are into the scene
// for very near objects (z < 5) pass all tests!
// for very far objects (z >> 5) only pass if near to +- 1.0f
float zDist = Math.Abs(result.Z);
if (zDist < 5.0f)
return true;
checkOffset = 1.0f + (checkOffset / zDist);
return
screenPoint.X >= -checkOffset && screenPoint.X <= +checkOffset &&
screenPoint.Y >= -checkOffset && screenPoint.Y <= +checkOffset;
} // if (result.z)
// Point is not in front of camera, return false.
return false;
} // IsVisible(point)
#endregion
#region Occlusion testing (not supported in XNA)
///
/// Occlusion intensity
///
/// Tex
/// Position
/// Size
/// Float
public static float OcclusionIntensity(Texture tex, Point pos, int size)
{
return 0.0f;
} // OcclusionIntensity(tex, pos, size)
#endregion
#region Constructor
///
/// Create base game
///
public BaseGame()
{
graphics = new GraphicsDeviceManager(this);
content = new ContentManager(Services);
int resolutionWidth = GameSettings.Default.ResolutionWidth;
int resolutionHeight = GameSettings.Default.ResolutionHeight;
// Use current desktop resolution if autodetect is selected.
if (resolutionWidth <= 0 ||
resolutionHeight <= 0)
{
resolutionWidth =
GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
resolutionHeight =
GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
} // if (resolutionWidth)
#if XBOX360
// Telling the Xbox 360 which resolution we want does not matter
graphics.IsFullScreen = true;
// Tell the Xbox 360 the resolution anyways, the viewport may just be
// 800x600 else!
graphics.PreferredBackBufferWidth =
GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
graphics.PreferredBackBufferHeight =
GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
#else
graphics.PreferredBackBufferWidth = resolutionWidth;
graphics.PreferredBackBufferHeight = resolutionHeight;
graphics.IsFullScreen = GameSettings.Default.Fullscreen;
#endif
#if DEBUG
// Don't limit the framerate to the vertical retrace in debug mode!
graphics.SynchronizeWithVerticalRetrace = false;
this.IsFixedTimeStep = false;
#endif
// Always turn on multisampling, looks much nice, especially on the Xbox360
this.graphics.PreferMultiSampling = true;
// Init the space camera
camera = new SimpleCamera(this);
this.Components.Add(camera);
} // BaseGame()
///
/// Initialize
///
protected override void Initialize()
{
#if !XBOX360
// Add screenshot capturer. Works only on windows platform.
// Note: Don't do this in constructor,
// we need the correct window name for screenshots!
this.Components.Add(new ScreenshotCapturer(this));
#endif
// Remember device
device = graphics.GraphicsDevice;
// Remember resolution
width = graphics.GraphicsDevice.Viewport.Width;
height = graphics.GraphicsDevice.Viewport.Height;
backBufferDepthFormat = graphics.PreferredDepthStencilFormat;
remMultiSampleType = device.PresentationParameters.MultiSampleType;
remMultiSampleQuality = device.PresentationParameters.MultiSampleQuality;
// Update resolution if it changes and restore device after it was lost
Window.ClientSizeChanged += new EventHandler(Window_ClientSizeChanged);
graphics.DeviceReset += new EventHandler(graphics_DeviceReset);
graphics_DeviceReset(null, EventArgs.Empty);
// 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();
numbersFont = new NumbersTextureFont(new Texture("NumbersFont"));
// Make sure we can use PS1 or PS2, see UsePS and UsePS20
if (device.GraphicsDeviceCapabilities.PixelShaderVersion.Major >= 2 &&
GameSettings.Default.PerformanceSettings < 2)
GameSettings.Default.PerformanceSettings = 2;
else if (
device.GraphicsDeviceCapabilities.PixelShaderVersion.Major >= 1 &&
GameSettings.Default.PerformanceSettings < 1)
GameSettings.Default.PerformanceSettings = 1;
// Init post screen glow
glowShader = new PostScreenMenu();
// Init light position
LightDirection = new Vector3(2, -7, 5);
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();
BaseGame.MeshRenderManager.ClearAll();
// Make sure there is nothing left
content.Unload();
} // if
base.UnloadGraphicsContent(unloadAllContent);
} // UnloadGraphicsContent(loadAllContent)
///
/// Graphics device reset
///
/// Sender
/// E
void graphics_DeviceReset(object sender, EventArgs e)
{
// Reset device
device = graphics.GraphicsDevice;
// Restore z buffer state
BaseGame.Device.RenderState.DepthBufferEnable = true;
BaseGame.Device.RenderState.DepthBufferWriteEnable = true;
// Set u/v addressing back to wrap
BaseGame.Device.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
BaseGame.Device.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
// Restore normal alpha blending
BaseGame.EnableAlphaBlending();
} // graphics_DeviceReset(sender, e)
///
/// Window client size changed
///
/// Sender
/// E
void Window_ClientSizeChanged(object sender, EventArgs e)
{
// Update width and height
width = device.Viewport.Width;//Window.ClientBounds.Width;
height = device.Viewport.Height;//Window.ClientBounds.Height;
aspectRatio = (float)width / (float)height;
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(
FieldOfView, aspectRatio, NearPlane, FarPlane);
} // Window_ClientSizeChanged(sender, e)
#endregion
#region Dispose
protected override void Dispose(bool disposing)
{
// We quit, make sure everyone knows about it.
quit = true;
base.Dispose(disposing);
} // Dispose(disposing)
#endregion
#region Update
///
/// Update
///
/// Game time
protected override void Update(GameTime gameTime)
{
Sound.Update();
Input.Update();
elapsedTimeThisFrameInMs =
(float)gameTime.ElapsedRealTime.TotalMilliseconds;
totalTimeMs += elapsedTimeThisFrameInMs;
// Make sure elapsedTimeThisFrameInMs is never 0
if (elapsedTimeThisFrameInMs <= 0)
elapsedTimeThisFrameInMs = 0.001f;
// Increase frame counter for FramesPerSecond
frameCountThisSecond++;
totalFrameCount++;
// One second elapsed?
if (totalTimeMs - startTimeThisSecond > 1000.0f)
{
// Calc fps
fpsLastSecond = (int)(frameCountThisSecond * 1000.0f /
(totalTimeMs - startTimeThisSecond));
// Reset startSecondTick and repaintCountSecond
startTimeThisSecond = totalTimeMs;
frameCountThisSecond = 0;
fpsInterpolated =
MathHelper.Lerp(fpsInterpolated, fpsLastSecond, 0.1f);
} // if (Math.Abs)
base.Update(gameTime);
} // Update(gameTime)
#endregion
#region Draw
bool showFps =
#if DEBUG
true;
#else
false;
#endif
///
/// Draw
///
/// Game time
protected override void Draw(GameTime gameTime)
{
#if DEBUG
// Only do this for unit tests, game handles this itself better!
if (this.GetType() == typeof(TestGame))
{
// Use alpha blending for blending out asteroids
BaseGame.EnableAlphaBlending();
//BaseGame.Device.RenderState.FillMode = FillMode.WireFrame;
// Render all meshes!
BaseGame.MeshRenderManager.Render();
// Execute post screen shader
glowShader.Show();
} // if
#endif
// Draw all sprites
SpriteHelper.DrawSprites(width, height);
// Show fps (use F1 or left shoulder+y button on gamepad to toggle)
if (Input.KeyboardF1JustPressed ||
// Also allow toggeling with gamepad
(Input.GamePad.Buttons.LeftShoulder == ButtonState.Pressed &&
Input.GamePadYJustPressed))
showFps = !showFps;
#if XBOX360
if (showFps)
TextureFont.WriteText(BaseGame.XToRes(32), BaseGame.YToRes(32),
"Fps: " + BaseGame.Fps + " " +
BaseGame.Width + "x" + BaseGame.Height);
#else
if (showFps)
TextureFont.WriteText(2, 2, "Fps: " + BaseGame.Fps);// +
//", Mouse: " + Input.MousePos);
#endif
// Show fonts
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 with shadow
///
/// Start position
/// End position
/// Color
public static void DrawLineWithShadow(Point startPoint, Point endPoint,
Color color)
{
lineManager2D.AddLine(
new Point(startPoint.X + 1, startPoint.Y + 1),
new Point(endPoint.X + 1, endPoint.Y + 1), Color.Black);
lineManager2D.AddLine(startPoint, endPoint, color);
} // DrawLineWithShadow(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.
///
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
#region Unit Testing
#if DEBUG
///
/// Test vector 3 transform
///
public static void TestVector3Transform()
{
// Put some point on the y axis
Vector3 point = new Vector3(+2500, +22500, +15000);
// Build a view matrix looking at 0, 0, 0 from above
// and a default projection matrix in 800x600.
Matrix matrix = new Matrix(
0.75f, 0, 0, 0,
0, 1, 0, 0,
0, 0, -1, -1,
0, 0, 49, 50);
//Matrix.CreateLookAt(new Vector3(0, 0, 50), Vector3.Zero, Vector3.Up) *
//Matrix.CreatePerspectiveFieldOfView(
// FieldOfView, 800.0f/600.0f, 1, 10000);
Log.Write("point=" + point);
Log.Write("matrix=" + matrix);
// Transform point
Vector3 resultXNA = Vector3.Transform(point, matrix);
// Should return {X:-0.125 Y:-1.505 Z:1.0} (ruffly)
// Does return {X:1875 Y:22500 Z:-14951}
Log.Write("resultXNA=" + resultXNA);
//Assert.IsTrue(Vector3.Distance(
// new Vector3(0.125f, -1.505f, 1.0f), resultXNA) < 0.001f);
// This returns the correct value
Vector4 result4 = Vector4.Transform(point, matrix);
if (result4.W == 0)
result4.W = BaseGame.Epsilon;
Vector3 result = new Vector3(
result4.X / result4.W,
result4.Y / result4.W,
result4.Z / result4.W);
Log.Write("result4=" + result4);
Log.Write("result=" + result);
Log.Write("Is result correct: " + (Vector3.Distance(
new Vector3(-0.125f, -1.505f, 1.0f), result) < 0.001f));
} // TestVector3Transform()
///
/// Test is visible
///
public static void TestIsVisible()
{
Model testModel = null;
Vector3 position = new Vector3(0, 0, 0);
TestGame.Start("TestIsVisible",
delegate // Init
{
testModel = new Model("asteroid3");
BaseGame.camera.SetPosition(new Vector3(0, 0, -80));
},
delegate // Render loop
{
if (IsVisible(position, 1.5f))
{
testModel.Render(Matrix.CreateScale(5));
} // testMesh
TextureFont.WriteText(1, 20,
"Position " + position + " is visible: " +
IsVisible(position, 1.0f));
TextureFont.WriteText(1, 100,
"Screen pos=" + Convert3DPointTo2D(position));
});
} // TestIsVisible()
#endif
#endregion
} // class BaseGame
} // namespace XnaGraphicEngine.Game