Camera class now abstract, split off into specific perspective cameras

This commit is contained in:
Gered 2013-09-02 13:05:24 -04:00
parent e5a12a4cb1
commit 421ecc9f43
8 changed files with 156 additions and 186 deletions

View file

@ -135,7 +135,6 @@
<Compile Include="Graphics\SpriteBatch.cs" /> <Compile Include="Graphics\SpriteBatch.cs" />
<Compile Include="Graphics\BillboardSpriteBatch.cs" /> <Compile Include="Graphics\BillboardSpriteBatch.cs" />
<Compile Include="Graphics\Helpers\FlatWireframeGrid.cs" /> <Compile Include="Graphics\Helpers\FlatWireframeGrid.cs" />
<Compile Include="Support\FreeMovementCamera.cs" />
<Compile Include="Support\StringBuilderExtensions.cs" /> <Compile Include="Support\StringBuilderExtensions.cs" />
<Compile Include="Graphics\Helpers\GraphicsHelpers.cs" /> <Compile Include="Graphics\Helpers\GraphicsHelpers.cs" />
<Compile Include="Support\BitExtensions.cs" /> <Compile Include="Support\BitExtensions.cs" />
@ -225,6 +224,8 @@
<Compile Include="Entities\EntityPreset.cs" /> <Compile Include="Entities\EntityPreset.cs" />
<Compile Include="Entities\EntityPresetArgs.cs" /> <Compile Include="Entities\EntityPresetArgs.cs" />
<Compile Include="Entities\SystemComponents\EntityPresetComponent.cs" /> <Compile Include="Entities\SystemComponents\EntityPresetComponent.cs" />
<Compile Include="Graphics\PerspectiveCamera.cs" />
<Compile Include="Graphics\EulerPerspectiveCamera.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<ItemGroup> <ItemGroup>

View file

@ -127,7 +127,7 @@ namespace Blarg.GameFramework.Graphics
throw new InvalidOperationException(); throw new InvalidOperationException();
_cameraPosition = GraphicsDevice.ViewContext.Camera.Position; _cameraPosition = GraphicsDevice.ViewContext.Camera.Position;
_cameraForward = GraphicsDevice.ViewContext.Camera.Forward; _cameraForward = GraphicsDevice.ViewContext.Camera.Direction;
if (shader == null) if (shader == null)
_shader = GraphicsDevice.Sprite3DShader; _shader = GraphicsDevice.Sprite3DShader;

View file

