AnimationFrame.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. using Microsoft.Xna.Framework;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. namespace OpenVIII.Battle.Dat
  6. {
  7. /// <summary>
  8. /// Section 3c: Model animation frames
  9. /// </summary>
  10. /// <see cref="http://wiki.ffrtt.ru/index.php/FF8/FileFormat_DAT#Animation"/>
  11. public struct AnimationFrame
  12. {
  13. #region Fields
  14. public readonly IReadOnlyList<Matrix> BoneMatrix;
  15. public readonly IReadOnlyList<Vector3> BonesVectorRotations;
  16. private const float Degrees = 360f;
  17. #endregion Fields
  18. #region Constructors
  19. private AnimationFrame(ExtapathyExtended.BitReader bitReader, Skeleton skeleton, AnimationFrame? previous = null)
  20. {
  21. /// <summary>
  22. /// Some enemies use additional information that is saved for bone AFTER rotation types. We
  23. /// are still not sure what it does as enemy works without it
  24. /// </summary>
  25. // ReSharper disable once UnusedLocalFunctionReturnValue
  26. (short unk1V, short unk2V, short unk3V) GetAdditionalRotationInformation()
  27. {
  28. short calc()
  29. {
  30. var unk1 = bitReader.ReadBits(1);
  31. const int special = 1024;
  32. if ((byte)unk1 <= 0) return special; // if checked here it'll crash
  33. var unk1V = bitReader.ReadBits(16);
  34. return checked((short)(unk1V + special));
  35. }
  36. return (calc(), calc(), calc());
  37. }
  38. //Step 1. It starts with bone0.position. Let's read that into AnimationFrames[animId]- it's only one position per currentFrame
  39. var x = bitReader.ReadPositionType() * .01f;
  40. var y = bitReader.ReadPositionType() * .01f;
  41. var z = bitReader.ReadPositionType() * .01f;
  42. Position = !previous.HasValue
  43. ? new Vector3(x, y, z)
  44. : new Vector3(
  45. previous.Value.Position.X + x,
  46. previous.Value.Position.Y + y,
  47. previous.Value.Position.Z + z);
  48. var modeTest = (byte)bitReader.ReadBits(1); //used to determine if additional info is required
  49. var boneMatrix = new Matrix[skeleton.CBones];
  50. var bonesVectorRotations = new Vector3[skeleton.CBones];
  51. //Step 2. We read the position and we need to store the bones rotations or save base rotation if currentFrame==0
  52. foreach (var k in Enumerable.Range(0, skeleton.CBones)) //bones iterator
  53. {
  54. if (previous.HasValue) //just like position the data for next frames are added to previous
  55. {
  56. bonesVectorRotations[k] = new Vector3
  57. {
  58. X = bitReader.ReadRotationType(),
  59. Y = bitReader.ReadRotationType(),
  60. Z = bitReader.ReadRotationType()
  61. };
  62. if (modeTest > 0)
  63. GetAdditionalRotationInformation();
  64. var previousFrame = previous.Value.BonesVectorRotations[k];
  65. var currentFrame = bonesVectorRotations[k];
  66. bonesVectorRotations[k] =
  67. previousFrame + currentFrame;
  68. }
  69. else //if this is zero currentFrame, then we need to set the base rotations for bones
  70. {
  71. bonesVectorRotations[k] = new Vector3
  72. {
  73. X = bitReader.ReadRotationType(),
  74. Y = bitReader.ReadRotationType(),
  75. Z = bitReader.ReadRotationType()
  76. };
  77. if (modeTest > 0)
  78. GetAdditionalRotationInformation();
  79. }
  80. //Step 3. We now have all bone rotations stored into short. We need to convert that into Matrix and 360/4096
  81. var boneRotation = bonesVectorRotations[k];
  82. boneRotation =
  83. Extended.S16VectorToFloat(
  84. boneRotation); //we had vector3 containing direct copy of short to float, now we need them in real floating point values
  85. boneRotation *= Degrees; //bone rotations are in 360 scope
  86. //maki way
  87. var xRot = Extended.GetRotationMatrixX(-boneRotation.X);
  88. var yRot = Extended.GetRotationMatrixY(-boneRotation.Y);
  89. var zRot = Extended.GetRotationMatrixZ(-boneRotation.Z);
  90. //this is the monogame way and gives same results as above.
  91. //Matrix xRot = Matrix.CreateRotationX(MathHelper.ToRadians(boneRotation.X));
  92. //Matrix yRot = Matrix.CreateRotationY(MathHelper.ToRadians(boneRotation.Y));
  93. //Matrix zRot = Matrix.CreateRotationZ(MathHelper.ToRadians(boneRotation.Z));
  94. var matrixZ = Extended.MatrixMultiply_transpose(yRot, xRot);
  95. matrixZ = Extended.MatrixMultiply_transpose(zRot, matrixZ);
  96. // ReSharper disable once CommentTypo
  97. if (skeleton.Bones[k].ParentId == 0xFFFF
  98. ) //if parentId is 0xFFFF then the current bone is core aka bone0
  99. {
  100. matrixZ.M41 = -Position.X;
  101. matrixZ.M42 = -Position.Y; //up/down
  102. matrixZ.M43 = Position.Z;
  103. matrixZ.M44 = 1;
  104. }
  105. else
  106. {
  107. var parentBone = boneMatrix[skeleton.Bones[k].ParentId]; //gets the parent bone
  108. matrixZ.M43 = skeleton.Bones[skeleton.Bones[k].ParentId].Size;
  109. var rMatrix = Matrix.Multiply(parentBone, matrixZ);
  110. rMatrix.M41 = parentBone.M11 * matrixZ.M41 + parentBone.M12 * matrixZ.M42 +
  111. parentBone.M13 * matrixZ.M43 + parentBone.M41;
  112. rMatrix.M42 = parentBone.M21 * matrixZ.M41 + parentBone.M22 * matrixZ.M42 +
  113. parentBone.M23 * matrixZ.M43 + parentBone.M42;
  114. rMatrix.M43 = parentBone.M31 * matrixZ.M41 + parentBone.M32 * matrixZ.M42 +
  115. parentBone.M33 * matrixZ.M43 + parentBone.M43;
  116. rMatrix.M44 = 1;
  117. matrixZ = rMatrix;
  118. }
  119. boneMatrix[k] = matrixZ;
  120. }
  121. BonesVectorRotations = bonesVectorRotations;
  122. BoneMatrix = boneMatrix;
  123. }
  124. #endregion Constructors
  125. #region Properties
  126. public Vector3 Position { get; }
  127. #endregion Properties
  128. #region Methods
  129. public static IReadOnlyList<AnimationFrame> CreateInstances(BinaryReader br, byte cFrames, Skeleton skeleton)
  130. {
  131. var bitReader = new ExtapathyExtended.BitReader(br.BaseStream);
  132. var animationFrames = new AnimationFrame[cFrames];
  133. foreach (var n in Enumerable.Range(0, cFrames)) //frames
  134. animationFrames[n] = n == 0
  135. ? new AnimationFrame(bitReader, skeleton)
  136. : new AnimationFrame(bitReader, skeleton, animationFrames[n - 1]);
  137. return animationFrames;
  138. }
  139. #endregion Methods
  140. }
  141. }