/Rendering/Cameras/PathCamera.cs
C# | 494 lines | 307 code | 52 blank | 135 comment | 33 complexity | 13fb8b42470ca7ff317ea4dfd13c42d4 MD5 | raw file
1using System; 2using System.IO; 3using Delta.Engine; 4using Delta.Utilities; 5using Delta.Utilities.Datatypes; 6using Delta.Utilities.Helpers; 7 8namespace Delta.Rendering.Cameras 9{ 10 /// <summary> 11 /// Path camera represent a camera witch moves on a given path. 12 /// A path consist of many positions in a 3D space. The camera stops moving, 13 /// when it reached the last position in the given path. 14 /// The UP-Vector is implemented in the base camera in the UpdateViewMatrix 15 /// method. Which is the UnitZ-Vector. 16 /// <para /> 17 /// </summary> 18 public class PathCamera : LookAtCamera 19 { 20 #region Constants 21 /// <summary> 22 /// The current version of the implementation of this Camera class. 23 /// </summary> 24 private const int ImplementationVersion = 1; 25 26 /// <summary> 27 /// Camera default speed. 28 /// </summary> 29 private const float DefaultSpeed = 10f; 30 #endregion 31 32 #region LoadColladaCameraPath (Static) 33 /// <summary> 34 /// Loads the exported camera path (view) matrices from a Collada (file) 35 /// data string. 36 /// </summary> 37 /// <param name="colladaMatricesString">Collada matrices string.</param> 38 /// <returns>Return the loaded array of matrices.</returns> 39 public static Matrix[] LoadColladaCameraPath(string colladaMatricesString) 40 { 41 float[] rawMatricesValues = 42 colladaMatricesString.ToFloatArray(); 43 44 // Build the matrices path from the raw path data 45 int matricesCount = rawMatricesValues.Length / 16; 46 Matrix[] matricesPath = new Matrix[matricesCount]; 47 // we have to scale down the exported matrix by the engine scaling 48 // 1m = 0.01 max units (cm) 49 Matrix correctionScaleMatrix = Matrix.CreateScale(0.01f); 50 for (int index = 0; index < matricesCount; index++) 51 { 52 // The view matrix is in Collada exported as inverse view matrix, 53 // probably because of optimization reasons of 3DsMax's viewport render 54 Matrix deltaViewMatrix = 55 LoadColladaMatrix(rawMatricesValues, index * 16).Inverse; 56 57 Matrix.Multiply(ref deltaViewMatrix, ref correctionScaleMatrix, 58 ref matricesPath[index]); 59 } 60 61 return matricesPath; 62 } 63 #endregion 64 65 #region LoadColladaMatrix (Static) 66 /// <summary> 67 /// Create matrix from collada float value array. The matrices in collada 68 /// are stored differently from the xna format (row based instead of 69 /// columns). 70 /// </summary> 71 /// <param name="mat">Array of floats containing this matrix, but 72 /// not exclusively. Can contain many more matrices by using the offset 73 /// parameter.</param> 74 /// <param name="offset">The offset index inside the mat array.</param> 75 /// <returns>Delta Matrix created from collada matrix data.</returns> 76 public static Matrix LoadColladaMatrix(float[] mat, int offset) 77 { 78 return new Matrix( 79 mat[offset + 0], mat[offset + 4], mat[offset + 8], mat[offset + 12], 80 mat[offset + 1], mat[offset + 5], mat[offset + 9], mat[offset + 13], 81 mat[offset + 2], mat[offset + 6], mat[offset + 10], mat[offset + 14], 82 mat[offset + 3], mat[offset + 7], mat[offset + 11], mat[offset + 15]); 83 } 84 #endregion 85 86 #region LookDirection (Public) 87 /// <summary> 88 /// Look direction 89 /// </summary> 90 public override Vector LookDirection 91 { 92 get 93 { 94 return target - position; 95 } 96 set 97 { 98 // Ignored anyway 99 lookDirection = value; 100 } 101 } 102 #endregion 103 104 #region IsFinished (Public) 105 /// <summary> 106 /// Has the camera reached the last node of the path? 107 /// </summary> 108 public bool IsFinished 109 { 110 get; 111 private set; 112 } 113 #endregion 114 115 #region Speed (Public) 116 /// <summary> 117 /// The speed of the camera, default is 10f. 118 /// </summary> 119 public float Speed 120 { 121 get; 122 set; 123 } 124 #endregion 125 126 #region Private 127 128 #region pathByMatrices (Private) 129 /// <summary> 130 /// Interpolated positions and direction as a matrix. 131 /// We get all the data from a Collada file. 132 /// </summary> 133 private readonly Matrix[] pathByMatrices; 134 #endregion 135 136 #region pathByPoints (Private) 137 /// <summary> 138 /// Path of the camera by position points. 139 /// </summary> 140 private readonly Vector[] pathByPoints; 141 #endregion 142 143 #region positionDistances (Private) 144 /// <summary> 145 /// Precalculated distances from the start to the current position, 146 /// to speed up the calculations on moving along the path. 147 /// </summary> 148 private readonly float[] positionDistances; 149 #endregion 150 151 #region targetPath (Private) 152 /// <summary> 153 /// The camera target positions path related to the camera 154 /// positions path. 155 /// This path can also be longer or shorter than the positions 156 /// path, because the current distance on the target path will be 157 /// calculated relatively to the length of the positions path. 158 /// </summary> 159 private readonly Vector[] targetPath; 160 #endregion 161 162 #region targetDistances (Private) 163 /// <summary> 164 /// Precalculated distances from the start to the current target 165 /// position, to speed up the calculations on moving along the path. 166 /// </summary> 167 private readonly float[] targetDistances; 168 #endregion 169 170 #region elapsedDistance (Private) 171 /// <summary> 172 /// The elapsed distance on the path. 173 /// </summary> 174 private float elapsedDistance; 175 #endregion 176 177 #region targetDistanceFactor (Private) 178 /// <summary> 179 /// The relative factor between the length of the camera path and 180 /// the length of the target path. 181 /// </summary> 182 private readonly float targetDistanceFactor; 183 #endregion 184 185 #endregion 186 187 #region Constructors 188 /// <summary> 189 /// Create path camera with a list of matrices. 190 /// </summary> 191 /// <param name="setPath">Matrix array containing path data.</param> 192 public PathCamera(Matrix[] setPath) 193 : this() 194 { 195 // Set the camera path (which is always valid) 196 pathByMatrices = setPath != null 197 ? setPath 198 : new Matrix[0]; 199 200 if (pathByMatrices.Length < 1) 201 { 202 Log.Warning("There is no camera path. We can't move any path " + 203 "without at least 1 matrix."); 204 UpdateViewMatrix(); 205 UpdateScreenProperties(); 206 } 207 208 positionDistances = new float[pathByMatrices.Length]; 209 float distanceCount = 0f; 210 for (int index = 1; index < pathByMatrices.Length; index++) 211 { 212 Matrix inv1 = Matrix.Invert(pathByMatrices[index]); 213 Matrix inv2 = Matrix.Invert(pathByMatrices[index - 1]); 214 215 distanceCount += (inv1.Translation - 216 inv2.Translation).Length; 217 218 positionDistances[index] = distanceCount; 219 } 220 } 221 222 /// <summary> 223 /// Create path camera with a list of vectors (positions) and 224 /// a list of target vectors (if available). 225 /// </summary> 226 /// <param name="setPath">Vector array containing 227 /// path points data.</param> 228 /// <param name="setPath">Vector array containing 229 /// target path points data.</param> 230 public PathCamera(Vector[] setPath, Vector[] setTargetPath = null) 231 : this() 232 { 233 // Set the camera path (which is always valid) 234 pathByPoints = (setPath != null) 235 ? setPath 236 : new Vector[0]; 237 238 positionDistances = new float[pathByPoints.Length]; 239 float distanceCount = 0f; 240 for (int index = 1; index < pathByPoints.Length; index++) 241 { 242 distanceCount += (pathByPoints[index] - 243 pathByPoints[index - 1]).Length; 244 positionDistances[index] = distanceCount; 245 } 246 247 if (pathByPoints.Length < 2) 248 { 249 Log.Warning("There is no camera path. We can't move any path " + 250 "without at least 2 positions."); 251 UpdateViewMatrix(); 252 UpdateScreenProperties(); 253 } 254 255 // Set the target path (if valid) 256 if (setTargetPath != null) 257 { 258 if (setTargetPath.Length == 0) 259 { 260 Log.Warning("The target path needs at least one position!"); 261 } 262 else 263 { 264 targetPath = setTargetPath; 265 266 targetDistances = new float[targetPath.Length]; 267 distanceCount = 0f; 268 for (int index = 1; index < targetPath.Length; index++) 269 { 270 distanceCount += (targetPath[index] - 271 targetPath[index - 1]).Length; 272 targetDistances[index] = distanceCount; 273 } 274 275 targetDistanceFactor = 276 targetDistances[targetDistances.Length - 1] / 277 positionDistances[positionDistances.Length - 1]; 278 } 279 } 280 } 281 282 /// <summary> 283 /// Private helper constructor to set the default values for 284 /// all other constructors above. 285 /// </summary> 286 private PathCamera() 287 : base(DefaultLookAtCamPosition, DefaultTargetPosition) 288 { 289 // Set the default values 290 Speed = DefaultSpeed; 291 IsFinished = false; 292 } 293 #endregion 294 295 #region Save (Public) 296 /// <summary> 297 /// Override saving data from BinaryStream. 298 /// </summary> 299 /// <param name="dataWriter">Binary writer used for writing data.</param> 300 public override void Save(BinaryWriter dataWriter) 301 { 302 base.Save(dataWriter); 303 304 // Save the implementation version first 305 dataWriter.Write(ImplementationVersion); 306 307 //throw new NotImplementedException("TODO"); 308 //dataWriter.Write(TargetPath.Length); 309 //for (int index = 0; index < TargetPath.Length; index++) 310 //{ 311 // TargetPath[index].Save(dataWriter); 312 //} 313 } 314 #endregion 315 316 #region Load (Public) 317 /// <summary> 318 /// Override loading data from BinaryStream. 319 /// </summary> 320 /// <param name="reader">Binary reader used for reading data.</param> 321 public override void Load(BinaryReader reader) 322 { 323 base.Load(reader); 324 325 // First read the implementation version 326 int version = reader.ReadInt32(); 327 switch (version) 328 { 329 // Version 1 330 case 1: 331 //throw new NotImplementedException("TODO"); 332 //int length = reader.ReadInt32(); 333 //TargetPath = new Vector[length]; 334 335 //for (int index = 0; index < length; index++) 336 //{ 337 // TargetPath[index].Load(reader); 338 //} 339 break; 340 341 default: 342 Log.InvalidVersionWarning(GetType().Name + ": " + Name, 343 version, ImplementationVersion); 344 break; 345 } 346 } 347 #endregion 348 349 #region Methods (Private) 350 351 #region InternalRun 352 /// <summary> 353 /// Internal run 354 /// </summary> 355 protected override void InternalRun() 356 { 357 elapsedDistance += Time.Delta * Speed; 358 359 if (pathByMatrices != null) 360 { 361 UpdateMatrixPath(); 362 } 363 else if (pathByPoints != null) 364 { 365 UpdatePointsPath(); 366 } 367 } 368 #endregion 369 370 #region UpdateMatrixPath 371 /// <summary> 372 /// Update the path camera according to the matrices. 373 /// </summary> 374 private void UpdateMatrixPath() 375 { 376 Matrix currentMatrix = Matrix.Identity; 377 if (elapsedDistance <= 0f) 378 { 379 currentMatrix = pathByMatrices[0]; 380 } 381 else if (elapsedDistance >= 382 positionDistances[positionDistances.Length - 1]) 383 { 384 IsFinished = true; 385 currentMatrix = pathByMatrices[pathByMatrices.Length - 1]; 386 } 387 else 388 { 389 int currentIndex = 0; 390 for (int index = 0; index < positionDistances.Length - 1; index++) 391 { 392 if (elapsedDistance >= positionDistances[index] && 393 elapsedDistance < positionDistances[index + 1]) 394 { 395 currentIndex = index; 396 break; 397 } 398 } 399 400 currentMatrix = pathByMatrices[currentIndex]; 401 } 402 403 viewMatrix = currentMatrix; 404 UpdateScreenProperties(); 405 } 406 #endregion 407 408 #region UpdatePointsPath 409 /// <summary> 410 /// Update the path camera according to the vector positions and target 411 /// positions (if available). 412 /// </summary> 413 private void UpdatePointsPath() 414 { 415 Vector direction; 416 Vector position = GetPointPosition(pathByPoints, positionDistances, 417 elapsedDistance, out direction); 418 419 Vector target = Vector.Zero; 420 if (targetPath != null) 421 { 422 if (targetPath.Length == 1) 423 { 424 target = targetPath[0]; 425 } 426 else 427 { 428 target = GetPointPosition(targetPath, targetDistances, 429 elapsedDistance * targetDistanceFactor, out direction); 430 } 431 } 432 else 433 { 434 target = position + direction; 435 } 436 437 if (elapsedDistance >= positionDistances[positionDistances.Length - 1]) 438 { 439 IsFinished = true; 440 } 441 442 viewMatrix = Matrix.CreateLookAt(position, 443 target, UpVector); 444 UpdateScreenProperties(); 445 } 446 #endregion 447 448 #region GetPointPosition 449 /// <summary> 450 /// Get a vector position by an array of positions, precalculated 451 /// distances (for performance) and a current distance. 452 /// <para /> 453 /// Basically the logic of a 2d graph but with a 3d vector as the 454 /// result. 455 /// </summary> 456 private Vector GetPointPosition(Vector[] positions, 457 float[] distances, float distance, out Vector direction) 458 { 459 int currentIndex = 0; 460 461 if (distance >= distances[distances.Length - 1]) 462 { 463 direction = positions[positions.Length - 1] - 464 positions[positions.Length - 2]; 465 return positions[positions.Length - 1]; 466 } 467 else if (distance <= 0f) 468 { 469 direction = positions[1] - positions[0]; 470 return positions[0]; 471 } 472 else 473 { 474 for (int index = 0; index < distances.Length - 1; index++) 475 { 476 if (distance >= distances[index] && 477 distance < distances[index + 1]) 478 { 479 currentIndex = index; 480 break; 481 } 482 } 483 } 484 485 float relativeDistance = distance - distances[currentIndex]; 486 direction = positions[currentIndex + 1] - positions[currentIndex]; 487 direction.Normalize(); 488 return positions[currentIndex] + (relativeDistance * direction); 489 } 490 #endregion 491 492 #endregion 493 } 494}