@ -2,100 +2,58 @@ using System;
namespace Blarg.GameFramework.Graphics namespace Blarg.GameFramework.Graphics
{ {
public class Camera public abstract class Camera
{ {
ViewContext _viewContext;
float _nearHeight;
float _nearWidth;
public readonly Frustum Frustum; public readonly Frustum Frustum;
public Matrix4x4 LookAt; public Matrix4x4 LookAt;
public Matrix4x4 Projection; public Matrix4x4 Projection;
public Vector3 Forward;
public Vector3 Up;
public Vector3 Orientation;
public Vector3 Position; public Vector3 Position;
public Vector3 Direction;
public Vector3 Up;
public float Near;
public float Far;
public int ViewportWidth { get; private set; } public int ViewportWidth { get; protected set; }
public int ViewportHeight { get; private set; } public int ViewportHeight { get; protected set; }
public float AspectRatio { get; private set; }
public float Near { get; private set; }
public float Far { get; private set; }
public float FieldOfViewAngle { get; private set; }
public Camera(ViewContext viewContext, float near = 1.0f, float far = 50.0f, float fieldOfView = MathConstants.Radians60) protected readonly ViewContext ViewContext;
protected float NearWidth;
protected float NearHeight;
public Camera(ViewContext viewContext, float near = 1.0f, float far = 50.0f)
{ {
if (viewContext == null) if (viewContext == null)
throw new ArgumentNullException("viewContext"); throw new ArgumentNullException("viewContext");
_viewContext = viewContext; ViewContext = viewContext;
Frustum = new Frustum(this); Frustum = new Frustum(this);
Position = Vector3.Zero; Position = Vector3.Zero;
Orientation = Vector3.Zero; Direction = Vector3.Forward;
Forward = Vector3.Zero;
Up = Vector3.Up; Up = Vector3.Up;
LookAt = Matrix4x4.Identity; LookAt = Matrix4x4.Identity;
FieldOfViewAngle = fieldOfView;
Near = near; Near = near;
Far = far; Far = far;
CalculateDefaultProjection( Update();
_viewContext.ViewportLeft,
_viewContext.ViewportTop,
_viewContext.ViewportRight,
_viewContext.ViewportBottom
);
} }
public virtual void OnUpdate(float delta) public abstract void Update();
{
}
public virtual void OnRender(float delta)
{
Vector3 movement = Vector3.Zero;
UpdateLookAtMatrix(ref movement);
_viewContext.ModelViewMatrix = LookAt;
Frustum.Calculate();
}
public virtual void OnResize(ref Rect size)
{
CalculateDefaultProjection(size.Left, size.Top, size.Right, size.Bottom);
_viewContext.ProjectionMatrix = Projection;
}
public virtual void UpdateProjectionMatrix()
{
CalculateDefaultProjection(
_viewContext.ViewportLeft,
_viewContext.ViewportTop,
_viewContext.ViewportRight,
_viewContext.ViewportBottom
);
}
public virtual void UpdateLookAtMatrix(ref Vector3 movement)
{
CalculateDefaultLookAt(ref movement);
}
public Ray Pick(int screenX, int screenY) public Ray Pick(int screenX, int screenY)
{ {
float nx = 2.0f * ((float)(screenX - (_viewContext.ViewportWidth / 2))) / ((float)_viewContext.ViewportWidth); float nx = 2.0f * ((float)(screenX - (ViewContext.ViewportWidth / 2))) / ((float)ViewContext.ViewportWidth);
float ny = 2.0f * -((float)(screenY - (_viewContext.ViewportHeight / 2))) / ((float)_viewContext.ViewportHeight); float ny = 2.0f * -((float)(screenY - (ViewContext.ViewportHeight / 2))) / ((float)ViewContext.ViewportHeight);
// pick ray calculation method copied from http://code.google.com/p/libgdx/ // pick ray calculation method copied from http://code.google.com/p/libgdx/
Vector3 vz = Vector3.Normalize(Forward * -1.0f); Vector3 vz = Vector3.Normalize(Direction * -1.0f);
Vector3 vx = Vector3.Normalize(Vector3.Cross(Vector3.Up, vz)); Vector3 vx = Vector3.Normalize(Vector3.Cross(Vector3.Up, vz));
Vector3 vy = Vector3.Normalize(Vector3.Cross(vz, vx)); Vector3 vy = Vector3.Normalize(Vector3.Cross(vz, vx));
Vector3 near_center = Position - (vz * Near); Vector3 near_center = Position - (vz * Near);
Vector3 a = (vx * _nearWidth) * nx; Vector3 a = (vx * NearWidth) * nx;
Vector3 b = (vy * _nearHeight) * ny; Vector3 b = (vy * NearHeight) * ny;
Vector3 near_point = a + b + near_center; Vector3 near_point = a + b + near_center;
Vector3 dir = Vector3.Normalize(near_point - Position); Vector3 dir = Vector3.Normalize(near_point - Position);
@ -105,8 +63,8 @@ namespace Blarg.GameFramework.Graphics
public Point2 Project(ref Vector3 objectPosition) public Point2 Project(ref Vector3 objectPosition)
{ {
Matrix4x4 modelview = _viewContext.ModelViewMatrix; Matrix4x4 modelview = ViewContext.ModelViewMatrix;
Matrix4x4 projection = _viewContext.ProjectionMatrix; Matrix4x4 projection = ViewContext.ProjectionMatrix;
return Project(ref objectPosition, ref modelview, ref projection); return Project(ref objectPosition, ref modelview, ref projection);
} }
@ -137,44 +95,14 @@ namespace Blarg.GameFramework.Graphics
// map to 2D viewport coordinates (ignoring Z) // map to 2D viewport coordinates (ignoring Z)
Point2 result; Point2 result;
result.X = (int)(((transformedX * 0.5f) + 0.5f) * (float)_viewContext.PixelScaler.ScaledWidth + (float)_viewContext.PixelScaler.ScaledViewport.Left); result.X = (int)(((transformedX * 0.5f) + 0.5f) * (float)ViewContext.PixelScaler.ScaledWidth + (float)ViewContext.PixelScaler.ScaledViewport.Left);
result.Y = (int)(((transformedY * 0.5f) + 0.5f) * (float)_viewContext.PixelScaler.ScaledHeight + (float)_viewContext.PixelScaler.ScaledViewport.Top); result.Y = (int)(((transformedY * 0.5f) + 0.5f) * (float)ViewContext.PixelScaler.ScaledHeight + (float)ViewContext.PixelScaler.ScaledViewport.Top);
// float z = (1.0f + transformedZ) * 0.5f; // would be between 0.0f and 1.0f // float z = (1.0f + transformedZ) * 0.5f; // would be between 0.0f and 1.0f
// adjust Y coordinate so that 0 is at the top of the screen instead of the bottom // adjust Y coordinate so that 0 is at the top of the screen instead of the bottom
result.Y = (int)_viewContext.PixelScaler.ScaledHeight - result.Y; result.Y = (int)ViewContext.PixelScaler.ScaledHeight - result.Y;
return result; return result;
} }
protected void CalculateDefaultProjection(int left, int top, int right, int bottom)
{
ViewportWidth = right - left;
ViewportHeight = bottom - top;
AspectRatio = (float)ViewportWidth / (float)ViewportHeight;
_nearHeight = Near * (float)Math.Tan(FieldOfViewAngle / 2.0f);
_nearWidth = _nearHeight * AspectRatio;
Projection = Matrix4x4.CreatePerspectiveFieldOfView(FieldOfViewAngle, AspectRatio, Near, Far);
}
protected void CalculateDefaultLookAt(ref Vector3 movement)
{
// final camera orientation. angles must be negative (or rather, inverted) for the camera matrix. also the matrix concatenation order is important!
Matrix4x4 rotation = Matrix4x4.CreateRotationY(-Orientation.Y) * Matrix4x4.CreateRotationX(-Orientation.X);
// apply orientation to forward, movement and up vectors so they're pointing in the right direction
Forward = Matrix4x4.Transform(rotation, Vector3.Forward);
Up = Matrix4x4.Transform(rotation, Vector3.Up);
Vector3 orientedMovement = Matrix4x4.Transform(rotation, movement);
// move the camera position
Position += orientedMovement;
Vector3 target = Forward + Position;
LookAt = Matrix4x4.CreateLookAt(Position, target, Vector3.Up);
}
} }
} }

