/* * Camera.cs * Authors: August Zinsser * * Copyright August Zinsser 2007 * This program is distributed under the terms of the GNU General Public License */ using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; namespace Pina3D { /// /// Simple camera class defined by a position, up vector and target (either a Vector3 point or a PinaObject) /// public class Camera { protected Vector3 mPosition; // The position of the camera protected Vector3? mTargetPoint; // Where the camera looks (only 1 of TargetPoint, Direction, or TargetObject is set) protected Vector3? mDirection; // '' protected PinaObject mTargetObject; // '' protected Vector3 mUpVector; // Which way is up for the camera protected Vector3 mForwardVector; // Based on camera direction protected Vector3 mLerpPosCounter; // > 0 when the camera is lerping between 2 positions protected Vector3 mLerpPosDuration; // Used for lerping protected float mLerpTargetCounter; // '' protected float mLerpTargetDuration; // '' protected Vector3 mIPosition; // '' protected Vector3 mFPosition; // '' protected Vector3? mITargetPoint; // '' protected Vector3? mFTargetPoint; // '' public Vector3 Position { set { mPosition = value; } get { return mPosition; } } public float X { set { mPosition.X = value; } get { return mPosition.X; } } public float Y { set { mPosition.Y = value; } get { return mPosition.Y; } } public float Z { set { mPosition.Z = value; } get { return mPosition.Z; } } /// /// Sets camera look behavior to always look at this point. Retuns null if camera behavior is set to Directional or Tracking (by setting /// the Forward or TargetObject property). /// public Vector3? TargetPoint { set { mTargetPoint = value; mDirection = null; mTargetObject = null; } get { return mTargetPoint; } } /// /// Sets the camera look behavior to track this object, even if it moves. Returns null if the camera behavior is set to Directional or /// Point Tracking (by setting the Forward or TargetPoint property). /// public PinaObject TargetObject { set { mTargetObject = value; mTargetPoint = null; mDirection = null; } get { return mTargetObject; } } public Vector3 Up { set { mUpVector = value; mUpVector.Normalize(); } get { return mUpVector; } } /// /// Sets the camera look behavior to always look in this direction. Getting does not change /// camera target behavior, nor does it indicate the camera behavior. /// public Vector3 Forward { set { mDirection = value; mForwardVector = Vector3.Normalize(value); mTargetObject = null; mTargetPoint = null; } get { if (mDirection.HasValue) return mForwardVector; else if (mTargetPoint.HasValue) return Vector3.Normalize(mTargetPoint.Value - mPosition); else if (mTargetObject != null) return Vector3.Normalize(mPosition - mTargetObject.Position); else return Vector3.Zero; } } public Vector3 Right { get { return Vector3.Normalize(Vector3.Cross(Forward, Up)); } } public bool IsLerping { get { return mLerpPosCounter.LengthSquared() > 0; } } /// /// Sets the camera to render objects in 2.5D screen space /// public void SetToScreenSpace() { Pina.ProjectionMatrix = Matrix.CreateOrthographicOffCenter(0f, Pina.Graphics.PreferredBackBufferWidth, -Pina.Graphics.PreferredBackBufferHeight, 0f, 0f, 1f); mPosition = new Vector3(0f, 0f, 0f); mUpVector = new Vector3(0f, -1f, 0f); Forward = new Vector3(0f, 0f, 1f); } /// /// Default Constructor /// /// public Camera() { mUpVector = Vector3.UnitZ; } /// /// Initialize position and target /// public void Initialize(Vector3 position, Vector3 target) { mPosition = position; TargetPoint = target; Pina.ViewMatrix = Matrix.CreateLookAt(mPosition, mTargetPoint.Value, mUpVector); } /// /// Lerp the X position of the camera (independent of lerping in other dimensions or targets/facings) /// public void LerpX(float initialX, float finalX, float duration) { mLerpPosDuration.X = duration; mLerpPosCounter.X = duration; mIPosition.X = initialX; mFPosition.X = finalX; if (duration <= 0) mPosition.X = finalX; } /// /// Lerp the Y position of the camera (independent of lerping in other dimensions or targets/facings) /// public void LerpY(float initialY, float finalY, float duration) { mLerpPosDuration.Y = duration; mLerpPosCounter.Y = duration; mIPosition.Y = initialY; mFPosition.Y = finalY; if (duration <= 0) mPosition.Y = finalY; } /// /// Lerp the Z position of the camera (independent of lerping in other dimensions or targets/facings) /// public void LerpZ(float initialZ, float finalZ, float duration) { mLerpPosDuration.Z = duration; mLerpPosCounter.Z = duration; mIPosition.Z = initialZ; mFPosition.Z = finalZ; if (duration <= 0) mPosition.Z = finalZ; } /// /// Lerp the camera between 2 positions and 2 target points (not facing directions) /// public void Lerp(Vector3 initialPosition, Vector3 finalPosition, Vector3 initialTarget, Vector3 finalTarget, float duration) { mLerpPosDuration = new Vector3(duration, duration, duration); mLerpPosCounter = new Vector3(duration, duration, duration); mLerpTargetDuration = duration; mLerpTargetCounter = duration; mIPosition = initialPosition; mFPosition = finalPosition; mITargetPoint = initialTarget; mFTargetPoint = finalTarget; if (duration <= 0) { mPosition = finalPosition; mTargetPoint = finalTarget; } } /// /// If the camera is currently Lerping, calling this will snap it to its destination and stop Lerping /// public void FinishLerping() { // Snap each dimension independently if (mLerpPosCounter.X > 0) { mLerpPosCounter.X = 0f; mPosition.X = mFPosition.X; } if (mLerpPosCounter.Y > 0) { mLerpPosCounter.Y = 0f; mPosition.Y = mFPosition.Y; } if (mLerpPosCounter.Z > 0) { mLerpPosCounter.Z = 0f; mPosition.Z = mFPosition.Z; } if (mLerpTargetCounter > 0) { mLerpTargetCounter = 0f; mTargetPoint = mFTargetPoint; } } /// /// Updates camera logic, mostly just Lerping /// public void Update() { Vector3 currentTargetPoint; // Lerp each dimension of position independently if (mLerpPosCounter.LengthSquared() > 0) { mLerpPosCounter -= new Vector3(Pina.ElapsedSeconds, Pina.ElapsedSeconds,Pina.ElapsedSeconds); if (mLerpPosCounter.X <= 0) { mLerpPosCounter.X = 0; mLerpPosDuration.X = 1f; } else { float t = mLerpPosCounter.X / mLerpPosDuration.X; mPosition.X = t * mIPosition.X + (1 - t) * mFPosition.X; } if (mLerpPosCounter.Y <= 0) { mLerpPosCounter.Y = 0; mLerpPosDuration.Y = 1f; } else { float t = mLerpPosCounter.Y / mLerpPosDuration.Y; mPosition.Y = t * mIPosition.Y + (1 - t) * mFPosition.Y; } if (mLerpPosCounter.Z <= 0) { mLerpPosCounter.Z = 0; mLerpPosDuration.Z = 1f; } else { float t = mLerpPosCounter.Z / mLerpPosDuration.Z; mPosition.Z = t * mIPosition.Z + (1 - t) * mFPosition.Z; } } // Lerp targets if (mLerpTargetCounter > 0) { mLerpTargetCounter -= Pina.ElapsedSeconds; if (mLerpTargetCounter <= 0) { mLerpTargetCounter = 0; mLerpTargetDuration = 1f; } float t = mLerpTargetCounter / mLerpTargetDuration; mTargetPoint = t * mITargetPoint + (1 - t) * mFTargetPoint; } // Check for a valid target if (mTargetPoint.HasValue) { currentTargetPoint = mTargetPoint.Value; } else if (mDirection.HasValue) { currentTargetPoint = mPosition + mDirection.Value; } else if (mTargetObject != null) { currentTargetPoint = mTargetObject.Position; } else { // Nothing for the camera to look at, so just look down. currentTargetPoint = mPosition - mUpVector; } // Update the view matrix Pina.ViewMatrix = Matrix.CreateLookAt(mPosition, currentTargetPoint, mUpVector); } } }