GLTFUtilityDracoLoader.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. // Copyright 2017 The Draco Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Linq;
  19. using System.Runtime.InteropServices;
  20. using Unity.Collections.LowLevel.Unsafe;
  21. using UnityEngine;
  22. public unsafe class GLTFUtilityDracoLoader {
  23. // These values must be exactly the same as the values in draco_types.h.
  24. // Attribute data type.
  25. enum DataType {
  26. DT_INVALID = 0,
  27. DT_INT8,
  28. DT_UINT8,
  29. DT_INT16,
  30. DT_UINT16,
  31. DT_INT32,
  32. DT_UINT32,
  33. DT_INT64,
  34. DT_UINT64,
  35. DT_FLOAT32,
  36. DT_FLOAT64,
  37. DT_BOOL
  38. }
  39. // These values must be exactly the same as the values in
  40. // geometry_attribute.h.
  41. // Attribute type.
  42. enum AttributeType {
  43. INVALID = -1,
  44. POSITION = 0,
  45. NORMAL = 1,
  46. COLOR = 2,
  47. TEX_COORD = 3,
  48. GENERIC = 4
  49. }
  50. // The order must be consistent with C++ interface.
  51. [StructLayout(LayoutKind.Sequential)] public struct DracoData {
  52. public int dataType;
  53. public IntPtr data;
  54. }
  55. [StructLayout(LayoutKind.Sequential)] public struct DracoAttribute {
  56. public int attributeType;
  57. public int dataType;
  58. public int numComponents;
  59. public int uniqueId;
  60. }
  61. [StructLayout(LayoutKind.Sequential)] public struct DracoMesh {
  62. public int numFaces;
  63. public int numVertices;
  64. public int numAttributes;
  65. }
  66. public struct MeshAttributes {
  67. public int pos, norms, uv, joints, weights, col;
  68. public MeshAttributes(int pos, int norms, int uv, int joints, int weights, int col) {
  69. this.pos = pos;
  70. this.norms = norms;
  71. this.uv = uv;
  72. this.joints = joints;
  73. this.weights = weights;
  74. this.col = col;
  75. }
  76. }
  77. [StructLayout(LayoutKind.Sequential)] public struct Vector4<T> where T : struct {
  78. public T x;
  79. public T y;
  80. public T z;
  81. public T w;
  82. }
  83. public class AsyncMesh {
  84. public int[] tris;
  85. public Vector3[] verts;
  86. public Vector2[] uv;
  87. public Vector3[] norms;
  88. public BoneWeight[] boneWeights;
  89. public Color[] colors;
  90. }
  91. #if !UNITY_EDITOR && (UNITY_WEBGL || UNITY_IOS)
  92. const string DRACODEC_UNITY_LIB = "__Internal";
  93. #elif UNITY_ANDROID || UNITY_STANDALONE || UNITY_WSA || UNITY_EDITOR || PLATFORM_LUMIN
  94. const string DRACODEC_UNITY_LIB = "dracodec_unity";
  95. #endif
  96. // Release data associated with DracoMesh.
  97. [DllImport(DRACODEC_UNITY_LIB)] private static extern void ReleaseDracoMesh(
  98. DracoMesh * * mesh);
  99. // Release data associated with DracoAttribute.
  100. [DllImport(DRACODEC_UNITY_LIB)] private static extern void
  101. ReleaseDracoAttribute(DracoAttribute * * attr);
  102. // Release attribute data.
  103. [DllImport(DRACODEC_UNITY_LIB)] private static extern void ReleaseDracoData(
  104. DracoData * * data);
  105. // Decodes compressed Draco::Mesh in buffer to mesh. On input, mesh
  106. // must be null. The returned mesh must released with ReleaseDracoMesh.
  107. [DllImport(DRACODEC_UNITY_LIB)] private static extern int DecodeDracoMesh(
  108. byte[] buffer, int length, DracoMesh * * mesh);
  109. // Returns the DracoAttribute at index in mesh. On input, attribute must be
  110. // null. The returned attr must be released with ReleaseDracoAttribute.
  111. [DllImport(DRACODEC_UNITY_LIB)] private static extern bool GetAttribute(
  112. DracoMesh * mesh, int index, DracoAttribute * * attr);
  113. // Returns the DracoAttribute of type at index in mesh. On input, attribute
  114. // must be null. E.g. If the mesh has two texture coordinates then
  115. // GetAttributeByType(mesh, AttributeType.TEX_COORD, 1, &attr); will return
  116. // the second TEX_COORD attribute. The returned attr must be released with
  117. // ReleaseDracoAttribute.
  118. [DllImport(DRACODEC_UNITY_LIB)] private static extern bool GetAttributeByType(
  119. DracoMesh * mesh, AttributeType type, int index, DracoAttribute * * attr);
  120. // Returns the DracoAttribute with unique_id in mesh. On input, attribute
  121. // must be null.The returned attr must be released with
  122. // ReleaseDracoAttribute.
  123. [DllImport(DRACODEC_UNITY_LIB)] private static extern bool
  124. GetAttributeByUniqueId(DracoMesh * mesh, int unique_id,
  125. DracoAttribute * * attr);
  126. // Returns an array of indices as well as the type of data in data_type. On
  127. // input, indices must be null. The returned indices must be released with
  128. // ReleaseDracoData.
  129. [DllImport(DRACODEC_UNITY_LIB)] private static extern bool GetMeshIndices(
  130. DracoMesh * mesh, DracoData * * indices);
  131. // Returns an array of attribute data as well as the type of data in
  132. // data_type. On input, data must be null. The returned data must be
  133. // released with ReleaseDracoData.
  134. [DllImport(DRACODEC_UNITY_LIB)] private static extern bool GetAttributeData(
  135. DracoMesh * mesh, DracoAttribute * attr, DracoData * * data);
  136. // Decodes a Draco mesh, creates a Unity mesh from the decoded data and
  137. // adds the Unity mesh to meshes. encodedData is the compressed Draco mesh.
  138. public unsafe AsyncMesh LoadMesh(byte[] encodedData, MeshAttributes attributes) {
  139. DracoMesh * mesh = null;
  140. if (DecodeDracoMesh(encodedData, encodedData.Length, & mesh) <= 0) {
  141. Debug.Log("Failed: Decoding error.");
  142. return null;
  143. }
  144. AsyncMesh unityMesh = CreateAsyncMesh(mesh, attributes);
  145. int numFaces = mesh -> numFaces;
  146. ReleaseDracoMesh( & mesh);
  147. if (numFaces > 0) return unityMesh;
  148. else return null;
  149. }
  150. // Creates a Unity mesh from the decoded Draco mesh.
  151. public unsafe AsyncMesh CreateAsyncMesh(DracoMesh * dracoMesh, MeshAttributes attributes) {
  152. int numFaces = dracoMesh -> numFaces;
  153. AsyncMesh mesh = new AsyncMesh();
  154. mesh.tris = new int[dracoMesh -> numFaces * 3];
  155. mesh.verts = new Vector3[dracoMesh -> numVertices];
  156. // Copy face indices.
  157. DracoData * indicesData;
  158. GetMeshIndices(dracoMesh, & indicesData);
  159. int elementSize =
  160. DataTypeSize((GLTFUtilityDracoLoader.DataType) indicesData -> dataType);
  161. int * indices = (int * ) (indicesData -> data);
  162. var indicesPtr = UnsafeUtility.AddressOf(ref mesh.tris[0]);
  163. UnsafeUtility.MemCpy(indicesPtr, indices,
  164. mesh.tris.Length * elementSize);
  165. ReleaseDracoData( & indicesData);
  166. DracoAttribute * attr = null;
  167. // Copy positions.
  168. if (GetAttributeByUniqueId(dracoMesh, attributes.pos, & attr)) {
  169. DracoData * posData = null;
  170. GetAttributeData(dracoMesh, attr, & posData);
  171. elementSize = DataTypeSize((GLTFUtilityDracoLoader.DataType) posData -> dataType) *
  172. attr -> numComponents;
  173. var newVerticesPtr = UnsafeUtility.AddressOf(ref mesh.verts[0]);
  174. UnsafeUtility.MemCpy(newVerticesPtr, (void * ) posData -> data,
  175. dracoMesh -> numVertices * elementSize);
  176. ReleaseDracoData( & posData);
  177. ReleaseDracoAttribute( & attr);
  178. }
  179. // Copy normals.
  180. if (GetAttributeByUniqueId(dracoMesh, attributes.norms, & attr)) {
  181. DracoData * normData = null;
  182. if (GetAttributeData(dracoMesh, attr, & normData)) {
  183. elementSize =
  184. DataTypeSize((GLTFUtilityDracoLoader.DataType) normData -> dataType) *
  185. attr -> numComponents;
  186. mesh.norms = new Vector3[dracoMesh -> numVertices];
  187. var newNormalsPtr = UnsafeUtility.AddressOf(ref mesh.norms[0]);
  188. UnsafeUtility.MemCpy(newNormalsPtr, (void * ) normData -> data,
  189. dracoMesh -> numVertices * elementSize);
  190. ReleaseDracoData( & normData);
  191. ReleaseDracoAttribute( & attr);
  192. }
  193. }
  194. // Copy texture coordinates.
  195. if (GetAttributeByUniqueId(dracoMesh, attributes.uv, & attr)) {
  196. DracoData * texData = null;
  197. if (GetAttributeData(dracoMesh, attr, & texData)) {
  198. elementSize =
  199. DataTypeSize((GLTFUtilityDracoLoader.DataType) texData -> dataType) *
  200. attr -> numComponents;
  201. mesh.uv = new Vector2[dracoMesh -> numVertices];
  202. var newUVsPtr = UnsafeUtility.AddressOf(ref mesh.uv[0]);
  203. UnsafeUtility.MemCpy(newUVsPtr, (void * ) texData -> data,
  204. dracoMesh -> numVertices * elementSize);
  205. ReleaseDracoData( & texData);
  206. ReleaseDracoAttribute( & attr);
  207. }
  208. }
  209. // Copy colors.
  210. if (GetAttributeByUniqueId(dracoMesh, attributes.col, & attr)) {
  211. DracoData * colorData = null;
  212. if (GetAttributeData(dracoMesh, attr, & colorData)) {
  213. elementSize =
  214. DataTypeSize((GLTFUtilityDracoLoader.DataType) colorData -> dataType) *
  215. attr -> numComponents;
  216. mesh.colors = new Color[dracoMesh -> numVertices];
  217. var newColorsPtr = UnsafeUtility.AddressOf(ref mesh.colors[0]);
  218. UnsafeUtility.MemCpy(newColorsPtr, (void * ) colorData -> data,
  219. dracoMesh -> numVertices * elementSize);
  220. ReleaseDracoData( & colorData);
  221. ReleaseDracoAttribute( & attr);
  222. }
  223. }
  224. // Copy weights.
  225. Vector4[] weights = null;
  226. if (GetAttributeByUniqueId(dracoMesh, attributes.weights, & attr)) {
  227. DracoData * weightData = null;
  228. if (GetAttributeData(dracoMesh, attr, & weightData)) {
  229. elementSize =
  230. DataTypeSize((GLTFUtilityDracoLoader.DataType) weightData -> dataType) *
  231. attr -> numComponents;
  232. if (attr -> dataType == 9) {
  233. weights = new Vector4[dracoMesh -> numVertices];
  234. var newWeightsPtr = UnsafeUtility.AddressOf(ref weights[0]);
  235. UnsafeUtility.MemCpy(newWeightsPtr, (void * ) weightData -> data,
  236. dracoMesh -> numVertices * elementSize);
  237. } else if (attr -> dataType == 4) {
  238. var newWeightsInt = new Vector4<UInt16>[dracoMesh -> numVertices];
  239. var newWeightsPtr = UnsafeUtility.AddressOf(ref newWeightsInt[0]);
  240. UnsafeUtility.MemCpy(newWeightsPtr, (void * ) weightData -> data,
  241. dracoMesh -> numVertices * elementSize);
  242. weights = newWeightsInt.Select(x => new Vector4(x.x, x.y, x.z, x.w)).ToArray();
  243. }
  244. ReleaseDracoData( & weightData);
  245. ReleaseDracoAttribute( & attr);
  246. }
  247. }
  248. // Copy joints.
  249. Vector4[] joints = null;
  250. if (GetAttributeByUniqueId(dracoMesh, attributes.joints, & attr)) {
  251. DracoData * jointData = null;
  252. if (GetAttributeData(dracoMesh, attr, & jointData)) {
  253. elementSize =
  254. DataTypeSize((GLTFUtilityDracoLoader.DataType) jointData -> dataType) *
  255. attr -> numComponents;
  256. if (attr -> dataType == 9) {
  257. joints = new Vector4[dracoMesh -> numVertices];
  258. var newJointsPtr = UnsafeUtility.AddressOf(ref joints[0]);
  259. UnsafeUtility.MemCpy(newJointsPtr, (void * ) jointData -> data,
  260. dracoMesh -> numVertices * elementSize);
  261. } else if (attr -> dataType == 4) {
  262. var newJointsInt = new Vector4<UInt16>[dracoMesh -> numVertices];
  263. var newJointsPtr = UnsafeUtility.AddressOf(ref newJointsInt[0]);
  264. UnsafeUtility.MemCpy(newJointsPtr, (void * ) jointData -> data,
  265. dracoMesh -> numVertices * elementSize);
  266. joints = newJointsInt.Select(x => new Vector4(x.x, x.y, x.z, x.w)).ToArray();
  267. }
  268. ReleaseDracoData( & jointData);
  269. ReleaseDracoAttribute( & attr);
  270. }
  271. }
  272. /* #if UNITY_2017_3_OR_NEWER
  273. mesh.indexFormat = (newVertices.Length > System.UInt16.MaxValue) ?
  274. UnityEngine.Rendering.IndexFormat.UInt32 :
  275. UnityEngine.Rendering.IndexFormat.UInt16;
  276. #else
  277. if (newVertices.Length > System.UInt16.MaxValue) {
  278. throw new System.Exception("Draco meshes with more than 65535 vertices are only supported from Unity 2017.3 onwards.");
  279. }
  280. #endif */
  281. if (joints != null && weights != null) {
  282. if (joints.Length == weights.Length) {
  283. BoneWeight[] boneWeights = new BoneWeight[weights.Length];
  284. for (int k = 0; k < boneWeights.Length; k++) {
  285. NormalizeWeights(ref weights[k]);
  286. boneWeights[k].weight0 = weights[k].x;
  287. boneWeights[k].weight1 = weights[k].y;
  288. boneWeights[k].weight2 = weights[k].z;
  289. boneWeights[k].weight3 = weights[k].w;
  290. boneWeights[k].boneIndex0 = Mathf.RoundToInt(joints[k].x);
  291. boneWeights[k].boneIndex1 = Mathf.RoundToInt(joints[k].y);
  292. boneWeights[k].boneIndex2 = Mathf.RoundToInt(joints[k].z);
  293. boneWeights[k].boneIndex3 = Mathf.RoundToInt(joints[k].w);
  294. }
  295. mesh.boneWeights = boneWeights;
  296. } else Debug.LogWarning("Draco: joints and weights not same length. Skipped");
  297. }
  298. return mesh;
  299. }
  300. public void NormalizeWeights(ref Vector4 weights) {
  301. float total = weights.x + weights.y + weights.z + weights.w;
  302. if (total == 0) return;
  303. float mult = 1f / total;
  304. weights.x *= mult;
  305. weights.y *= mult;
  306. weights.z *= mult;
  307. weights.w *= mult;
  308. }
  309. private int DataTypeSize(DataType dt) {
  310. switch (dt) {
  311. case DataType.DT_INT8:
  312. case DataType.DT_UINT8:
  313. return 1;
  314. case DataType.DT_INT16:
  315. case DataType.DT_UINT16:
  316. return 2;
  317. case DataType.DT_INT32:
  318. case DataType.DT_UINT32:
  319. return 4;
  320. case DataType.DT_INT64:
  321. case DataType.DT_UINT64:
  322. return 8;
  323. case DataType.DT_FLOAT32:
  324. return 4;
  325. case DataType.DT_FLOAT64:
  326. return 8;
  327. case DataType.DT_BOOL:
  328. return 1;
  329. default:
  330. return -1;
  331. }
  332. }
  333. }