/*
* Space3D.cs
* Authors: August Zinsser, Adam Nabinger
* Copyright (c) 2007-2008 Cornell University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using TACParticleEngine.Emitters;
namespace MaritimeDefender
{
///
/// Defines a basic 3D coordinate system where +x is to the right of the screen, +y is down, and +z is into the screen.
/// Holds the locations of entities in this space, but doesn't apply any logic to them.
/// This is essentially a render queue for 2D objects in parallax worlds.
///
[Obsolete("THIS CLASS IS OBSOLETE AND SHOULD NOT BE USED BY ANY NEW CLASSES. IT EXISTS ONLY FOR BACKWARDS COMPATIBILITY WITH METEOR MADNESS.")]
internal class Space3D : IDisposable
{
#region Constants
///
/// Constant for the amount of depth adjustment needed for correct visuals
///
public const float mMeteorCameraFocalLength = .2F;
#endregion
#region Fields
// General entities to render
private readonly List mEntities;
///
/// Reference to the current Maritime Defender game screen
///
private static MaritimeDefender m_Game;
///
/// Far clipping plane
///
internal float MaxZ;
///
/// Near clipping plane
///
internal float MinZ;
///
/// The list of meteors to render
///
public List mMeteors;
///
/// The list of photon torpedos to render
///
public List mPhotonTorpedos;
///
/// The list of collectables to be rendered
///
public List mFlotsam;
///
/// The list of rocket pods to be rendered
///
public List mRocketPod;
#endregion
#region Predicates
/*
///
/// Defines when the an entity is past the near clipping plane so that it will be drawn
///
//public static readonly Predicate PastMinZ = delegate(Entity o) { return o.Z < m_Game.GameBoard.MinZ; };
*
* Predicate causes crash. Probably due to lack of thread safety.
* Moved to instance variable of Boss.cs
*
System.NullReferenceException: Object reference not set to an instance of an object.
at MaritimeDefender.Space3D.<.cctor>b__0(Entity o)
at System.Collections.Generic.List`1.RemoveAll(Predicate`1 match)
at MaritimeDefender.Boss.Update(GameTime gameTime)
at MaritimeDefender.MaritimeDefender.Update(GameTime gameTime)
at Microsoft.Xna.Framework.Game.Update(GameTime gameTime)
at Microsoft.Xna.Framework.Game.Tick()
at Microsoft.Xna.Framework.Game.HostIdle(Object sender, EventArgs e)
at Microsoft.Xna.Framework.GameHost.OnIdle()
at Microsoft.Xna.Framework.WindowsGameHost.ApplicationIdle(Object sender, EventArgs e)
at System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FDoIdle(Int32 grfidlef)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at Microsoft.Xna.Framework.WindowsGameHost.Run()
at Microsoft.Xna.Framework.Game.Run()
at Game.Program.Main(String[] args)
000.036279 27980978226008 256 MainGame GameScreen Stack Trace:
Game.LoginScreen
* */
#endregion
#region Creation
///
/// Constructor
///
/// Reference to the current Maritime Defender game screen
public Space3D(MaritimeDefender p_Game)
{
m_Game = p_Game;
mEntities = new List();
MaxZ = 100.0F;
MinZ = -1.0F;
}
#endregion
#region Management
///
/// Adds a new Entity into the field for drawing
///
/// The entity to be added in
public void AddEntity(Entity newEntity)
{
mEntities.Add(newEntity);
}
///
/// Removes an entity from the field
///
/// The entity to be removed
public void RemoveEntity(Entity oldEntity)
{
mEntities.Remove(oldEntity);
}
///
/// Removes all entities that match the given predicate
///
/// The predicate to use for checking what entities can be removed
public void RemoveAll(Predicate match)
{
mEntities.RemoveAll(match);
}
///
/// Clears out all the entities
///
public void Dispose()
{
mEntities.Clear();
}
#endregion
#region Render
///
/// Draws all registered visible entities
///
/// Reference to the current spritebatch for randering sprites
public void Render(SpriteBatch SpriteBatch)
{
// First draw general entities
foreach (Entity e in mEntities)
{
// Special case draws that require special handling
if (e is CheetahFighter)
{
// Draw the cheetah at its source image aspect, but keep it's bounding box almost square for game collisions
float depth = e.Z - MinZ + mMeteorCameraFocalLength;
float depthRange = MaxZ - MinZ + mMeteorCameraFocalLength;
Rectangle proj = Project(e.X, e.Y, e.Z, e.Width, e.Height);
proj.Height = (int)(proj.Width * (e.Texture.Height / (float)e.Texture.Width));
e.Draw(SpriteBatch, proj, depth / depthRange);
}
// General draws
else if (e.Visible && IsInView(e))
{
float depth = e.Z - MinZ + mMeteorCameraFocalLength;
float depthRange = MaxZ - MinZ + mMeteorCameraFocalLength;
e.Draw(SpriteBatch, Project(e.X, e.Y, e.Z, e.Width, e.Height), depth / depthRange);
}
}
if (mMeteors != null)
{
foreach (Meteor e in mMeteors)
{
if (e.Visible && IsInView(e))
{
float depth = e.Z - MinZ + mMeteorCameraFocalLength;
float depthRange = MaxZ - MinZ + mMeteorCameraFocalLength;
e.Draw(SpriteBatch, Project(e.X, e.Y, e.Z, e.Width, e.Height), depth / depthRange);
}
}
}
if (mPhotonTorpedos != null)
{
foreach (PhotonTorpedo e in mPhotonTorpedos)
{
if (e.Visible && IsInView(e))
{
float depth = e.Z - MinZ + mMeteorCameraFocalLength;
float depthRange = MaxZ - MinZ + mMeteorCameraFocalLength;
e.Draw(SpriteBatch, Project(e.X, e.Y, e.Z, e.Width, e.Height), depth / depthRange);
}
}
}
if (mFlotsam != null)
{
foreach (Flotsam e in mFlotsam)
{
if (e.Visible && IsInView(e))
{
float depth = e.Z - MinZ + mMeteorCameraFocalLength;
float depthRange = MaxZ - MinZ + mMeteorCameraFocalLength;
e.Draw(SpriteBatch, Project(e.X, e.Y, e.Z, e.Width, e.Height), depth / depthRange);
}
}
}
if (mRocketPod != null)
{
foreach (RocketPod e in mRocketPod)
{
if (e.Visible && IsInView(e))
{
float depth = e.Z - MinZ + mMeteorCameraFocalLength;
float depthRange = MaxZ - MinZ + mMeteorCameraFocalLength;
e.Draw(SpriteBatch, Project(e.X, e.Y, e.Z, e.Width, e.Height), depth / depthRange);
}
}
}
}
#endregion
#region Util
///
/// Translates the given dimensions to space3D coordinates
///
/// The x-coordinate position
/// The y-coordinate position
/// The Z-coordinate position
/// The width
/// The height
/// A rectangle of the translated dimensions
public static Rectangle Project(float X, float Y, float Z, float W, float H)
{
// Calculate the apparent increase/decrease in size of an object based on its distance from the camera
float depth = 1f + Z + mMeteorCameraFocalLength;
float scaleFactor = (mMeteorCameraFocalLength + 1f) / depth;
Viewport vp = m_Game.GraphicsDevice.Viewport;
int Height = vp.Height;
int Width = vp.Width;
// Translate into screen coordinates
int screenX = (int)(((scaleFactor * X) + 1) * .5F * Width);
int screenY = (int)(((scaleFactor * Y) + 1) * .5F * Height);
int screenW = (int)((scaleFactor * W) * Width);
int screenH = (int)((scaleFactor * H) * Width);
return new Rectangle(screenX, screenY, screenW, screenH);
}
///
/// Check if the entity is behind the camera. The farplane for meteor madness is used more as a guideline for
/// spawning/killing not for rendering purposes.
///
/// The entity to check
/// Whether or not the given entity is within viewing distance
private bool IsInView(Entity ent)
{
if (ent.Z < MinZ - mMeteorCameraFocalLength ||
ent.Z > MaxZ)
return false;
return true;
}
#endregion
}
}