View file

@ -0,0 +1,37 @@
using System;
using Blarg.GameFramework.TileMap;
namespace Blarg.GameFramework.Graphics
{
public class EulerPerspectiveCamera : PerspectiveCamera
{
public float Yaw;
public float Pitch;
public Quaternion Rotation;
public EulerPerspectiveCamera(ViewContext viewContext, float near = 1.0f, float far = 50.0f, float fieldOfView = MathConstants.Radians60)
: base(viewContext, near, far, fieldOfView)
{
}
public override void Update()
{
UpdateRotation();
CalculateProjection();
CalculateLookAt();
}
public void UpdateRotation()
{
var rotationX = Quaternion.CreateFromAxisAngle(-Pitch, Vector3.XAxis);
var rotationY = Quaternion.CreateFromAxisAngle(-Yaw, Vector3.YAxis);
Rotation = rotationY * rotationX;
Direction = Quaternion.Transform(Rotation, Vector3.Forward);
Up = Quaternion.Transform(Rotation, Vector3.Up);
}
}
}

View file

@ -0,0 +1,43 @@
using System;
namespace Blarg.GameFramework.Graphics
{
public class PerspectiveCamera : Camera
{
public float FieldOfViewAngle;
public PerspectiveCamera(ViewContext viewContext, float near = 1.0f, float far = 50.0f, float fieldOfView = MathConstants.Radians60)
: base(viewContext, near, far)
{
FieldOfViewAngle = fieldOfView;
}
public override void Update()
{
CalculateProjection();
CalculateLookAt();
}
protected void CalculateLookAt()
{
var target = Position + Direction;
Matrix4x4.CreateLookAt(ref Position, ref target, ref Up, out LookAt);
ViewContext.ModelViewMatrix = LookAt;
}
protected void CalculateProjection()
{
ViewportWidth = ViewContext.ViewportRight - ViewContext.ViewportLeft;
ViewportHeight = ViewContext.ViewportBottom - ViewContext.ViewportTop;
float aspectRatio = (float)ViewportWidth / (float)ViewportHeight;
NearHeight = Near * (float)Math.Tan(FieldOfViewAngle / 2.0f);
NearWidth = NearHeight * aspectRatio;
Matrix4x4.CreatePerspectiveFieldOfView(FieldOfViewAngle, aspectRatio, Near, Far, out Projection);
ViewContext.ProjectionMatrix = Projection;
}
}
}

