#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
}
}