Triangle.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics.CodeAnalysis;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Runtime.InteropServices;
  9. namespace OpenVIII.Battle.Dat
  10. {
  11. /// <summary>
  12. /// Section 2e: Triangle
  13. /// </summary>
  14. /// <see cref="http://wiki.ffrtt.ru/index.php?title=FF8/FileFormat_DAT#Useful_structures"/>
  15. [StructLayout(LayoutKind.Explicit, Pack = 1, Size = ByteSize)]
  16. public class Triangle
  17. {
  18. #region Fields
  19. [field: FieldOffset(10)]
  20. public readonly ushort TexUnk;
  21. [field: FieldOffset(14)]
  22. public readonly ushort U;
  23. [field: FieldOffset(6)]
  24. public readonly UV Vta;
  25. [field: FieldOffset(8)]
  26. public readonly UV Vtb;
  27. [field: FieldOffset(12)]
  28. public readonly UV Vtc;
  29. private const int ByteSize = 16;
  30. [field: FieldOffset(0)]
  31. private readonly ushort _a;
  32. [field: FieldOffset(2)]
  33. private readonly ushort _b;
  34. [field: FieldOffset(4)]
  35. private readonly ushort _c;
  36. #endregion Fields
  37. #region Constructors
  38. [SuppressMessage("ReSharper", "UnusedMember.Local")]
  39. private Triangle()
  40. { }
  41. [SuppressMessage("ReSharper", "UnusedMember.Local")]
  42. private Triangle(BinaryReader br)
  43. {
  44. _a = br.ReadUInt16();//0
  45. _b = br.ReadUInt16();//2
  46. _c = br.ReadUInt16();//4
  47. Vta = UV.CreateInstance(br);//6
  48. Vtb = UV.CreateInstance(br);//8
  49. TexUnk = br.ReadUInt16();//10
  50. Vtc = UV.CreateInstance(br);//12
  51. U = br.ReadUInt16();//14
  52. }
  53. #endregion Constructors
  54. #region Properties
  55. public static byte Count => 3;
  56. public ushort A => (ushort)(_a & 0xFFF);
  57. public ushort B => (ushort)(_b & 0xFFF);
  58. public ushort C => (ushort)(_c & 0xFFF);
  59. public byte TextureIndex => (byte)((TexUnk >> 6) & 0b111);
  60. #endregion Properties
  61. #region Indexers
  62. public byte this[int i]
  63. {
  64. get
  65. {
  66. switch (i)
  67. {
  68. case 0:
  69. return 0;
  70. case 1:
  71. return 1;
  72. case 2:
  73. return 2;
  74. }
  75. throw new IndexOutOfRangeException($"{this} :: 0-2 are only valid values");
  76. }
  77. }
  78. #endregion Indexers
  79. #region Methods
  80. public static Triangle CreateInstance(BinaryReader br) => Extended.ByteArrayToClass<Triangle>(br.ReadBytes(ByteSize));
  81. public static IReadOnlyList<Triangle> CreateInstances(BinaryReader br, ushort count) => Enumerable.Range(0, count).Select(_ => CreateInstance(br)).ToList().AsReadOnly();
  82. public VertexPositionTexture[] GenerateVPT(List<VectorBoneGRP> vertices, Quaternion rotation,
  83. Vector3 translationPosition, TextureHandler preVarTex)
  84. {
  85. var tempVPT = new VertexPositionTexture[Count];
  86. VertexPositionTexture GetVPT(Triangle triangle, byte i)
  87. {
  88. Vector3 GetVertex(ref Triangle refTriangle, byte j)
  89. {
  90. return DatFile.TransformVertex(vertices[refTriangle.GetIndex(j)], translationPosition, rotation);
  91. }
  92. // begin stupid hacks to fix Rinoa and Ward in remaster. might break when going above 3X upscale
  93. // if you choose to upscale more crop off dead bottom 128 pixels from SE's render.
  94. // also hack adjusts aspect ratio to handle the eye closing animation frames.
  95. // in remaster you just need to shift all the uv's to the right when eyes close.
  96. var (x, y) = preVarTex.ScaleFactor;
  97. float height;
  98. if (x > y)
  99. height = (float)(preVarTex.Height / Math.Floor(y));
  100. else
  101. height = (float)(preVarTex.Height / Math.Floor(x));
  102. var (newAR, oldAR) = ((float)preVarTex.Width / preVarTex.Height,
  103. preVarTex.ClassicWidth / height);
  104. var width = Math.Abs(newAR - oldAR) < float.Epsilon ? preVarTex.ClassicWidth : height * newAR;
  105. // end hack.
  106. return new VertexPositionTexture(GetVertex(ref triangle, i),
  107. triangle.GetUV(i).ToVector2(width, height));
  108. }
  109. tempVPT[0] = GetVPT(this, this[0]);
  110. tempVPT[1] = GetVPT(this, this[1]);
  111. tempVPT[2] = GetVPT(this, this[2]);
  112. return tempVPT;
  113. }
  114. public ushort GetIndex(int i)
  115. {
  116. switch (i)
  117. {
  118. case 0:
  119. return C; //for some reason C is first in triangle
  120. case 1:
  121. return A;
  122. case 2:
  123. return B;
  124. }
  125. throw new IndexOutOfRangeException($"{this} :: 0-2 are only valid values");
  126. }
  127. public UV GetUV(int i)
  128. {
  129. switch (i)
  130. {
  131. case 0:
  132. return Vta;
  133. case 1:
  134. return Vtb;
  135. case 2:
  136. return Vtc;
  137. }
  138. throw new IndexOutOfRangeException($"{this} :: 0-2 are only valid values");
  139. }
  140. #endregion Methods
  141. }
  142. }