View file

@ -55,7 +55,7 @@ namespace Blarg.GameFramework.Graphics
// not using the default camera, and clearing ("nulling") the camera // not using the default camera, and clearing ("nulling") the camera
else if (!_isUsingDefaultCamera && value == null) else if (!_isUsingDefaultCamera && value == null)
{ {
_camera = new Camera(this); _camera = new PerspectiveCamera(this);
_isUsingDefaultCamera = true; _isUsingDefaultCamera = true;
cameraWasChanged = true; cameraWasChanged = true;
} }
@ -183,7 +183,7 @@ namespace Blarg.GameFramework.Graphics
_viewport = viewport; _viewport = viewport;
_viewportIsFixedSize = isFixedSizeViewport; _viewportIsFixedSize = isFixedSizeViewport;
_screenOrientation = ScreenOrientation.Rotation0; _screenOrientation = ScreenOrientation.Rotation0;
_camera = new Camera(this); _camera = new PerspectiveCamera(this);
_isUsingDefaultCamera = true; _isUsingDefaultCamera = true;
_pixelScaler = new NoScaleOrthoPixelScaler(); _pixelScaler = new NoScaleOrthoPixelScaler();
_isUsingDefaultPixelScaler = true; _isUsingDefaultPixelScaler = true;
@ -207,18 +207,12 @@ namespace Blarg.GameFramework.Graphics
public void OnRender(float delta) public void OnRender(float delta)
{ {
if (_camera != null) if (_camera != null)
_camera.OnRender(delta); _camera.Update();
} }
public void OnApply(ref Rect size, ScreenOrientation screenOrientation = ScreenOrientation.Rotation0) public void OnApply(ref Rect size, ScreenOrientation screenOrientation = ScreenOrientation.Rotation0)
{ {
SetupViewport(ref size, screenOrientation); SetupViewport(ref size, screenOrientation);
// ensures it's set up for rendering immediately when this call returns
// NOTE: we assume OnApply() is going to be called in some other class's
// OnRender() event only (like, e.g. if a new framebuffer is bound)
if (_camera != null)
_camera.OnRender(0.0f);
} }
private void SetupViewport(ref Rect size, ScreenOrientation screenOrientation) private void SetupViewport(ref Rect size, ScreenOrientation screenOrientation)
@ -257,7 +251,7 @@ namespace Blarg.GameFramework.Graphics
// we also **don't** want the camera to work with a rotated viewport // we also **don't** want the camera to work with a rotated viewport
if (_camera != null) if (_camera != null)
_camera.OnResize(ref _viewport); _camera.Update();
} }
} }
} }

View file

@ -1,73 +0,0 @@
using System;
using Blarg.GameFramework.Graphics;
using Blarg.GameFramework.Input;
namespace Blarg.GameFramework.Support
{
public class FreeMovementCamera : Camera
{
Vector3 _movement;
public FreeMovementCamera(ViewContext viewContext)
: base(viewContext)
{
}
public void Move(float x, float y, float z)
{
Position = new Vector3(Position.X + x, Position.Y + y, Position.Z + z);
}
public void Orient(float x, float y, float z)
{
Orientation = new Vector3(Orientation.X + x, Orientation.Y + y, Orientation.Z + z);
}
public override void OnUpdate(float delta)
{
float pointerDeltaX = 0.0f;
float pointerDeltaY = 0.0f;
bool isPointerTouching = false;
if (Framework.Mouse != null && Framework.Mouse.IsDown(MouseButton.Left))
{
pointerDeltaX = Framework.Mouse.DeltaX;
pointerDeltaY = Framework.Mouse.DeltaY;
isPointerTouching = true;
}
else if (Framework.TouchScreen != null && Framework.TouchScreen.IsTouching)
{
pointerDeltaX = Framework.TouchScreen.PrimaryPointer.DeltaX;
pointerDeltaY = Framework.TouchScreen.PrimaryPointer.DeltaY;
isPointerTouching = true;
}
if (isPointerTouching)
{
var orientation = Orientation;
orientation.Y += pointerDeltaX * 0.01f;
orientation.X += pointerDeltaY * 0.01f;
orientation.Y = MathHelpers.RolloverClamp(orientation.Y, MathConstants.Radians0, MathConstants.Radians360);
orientation.X = MathHelpers.RolloverClamp(orientation.X, MathConstants.Radians0, MathConstants.Radians360);
Orientation = orientation;
}
_movement = Vector3.Zero;
if (Framework.Keyboard.IsDown(Key.Up))
_movement.Z -= delta * 6.0f;
if (Framework.Keyboard.IsDown(Key.Down))
_movement.Z += delta * 6.0f;
if (Framework.Keyboard.IsDown(Key.Left))
_movement.X -= delta * 6.0f;
if (Framework.Keyboard.IsDown(Key.Right))
_movement.X += delta * 6.0f;
UpdateLookAtMatrix(ref _movement);
}
}
}

