/*
* Pina.cs
* Authors: August Zinsser
*
* Copyright August Zinsser 2007
* This program is distributed under the terms of the GNU General Public License
*/
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Pina3D.Particles;
namespace Pina3D
{
///
/// A 3D module built to drop into an XNA game. It can load and render Collada files exported from Maya and render them
/// with Direct3D. The coordinate system is right-handed with Z up.
///
public static class Pina
{
private static GraphicsDeviceManager mGraphics; // Reference to internal XNA variable
private static ContentManager mContent; // ''
private static ResourceContentManager mResContent; // ''
private static Matrix mBasis; // Holds the coordinates Pina assumes
private static Matrix mWorldMatrix; // Space transform. Keep some premultiplied xfms around to optimize calculations.
private static Matrix mViewMatrix; // '' The Camera class updates this whenever it moves
private static Matrix mInvViewMatrix; // ''
private static Matrix mProjectionMatrix; // ''
private static Matrix mViewProjectionMatrix; // ''
private static float mFieldOfView; // Projection space parameter
private static float mAspectRatio; // ''
private static float mNearPlane; // ''
private static float mFarPlane; // ''
private static Camera mCamera; // The current camera
private static RenderQueue mRenderQueue; // Holds a list of ColladaModels that should render themselves each Render cycle
private static Vector3 mGlobalLightDirection; // A global directional light (like the sun)
private static float mGlobalLightPower; // ''
private static Color mGlobalLightColor; // ''
private static float mGlobalLightAmbient; // ''
private static float mTotalMilliseconds; // The number of milliseconds that have passed in total
private static float mDT; // The number of seconds that have passed since the last Update cal
private static int mShaderModel; // Which version of pixel shaders to use (1 or 2)
public static GraphicsDeviceManager Graphics { get { return mGraphics; } }
public static ContentManager Content { get { return mContent; } }
public static ResourceContentManager ResourceContent { get { return mResContent; } }
///
/// The coordinate system used by pina
///
public static Matrix Basis { get { return mBasis; } }
///
/// Changed by each model each render cycle.
///
internal static Matrix WorldMatrix { set { mWorldMatrix = value; } get { return mWorldMatrix; } }
public static Matrix ViewMatrix
{
set
{
mViewMatrix = value;
mInvViewMatrix = Matrix.Invert(mViewMatrix);
mViewProjectionMatrix = mViewMatrix * mProjectionMatrix;
}
get { return mViewMatrix; }
}
public static Matrix InvViewMatrix { get { return mInvViewMatrix; } }
public static Matrix ViewProjectionMatrix { get { return mViewProjectionMatrix; } }
public static Matrix ProjectionMatrix
{
set
{
mProjectionMatrix = value;
mViewProjectionMatrix = mViewMatrix * mProjectionMatrix;
}
get { return mProjectionMatrix; }
}
public static float AspectRatio
{
set
{
mAspectRatio = value;
RecalculateProjectionMatrix();
}
get { return mAspectRatio; }
}
public static float NearPlane
{
set
{
mNearPlane = value;
RecalculateProjectionMatrix();
}
get { return mNearPlane; }
}
public static float FarPlane
{
set
{
mFarPlane = value;
RecalculateProjectionMatrix();
}
get { return mFarPlane; }
}
public static float FieldOfView
{
set
{
mFieldOfView = value;
RecalculateProjectionMatrix();
}
get { return mFieldOfView; }
}
///
/// Multiple cameras can be swapped in and out of this property, to change views.
///
public static Camera Camera { set { mCamera = value; } get { return mCamera; } }
///
/// Always white in color. Add a tint to a scene by setting GlobalLightColor and GlobalLightAmbient > 0.
///
public static Vector3 GlobalLightDirection
{
set
{
mGlobalLightDirection = value;
mGlobalLightDirection.Normalize();
}
get { return mGlobalLightDirection; }
}
///
/// Only affects global ambient light (global directional light is always white)
///
public static Color GlobalLightColor { set { mGlobalLightColor = value; } get { return mGlobalLightColor; } }
///
/// The power of the directional (white) light
///
public static float GlobalLightPower { set { mGlobalLightPower = value; } get { return mGlobalLightPower; } }
///
/// The power of the ambient (possibly colored) light
///
public static float GlobalLightAmbient { set { mGlobalLightAmbient = value; } get { return mGlobalLightAmbient; } }
///
/// Since Pina was initialized
///
public static float TotalMilliseconds { get { return mTotalMilliseconds; } }
///
/// Since Pina was initialized
///
public static float TotalSeconds { get { return mTotalMilliseconds / 1000.0f; } }
///
/// Time since Pina last received an Update call from its client class
///
public static float ElapsedSeconds { get { return mDT; } }
///
/// The current shader model Pina is running in
///
public static int ShaderModel { get { return mShaderModel; } }
public static int ScreenWidth { get { return mGraphics.PreferredBackBufferWidth; } }
public static int ScreenHeight { get { return mGraphics.PreferredBackBufferHeight; } }
internal static RenderQueue RenderQueue { get { return mRenderQueue; } }
///
/// Allocates Pina components
///
static Pina()
{
// Initialize components
mCamera = new Camera();
mRenderQueue = new RenderQueue();
}
///
/// Assigns values to Pina components and variables. The ProjectionMatrix uses the current GraphicsDeviceManager's
/// PreferredBackBufferWidth and Height in addition to the arguments to this function.
///
/// Handle to an XNA game's ContentManager
/// Handle to an XNA game's GraphicsDeviceManager
/// The maximum render distance (from the camera)
/// The minimum render distance (from the camera)
/// Aperture size of the camera
/// Starting position of the camera
/// Starting aim target of the camera
/// Handle to the client XNA game
/// Optional particle budget. If null, particles can be added until an integer overflow occurs.
public static void Initialize(Game game, GraphicsDeviceManager graphics, ContentManager content, float fieldOfView, float nearPlane, float farPlane, Vector3 cameraPosition, Vector3 cameraTarget, int? particleBudget)
{
// Device handles
mGraphics = graphics;
mContent = content;
mResContent = new ResourceContentManager(game.Services, Resources.ResourceManager);
// Check for Pixel Shaders
if (mGraphics.GraphicsDevice.GraphicsDeviceCapabilities.PixelShaderVersion.Major >= 2)
mShaderModel = 2;
else if (mGraphics.GraphicsDevice.GraphicsDeviceCapabilities.PixelShaderVersion.Major >= 1)
mShaderModel = 1;
else
throw new Exception("Graphics Device does not seem to support Pixel Shader model 1.0");
// Clip space parameters
mAspectRatio = (float)mGraphics.PreferredBackBufferWidth / (float)mGraphics.PreferredBackBufferHeight;
mFieldOfView = fieldOfView;
mNearPlane = nearPlane;
mFarPlane = farPlane;
RecalculateProjectionMatrix();
// Basic space initialization
Vector3 forward = Vector3.UnitY;
Vector3 right = Vector3.UnitX;
Vector3 up = Vector3.UnitZ;
mBasis = new Matrix(
right.X, right.Y, right.Z, 0f,
up.X, up.Y, up.Z, 0f,
-forward.X, -forward.Y, -forward.Z, 0f,
0f, 0f, 0f, 1f);
mWorldMatrix = Matrix.Identity;
// Default light vector
GlobalLightDirection = new Vector3(1f, 1f, 10f);
mCamera.Initialize(cameraPosition, cameraTarget);
Sparx.Initialize(particleBudget.HasValue ? particleBudget.Value : int.MaxValue, false);
}
///
/// Updates all Pina components. This overload provides a way to update Pina more or less often than the client game using it.
///
/// Manually set the time since this function was last called.
public static void Update(float dT)
{
mDT = dT;
mTotalMilliseconds += dT;
mCamera.Update();
Sparx.Update();
}
///
/// Updates all Pina components.
///
///
public static void Update(GameTime gameTime)
{
Pina.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
}
///
/// Renders everything in the RenderQueue and all registered Sparx Particles directly to the screen.
///
/// Whether or not the clear the backbuffer color before rendering
public static void RenderToScreen(bool clearBuffer)
{
if (clearBuffer)
mGraphics.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.White, 1.0f, 0);
// XNA's SpriteBatches change the following graphics driver's settings
// (Unimportant settings are commented out to easily change them back if necessary)
Pina.Graphics.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
Pina.Graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;
Pina.Graphics.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;
Pina.Graphics.GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add;
Pina.Graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
Pina.Graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
Pina.Graphics.GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = false;
Pina.Graphics.GraphicsDevice.RenderState.AlphaTestEnable = true;
Pina.Graphics.GraphicsDevice.RenderState.AlphaFunction = CompareFunction.Greater;
Pina.Graphics.GraphicsDevice.RenderState.ReferenceAlpha = 0;
Pina.Graphics.GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Mirror;
Pina.Graphics.GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Mirror;
Pina.Graphics.GraphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Linear;
Pina.Graphics.GraphicsDevice.SamplerStates[0].MinFilter = TextureFilter.Linear;
Pina.Graphics.GraphicsDevice.SamplerStates[0].MipFilter = TextureFilter.Linear;
Pina.Graphics.GraphicsDevice.SamplerStates[0].MipMapLevelOfDetailBias = 0.0f;
Pina.Graphics.GraphicsDevice.SamplerStates[0].MaxMipLevel = 0;
// Render each registered object
mRenderQueue.Render(); // Also renders Sparx's particles
}
///
/// Adds a model to the render queue
///
///
public static void AddRenderable(Renderable renderable)
{
mRenderQueue.Add(renderable);
}
///
/// Adds a particle to the render queue
///
///
public static void AddRenderable(Particle particle, RenderQueue.BlendMode blendMode)
{
mRenderQueue.Add(particle, blendMode);
}
///
/// Removes a model from the render queue
///
///
public static void RemoveRenderable(Renderable renderable)
{
mRenderQueue.Remove(renderable);
}
///
/// Removes a particle from the render queue
///
///
public static void RemoveRenderable(Particle particle)
{
mRenderQueue.Remove(particle);
}
///
/// Clears the render queue, causing nothing to render.
///
public static void FlushRenderables()
{
mRenderQueue.Flush();
}
///
/// Recalculates the internal projection matrix based on the internal projection space parameters
///
private static void RecalculateProjectionMatrix()
{
ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(mFieldOfView, mAspectRatio, mNearPlane, mFarPlane);
}
}
}