BinaryTable.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Numerics;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. namespace SharpGLTF
  9. {
  10. /// <summary>
  11. /// Function for converting data into binary buffers
  12. /// Specs see https://github.com/CesiumGS/3d-tiles/tree/main/specification/Metadata#binary-table-format
  13. /// </summary>
  14. public static class BinaryTable
  15. {
  16. public static List<byte> GetBytesForArray<T>(List<List<T>> values)
  17. {
  18. var bytes = new List<byte>();
  19. foreach (var value in values)
  20. {
  21. var b = GetBytes(value);
  22. bytes.AddRange(b);
  23. }
  24. return bytes;
  25. }
  26. /// <summary>
  27. /// Converts a list of primitive types into a byte array
  28. /// </summary>
  29. /// <typeparam name="T"></typeparam>
  30. /// <param name="values"></param>
  31. /// <returns>byte array</returns>
  32. public static byte[] GetBytes<T>(IReadOnlyList<T> values)
  33. {
  34. Guard.IsTrue(values.Count > 0, nameof(values), "values must have at least one element");
  35. if (typeof(T) == typeof(string))
  36. {
  37. var res = string.Join("", values);
  38. return Encoding.UTF8.GetBytes(res);
  39. }
  40. else if (typeof(T) == typeof(Vector2))
  41. {
  42. return Vector2ToBytes(values);
  43. }
  44. else if (typeof(T) == typeof(Vector3))
  45. {
  46. return Vector3ToBytes(values);
  47. }
  48. else if (typeof(T) == typeof(Vector4))
  49. {
  50. return Vector4ToBytes(values);
  51. }
  52. else if(typeof(T) == typeof(Matrix4x4))
  53. {
  54. return Matrix4x4ToBytes(values);
  55. }
  56. else if (typeof(T).IsPrimitive)
  57. {
  58. if (typeof(T) == typeof(bool))
  59. {
  60. var bits = new BitArray(values.Cast<bool>().ToArray());
  61. byte[] ret = new byte[(bits.Length - 1) / 8 + 1];
  62. bits.CopyTo(ret, 0);
  63. return ret;
  64. }
  65. var size = GetSize<T>();
  66. var result = new byte[values.Count * size];
  67. Buffer.BlockCopy(values.ToArray(), 0, result, 0, result.Length);
  68. return result;
  69. }
  70. else
  71. {
  72. // other types (like datetime, mat2, mat3) are not implemented
  73. // see https://github.com/CesiumGS/3d-tiles/tree/main/specification/Metadata#binary-table-format
  74. throw new NotImplementedException();
  75. }
  76. }
  77. private static byte[] Matrix4x4ToBytes<T>(IReadOnlyList<T> values)
  78. {
  79. var result = new byte[values.Count * 64];
  80. for (int i = 0; i < values.Count; i++)
  81. {
  82. var mat = (Matrix4x4)(object)values[i];
  83. Buffer.BlockCopy(BitConverter.GetBytes(mat.M11), 0, result, i * 64, 4);
  84. Buffer.BlockCopy(BitConverter.GetBytes(mat.M12), 0, result, i * 64 + 4, 4);
  85. Buffer.BlockCopy(BitConverter.GetBytes(mat.M13), 0, result, i * 64 + 8, 4);
  86. Buffer.BlockCopy(BitConverter.GetBytes(mat.M14), 0, result, i * 64 + 12, 4);
  87. Buffer.BlockCopy(BitConverter.GetBytes(mat.M21), 0, result, i * 64 + 16, 4);
  88. Buffer.BlockCopy(BitConverter.GetBytes(mat.M22), 0, result, i * 64 + 20, 4);
  89. Buffer.BlockCopy(BitConverter.GetBytes(mat.M23), 0, result, i * 64 + 24, 4);
  90. Buffer.BlockCopy(BitConverter.GetBytes(mat.M24), 0, result, i * 64 + 28, 4);
  91. Buffer.BlockCopy(BitConverter.GetBytes(mat.M31), 0, result, i * 64 + 32, 4);
  92. Buffer.BlockCopy(BitConverter.GetBytes(mat.M32), 0, result, i * 64 + 36, 4);
  93. Buffer.BlockCopy(BitConverter.GetBytes(mat.M33), 0, result, i * 64 + 40, 4);
  94. Buffer.BlockCopy(BitConverter.GetBytes(mat.M34), 0, result, i * 64 + 44, 4);
  95. Buffer.BlockCopy(BitConverter.GetBytes(mat.M41), 0, result, i * 64 + 48, 4);
  96. Buffer.BlockCopy(BitConverter.GetBytes(mat.M42), 0, result, i * 64 + 52, 4);
  97. Buffer.BlockCopy(BitConverter.GetBytes(mat.M43), 0, result, i * 64 + 56, 4);
  98. Buffer.BlockCopy(BitConverter.GetBytes(mat.M44), 0, result, i * 64 + 60, 4);
  99. }
  100. return result;
  101. }
  102. private static byte[] Vector2ToBytes<T>(IReadOnlyList<T> values)
  103. {
  104. var result = new byte[values.Count * 8];
  105. for (int i = 0; i < values.Count; i++)
  106. {
  107. var vec = (Vector2)(object)values[i];
  108. Buffer.BlockCopy(BitConverter.GetBytes(vec.X), 0, result, i * 8, 4);
  109. Buffer.BlockCopy(BitConverter.GetBytes(vec.Y), 0, result, i * 8 + 4, 4);
  110. }
  111. return result;
  112. }
  113. private static byte[] Vector3ToBytes<T>(IReadOnlyList<T> values)
  114. {
  115. var result = new byte[values.Count * 12];
  116. for (int i = 0; i < values.Count; i++)
  117. {
  118. var vec = (Vector3)(object)values[i];
  119. Buffer.BlockCopy(BitConverter.GetBytes(vec.X), 0, result, i * 12, 4);
  120. Buffer.BlockCopy(BitConverter.GetBytes(vec.Y), 0, result, i * 12 + 4, 4);
  121. Buffer.BlockCopy(BitConverter.GetBytes(vec.Z), 0, result, i * 12 + 8, 4);
  122. }
  123. return result;
  124. }
  125. private static byte[] Vector4ToBytes<T>(IReadOnlyList<T> values)
  126. {
  127. var result = new byte[values.Count * 16];
  128. for (int i = 0; i < values.Count; i++)
  129. {
  130. var vec = (Vector4)(object)values[i];
  131. Buffer.BlockCopy(BitConverter.GetBytes(vec.X), 0, result, i * 16, 4);
  132. Buffer.BlockCopy(BitConverter.GetBytes(vec.Y), 0, result, i * 16 + 4, 4);
  133. Buffer.BlockCopy(BitConverter.GetBytes(vec.Z), 0, result, i * 16 + 8, 4);
  134. Buffer.BlockCopy(BitConverter.GetBytes(vec.W), 0, result, i * 16 + 12, 4);
  135. }
  136. return result;
  137. }
  138. public static List<int> GetStringOffsets(List<string> values)
  139. {
  140. var offsets = new List<int>() { 0 };
  141. foreach (var value in values)
  142. {
  143. var length = Encoding.UTF8.GetBytes(value).Length;
  144. offsets.Add(offsets.Last() + length);
  145. }
  146. return offsets;
  147. }
  148. public static List<int> GetStringOffsets(List<List<string>> values)
  149. {
  150. var offsets = new List<int>() {};
  151. foreach (var arr in values)
  152. {
  153. var arrOffsets = GetStringOffsets(arr);
  154. var last = offsets.LastOrDefault();
  155. foreach (var offset in arrOffsets)
  156. {
  157. if(!offsets.Contains(last + offset))
  158. {
  159. offsets.Add(last + offset);
  160. }
  161. }
  162. }
  163. return offsets;
  164. }
  165. public static List<int> GetArrayOffsets<T>(List<List<T>> values)
  166. {
  167. var offsets = new List<int>() { 0 };
  168. foreach (var value in values)
  169. {
  170. offsets.Add(offsets.Last() + value.Count);
  171. }
  172. return offsets;
  173. }
  174. public static int GetSize<T>()
  175. {
  176. #if NETSTANDARD2_0
  177. var isValueType = typeof(T).IsValueType;
  178. #else
  179. var isValueType = !System.Runtime.CompilerServices.RuntimeHelpers.IsReferenceOrContainsReferences<T>();
  180. #endif
  181. Guard.IsTrue(isValueType, nameof(T), "T must be a value type");
  182. var type = typeof(T);
  183. var size = Marshal.SizeOf<T>();
  184. return size;
  185. }
  186. }
  187. }