View file

@ -4,6 +4,7 @@ using Blarg.GameFramework.Events;
using Blarg.GameFramework.Graphics; using Blarg.GameFramework.Graphics;
using Blarg.GameFramework.Graphics.Helpers; using Blarg.GameFramework.Graphics.Helpers;
using Blarg.GameFramework.Graphics.ScreenEffects; using Blarg.GameFramework.Graphics.ScreenEffects;
using Blarg.GameFramework.Input;
using Blarg.GameFramework.States; using Blarg.GameFramework.States;
using Blarg.GameFramework.Support; using Blarg.GameFramework.Support;
@ -12,7 +13,7 @@ namespace Game
public class TestGameState : GameState public class TestGameState : GameState
{ {
FlatWireframeGrid _grid; FlatWireframeGrid _grid;
FreeMovementCamera _camera; EulerPerspectiveCamera _camera;
public TestGameState(StateManager stateManager, EventManager eventManager) public TestGameState(StateManager stateManager, EventManager eventManager)
: base(stateManager, eventManager) : base(stateManager, eventManager)
@ -21,7 +22,7 @@ namespace Game
public override void OnPush() public override void OnPush()
{ {
_camera = new FreeMovementCamera(Framework.GraphicsDevice.ViewContext); _camera = new EulerPerspectiveCamera(Framework.GraphicsDevice.ViewContext);
_camera.Position = new Vector3(0.0f, 5.0f, 0.0f); _camera.Position = new Vector3(0.0f, 5.0f, 0.0f);
Framework.GraphicsDevice.ViewContext.Camera = _camera; Framework.GraphicsDevice.ViewContext.Camera = _camera;
@ -54,7 +55,46 @@ namespace Game
base.OnUpdate(delta); base.OnUpdate(delta);
if (Framework.Keyboard.IsPressed(Blarg.GameFramework.Input.Key.Escape)) if (Framework.Keyboard.IsPressed(Blarg.GameFramework.Input.Key.Escape))
Framework.Application.Quit(); Framework.Application.Quit();
_camera.OnUpdate(delta);
float pointerDeltaX = 0.0f;
float pointerDeltaY = 0.0f;
bool isPointerTouching = false;
if (Framework.Mouse != null && Framework.Mouse.IsDown(MouseButton.Left))
{
pointerDeltaX = Framework.Mouse.DeltaX;
pointerDeltaY = Framework.Mouse.DeltaY;
isPointerTouching = true;
}
else if (Framework.TouchScreen != null && Framework.TouchScreen.IsTouching)
{
pointerDeltaX = Framework.TouchScreen.PrimaryPointer.DeltaX;
pointerDeltaY = Framework.TouchScreen.PrimaryPointer.DeltaY;
isPointerTouching = true;
}
if (isPointerTouching)
{
_camera.Yaw += pointerDeltaX * 0.01f;
_camera.Pitch += pointerDeltaY * 0.01f;
_camera.Pitch = MathHelpers.RolloverClamp(_camera.Pitch, MathConstants.Radians0, MathConstants.Radians360);
_camera.Yaw = MathHelpers.RolloverClamp(_camera.Yaw, MathConstants.Radians0, MathConstants.Radians360);
}
var movement = Vector3.Zero;
if (Framework.Keyboard.IsDown(Key.Up))
movement.Z -= delta * 6.0f;
if (Framework.Keyboard.IsDown(Key.Down))
movement.Z += delta * 6.0f;
if (Framework.Keyboard.IsDown(Key.Left))
movement.X -= delta * 6.0f;
if (Framework.Keyboard.IsDown(Key.Right))
movement.X += delta * 6.0f;
_camera.Position += Quaternion.Transform(_camera.Rotation, movement);
} }
} }
} }