diff --git a/Blarg.GameFramework/Blarg.GameFramework.csproj b/Blarg.GameFramework/Blarg.GameFramework.csproj index a8477c5..1a7b5da 100644 --- a/Blarg.GameFramework/Blarg.GameFramework.csproj +++ b/Blarg.GameFramework/Blarg.GameFramework.csproj @@ -135,7 +135,6 @@ - @@ -225,6 +224,8 @@ + + diff --git a/Blarg.GameFramework/Graphics/BillboardSpriteBatch.cs b/Blarg.GameFramework/Graphics/BillboardSpriteBatch.cs index 32f902d..5f6464e 100644 --- a/Blarg.GameFramework/Graphics/BillboardSpriteBatch.cs +++ b/Blarg.GameFramework/Graphics/BillboardSpriteBatch.cs @@ -127,7 +127,7 @@ namespace Blarg.GameFramework.Graphics throw new InvalidOperationException(); _cameraPosition = GraphicsDevice.ViewContext.Camera.Position; - _cameraForward = GraphicsDevice.ViewContext.Camera.Forward; + _cameraForward = GraphicsDevice.ViewContext.Camera.Direction; if (shader == null) _shader = GraphicsDevice.Sprite3DShader; diff --git a/Blarg.GameFramework/Graphics/Camera.cs b/Blarg.GameFramework/Graphics/Camera.cs index 29a047d..d64b785 100644 --- a/Blarg.GameFramework/Graphics/Camera.cs +++ b/Blarg.GameFramework/Graphics/Camera.cs @@ -2,100 +2,58 @@ using System; namespace Blarg.GameFramework.Graphics { - public class Camera + public abstract class Camera { - ViewContext _viewContext; - float _nearHeight; - float _nearWidth; - public readonly Frustum Frustum; public Matrix4x4 LookAt; public Matrix4x4 Projection; - public Vector3 Forward; - public Vector3 Up; - - public Vector3 Orientation; public Vector3 Position; + public Vector3 Direction; + public Vector3 Up; + public float Near; + public float Far; - public int ViewportWidth { get; private set; } - public int ViewportHeight { get; private 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 int ViewportWidth { get; protected set; } + public int ViewportHeight { get; protected 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) throw new ArgumentNullException("viewContext"); - _viewContext = viewContext; + ViewContext = viewContext; Frustum = new Frustum(this); Position = Vector3.Zero; - Orientation = Vector3.Zero; - Forward = Vector3.Zero; + Direction = Vector3.Forward; Up = Vector3.Up; LookAt = Matrix4x4.Identity; - FieldOfViewAngle = fieldOfView; Near = near; Far = far; - CalculateDefaultProjection( - _viewContext.ViewportLeft, - _viewContext.ViewportTop, - _viewContext.ViewportRight, - _viewContext.ViewportBottom - ); + Update(); } - public virtual void OnUpdate(float delta) - { - } - - 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 abstract void Update(); public Ray Pick(int screenX, int screenY) { - 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 nx = 2.0f * ((float)(screenX - (ViewContext.ViewportWidth / 2))) / ((float)ViewContext.ViewportWidth); + float ny = 2.0f * -((float)(screenY - (ViewContext.ViewportHeight / 2))) / ((float)ViewContext.ViewportHeight); // 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 vy = Vector3.Normalize(Vector3.Cross(vz, vx)); Vector3 near_center = Position - (vz * Near); - Vector3 a = (vx * _nearWidth) * nx; - Vector3 b = (vy * _nearHeight) * ny; + Vector3 a = (vx * NearWidth) * nx; + Vector3 b = (vy * NearHeight) * ny; Vector3 near_point = a + b + near_center; Vector3 dir = Vector3.Normalize(near_point - Position); @@ -105,8 +63,8 @@ namespace Blarg.GameFramework.Graphics public Point2 Project(ref Vector3 objectPosition) { - Matrix4x4 modelview = _viewContext.ModelViewMatrix; - Matrix4x4 projection = _viewContext.ProjectionMatrix; + Matrix4x4 modelview = ViewContext.ModelViewMatrix; + Matrix4x4 projection = ViewContext.ProjectionMatrix; return Project(ref objectPosition, ref modelview, ref projection); } @@ -137,44 +95,14 @@ namespace Blarg.GameFramework.Graphics // map to 2D viewport coordinates (ignoring Z) Point2 result; - 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.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); // 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 - result.Y = (int)_viewContext.PixelScaler.ScaledHeight - result.Y; + result.Y = (int)ViewContext.PixelScaler.ScaledHeight - result.Y; 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); - } } } diff --git a/Blarg.GameFramework/Graphics/EulerPerspectiveCamera.cs b/Blarg.GameFramework/Graphics/EulerPerspectiveCamera.cs new file mode 100644 index 0000000..c67cab6 --- /dev/null +++ b/Blarg.GameFramework/Graphics/EulerPerspectiveCamera.cs @@ -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); + } + } +} + diff --git a/Blarg.GameFramework/Graphics/PerspectiveCamera.cs b/Blarg.GameFramework/Graphics/PerspectiveCamera.cs new file mode 100644 index 0000000..5449948 --- /dev/null +++ b/Blarg.GameFramework/Graphics/PerspectiveCamera.cs @@ -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; + } + } +} + diff --git a/Blarg.GameFramework/Graphics/ViewContext.cs b/Blarg.GameFramework/Graphics/ViewContext.cs index 9c9a7f2..1280e8d 100644 --- a/Blarg.GameFramework/Graphics/ViewContext.cs +++ b/Blarg.GameFramework/Graphics/ViewContext.cs @@ -55,7 +55,7 @@ namespace Blarg.GameFramework.Graphics // not using the default camera, and clearing ("nulling") the camera else if (!_isUsingDefaultCamera && value == null) { - _camera = new Camera(this); + _camera = new PerspectiveCamera(this); _isUsingDefaultCamera = true; cameraWasChanged = true; } @@ -183,7 +183,7 @@ namespace Blarg.GameFramework.Graphics _viewport = viewport; _viewportIsFixedSize = isFixedSizeViewport; _screenOrientation = ScreenOrientation.Rotation0; - _camera = new Camera(this); + _camera = new PerspectiveCamera(this); _isUsingDefaultCamera = true; _pixelScaler = new NoScaleOrthoPixelScaler(); _isUsingDefaultPixelScaler = true; @@ -207,18 +207,12 @@ namespace Blarg.GameFramework.Graphics public void OnRender(float delta) { if (_camera != null) - _camera.OnRender(delta); + _camera.Update(); } public void OnApply(ref Rect size, ScreenOrientation screenOrientation = ScreenOrientation.Rotation0) { 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) @@ -257,7 +251,7 @@ namespace Blarg.GameFramework.Graphics // we also **don't** want the camera to work with a rotated viewport if (_camera != null) - _camera.OnResize(ref _viewport); + _camera.Update(); } } } diff --git a/Blarg.GameFramework/Support/FreeMovementCamera.cs b/Blarg.GameFramework/Support/FreeMovementCamera.cs deleted file mode 100644 index de150c1..0000000 --- a/Blarg.GameFramework/Support/FreeMovementCamera.cs +++ /dev/null @@ -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); - } - } -} diff --git a/Game.Core/TestGameState.cs b/Game.Core/TestGameState.cs index ac15591..fe68c25 100644 --- a/Game.Core/TestGameState.cs +++ b/Game.Core/TestGameState.cs @@ -4,6 +4,7 @@ using Blarg.GameFramework.Events; using Blarg.GameFramework.Graphics; using Blarg.GameFramework.Graphics.Helpers; using Blarg.GameFramework.Graphics.ScreenEffects; +using Blarg.GameFramework.Input; using Blarg.GameFramework.States; using Blarg.GameFramework.Support; @@ -12,7 +13,7 @@ namespace Game public class TestGameState : GameState { FlatWireframeGrid _grid; - FreeMovementCamera _camera; + EulerPerspectiveCamera _camera; public TestGameState(StateManager stateManager, EventManager eventManager) : base(stateManager, eventManager) @@ -21,7 +22,7 @@ namespace Game 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); Framework.GraphicsDevice.ViewContext.Camera = _camera; @@ -54,7 +55,46 @@ namespace Game base.OnUpdate(delta); if (Framework.Keyboard.IsPressed(Blarg.GameFramework.Input.Key.Escape)) 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); } } }