/*
* 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);
}
}
}