Last active
March 27, 2018 15:31
-
-
Save Apostolique/475b3827fd3094fdbec09b3e899b5db0 to your computer and use it in GitHub Desktop.
Camera2D.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Microsoft.Xna.Framework; | |
using Microsoft.Xna.Framework.Graphics; | |
using MonoGame.Extended.ViewportAdapters; | |
namespace MonoGame.Extended | |
{ | |
public class Camera2D : IMovable, IRotatable | |
{ | |
private readonly ViewportAdapter _viewportAdapter; | |
private float _maximumZoom = float.MaxValue; | |
private float _minimumZoom; | |
private float _zoom; | |
private float _depth; | |
public Camera2D(GraphicsDevice graphicsDevice) | |
: this(new DefaultViewportAdapter(graphicsDevice)) | |
{ | |
} | |
public Camera2D(ViewportAdapter viewportAdapter) | |
{ | |
_viewportAdapter = viewportAdapter; | |
Rotation = 0; | |
Zoom = 1; | |
Depth = 1; | |
Origin = new Vector2(viewportAdapter.VirtualWidth/2f, viewportAdapter.VirtualHeight/2f); | |
Position = Vector2.Zero; | |
} | |
public Vector2 Origin { get; set; } | |
public float Zoom | |
{ | |
get { return _zoom; } | |
set | |
{ | |
if ((value < MinimumZoom) || (value > MaximumZoom)) | |
throw new ArgumentException("Zoom must be between MinimumZoom and MaximumZoom"); | |
_zoom = value; | |
} | |
} | |
public float MinimumZoom | |
{ | |
get { return _minimumZoom; } | |
set | |
{ | |
if (value < 0) | |
throw new ArgumentException("MinimumZoom must be greater than zero"); | |
if (Zoom < value) | |
Zoom = MinimumZoom; | |
_minimumZoom = value; | |
} | |
} | |
public float MaximumZoom | |
{ | |
get { return _maximumZoom; } | |
set | |
{ | |
if (value < 0) | |
throw new ArgumentException("MaximumZoom must be greater than zero"); | |
if (Zoom > value) | |
Zoom = value; | |
_maximumZoom = value; | |
} | |
} | |
public float Depth | |
{ | |
get { return _depth; } | |
set | |
{ | |
_depth = value; | |
} | |
} | |
public RectangleF BoundingRectangle | |
{ | |
get | |
{ | |
var frustum = GetBoundingFrustum(); | |
var corners = frustum.GetCorners(); | |
var topLeft = corners[0]; | |
var bottomRight = corners[2]; | |
var width = bottomRight.X - topLeft.X; | |
var height = bottomRight.Y - topLeft.Y; | |
return new RectangleF(topLeft.X, topLeft.Y, width, height); | |
} | |
} | |
public Vector2 Position { get; set; } | |
public float Rotation { get; set; } | |
public void Move(Vector2 direction) | |
{ | |
Position += Vector2.Transform(direction, Matrix.CreateRotationZ(-Rotation)); | |
} | |
public void Rotate(float deltaRadians) | |
{ | |
Rotation += deltaRadians; | |
} | |
public void ZoomIn(float deltaZoom) | |
{ | |
ClampZoom(Zoom + deltaZoom); | |
} | |
public void ZoomOut(float deltaZoom) | |
{ | |
ClampZoom(Zoom - deltaZoom); | |
} | |
public void DollyIn(float deltaDolly) | |
{ | |
Depth -= deltaDolly; | |
} | |
public void DollyOut(float deltaDolly) | |
{ | |
Depth += deltaDolly; | |
} | |
private void ClampZoom(float value) | |
{ | |
if (value < MinimumZoom) | |
Zoom = MinimumZoom; | |
else | |
Zoom = value > MaximumZoom ? MaximumZoom : value; | |
} | |
public void LookAt(Vector2 position) | |
{ | |
Position = position - new Vector2(_viewportAdapter.VirtualWidth/2f, _viewportAdapter.VirtualHeight/2f); | |
} | |
public Vector2 WorldToScreen(float x, float y) | |
{ | |
return WorldToScreen(new Vector2(x, y)); | |
} | |
public Vector2 WorldToScreen(Vector2 worldPosition) | |
{ | |
var viewport = _viewportAdapter.Viewport; | |
return Vector2.Transform(worldPosition + new Vector2(viewport.X, viewport.Y), GetViewMatrix()); | |
} | |
public Vector2 ScreenToWorld(float x, float y) | |
{ | |
return ScreenToWorld(new Vector2(x, y)); | |
} | |
public Vector2 ScreenToWorld(Vector2 screenPosition) | |
{ | |
var viewport = _viewportAdapter.Viewport; | |
return Vector2.Transform(screenPosition - new Vector2(viewport.X, viewport.Y), | |
Matrix.Invert(GetViewMatrix())); | |
} | |
/// <summary> | |
/// Outputs a depth value. | |
/// Let's say you want a layer with a depth of 2 to be drawn at a scale of 0.5. | |
/// You'd call this function with 0.5 as the zoom parameter and 2 as the depth parameter: | |
/// | |
/// ZoomToDepth(0.5f, 2f); | |
/// | |
/// The result would be 4. In other words, you'd have to set the camera's depth to 4. | |
/// <seealso cref="DepthToZoom(float, float)"/> | |
/// </summary> | |
public float ZoomToDepth(float zoom, float depth) | |
{ | |
return (1f/Zoom) + depth; | |
} | |
/// <summary> | |
/// Outputs a zoom value. | |
/// Finds a depth layer's zoom value relative to an other depth layer. | |
/// <seealso cref="ZoomToDepth(float, float)"/> | |
/// </summary> | |
public float DepthToZoom(float depth, float depthOffset) | |
{ | |
return (1f/(depth - depthOffset)); | |
} | |
public float DepthScale(float depth) | |
{ | |
return 1 - depth; | |
} | |
public bool IsVisible(float depth) | |
{ | |
float zoom = DepthToZoom(Depth, depth); | |
return zoom > 0 && zoom < 10; | |
} | |
public Matrix GetViewMatrix() | |
{ | |
return GetViewMatrix(0); | |
} | |
public Matrix GetViewMatrix(float depth) | |
{ | |
return GetViewMatrix(depth, DepthScale(depth)); | |
} | |
public Matrix GetViewMatrix(float depth, float scale) | |
{ | |
return GetVirtualViewMatrix(depth, scale)*_viewportAdapter.GetScaleMatrix(); | |
} | |
private Matrix GetVirtualViewMatrix() | |
{ | |
return GetVirtualViewMatrix(0, DepthScale(0)); | |
} | |
private Matrix GetVirtualViewMatrix(float scale) | |
{ | |
return GetVirtualViewMatrix(0, scale); | |
} | |
private Matrix GetVirtualViewMatrix(float depth, float scale) | |
{ | |
float depthZoom = DepthToZoom(Depth, depth); | |
return | |
Matrix.CreateScale(scale, scale, 1)* | |
Matrix.CreateTranslation(new Vector3(-Position, 0.0f))* | |
Matrix.CreateTranslation(new Vector3(-Origin, 0.0f))* | |
Matrix.CreateRotationZ(Rotation)* | |
Matrix.CreateScale(Zoom, Zoom, 1)* | |
Matrix.CreateScale(depthZoom, depthZoom, 1)* | |
Matrix.CreateTranslation(new Vector3(Origin, 0.0f)); | |
} | |
public Matrix GetInverseViewMatrix() | |
{ | |
return Matrix.Invert(GetViewMatrix()); | |
} | |
public Matrix GetInverseViewMatrix(float depth) | |
{ | |
return Matrix.Invert(GetViewMatrix(depth)); | |
} | |
public Matrix GetInverseViewMatrix(float depth, float scale) | |
{ | |
return Matrix.Invert(GetViewMatrix(depth, scale)); | |
} | |
private Matrix GetProjectionMatrix(Matrix viewMatrix) | |
{ | |
var projection = Matrix.CreateOrthographicOffCenter(0, _viewportAdapter.VirtualWidth, | |
_viewportAdapter.VirtualHeight, 0, -1, 0); | |
Matrix.Multiply(ref viewMatrix, ref projection, out projection); | |
return projection; | |
} | |
public BoundingFrustum GetBoundingFrustum() | |
{ | |
return GetBoundingFrustum(0, DepthScale(0)); | |
} | |
public BoundingFrustum GetBoundingFrustum(float depth) | |
{ | |
return GetBoundingFrustum(depth, DepthScale(depth)); | |
} | |
public BoundingFrustum GetBoundingFrustum(float depth, float scale) | |
{ | |
var viewMatrix = GetVirtualViewMatrix(depth, scale); | |
var projectionMatrix = GetProjectionMatrix(viewMatrix); | |
return new BoundingFrustum(projectionMatrix); | |
} | |
public ContainmentType Contains(Point point) | |
{ | |
return Contains(point.ToVector2()); | |
} | |
public ContainmentType Contains(Vector2 vector2) | |
{ | |
return GetBoundingFrustum().Contains(new Vector3(vector2.X, vector2.Y, 0)); | |
} | |
public ContainmentType Contains(Rectangle rectangle) | |
{ | |
var max = new Vector3(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height, 0.5f); | |
var min = new Vector3(rectangle.X, rectangle.Y, 0.5f); | |
var boundingBox = new BoundingBox(min, max); | |
return GetBoundingFrustum().Contains(boundingBox); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment