Tile.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. namespace OpenVIII.Fields
  7. {
  8. public partial class Background
  9. {
  10. #region Classes
  11. public sealed class Tile
  12. {
  13. #region Fields
  14. public const int Size = 16;
  15. /// <summary>
  16. /// Size of Texture Segment
  17. /// </summary>
  18. public static readonly Vector2 TextureSize = new Vector2(FourBitTexturePageWidth, FourBitTexturePageWidth);
  19. public byte AnimationID = 0xFF;
  20. public byte AnimationState;
  21. public byte Blend;
  22. public BlendMode BlendMode = BlendMode.None;
  23. public Bppflag Depth;
  24. /// <summary>
  25. /// bit is either 1 or 0. if 0 don't draw tile.
  26. /// </summary>
  27. /// <see cref="https://github.com/myst6re/deling/blob/develop/files/BackgroundFile.h#L49"/>
  28. public bool Draw = true;
  29. /// <summary>
  30. /// Layer ID, Used to control which parts draw.
  31. /// </summary>
  32. public byte LayerID;
  33. /// <summary>
  34. /// Gets +1 if Overlaps() == true;
  35. /// </summary>
  36. public byte OverLapID = 0;
  37. public byte PaletteID;
  38. /// <summary>
  39. /// for outputting the source tiles to texture pages. some tiles have the same source rectangle. So skip.
  40. /// </summary>
  41. public bool Skip = false;
  42. /// <summary>
  43. /// Source X from Texture Data
  44. /// </summary>
  45. public ushort SourceX;
  46. //[6-10]
  47. /// <summary>
  48. /// Source Y from Texture Data
  49. /// </summary>
  50. public ushort SourceY;
  51. public byte TextureID;
  52. public int TileID;
  53. /// <summary>
  54. /// Distance from left side
  55. /// </summary>
  56. public short X;
  57. /// <summary>
  58. /// Distance from top side
  59. /// </summary>
  60. public short Y;
  61. /// <summary>
  62. /// Larger number is farther away. So OrderByDescending for draw order.
  63. /// </summary>
  64. public ushort Z;
  65. #endregion Fields
  66. #region Properties
  67. public Rectangle ExpandedSource => new Rectangle(ExpandedSourceX, SourceY, Size, Size);
  68. public int ExpandedSourceX => Is4Bit ? SourceX * 2 : SourceX;
  69. public Rectangle GetRectangle => new Rectangle(X, Y, Size, Size);
  70. public int Height => Size;
  71. public bool Is4Bit => Test4Bit(Depth);
  72. public bool Is8Bit => Test8Bit(Depth);
  73. /// <summary>
  74. /// Pupu goes in a loop and +1 the ID when it detects an overlap.
  75. /// There are some wasted bits
  76. /// </summary>
  77. public uint PupuID { get; set; }
  78. public Rectangle Source => new Rectangle(SourceX, SourceY, Size, Size);
  79. /// <summary>
  80. /// TopLeft UV
  81. /// </summary>
  82. public Vector2 UV => new Vector2(SourceX, SourceY) / TextureSize;
  83. public int Width => Size;
  84. // 4 bits
  85. public float ZFloat => Z / 4096f;
  86. #endregion Properties
  87. #region Methods
  88. /// <summary>
  89. /// TopLeft Cord
  90. /// </summary>
  91. /// <param name="in"></param>
  92. public static implicit operator Vector2(Tile @in) => new Vector2(@in.SourceX, @in.SourceY);
  93. public static implicit operator Vector3(Tile @in) => new Vector3(@in.X, @in.Y, @in.ZFloat);
  94. public static Tile Load(BinaryReader br, int id, byte type)
  95. {
  96. var p = br.BaseStream.Position;
  97. var t = new Tile { X = br.ReadInt16() };
  98. if (t.X == 0x7FFF)
  99. return null;
  100. t.Y = br.ReadInt16();
  101. if (type == 1)
  102. {
  103. t.Z = br.ReadUInt16();
  104. var texIdBuffer = br.ReadUInt16();
  105. t.TextureID = GetTextureID(texIdBuffer);
  106. t.PaletteID = GetPaletteID(br);
  107. t.SourceX = br.ReadByte();
  108. t.SourceY = br.ReadByte();
  109. t.LayerID = GetLayerID(br);
  110. t.BlendMode = (BlendMode)br.ReadByte();
  111. t.AnimationID = br.ReadByte();
  112. t.AnimationState = br.ReadByte();
  113. t.Draw = GetDraw(texIdBuffer);
  114. t.Blend = GetBlend(texIdBuffer);
  115. t.Depth = GetDepth(texIdBuffer);
  116. t.TileID = id;
  117. }
  118. else if (type == 2)
  119. {
  120. t.SourceX = br.ReadUInt16();
  121. t.SourceY = br.ReadUInt16();
  122. t.Z = br.ReadUInt16();
  123. var texIdBuffer = br.ReadUInt16();
  124. t.TextureID = GetTextureID(texIdBuffer);
  125. t.PaletteID = GetPaletteID(br);
  126. t.AnimationID = br.ReadByte();
  127. t.AnimationState = br.ReadByte();
  128. t.Draw = GetDraw(texIdBuffer);
  129. t.Blend = GetBlend(texIdBuffer);
  130. t.Depth = GetDepth(texIdBuffer);
  131. t.TileID = id;
  132. t.BlendMode = (((t.Blend & 1) != 0) ? BlendMode.Add : BlendMode.None);
  133. }
  134. t.GeneratePupu();
  135. Debug.Assert(p - br.BaseStream.Position == -16);
  136. return t;
  137. }
  138. private static byte GetTextureID(ushort texIdBuffer)
  139. {
  140. return (byte)(texIdBuffer & 0xF);
  141. }
  142. public static bool Test16Bit(Bppflag depth) => depth.HasFlag(Bppflag._16bpp) && !depth.HasFlag(Bppflag._8bpp);
  143. public static bool Test4Bit(Bppflag depth) => depth == 0;
  144. public static bool Test8Bit(Bppflag depth) => !depth.HasFlag(Bppflag._16bpp) && depth.HasFlag(Bppflag._8bpp);
  145. /*
  146. public bool Is16Bit => Test16Bit(Depth);
  147. */
  148. /*
  149. public byte SourceOverLapID { get; internal set; }
  150. */
  151. public VertexPositionTexture[] GetQuad(float scale)
  152. {
  153. var vertexPositionTextures = GetCorners(scale); // 4 unique corners.
  154. //create 2 triangles
  155. var r = new List<VertexPositionTexture>
  156. {
  157. vertexPositionTextures[3],
  158. vertexPositionTextures[1],
  159. vertexPositionTextures[0],
  160. vertexPositionTextures[3],
  161. vertexPositionTextures[2],
  162. vertexPositionTextures[1],
  163. };
  164. return r.ToArray();
  165. }
  166. public bool Intersect(Tile tile, bool rev = false)
  167. {
  168. var flip = !rev && tile.Intersect(this, true);
  169. var ret = flip ||
  170. X >= tile.X &&
  171. X < tile.X + Size &&
  172. Y >= tile.Y &&
  173. Y < tile.Y + Size;// &&
  174. //Z == tile.Z &&
  175. //LayerID == tile.LayerID &&
  176. //BlendMode == tile.BlendMode &&
  177. //AnimationID == tile.AnimationID &&
  178. //AnimationState == tile.AnimationState;
  179. return ret;
  180. }
  181. public override string ToString() =>
  182. $"Tile: {TileID}; " +
  183. $"Loc: {GetRectangle}; " +
  184. $"Z: {Z}; " +
  185. $"Source: {ExpandedSource}; " +
  186. $"TextureID: {TextureID}; " +
  187. $"PaletteID: {PaletteID}; " +
  188. $"LayerID: {LayerID}; " +
  189. $"BlendMode: {BlendMode}; " +
  190. $"AnimationID: {AnimationID}; " +
  191. $"AnimationState: {AnimationState}; " +
  192. $"4 bit? {Is4Bit}";
  193. private static byte GetBlend(ushort texIdBuffer) => (byte)((texIdBuffer >> 5) & 0x3);
  194. private static Bppflag GetDepth(ushort texIdBuffer) => (Bppflag)(texIdBuffer >> 7 & 0x3);
  195. private static bool GetDraw(ushort texIdBuffer) => ((texIdBuffer >> 4) & 0x1) != 0;
  196. private static byte GetLayerID(BinaryReader br) => (byte)(br.ReadByte() >> 1);
  197. private static byte GetPaletteID(BinaryReader br) => (byte)((br.ReadUInt16() >> 6) & 0xF);
  198. /*
  199. public bool SourceIntersect(Tile tile)
  200. =>
  201. Rectangle.Intersect(ExpandedSource, tile.ExpandedSource) != Rectangle.Empty;
  202. */
  203. private void GeneratePupu()
  204. {
  205. const int bitsPerLong = sizeof(ulong) * 8;
  206. const int bitsPerByte = sizeof(byte) * 8;
  207. const int bitsPerNibble = bitsPerByte / 2;
  208. var bits = bitsPerLong;
  209. bits -= bitsPerNibble;
  210. PupuID = (((uint)LayerID & 0xF) << bits);
  211. bits -= bitsPerNibble;
  212. PupuID += (((uint)BlendMode & 0xF) << bits);
  213. bits -= bitsPerByte;
  214. PupuID += ((uint)AnimationID << bits);
  215. bits -= bitsPerByte;
  216. PupuID += ((uint)(AnimationState) << bits);
  217. Debug.Assert(((PupuID & 0xF0000000) >> 28) == LayerID);
  218. Debug.Assert((BlendMode)((PupuID & 0x0F000000) >> 24) == BlendMode);
  219. Debug.Assert(((PupuID & 0x00FF0000) >> 16) == AnimationID);
  220. Debug.Assert(((PupuID & 0x0000FF00) >> 8) == AnimationState);
  221. }
  222. private List<VertexPositionTexture> GetCorners(float scale)
  223. {
  224. var sizeVertex = new Vector2(Size, Size) / scale;
  225. var sizeUV = new Vector2(Size) / TextureSize;
  226. var r = new List<VertexPositionTexture>(4);
  227. for (var i = 0; i < r.Capacity; i++)
  228. {
  229. var vpt = new VertexPositionTexture(((Vector3)this) / scale, UV);
  230. switch (i)
  231. {
  232. case 0://top left
  233. break;
  234. case 1://top right
  235. vpt.Position.X += sizeVertex.X;
  236. break;
  237. case 2://bottom right
  238. vpt.Position.X += sizeVertex.X;
  239. vpt.Position.Y += sizeVertex.Y;
  240. break;
  241. case 3://bottom left
  242. vpt.Position.Y += sizeVertex.Y;
  243. break;
  244. }
  245. switch (i)
  246. {
  247. case 0://top left
  248. break;
  249. case 1://top right
  250. vpt.TextureCoordinate.X += sizeUV.X;
  251. break;
  252. case 2://bottom right
  253. vpt.TextureCoordinate.X += sizeUV.X;
  254. vpt.TextureCoordinate.Y += sizeUV.Y;
  255. break;
  256. case 3://bottom left
  257. vpt.TextureCoordinate.Y += sizeUV.Y;
  258. break;
  259. }
  260. var vectorFlip = new Vector3(-1, -1, 1);
  261. vpt.Position *= vectorFlip;
  262. r.Add(vpt);
  263. }
  264. return r;
  265. }
  266. #endregion Methods
  267. }
  268. #endregion Classes
  269. }
  270. }