/Rendering/Cameras/PathCamera.cs
C# | 494 lines | 307 code | 52 blank | 135 comment | 33 complexity | 13fb8b42470ca7ff317ea4dfd13c42d4 MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using System.IO;
- using Delta.Engine;
- using Delta.Utilities;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
-
- namespace Delta.Rendering.Cameras
- {
- /// <summary>
- /// Path camera represent a camera witch moves on a given path.
- /// A path consist of many positions in a 3D space. The camera stops moving,
- /// when it reached the last position in the given path.
- /// The UP-Vector is implemented in the base camera in the UpdateViewMatrix
- /// method. Which is the UnitZ-Vector.
- /// <para />
- /// </summary>
- public class PathCamera : LookAtCamera
- {
- #region Constants
- /// <summary>
- /// The current version of the implementation of this Camera class.
- /// </summary>
- private const int ImplementationVersion = 1;
-
- /// <summary>
- /// Camera default speed.
- /// </summary>
- private const float DefaultSpeed = 10f;
- #endregion
-
- #region LoadColladaCameraPath (Static)
- /// <summary>
- /// Loads the exported camera path (view) matrices from a Collada (file)
- /// data string.
- /// </summary>
- /// <param name="colladaMatricesString">Collada matrices string.</param>
- /// <returns>Return the loaded array of matrices.</returns>
- public static Matrix[] LoadColladaCameraPath(string colladaMatricesString)
- {
- float[] rawMatricesValues =
- colladaMatricesString.ToFloatArray();
-
- // Build the matrices path from the raw path data
- int matricesCount = rawMatricesValues.Length / 16;
- Matrix[] matricesPath = new Matrix[matricesCount];
- // we have to scale down the exported matrix by the engine scaling
- // 1m = 0.01 max units (cm)
- Matrix correctionScaleMatrix = Matrix.CreateScale(0.01f);
- for (int index = 0; index < matricesCount; index++)
- {
- // The view matrix is in Collada exported as inverse view matrix,
- // probably because of optimization reasons of 3DsMax's viewport render
- Matrix deltaViewMatrix =
- LoadColladaMatrix(rawMatricesValues, index * 16).Inverse;
-
- Matrix.Multiply(ref deltaViewMatrix, ref correctionScaleMatrix,
- ref matricesPath[index]);
- }
-
- return matricesPath;
- }
- #endregion
-
- #region LoadColladaMatrix (Static)
- /// <summary>
- /// Create matrix from collada float value array. The matrices in collada
- /// are stored differently from the xna format (row based instead of
- /// columns).
- /// </summary>
- /// <param name="mat">Array of floats containing this matrix, but
- /// not exclusively. Can contain many more matrices by using the offset
- /// parameter.</param>
- /// <param name="offset">The offset index inside the mat array.</param>
- /// <returns>Delta Matrix created from collada matrix data.</returns>
- public static Matrix LoadColladaMatrix(float[] mat, int offset)
- {
- return new Matrix(
- mat[offset + 0], mat[offset + 4], mat[offset + 8], mat[offset + 12],
- mat[offset + 1], mat[offset + 5], mat[offset + 9], mat[offset + 13],
- mat[offset + 2], mat[offset + 6], mat[offset + 10], mat[offset + 14],
- mat[offset + 3], mat[offset + 7], mat[offset + 11], mat[offset + 15]);
- }
- #endregion
-
- #region LookDirection (Public)
- /// <summary>
- /// Look direction
- /// </summary>
- public override Vector LookDirection
- {
- get
- {
- return target - position;
- }
- set
- {
- // Ignored anyway
- lookDirection = value;
- }
- }
- #endregion
-
- #region IsFinished (Public)
- /// <summary>
- /// Has the camera reached the last node of the path?
- /// </summary>
- public bool IsFinished
- {
- get;
- private set;
- }
- #endregion
-
- #region Speed (Public)
- /// <summary>
- /// The speed of the camera, default is 10f.
- /// </summary>
- public float Speed
- {
- get;
- set;
- }
- #endregion
-
- #region Private
-
- #region pathByMatrices (Private)
- /// <summary>
- /// Interpolated positions and direction as a matrix.
- /// We get all the data from a Collada file.
- /// </summary>
- private readonly Matrix[] pathByMatrices;
- #endregion
-
- #region pathByPoints (Private)
- /// <summary>
- /// Path of the camera by position points.
- /// </summary>
- private readonly Vector[] pathByPoints;
- #endregion
-
- #region positionDistances (Private)
- /// <summary>
- /// Precalculated distances from the start to the current position,
- /// to speed up the calculations on moving along the path.
- /// </summary>
- private readonly float[] positionDistances;
- #endregion
-
- #region targetPath (Private)
- /// <summary>
- /// The camera target positions path related to the camera
- /// positions path.
- /// This path can also be longer or shorter than the positions
- /// path, because the current distance on the target path will be
- /// calculated relatively to the length of the positions path.
- /// </summary>
- private readonly Vector[] targetPath;
- #endregion
-
- #region targetDistances (Private)
- /// <summary>
- /// Precalculated distances from the start to the current target
- /// position, to speed up the calculations on moving along the path.
- /// </summary>
- private readonly float[] targetDistances;
- #endregion
-
- #region elapsedDistance (Private)
- /// <summary>
- /// The elapsed distance on the path.
- /// </summary>
- private float elapsedDistance;
- #endregion
-
- #region targetDistanceFactor (Private)
- /// <summary>
- /// The relative factor between the length of the camera path and
- /// the length of the target path.
- /// </summary>
- private readonly float targetDistanceFactor;
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create path camera with a list of matrices.
- /// </summary>
- /// <param name="setPath">Matrix array containing path data.</param>
- public PathCamera(Matrix[] setPath)
- : this()
- {
- // Set the camera path (which is always valid)
- pathByMatrices = setPath != null
- ? setPath
- : new Matrix[0];
-
- if (pathByMatrices.Length < 1)
- {
- Log.Warning("There is no camera path. We can't move any path " +
- "without at least 1 matrix.");
- UpdateViewMatrix();
- UpdateScreenProperties();
- }
-
- positionDistances = new float[pathByMatrices.Length];
- float distanceCount = 0f;
- for (int index = 1; index < pathByMatrices.Length; index++)
- {
- Matrix inv1 = Matrix.Invert(pathByMatrices[index]);
- Matrix inv2 = Matrix.Invert(pathByMatrices[index - 1]);
-
- distanceCount += (inv1.Translation -
- inv2.Translation).Length;
-
- positionDistances[index] = distanceCount;
- }
- }
-
- /// <summary>
- /// Create path camera with a list of vectors (positions) and
- /// a list of target vectors (if available).
- /// </summary>
- /// <param name="setPath">Vector array containing
- /// path points data.</param>
- /// <param name="setPath">Vector array containing
- /// target path points data.</param>
- public PathCamera(Vector[] setPath, Vector[] setTargetPath = null)
- : this()
- {
- // Set the camera path (which is always valid)
- pathByPoints = (setPath != null)
- ? setPath
- : new Vector[0];
-
- positionDistances = new float[pathByPoints.Length];
- float distanceCount = 0f;
- for (int index = 1; index < pathByPoints.Length; index++)
- {
- distanceCount += (pathByPoints[index] -
- pathByPoints[index - 1]).Length;
- positionDistances[index] = distanceCount;
- }
-
- if (pathByPoints.Length < 2)
- {
- Log.Warning("There is no camera path. We can't move any path " +
- "without at least 2 positions.");
- UpdateViewMatrix();
- UpdateScreenProperties();
- }
-
- // Set the target path (if valid)
- if (setTargetPath != null)
- {
- if (setTargetPath.Length == 0)
- {
- Log.Warning("The target path needs at least one position!");
- }
- else
- {
- targetPath = setTargetPath;
-
- targetDistances = new float[targetPath.Length];
- distanceCount = 0f;
- for (int index = 1; index < targetPath.Length; index++)
- {
- distanceCount += (targetPath[index] -
- targetPath[index - 1]).Length;
- targetDistances[index] = distanceCount;
- }
-
- targetDistanceFactor =
- targetDistances[targetDistances.Length - 1] /
- positionDistances[positionDistances.Length - 1];
- }
- }
- }
-
- /// <summary>
- /// Private helper constructor to set the default values for
- /// all other constructors above.
- /// </summary>
- private PathCamera()
- : base(DefaultLookAtCamPosition, DefaultTargetPosition)
- {
- // Set the default values
- Speed = DefaultSpeed;
- IsFinished = false;
- }
- #endregion
-
- #region Save (Public)
- /// <summary>
- /// Override saving data from BinaryStream.
- /// </summary>
- /// <param name="dataWriter">Binary writer used for writing data.</param>
- public override void Save(BinaryWriter dataWriter)
- {
- base.Save(dataWriter);
-
- // Save the implementation version first
- dataWriter.Write(ImplementationVersion);
-
- //throw new NotImplementedException("TODO");
- //dataWriter.Write(TargetPath.Length);
- //for (int index = 0; index < TargetPath.Length; index++)
- //{
- // TargetPath[index].Save(dataWriter);
- //}
- }
- #endregion
-
- #region Load (Public)
- /// <summary>
- /// Override loading data from BinaryStream.
- /// </summary>
- /// <param name="reader">Binary reader used for reading data.</param>
- public override void Load(BinaryReader reader)
- {
- base.Load(reader);
-
- // First read the implementation version
- int version = reader.ReadInt32();
- switch (version)
- {
- // Version 1
- case 1:
- //throw new NotImplementedException("TODO");
- //int length = reader.ReadInt32();
- //TargetPath = new Vector[length];
-
- //for (int index = 0; index < length; index++)
- //{
- // TargetPath[index].Load(reader);
- //}
- break;
-
- default:
- Log.InvalidVersionWarning(GetType().Name + ": " + Name,
- version, ImplementationVersion);
- break;
- }
- }
- #endregion
-
- #region Methods (Private)
-
- #region InternalRun
- /// <summary>
- /// Internal run
- /// </summary>
- protected override void InternalRun()
- {
- elapsedDistance += Time.Delta * Speed;
-
- if (pathByMatrices != null)
- {
- UpdateMatrixPath();
- }
- else if (pathByPoints != null)
- {
- UpdatePointsPath();
- }
- }
- #endregion
-
- #region UpdateMatrixPath
- /// <summary>
- /// Update the path camera according to the matrices.
- /// </summary>
- private void UpdateMatrixPath()
- {
- Matrix currentMatrix = Matrix.Identity;
- if (elapsedDistance <= 0f)
- {
- currentMatrix = pathByMatrices[0];
- }
- else if (elapsedDistance >=
- positionDistances[positionDistances.Length - 1])
- {
- IsFinished = true;
- currentMatrix = pathByMatrices[pathByMatrices.Length - 1];
- }
- else
- {
- int currentIndex = 0;
- for (int index = 0; index < positionDistances.Length - 1; index++)
- {
- if (elapsedDistance >= positionDistances[index] &&
- elapsedDistance < positionDistances[index + 1])
- {
- currentIndex = index;
- break;
- }
- }
-
- currentMatrix = pathByMatrices[currentIndex];
- }
-
- viewMatrix = currentMatrix;
- UpdateScreenProperties();
- }
- #endregion
-
- #region UpdatePointsPath
- /// <summary>
- /// Update the path camera according to the vector positions and target
- /// positions (if available).
- /// </summary>
- private void UpdatePointsPath()
- {
- Vector direction;
- Vector position = GetPointPosition(pathByPoints, positionDistances,
- elapsedDistance, out direction);
-
- Vector target = Vector.Zero;
- if (targetPath != null)
- {
- if (targetPath.Length == 1)
- {
- target = targetPath[0];
- }
- else
- {
- target = GetPointPosition(targetPath, targetDistances,
- elapsedDistance * targetDistanceFactor, out direction);
- }
- }
- else
- {
- target = position + direction;
- }
-
- if (elapsedDistance >= positionDistances[positionDistances.Length - 1])
- {
- IsFinished = true;
- }
-
- viewMatrix = Matrix.CreateLookAt(position,
- target, UpVector);
- UpdateScreenProperties();
- }
- #endregion
-
- #region GetPointPosition
- /// <summary>
- /// Get a vector position by an array of positions, precalculated
- /// distances (for performance) and a current distance.
- /// <para />
- /// Basically the logic of a 2d graph but with a 3d vector as the
- /// result.
- /// </summary>
- private Vector GetPointPosition(Vector[] positions,
- float[] distances, float distance, out Vector direction)
- {
- int currentIndex = 0;
-
- if (distance >= distances[distances.Length - 1])
- {
- direction = positions[positions.Length - 1] -
- positions[positions.Length - 2];
- return positions[positions.Length - 1];
- }
- else if (distance <= 0f)
- {
- direction = positions[1] - positions[0];
- return positions[0];
- }
- else
- {
- for (int index = 0; index < distances.Length - 1; index++)
- {
- if (distance >= distances[index] &&
- distance < distances[index + 1])
- {
- currentIndex = index;
- break;
- }
- }
- }
-
- float relativeDistance = distance - distances[currentIndex];
- direction = positions[currentIndex + 1] - positions[currentIndex];
- direction.Normalize();
- return positions[currentIndex] + (relativeDistance * direction);
- }
- #endregion
-
- #endregion
- }
- }