#region Copyright /* -------------------------------------------------------------------------------- This source file is part of Xenocide by Project Xenocide Team For the latest info on Xenocide, see http://www.projectxenocide.com/ This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA. -------------------------------------------------------------------------------- */ /* * @file FacilityScene.cs * @date Created: 2007/04/23 * @author File creator: dteviot * @author Credits: none */ #endregion #region Using Statements using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Drawing; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Content; using ProjectXenocide.Model.StaticData.Facilities; using ProjectXenocide.Model.Geoscape.Outposts; #endregion namespace ProjectXenocide.UI.Scenes.Facility { /// /// Shows the facilities (and their layout) in a X-Corp outpost. /// public class FacilityScene : IDisposable { /// /// Constructor /// /// Layout of base's facilities to show in scene [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "Will throw exception if floorplan is null")] public FacilityScene(Floorplan floorplan) { this.floorplan = floorplan; this.cameraPosition = ComputeCameraPosition(); this.buildTimes = new BuildTimes(this.floorplan); } /// /// Implement IDisposable /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Implement IDisposable /// /// false when called from a finalizer protected virtual void Dispose(bool disposing) { if (disposing) { if (basicEffect != null) { basicEffect.Dispose(); basicEffect = null; } if (grid != null) { grid.Dispose(); grid = null; } } } /// /// Load the graphic content of the scene /// /// content manager that fetches the content /// the display public void LoadContent(ContentManager content, GraphicsDevice device) { { InitializeEffect(device); grid.LoadContent(device, new Grid(floorplan.CellsWide, floorplan.CellsHigh)); models.LoadContent(content); buildTimes.LoadContent(content, device); } } /// /// Render scene /// /// Device to use for render /// Where to draw the scene on the display [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification="Will throw exception if device is null")] public void Draw(GraphicsDevice device, CeGui.Rect sceneWindow) { // only draw in area we've been told to Viewport oldview = device.Viewport; device.Viewport = CalcViewportForSceneWindow(sceneWindow, device.Viewport); basicEffect.Projection = GetProjectionMatrix(AspectRatio); Matrix viewMatrix = Matrix.CreateLookAt( cameraPosition, Vector3.Zero, Vector3.Forward ); basicEffect.View = viewMatrix; device.RenderState.CullMode = CullMode.CullCounterClockwiseFace; device.RenderState.DepthBufferEnable = true; device.RenderState.DepthBufferWriteEnable = true; // draw the grid grid.ConfigureEffect(basicEffect); grid.Draw(device, basicEffect); // Draw the facilities. foreach (FacilityHandle handle in floorplan.Facilities) { Draw(handle); } // Draw the "new facility position" marker (if we're in add facility mode) if (null != newFacility) { Draw(newFacility); } // Draw the "build time remaining" labels // Must draw them last, because the alpha blending of the labels overwrites the z-buffer buildTimes.ConfigureEffect(basicEffect); buildTimes.Draw(device, basicEffect); // restore viewport device.Viewport = oldview; } /// /// Convert a position in the viewport to a cell in the base's layout /// /// The position in the viewport (in relative co-ords) /// The cell in the base's layout or -1, -1 if point isn't inside base public Vector2 WindowToCell(PointF coords) { // basic trig, compute viewing fustrum dimension at distance base floorplan is double opositeSideLength = Math.Tan(ViewAngle / 2.0) * cameraPosition.Y * 2.0f; double x = opositeSideLength * AspectRatio * (coords.X - 0.5); double z = opositeSideLength * (coords.Y - 0.5); // Allow for center of screen being center of base x += (floorplan.CellsWide / 2.0f); z += (floorplan.CellsHigh / 2.0f); // check that result is within the base if ( (x < 0.0f) || (floorplan.CellsWide < x) || (z < 0.0f) || (floorplan.CellsHigh < z)) { x = -1.0f; z = -1.0f; } return new Vector2((float)Math.Floor(x), (float)Math.Floor(z)); } /// /// Render facility into scene /// /// The facility (and it's position in the base) private void Draw(FacilityHandle handle) { // Only draw the facility if it has a position inside the base if (handle.HasPosition) { FacilityInfo info = handle.FacilityInfo; // calcuate position to draw model at float xdisp = handle.X + ((info.XSize - floorplan.CellsWide) / 2.0f); float zdisp = handle.Y + ((info.YSize - floorplan.CellsHigh) / 2.0f); Matrix displacement = Matrix.CreateTranslation(xdisp, 0.0f, zdisp); models.Draw(basicEffect, info.Id, displacement); } } /// /// Set up the basic effect for rendering /// /// private void InitializeEffect(GraphicsDevice device) { basicEffect = new BasicEffect(device, null); basicEffect.Alpha = 1.0f; basicEffect.DiffuseColor = new Vector3(1.0f, 1.0f, 1.0f); basicEffect.SpecularColor = new Vector3(0.25f, 0.25f, 0.25f); basicEffect.SpecularPower = 5.0f; basicEffect.AmbientLightColor = new Vector3(0.40f, 0.40f, 0.40f); basicEffect.DirectionalLight0.Enabled = false; basicEffect.DirectionalLight0.DiffuseColor = Vector3.One; basicEffect.DirectionalLight0.SpecularColor = Vector3.One; basicEffect.DirectionalLight1.Enabled = false; basicEffect.LightingEnabled = false; } /// /// Compute the projection matrix for the scene /// /// window's aspect ratio /// The calculated projection matrix private static Matrix GetProjectionMatrix(float aspectRatio) { return Matrix.CreatePerspectiveFieldOfView( ViewAngle, aspectRatio, nearClipPlane, farClipPlane); } /// /// Deterime where camera is located. /// Its above center of base, just high enough to see all facilities /// /// Position for the camera private Vector3 ComputeCameraPosition() { // assumes aspect ratio is 1.0. // problem is, at this point in time, it's not known. float oposite = MathHelper.Max(floorplan.CellsHigh, floorplan.CellsWide) * 0.5f; float adjacent = (float)(oposite / Math.Tan(ViewAngle * 0.5)); return new Vector3(0.0f, adjacent + 1.0f, 0.0f); } /// /// convert Window's co-ordinates to viewport co-ordinates /// /// Window co-ords to translate /// The current viewport /// Viewport co-ordinates private Viewport CalcViewportForSceneWindow(CeGui.Rect windowCoords, Viewport viewport) { int fullHeight = viewport.Height; int fullWidth = viewport.Width; viewport.X = (int)(fullWidth * windowCoords.Left); viewport.Y = (int)(fullHeight * windowCoords.Top); viewport.Width = (int)(fullWidth * windowCoords.Width); viewport.Height = (int)(fullHeight * windowCoords.Height); // compute the aspect ratio while we're about it aspectRatio = (float)viewport.Width / (float)viewport.Height; return viewport; } #region Fields /// /// The facility we are adding to the base /// public FacilityHandle NewFacility { get { return newFacility; } set { newFacility = value; } } /// /// The position of the camera, in polar co-ordinates. /// at current time, camera is fixed /// private Vector3 cameraPosition; /// /// The viewport's aspect ratio /// protected float AspectRatio { get { return aspectRatio; } } /// /// The viewport's aspect ratio /// private float aspectRatio; /// /// The basic effect used for rendering /// private BasicEffect basicEffect; /// /// Grid that shows the cells holding facilities /// private LineMesh grid = new LineMesh(); /// /// The 3D models of facilities. /// private FacilityModels models = new FacilityModels(); /// /// Layout of base's facilities to show in scene /// private Floorplan floorplan; /// /// The facility we are adding to the base /// private FacilityHandle newFacility; /// /// Quads to decorate facilities under construction with their build times /// private BuildTimes buildTimes; #endregion #region Constant definitions /// /// Used in constructing viewing fustrum /// private const float nearClipPlane = 0.1f; /// /// Used in constructing viewing fustrum /// private const float farClipPlane = 20.0f; /// /// Used in constructing viewing fustrum /// private const float ViewAngle = (float)Math.PI / 4.0f; // 45 degres #endregion } }