debug_MCH.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Runtime.InteropServices;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. namespace OpenVIII
  11. {
  12. public class Debug_MCH
  13. {
  14. static float MODEL_SCALE = 20f;
  15. private const float TEX_SIZEW = 256f;
  16. private const float TEX_SIZEH = 256f;
  17. private Vector2[] textureSizes;
  18. private uint pBase;
  19. private MemoryStream ms;
  20. private BinaryReader br;
  21. [StructLayout(LayoutKind.Sequential, Size =64, Pack =1)]
  22. private struct Header
  23. {
  24. public uint cSkeletonBones;
  25. public uint cVertices;
  26. public uint cTexAnimations;
  27. public uint cFaces;
  28. public uint cUnk;
  29. public uint cSkinObjects;
  30. public uint Unk;
  31. public ushort cTris;
  32. public ushort cQuads;
  33. public uint pBones;
  34. public uint pVertices;
  35. public uint pTexAnimations;
  36. public uint pFaces;
  37. public uint pUnk;
  38. public uint pSkinObjects;
  39. public uint pAnimation;
  40. public uint Unk2;
  41. }
  42. /// <summary>
  43. /// Main anim struct. Model can contain AnimationEntry[] animations, which hold AnimFrame[] frames
  44. /// </summary>
  45. private struct Animation
  46. {
  47. public uint cAnimations;
  48. public AnimationEntry[] animations;
  49. }
  50. /// <summary>
  51. /// Animation struct- it holds all available animation frame keypoints for selected animation
  52. /// </summary>
  53. private struct AnimationEntry
  54. {
  55. public uint cAnimFrames;
  56. public AnimFrame[] animationFrames;
  57. }
  58. private struct AnimFrame
  59. {
  60. private Vector3 Bone0pos;
  61. public Vector3[] vecRot;
  62. public Matrix[] matrixRot;
  63. public Vector3 bone0pos { get => Bone0pos*.01f; set => Bone0pos = value; }
  64. }
  65. private struct Skeleton
  66. {
  67. public Bone[] bones;
  68. public SkinData[] skins;
  69. }
  70. [StructLayout(LayoutKind.Sequential, Size = 0x40, Pack = 1)]
  71. private struct Bone
  72. {
  73. public ushort parentBone;
  74. public ushort unk;
  75. public uint unk2;
  76. public short size;
  77. [MarshalAs(UnmanagedType.ByValArray, SizeConst =54)]
  78. public byte[] unkBuffer;
  79. public float GetSize() => size / MODEL_SCALE;
  80. }
  81. [StructLayout(LayoutKind.Sequential, Size = 64, Pack = 1)]
  82. private struct Face
  83. {
  84. public int polygonType;
  85. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  86. public byte[] unk;
  87. public short unknown;
  88. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
  89. public byte[] unk2;
  90. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  91. public short[] verticesA;
  92. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  93. public short[] verticesB;
  94. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  95. public int[] vertColor;
  96. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  97. public TextureMap[] TextureMap;
  98. public ushort padding;
  99. public ushort texIndex;
  100. public ulong padding2;
  101. public bool BIsQuad => polygonType == 0x2d010709;
  102. }
  103. [StructLayout(LayoutKind.Sequential, Size = 2, Pack = 1)]
  104. private struct TextureMap
  105. {
  106. public byte u;
  107. public byte v;
  108. }
  109. private struct SkinData
  110. {
  111. public short vertIndex;
  112. public short cVerts;
  113. public short boneId;
  114. public short unk;
  115. }
  116. /// <summary>
  117. /// This is final combination of skin+vertices data. You query the class as the face is querying ABCD, but it has boneId paired to it
  118. /// </summary>
  119. private struct GroupedVertices
  120. {
  121. public byte boneId;
  122. public Vector3 vertex;
  123. }
  124. private Header header;
  125. private Animation animation;
  126. private Skeleton skeleton;
  127. private Face[] faces;
  128. private Vector4[] vertices;
  129. private GroupedVertices[] gVertices;
  130. public enum mchMode
  131. {
  132. World,
  133. FieldMain,
  134. FieldNPC
  135. }
  136. mchMode currentMchMode;
  137. public Debug_MCH(MemoryStream ms, BinaryReader br,mchMode mchMode = mchMode.World,float modelScale = 20f)
  138. {
  139. this.ms = ms;
  140. this.br = br;
  141. this.currentMchMode = mchMode;
  142. MODEL_SCALE = modelScale;
  143. pBase = (uint)ms.Position;
  144. header = Extended.ByteArrayToStructure<Header>(br.ReadBytes(64));
  145. if (header.Unk != 0)
  146. {
  147. ms.Seek(0, SeekOrigin.End); //rewind to end
  148. return;
  149. }
  150. ReadGeometry();
  151. ReadSkeleton();
  152. ReadAnimation();
  153. PairSkinWithVertex();
  154. }
  155. /// <summary>
  156. /// to be used for VRAM atlases- it provides the texture sizes relative to texIndexes in GetVertexPositions. If not called then default 256x256 range is used
  157. /// </summary>
  158. /// <param name="textures"></param>
  159. /// <param name="textureIndexes"></param>
  160. public void AssignTextureSizes(Texture2D[] textures, int[] textureIndexes)
  161. {
  162. textureSizes = new Vector2[textureIndexes.Length];
  163. for (var i = 0; i < textureIndexes.Length; i++)
  164. textureSizes[i] = new Vector2(textures[textureIndexes[i]].Width, textures[textureIndexes[i]].Height);
  165. }
  166. /// <summary>
  167. /// to be used for VRAM atlases- it provides the texture sizes relative to texIndexes in GetVertexPositions. If not called then default 256x256 range is used
  168. /// </summary>
  169. /// <param name="textures"></param>
  170. /// <param name="textureIndexes"></param>
  171. public void AssignTextureSizes(TextureHandler[] textures, int[] textureIndexes)
  172. {
  173. textureSizes = new Vector2[textureIndexes.Length];
  174. for (var i = 0; i < textureIndexes.Length; i++)
  175. textureSizes[i] = new Vector2(textures[textureIndexes[i]].ClassicWidth, textures[textureIndexes[i]].ClassicHeight);
  176. }
  177. public bool bValid() => header.Unk == 0;
  178. public uint GetAnimationCount() => animation.cAnimations;
  179. public uint GetAnimationFramesCount(int animId) => animation.animations[animId].cAnimFrames;
  180. /// <summary>
  181. /// as index data for skinning boneId is not in the same place as vertices, therefore we create sorted buffer with Vertex and their boneId
  182. /// </summary>
  183. private void PairSkinWithVertex()
  184. {
  185. gVertices = new GroupedVertices[header.cVertices];
  186. var innerIndex = 0;
  187. for(var i = 0; i<skeleton.skins.Length; i++)
  188. {
  189. for(int n = skeleton.skins[i].vertIndex; n<skeleton.skins[i].vertIndex + skeleton.skins[i].cVerts; n++)
  190. {
  191. gVertices[innerIndex] = new GroupedVertices()
  192. {
  193. boneId = (byte)(skeleton.skins[i].boneId - 1),
  194. vertex = new Vector3(-vertices[innerIndex].X, -vertices[innerIndex].Z, -vertices[innerIndex].Y) //debug, replace with vertices[innerindex]
  195. };
  196. gVertices[innerIndex].vertex = Vector3.Transform(gVertices[innerIndex].vertex, Matrix.CreateFromYawPitchRoll(2920, 0, 0));
  197. innerIndex++;
  198. }
  199. }
  200. }
  201. private void ReadSkeleton()
  202. {
  203. ms.Seek(pBase + header.pBones, SeekOrigin.Begin);
  204. if (ms.Position > ms.Length)
  205. return; //error handler
  206. skeleton = new Skeleton();
  207. skeleton.bones = new Bone[header.cSkeletonBones];
  208. for (var i = 0; i < header.cSkeletonBones; i++)
  209. skeleton.bones[i] = Extended.ByteArrayToStructure<Bone>(br.ReadBytes(64));
  210. ReadSkinning();
  211. return;
  212. }
  213. private void ReadSkinning()
  214. {
  215. ms.Seek(pBase + header.pSkinObjects, SeekOrigin.Begin);
  216. if (ms.Position > ms.Length)
  217. return; //error handler
  218. skeleton.skins = new SkinData[header.cSkinObjects];
  219. for (var i = 0; i < header.cSkinObjects; i++)
  220. skeleton.skins[i] = Extended.ByteArrayToStructure<SkinData>(br.ReadBytes(8));
  221. return;
  222. }
  223. private void ReadGeometry()
  224. {
  225. ms.Seek(pBase + header.pVertices, SeekOrigin.Begin);
  226. if (ms.Position > ms.Length || header.pVertices+ms.Position > ms.Length) //pvert error handler
  227. return; //error handler
  228. vertices = new Vector4[header.cVertices];
  229. for (var i = 0; i < vertices.Length; i++)
  230. vertices[i] = new Vector4(br.ReadInt16(), -br.ReadInt16(), br.ReadInt16(), br.ReadInt16()); //change second to -Y because of warped geom
  231. ms.Seek(pBase + header.pFaces, SeekOrigin.Begin);
  232. var face = new List<Face>();
  233. for(var i = 0; i<header.cFaces; i++)
  234. face.Add(Extended.ByteArrayToStructure<Face>(br.ReadBytes(64)));
  235. faces = face.ToArray();
  236. return;
  237. }
  238. /// <summary>
  239. /// Method to parse available binary data to "animation" structure and calculates the final Matrix
  240. /// <paramref name="bIndependentStream">if true- then ms and br are custom/modified externally</paramref>
  241. /// </summary>
  242. private void ReadAnimation(bool bIndependentStream = false)
  243. {
  244. if(!bIndependentStream) //if normal parsing, then jump to animation pointer
  245. ms.Seek(pBase + header.pAnimation, SeekOrigin.Begin);
  246. //if not, then do nothing- ms will point to animation buffer
  247. if (ms.Position > ms.Length)
  248. return; //error handler
  249. var animationCount = br.ReadUInt16();
  250. var innerIndex = 0;
  251. animation = new Animation() { cAnimations = animationCount, animations= new AnimationEntry[animationCount] };
  252. while (animationCount > 0)
  253. {
  254. var animationFramesCount = br.ReadUInt16();
  255. var cBones = br.ReadUInt16();
  256. if (animationFramesCount * cBones >= ms.Length || cBones == 0 || cBones>100)
  257. {
  258. Console.WriteLine($"Debug_MCH: Error at ReadAnimation()- animFrameCount was {animationFramesCount} and cBones were {cBones}, but that's more than file size!");
  259. break;
  260. }
  261. animation.animations[innerIndex] = new AnimationEntry() { cAnimFrames = animationFramesCount, animationFrames = new AnimFrame[animationFramesCount] };
  262. var animKeypoints = new List<AnimFrame>();
  263. while (animationFramesCount > 0)
  264. {
  265. var keyPoint = new AnimFrame() { bone0pos= new Vector3(x:br.ReadInt16(),z:-br.ReadInt16(), y:br.ReadInt16())};
  266. var vetRot = new Vector3[cBones];
  267. var matrixRot = new Matrix[cBones];
  268. for (var i = 0; i < cBones; i++)
  269. {
  270. short x, y, z;
  271. if (currentMchMode == mchMode.World)
  272. {
  273. x = br.ReadInt16();
  274. y = br.ReadInt16();
  275. z = br.ReadInt16();
  276. }
  277. else //Field NPC dataset - s16 4bytes simplified
  278. {
  279. var rot1 = br.ReadByte();
  280. var rot2 = br.ReadByte();
  281. var rot3 = br.ReadByte();
  282. var rot4 = br.ReadByte();
  283. x = (short)(rot1 << 2 | (rot4 >> 2 * 0 & 3) << 10);
  284. y = (short)(rot2 << 2 | (rot4 >> 2 * 1 & 3) << 10);
  285. z = (short)(rot3 << 2 | (rot4 >> 2 * 2 & 3) << 10);
  286. }
  287. var shortVector = new Vector3()
  288. {
  289. X = -y,
  290. Y = -x,
  291. Z = -z
  292. };
  293. vetRot[i] = Extended.S16VectorToFloat(shortVector) * 360f;
  294. }
  295. animationFramesCount--;
  296. keyPoint.vecRot = vetRot;
  297. keyPoint.matrixRot = matrixRot;
  298. animKeypoints.Add(keyPoint);
  299. }
  300. animationCount--;
  301. animation.animations[innerIndex].animationFrames = animKeypoints.ToArray();
  302. innerIndex++;
  303. }
  304. CalculateBoneMatrix();
  305. return;
  306. }
  307. private void CalculateBoneMatrix()
  308. {
  309. for (var animId = 0; animId < animation.animations.Length; animId++)
  310. for (var frameId = 0; frameId < animation.animations[animId].cAnimFrames; frameId++)
  311. for (var boneId = 0; boneId < skeleton.bones.Length; boneId++)
  312. {
  313. var boneRotation = animation.animations[animId].animationFrames[frameId].vecRot[boneId];
  314. var xRot = Extended.GetRotationMatrixX(-boneRotation.X);
  315. var yRot = Extended.GetRotationMatrixY(-boneRotation.Y);
  316. var zRot = Extended.GetRotationMatrixZ(-boneRotation.Z);
  317. var MatrixZ = Extended.MatrixMultiply_transpose(yRot, xRot);
  318. MatrixZ = Extended.MatrixMultiply_transpose(zRot, MatrixZ);
  319. if (skeleton.bones[boneId].parentBone == 0) //if parentId is 0 then the current bone is core aka bone0
  320. {
  321. MatrixZ.M41 = animation.animations[animId].animationFrames[frameId].bone0pos.X;
  322. MatrixZ.M42 = animation.animations[animId].animationFrames[frameId].bone0pos.Y; //up/down
  323. MatrixZ.M43 = animation.animations[animId].animationFrames[frameId].bone0pos.Z;
  324. MatrixZ.M44 = 1;
  325. }
  326. else
  327. {
  328. var parentBone = animation.animations[animId].animationFrames[frameId].matrixRot[skeleton.bones[boneId].parentBone-1]; //gets the parent bone
  329. MatrixZ.M43 = skeleton.bones[skeleton.bones[boneId].parentBone-1].GetSize();
  330. var rMatrix = Matrix.Multiply(parentBone, MatrixZ);
  331. rMatrix.M41 = parentBone.M11 * MatrixZ.M41 + parentBone.M12 * MatrixZ.M42 + parentBone.M13 * MatrixZ.M43 + parentBone.M41;
  332. rMatrix.M42 = parentBone.M21 * MatrixZ.M41 + parentBone.M22 * MatrixZ.M42 + parentBone.M23 * MatrixZ.M43 + parentBone.M42;
  333. rMatrix.M43 = parentBone.M31 * MatrixZ.M41 + parentBone.M32 * MatrixZ.M42 + parentBone.M33 * MatrixZ.M43 + parentBone.M43;
  334. rMatrix.M44 = 1;
  335. MatrixZ = rMatrix;
  336. }
  337. animation.animations[animId].animationFrames[frameId].matrixRot[boneId] = MatrixZ;
  338. }
  339. }
  340. //public int xa = 0;
  341. //private static float xb = 0f;
  342. //private static float cx = 0f;
  343. /// <summary>
  344. /// [WIP] - this method should return vertices based on animation/skeleton, not 'as-is'
  345. /// </summary>
  346. /// <param name="position">abs X Y Z position to draw model</param>
  347. /// <param name="animationId">absolute index of animation 0-based</param>
  348. /// <param name="animationFrame">index of animation frame- 0-based, length vary</param>
  349. /// <returns>Tuple{item1= VertexPositionColorTexture; item2= clutIndex</returns>
  350. public Tuple<VertexPositionColorTexture[], byte[]> GetVertexPositions(Vector3 position, Quaternion rotation, int animationId, int animationFrame)
  351. {
  352. var facesVertices = new List<VertexPositionColorTexture>();
  353. var texIndexes = new List<byte>();
  354. for(var i = 0; i<faces.Length; i++)
  355. {
  356. //We should have pre-calculated Matrices for all bones, frames, animations. Therefore we need to calculate final Vertex position
  357. var vertsCollection = faces[i].verticesA;
  358. if (!faces[i].BIsQuad) //triangle
  359. {
  360. //let's first get the vertices we need from face. Those are indexes. We need to get their associated boneId to perform
  361. //operations on them. Let's loop by face
  362. for (var k = 0; k < 3; k++)
  363. {
  364. var face = CalculateFinalVertex(gVertices[vertsCollection[k]], animationId, animationFrame);
  365. face = Vector3.Transform(face, Matrix.CreateFromQuaternion(rotation));
  366. face = Vector3.Transform(face, Matrix.CreateTranslation(position));
  367. var clr = new Color(faces[i].vertColor[0], faces[i].vertColor[1], faces[i].vertColor[2], faces[i].vertColor[3]);
  368. Vector2 texData;
  369. if(textureSizes != null)
  370. texData = new Vector2(faces[i].TextureMap[k].u/ textureSizes[faces[i].texIndex].X, faces[i].TextureMap[k].v/ textureSizes[faces[i].texIndex].Y);
  371. else
  372. texData = new Vector2(faces[i].TextureMap[k].u / TEX_SIZEW, faces[i].TextureMap[k].v / TEX_SIZEH);
  373. facesVertices.Add( new VertexPositionColorTexture(face, clr, texData));
  374. texIndexes.Add((byte)faces[i].texIndex);
  375. }
  376. }
  377. else //retriangulation
  378. {
  379. var faceA = CalculateFinalVertex(gVertices[vertsCollection[0]], animationId, animationFrame);
  380. faceA = Vector3.Transform(faceA, Matrix.CreateFromQuaternion(rotation));
  381. faceA = Vector3.Transform(faceA, Matrix.CreateTranslation(position));
  382. var faceB = CalculateFinalVertex(gVertices[vertsCollection[1]], animationId, animationFrame);
  383. faceB = Vector3.Transform(faceB, Matrix.CreateFromQuaternion(rotation));
  384. faceB = Vector3.Transform(faceB, Matrix.CreateTranslation(position));
  385. var faceC = CalculateFinalVertex(gVertices[vertsCollection[2]], animationId, animationFrame);
  386. faceC = Vector3.Transform(faceC, Matrix.CreateFromQuaternion(rotation));
  387. faceC = Vector3.Transform(faceC, Matrix.CreateTranslation(position));
  388. var faceD = CalculateFinalVertex(gVertices[vertsCollection[3]], animationId, animationFrame);
  389. faceD = Vector3.Transform(faceD, Matrix.CreateFromQuaternion(rotation));
  390. faceD = Vector3.Transform(faceD, Matrix.CreateTranslation(position));
  391. var widthDividor = textureSizes == null ? TEX_SIZEW : textureSizes[faces[i].texIndex].X; //if VRAM indexes of tex sizes are not null, then use them for UV calculation
  392. var heightDividor = textureSizes == null ? TEX_SIZEH : textureSizes[faces[i].texIndex].Y;
  393. var t1 = new Vector2(faces[i].TextureMap[0].u / widthDividor, faces[i].TextureMap[0].v / heightDividor);
  394. var t2 = new Vector2(faces[i].TextureMap[1].u / widthDividor, faces[i].TextureMap[1].v / heightDividor);
  395. var t3 = new Vector2(faces[i].TextureMap[2].u / widthDividor, faces[i].TextureMap[2].v / heightDividor);
  396. var t4 = new Vector2(faces[i].TextureMap[3].u / widthDividor, faces[i].TextureMap[3].v / heightDividor);
  397. var clr = new Color(faces[i].vertColor[0], faces[i].vertColor[1], faces[i].vertColor[2], faces[i].vertColor[3]);
  398. facesVertices.Add(new VertexPositionColorTexture(faceA, clr, t1));
  399. facesVertices.Add(new VertexPositionColorTexture(faceB, clr, t2));
  400. facesVertices.Add(new VertexPositionColorTexture(faceD, clr, t4));
  401. facesVertices.Add(new VertexPositionColorTexture(faceA, clr, t1));
  402. facesVertices.Add(new VertexPositionColorTexture(faceC, clr, t3));
  403. facesVertices.Add(new VertexPositionColorTexture(faceD, clr, t4));
  404. texIndexes.Add((byte)faces[i].texIndex);
  405. texIndexes.Add((byte)faces[i].texIndex);
  406. texIndexes.Add((byte)faces[i].texIndex);
  407. texIndexes.Add((byte)faces[i].texIndex);
  408. texIndexes.Add((byte)faces[i].texIndex);
  409. texIndexes.Add((byte)faces[i].texIndex);
  410. }
  411. }
  412. //var a = (from b in faces from c in b.TextureMap select new { x = c.u, y = c.v, w = b.texIndex }).ToArray();
  413. //var min = a.Min(x => x.x);
  414. //var max = a.Max(x => x.x);
  415. //var mina = a.Min(x => x.y);
  416. //var maxb = a.Max(x => x.y);
  417. return new Tuple<VertexPositionColorTexture[], byte[]>(facesVertices.ToArray(), texIndexes.ToArray());
  418. }
  419. private Vector3 CalculateFinalVertex(GroupedVertices groupedVertex, int animationId, int animationFrame)
  420. {
  421. var vertex = groupedVertex.vertex / MODEL_SCALE;
  422. var faceMatrix = animation.animations[animationId].animationFrames[animationFrame].matrixRot[groupedVertex.boneId];
  423. var face = new Vector3(
  424. faceMatrix.M11 * vertex.X + faceMatrix.M41 + faceMatrix.M12 * vertex.Z + faceMatrix.M13 * -vertex.Y,
  425. faceMatrix.M21 * vertex.X + faceMatrix.M42 + faceMatrix.M22 * vertex.Z + faceMatrix.M23 * -vertex.Y,
  426. faceMatrix.M31 * vertex.X + faceMatrix.M43 + faceMatrix.M32 * vertex.Z + faceMatrix.M33 * -vertex.Y
  427. );
  428. return face;
  429. }
  430. /// <summary>
  431. /// This function takes animations data as input and merges it to current Mch instance.
  432. /// This is mandatory for main characters in fields, as their base does not contain animations
  433. /// </summary>
  434. /// <param name="animationsBuffer"></param>
  435. public void MergeAnimations(MemoryStream ms, BinaryReader br)
  436. {
  437. this.ms = ms;
  438. this.br = br;
  439. ReadAnimation(true);
  440. }
  441. }
  442. }