/* * 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 } }