CameraStruct.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. using Microsoft.Xna.Framework;
  2. using System;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. #pragma warning disable 169
  7. #pragma warning disable 649
  8. namespace OpenVIII.Battle
  9. {
  10. public partial class Camera
  11. {
  12. #region Structs
  13. [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 1092)]
  14. [SuppressMessage("ReSharper", "UnassignedReadonlyField")]
  15. [SuppressMessage("ReSharper", "UnusedMember.Global")]
  16. public struct CameraStruct
  17. {
  18. public byte AnimationId { get; set; } //000
  19. private byte KeyframeCount;
  20. public ushort ControlWord { get; private set; }
  21. public ushort StartingFov { get; private set; } //usually ~280
  22. public ushort EndingFov { get; private set; } //006
  23. private ushort StartingCameraRoll; //usually 0 unless you're aiming for some wicked animation
  24. private ushort EndingCameraRoll; //
  25. private readonly ushort _startingTime; //usually 0, that's pretty logical
  26. /// <summary>
  27. /// Time is calculated from number of frames. You basically set starting position
  28. /// World+lookat and ending position, then mark number of frames to interpolate between
  29. /// them. Every frame is one draw call and it costs 16.
  30. /// </summary>
  31. /// ReSharper disable once CommentTypo
  32. /// <remarks>starting time needs to be equal or higher for next animation frame to be read; If next frame==0xFFFF then it's all done</remarks>
  33. public ushort Time { get; private set; }
  34. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
  35. private readonly byte[] _unkBytes; //010
  36. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  37. private readonly ushort[] StartFramesOffsets; //024 - start frames for each key frame?
  38. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  39. private readonly short[] _cameraWorldZ; //064
  40. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  41. private readonly short[] _cameraWorldX; //0A4
  42. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  43. private readonly short[] _cameraWorldY; //0E4
  44. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  45. private readonly byte[] IsFrameDurationsShot; //124
  46. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  47. private readonly short[] _cameraLookAtZ; //144
  48. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  49. private readonly short[] _cameraLookAtX; //184
  50. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  51. private readonly short[] _cameraLookAtY; //1C4
  52. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  53. private readonly byte[] IsFrameEndingShots; //204
  54. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
  55. private readonly byte[] _unkByte224; //224
  56. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
  57. private readonly byte[] _unkByte2A4; //2A4
  58. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
  59. private readonly byte[] _unkByte324; //324
  60. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
  61. private readonly byte[] _unkByte3A4; //3A4
  62. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
  63. private readonly byte[] _unkByte424; //424
  64. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
  65. private readonly byte[] _unkByte4A4; //4A4
  66. private static Vector3 Offset => new Vector3(40, 40, -40);
  67. //private (Vector3 World, Vector3 LookAt) this[int i] => (CameraWorld(i), CameraLookAt(i));
  68. private Vector3 CameraWorld(int i) =>
  69. new Vector3(
  70. _cameraWorldX[i],
  71. -(_cameraWorldY[i]),
  72. -(_cameraWorldZ[i])) / Memory.CameraScale + Offset;
  73. private Vector3 CameraLookAt(int i) =>
  74. new Vector3(
  75. _cameraLookAtX[i],
  76. -(_cameraLookAtY[i]),
  77. -(_cameraLookAtZ[i])) / Memory.CameraScale + Offset;
  78. public void UpdateTime() => CurrentTime += Memory.ElapsedGameTime;
  79. public TimeSpan CurrentTime { get; private set; }
  80. public TimeSpan TotalTime => TimeSpan.FromTicks(TotalTimePerFrame.Ticks * Time);
  81. /// <summary>
  82. /// (1000) milliseconds / frames per second, maybe not quite fps.
  83. /// </summary>
  84. private static TimeSpan TotalTimePerFrame => TimeSpan.FromMilliseconds(1000d / 240d);
  85. public void ResetTime() => CurrentTime = TimeSpan.Zero;
  86. public void ReadAnimation(BinaryReader br)
  87. {
  88. ControlWord = br.ReadUInt16();
  89. if (ControlWord == 0xFFFF) return;
  90. var currentPosition = br.ReadUInt16(); //getter for *current_position
  91. br.BaseStream.Seek(-2, SeekOrigin.Current); //roll back one WORD because no increment
  92. switch ((ControlWord >> 6) & 3)
  93. {
  94. case 1:
  95. StartingFov = 0x200;
  96. EndingFov = 0x200;
  97. break;
  98. case 2:
  99. StartingFov = currentPosition;
  100. EndingFov = currentPosition;
  101. br.ReadUInt16(); //current_position++
  102. break;
  103. case 3:
  104. StartingFov = currentPosition;
  105. br.BaseStream.Seek(2,
  106. SeekOrigin
  107. .Current); //skipping WORD, because we already rolled back one WORD above this switch
  108. currentPosition = br.ReadUInt16();
  109. EndingFov = currentPosition;
  110. break;
  111. }
  112. switch ((ControlWord >> 8) & 3)
  113. {
  114. case 0: //TODO!!
  115. StartingCameraRoll = 00000000; //TODO, what's ff8vars.unkWord1D977A2?
  116. EndingCameraRoll = 00000000; //same as above; cam->unkWord00A = ff8vars.unkWord1D977A2;
  117. break;
  118. case 1:
  119. StartingCameraRoll = 0;
  120. EndingCameraRoll = 0;
  121. break;
  122. case 2:
  123. currentPosition = br.ReadUInt16(); //* + current_position++;
  124. StartingCameraRoll = currentPosition;
  125. EndingCameraRoll = currentPosition;
  126. break;
  127. case 3:
  128. currentPosition = br.ReadUInt16(); //* + current_position++;
  129. StartingCameraRoll = currentPosition;
  130. currentPosition = br.ReadUInt16(); //* + current_position++;
  131. EndingCameraRoll = currentPosition;
  132. break;
  133. }
  134. byte keyFrameCount = 0;
  135. ushort totalFrameCount = 0;
  136. switch (ControlWord & 1)
  137. {
  138. case 0:
  139. while (true
  140. ) //I'm setting this to true and breaking in code as this works on peeking on next variable via pointer and that's not possible here without unsafe block
  141. {
  142. StartFramesOffsets[keyFrameCount] = totalFrameCount; //looks like this is the camera index
  143. currentPosition = br.ReadUInt16();
  144. if ((short)currentPosition < 0
  145. ) //reverse of *current_position >= 0, also cast to signed is important here
  146. break;
  147. totalFrameCount +=
  148. (ushort)(currentPosition *
  149. 16); //here is increment of short*, but I already did that above
  150. IsFrameDurationsShot[keyFrameCount] =
  151. (byte)(br
  152. .ReadUInt16()
  153. ); //cam->unkByte124[keyFrameCount] = *current_position++; - looks like we are wasting one byte due to integer sizes
  154. _cameraWorldX[keyFrameCount] = br.ReadInt16();
  155. _cameraWorldY[keyFrameCount] = br.ReadInt16();
  156. _cameraWorldZ[keyFrameCount] = br.ReadInt16();
  157. IsFrameEndingShots[keyFrameCount] =
  158. (byte)(br.ReadUInt16()); //m->unkByte204[keyFrameCount] = *current_position++;
  159. _cameraLookAtX[keyFrameCount] = br.ReadInt16();
  160. _cameraLookAtY[keyFrameCount] = br.ReadInt16();
  161. _cameraLookAtZ[keyFrameCount] = br.ReadInt16();
  162. keyFrameCount++;
  163. }
  164. if (keyFrameCount > 2)
  165. {
  166. //ff8Functions.Sub50D010(cam->unkWord024, cam->unkWord064, cam->unkWord0A4, cam->unkWord0E4, keyFrameCount, cam->unkByte224, cam->unkByte2A4, cam->unkByte324);
  167. //ff8Functions.Sub50D010(cam->unkWord024, cam->unkWord144, cam->unkWord184, cam->unkWord1C4, keyFrameCount, cam->unkByte3A4, cam->unkByte424, cam->unkByte4A4);
  168. }
  169. break;
  170. case 1:
  171. {
  172. goto case 0;
  173. //if (currentPosition >= 0)
  174. //{
  175. // var local14 = (short)(br.BaseStream.Position + 5 * 2);
  176. // var local10 = (short)(br.BaseStream.Position + 6 * 2);
  177. // var local2C = (short)(br.BaseStream.Position + 7 * 2);
  178. // var local18 = (short)(br.BaseStream.Position + 1 * 2);
  179. // var local1C = (short)(br.BaseStream.Position + 2 * 2);
  180. // while (true)
  181. // {
  182. // StartFramesOffsets[keyFrameCount] = totalFrameCount;
  183. // currentPosition = br.ReadUInt16();
  184. // if ((short)currentPosition < 0) //reverse of *current_position >= 0, also cast to signed is important here
  185. // break;
  186. // totalFrameCount += (ushort)(currentPosition * 16);
  187. // //ff8Functions.Sub503AE0(++local18, ++local1C, ++ebx, *(BYTE*)current_position, &cam->unkWord064[keyFrameCount], &cam->unkWord0A4[keyFrameCount], &cam->unkWord0E4[keyFrameCount]);
  188. // //ff8Functions.Sub503AE0(++local14, ++local10, ++local2C, *(BYTE*)(current_position + 4), &cam->unkWord144[keyFrameCount], &cam->unkWord184[keyFrameCount], &cam->unkWord1C4[keyFrameCount]);
  189. // IsFrameEndingShots[keyFrameCount] = 0xFB;
  190. // IsFrameDurationsShot[keyFrameCount] = 0xFB;
  191. // local1C += 8;
  192. // local18 += 8;
  193. // currentPosition += 8;
  194. // local2C += 8;
  195. // //ebx += 8;
  196. // local10 += 8;
  197. // local14 += 8;
  198. // keyFrameCount++;
  199. // }
  200. //}
  201. //break;
  202. }
  203. }
  204. if ((ControlWord & 0x3E) == 0x1E)
  205. {
  206. //ff8Functions.Sub503300();
  207. }
  208. KeyframeCount = keyFrameCount;
  209. Time = totalFrameCount;
  210. //cam.startingTime = 0;
  211. CurrentTime = TimeSpan.Zero;
  212. }
  213. public bool Done => CurrentTime >= TotalTime || (_cameraLookAtX[0], _cameraLookAtY[0], _cameraLookAtZ[0],
  214. _cameraWorldX[0], _cameraWorldY[0], _cameraWorldZ[0]) == (0, 0, 0, 0, 0, 0);
  215. public (Vector3 CamTarget, Vector3 CamPosition, Matrix View, Matrix Projection) UpdatePosition()
  216. {
  217. var step = CurrentTime.Ticks / (float)TotalTime.Ticks;
  218. if (step > 1f) step = 1f;
  219. var camTarget = Vector3.SmoothStep(CameraLookAt(0), CameraLookAt(1), step);
  220. var camPosition = Vector3.SmoothStep(CameraWorld(0), CameraWorld(1), step);
  221. var fov =
  222. MathHelper.ToRadians(
  223. (float)(2f * Math.Atan(240f / (2f *
  224. MathHelper.SmoothStep(StartingFov, EndingFov, step))) *
  225. 57.29577951f));
  226. var viewMatrix = Matrix.CreateLookAt(camPosition, camTarget, Vector3.Up);
  227. var projectionMatrix = (Memory.Graphics != null)
  228. ? Matrix.CreatePerspectiveFieldOfView(fov,
  229. Memory.Graphics.GraphicsDevice.Viewport.AspectRatio, 1f, 1000f)
  230. : Matrix.Identity;
  231. return (camTarget, camPosition, viewMatrix, projectionMatrix);
  232. }
  233. }
  234. #endregion Structs
  235. }
  236. }