GLTFMesh.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using Newtonsoft.Json;
  7. using UnityEngine;
  8. using UnityEngine.Scripting;
  9. namespace Siccity.GLTFUtility {
  10. // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#mesh
  11. [Preserve] public class GLTFMesh {
  12. #region Serialization
  13. [JsonProperty(Required = Required.Always)] public List<GLTFPrimitive> primitives;
  14. /// <summary> Morph target weights </summary>
  15. public List<float> weights;
  16. public string name;
  17. public Extras extras;
  18. public class Extras {
  19. /// <summary>
  20. /// Morph target names. Not part of the official spec, but pretty much a standard.
  21. /// Discussed here https://github.com/KhronosGroup/glTF/issues/1036
  22. /// </summary>
  23. public string[] targetNames;
  24. }
  25. #endregion
  26. #region Import
  27. public class ImportResult {
  28. public Material[] materials;
  29. public Mesh mesh;
  30. }
  31. public class ImportTask : Importer.ImportTask<ImportResult[]> {
  32. private class MeshData {
  33. string name;
  34. List<Vector3> normals = new List<Vector3>();
  35. List<List<int>> submeshTris = new List<List<int>>();
  36. List<RenderingMode> submeshTrisMode = new List<RenderingMode>();
  37. List<Vector3> verts = new List<Vector3>();
  38. List<Vector4> tangents = new List<Vector4>();
  39. List<Color> colors = new List<Color>();
  40. List<BoneWeight> weights = null;
  41. List<Vector2> uv1 = null;
  42. List<Vector2> uv2 = null;
  43. List<Vector2> uv3 = null;
  44. List<Vector2> uv4 = null;
  45. List<Vector2> uv5 = null;
  46. List<Vector2> uv6 = null;
  47. List<Vector2> uv7 = null;
  48. List<Vector2> uv8 = null;
  49. List<BlendShape> blendShapes = new List<BlendShape>();
  50. List<int> submeshVertexStart = new List<int>();
  51. private class BlendShape {
  52. public string name;
  53. public Vector3[] pos, norm, tan;
  54. }
  55. public MeshData(GLTFMesh gltfMesh, GLTFAccessor.ImportResult[] accessors, GLTFBufferView.ImportResult[] bufferViews) {
  56. name = gltfMesh.name;
  57. if (gltfMesh.primitives.Count == 0) {
  58. Debug.LogWarning("0 primitives in mesh");
  59. } else {
  60. for (int i = 0; i < gltfMesh.primitives.Count; i++) {
  61. GLTFPrimitive primitive = gltfMesh.primitives[i];
  62. // Load draco mesh
  63. if (primitive.extensions != null && primitive.extensions.KHR_draco_mesh_compression != null) {
  64. GLTFPrimitive.DracoMeshCompression draco = primitive.extensions.KHR_draco_mesh_compression;
  65. GLTFBufferView.ImportResult bufferView = bufferViews[draco.bufferView];
  66. GLTFUtilityDracoLoader loader = new GLTFUtilityDracoLoader();
  67. byte[] buffer = new byte[bufferView.byteLength];
  68. bufferView.stream.Seek(bufferView.byteOffset, System.IO.SeekOrigin.Begin);
  69. bufferView.stream.Read(buffer, 0, bufferView.byteLength);
  70. GLTFUtilityDracoLoader.MeshAttributes attribs = new GLTFUtilityDracoLoader.MeshAttributes(
  71. primitive.extensions.KHR_draco_mesh_compression.attributes.POSITION ?? -1,
  72. primitive.extensions.KHR_draco_mesh_compression.attributes.NORMAL ?? -1,
  73. primitive.extensions.KHR_draco_mesh_compression.attributes.TEXCOORD_0 ?? -1,
  74. primitive.extensions.KHR_draco_mesh_compression.attributes.JOINTS_0 ?? -1,
  75. primitive.extensions.KHR_draco_mesh_compression.attributes.WEIGHTS_0 ?? -1,
  76. primitive.extensions.KHR_draco_mesh_compression.attributes.COLOR_0 ?? -1
  77. );
  78. //Mesh mesh = loader.LoadMesh(buffer, attribs);
  79. GLTFUtilityDracoLoader.AsyncMesh asyncMesh = loader.LoadMesh(buffer, attribs);
  80. if (asyncMesh == null) Debug.LogWarning("Draco mesh couldn't be loaded");
  81. submeshTrisMode.Add(primitive.mode);
  82. // Tris
  83. int vertCount = verts.Count();
  84. submeshTris.Add(asyncMesh.tris.Reverse().Select(x => x + vertCount).ToList());
  85. verts.AddRange(asyncMesh.verts.Select(x => new Vector3(-x.x, x.y, x.z)));
  86. if (asyncMesh.norms != null) {
  87. normals.AddRange(asyncMesh.norms.Select(v => { v.x = -v.x; return v; }));
  88. }
  89. //tangents.AddRange(asyncMesh.tangents.Select(v => { v.y = -v.y; v.z = -v.z; return v; }));
  90. // Weights
  91. if (asyncMesh.boneWeights != null) {
  92. if (weights == null) weights = new List<BoneWeight>();
  93. weights.AddRange(asyncMesh.boneWeights);
  94. }
  95. // BlendShapes not supported yet
  96. /* for (int k = 0; k < mesh.blendShapeCount; k++) {
  97. int frameCount = mesh.GetBlendShapeFrameCount(k);
  98. BlendShape blendShape = new BlendShape();
  99. blendShape.pos = new Vector3[frameCount];
  100. blendShape.norm = new Vector3[frameCount];
  101. blendShape.tan = new Vector3[frameCount];
  102. for (int o = 0; o < frameCount; o++) {
  103. mesh.GetBlendShapeFrameVertices(k, o, blendShape.pos, blendShape.norm, blendShape.tan);
  104. }
  105. blendShapes.Add(blendShape);
  106. } */
  107. // UVs
  108. if (asyncMesh.uv != null) {
  109. if (uv1 == null) uv1 = new List<Vector2>();
  110. uv1.AddRange(asyncMesh.uv.Select(x => new Vector2(x.x, -x.y)));
  111. }
  112. }
  113. // Load normal mesh
  114. else {
  115. int vertStartIndex = verts.Count;
  116. submeshVertexStart.Add(vertStartIndex);
  117. // Verts - (X points left in GLTF)
  118. if (primitive.attributes.POSITION.HasValue) {
  119. IEnumerable<Vector3> newVerts = accessors[primitive.attributes.POSITION.Value].ReadVec3(true).Select(v => { v.x = -v.x; return v; });
  120. verts.AddRange(newVerts);
  121. }
  122. int vertCount = verts.Count;
  123. // Tris - (Invert all triangles. Instead of flipping each triangle, just flip the entire array. Much easier)
  124. if (primitive.indices.HasValue) {
  125. submeshTris.Add(new List<int>(accessors[primitive.indices.Value].ReadInt().Reverse().Select(x => x + vertStartIndex)));
  126. submeshTrisMode.Add(primitive.mode);
  127. }
  128. /// Normals - (X points left in GLTF)
  129. if (primitive.attributes.NORMAL.HasValue) {
  130. normals.AddRange(accessors[primitive.attributes.NORMAL.Value].ReadVec3(true).Select(v => { v.x = -v.x; return v; }));
  131. }
  132. // Tangents - (X points left in GLTF)
  133. if (primitive.attributes.TANGENT.HasValue) {
  134. tangents.AddRange(accessors[primitive.attributes.TANGENT.Value].ReadVec4(true).Select(v => { v.y = -v.y; v.z = -v.z; return v; }));
  135. }
  136. // Vertex colors
  137. if (primitive.attributes.COLOR_0.HasValue) {
  138. colors.AddRange(accessors[primitive.attributes.COLOR_0.Value].ReadColor());
  139. }
  140. // Weights
  141. if (primitive.attributes.WEIGHTS_0.HasValue && primitive.attributes.JOINTS_0.HasValue) {
  142. Vector4[] weights0 = accessors[primitive.attributes.WEIGHTS_0.Value].ReadVec4(true);
  143. Vector4[] joints0 = accessors[primitive.attributes.JOINTS_0.Value].ReadVec4();
  144. if (joints0.Length == weights0.Length) {
  145. BoneWeight[] boneWeights = new BoneWeight[weights0.Length];
  146. for (int k = 0; k < boneWeights.Length; k++) {
  147. NormalizeWeights(ref weights0[k]);
  148. boneWeights[k].weight0 = weights0[k].x;
  149. boneWeights[k].weight1 = weights0[k].y;
  150. boneWeights[k].weight2 = weights0[k].z;
  151. boneWeights[k].weight3 = weights0[k].w;
  152. boneWeights[k].boneIndex0 = Mathf.RoundToInt(joints0[k].x);
  153. boneWeights[k].boneIndex1 = Mathf.RoundToInt(joints0[k].y);
  154. boneWeights[k].boneIndex2 = Mathf.RoundToInt(joints0[k].z);
  155. boneWeights[k].boneIndex3 = Mathf.RoundToInt(joints0[k].w);
  156. }
  157. if (weights == null) weights = new List<BoneWeight>(new BoneWeight[vertCount - boneWeights.Length]);
  158. weights.AddRange(boneWeights);
  159. } else Debug.LogWarning("WEIGHTS_0 and JOINTS_0 not same length. Skipped");
  160. } else {
  161. if (weights != null) weights.AddRange(new BoneWeight[vertCount - weights.Count]);
  162. }
  163. // UVs
  164. ReadUVs(ref uv1, accessors, primitive.attributes.TEXCOORD_0, vertCount);
  165. ReadUVs(ref uv2, accessors, primitive.attributes.TEXCOORD_1, vertCount);
  166. ReadUVs(ref uv3, accessors, primitive.attributes.TEXCOORD_2, vertCount);
  167. ReadUVs(ref uv4, accessors, primitive.attributes.TEXCOORD_3, vertCount);
  168. ReadUVs(ref uv5, accessors, primitive.attributes.TEXCOORD_4, vertCount);
  169. ReadUVs(ref uv6, accessors, primitive.attributes.TEXCOORD_5, vertCount);
  170. ReadUVs(ref uv7, accessors, primitive.attributes.TEXCOORD_6, vertCount);
  171. ReadUVs(ref uv8, accessors, primitive.attributes.TEXCOORD_7, vertCount);
  172. }
  173. }
  174. bool hasTargetNames = gltfMesh.extras != null && gltfMesh.extras.targetNames != null;
  175. if (hasTargetNames) {
  176. if (gltfMesh.primitives.All(x => x.targets.Count != gltfMesh.extras.targetNames.Length)) {
  177. Debug.LogWarning("Morph target names found in mesh " + name + " but array length does not match primitive morph target array length");
  178. hasTargetNames = false;
  179. }
  180. }
  181. // Read blend shapes after knowing final vertex count
  182. int finalVertCount = verts.Count;
  183. for (int i = 0; i < gltfMesh.primitives.Count; i++) {
  184. GLTFPrimitive primitive = gltfMesh.primitives[i];
  185. if (primitive.targets != null) {
  186. for (int k = 0; k < primitive.targets.Count; k++) {
  187. BlendShape blendShape = new BlendShape();
  188. blendShape.pos = GetMorphWeights(primitive.targets[k].POSITION, submeshVertexStart[i], finalVertCount, accessors);
  189. blendShape.norm = GetMorphWeights(primitive.targets[k].NORMAL, submeshVertexStart[i], finalVertCount, accessors);
  190. blendShape.tan = GetMorphWeights(primitive.targets[k].TANGENT, submeshVertexStart[i], finalVertCount, accessors);
  191. if (hasTargetNames) blendShape.name = gltfMesh.extras.targetNames[k];
  192. else blendShape.name = "morph-" + blendShapes.Count;
  193. blendShapes.Add(blendShape);
  194. }
  195. }
  196. }
  197. }
  198. }
  199. private Vector3[] GetMorphWeights(int? accessor, int vertStartIndex, int vertCount, GLTFAccessor.ImportResult[] accessors) {
  200. if (accessor.HasValue) {
  201. if (accessors[accessor.Value] == null) {
  202. Debug.LogWarning("Accessor is null");
  203. return new Vector3[vertCount];
  204. }
  205. Vector3[] accessorData = accessors[accessor.Value].ReadVec3(true).Select(v => { v.x = -v.x; return v; }).ToArray();
  206. if (accessorData.Length != vertCount) {
  207. Vector3[] resized = new Vector3[vertCount];
  208. Array.Copy(accessorData, 0, resized, vertStartIndex, accessorData.Length);
  209. return resized;
  210. } else return accessorData;
  211. } else return new Vector3[vertCount];
  212. }
  213. public Mesh ToMesh() {
  214. Mesh mesh = new Mesh();
  215. if (verts.Count >= ushort.MaxValue) mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
  216. mesh.vertices = verts.ToArray();
  217. mesh.subMeshCount = submeshTris.Count;
  218. var onlyTriangles = true;
  219. for (int i = 0; i < submeshTris.Count; i++) {
  220. switch (submeshTrisMode[i]) {
  221. case RenderingMode.POINTS:
  222. mesh.SetIndices(submeshTris[i].ToArray(), MeshTopology.Points, i);
  223. onlyTriangles = false;
  224. break;
  225. case RenderingMode.LINES:
  226. mesh.SetIndices(submeshTris[i].ToArray(), MeshTopology.Lines, i);
  227. onlyTriangles = false;
  228. break;
  229. case RenderingMode.LINE_STRIP:
  230. mesh.SetIndices(submeshTris[i].ToArray(), MeshTopology.LineStrip, i);
  231. onlyTriangles = false;
  232. break;
  233. case RenderingMode.TRIANGLES:
  234. mesh.SetTriangles(submeshTris[i].ToArray(), i);
  235. break;
  236. default:
  237. Debug.LogWarning("GLTF rendering mode " + submeshTrisMode[i] + " not supported.");
  238. return null;
  239. }
  240. }
  241. mesh.colors = colors.ToArray();
  242. if (uv1 != null) mesh.uv = uv1.ToArray();
  243. if (uv2 != null) mesh.uv2 = uv2.ToArray();
  244. if (uv3 != null) mesh.uv3 = uv3.ToArray();
  245. if (uv4 != null) mesh.uv4 = uv4.ToArray();
  246. if (uv5 != null) mesh.uv5 = uv5.ToArray();
  247. if (uv6 != null) mesh.uv6 = uv6.ToArray();
  248. if (uv7 != null) mesh.uv7 = uv7.ToArray();
  249. if (uv8 != null) mesh.uv8 = uv8.ToArray();
  250. if (weights != null) mesh.boneWeights = weights.ToArray();
  251. mesh.RecalculateBounds();
  252. // Blend shapes
  253. for (int i = 0; i < blendShapes.Count; i++) {
  254. mesh.AddBlendShapeFrame(blendShapes[i].name, 1f, blendShapes[i].pos, blendShapes[i].norm, blendShapes[i].tan);
  255. }
  256. if (normals.Count == 0 && onlyTriangles)
  257. mesh.RecalculateNormals();
  258. else
  259. mesh.normals = normals.ToArray();
  260. if (tangents.Count == 0 && onlyTriangles)
  261. mesh.RecalculateTangents();
  262. else
  263. mesh.tangents = tangents.ToArray();
  264. mesh.name = name;
  265. return mesh;
  266. }
  267. public void NormalizeWeights(ref Vector4 weights) {
  268. float total = weights.x + weights.y + weights.z + weights.w;
  269. float mult = 1f / total;
  270. weights.x *= mult;
  271. weights.y *= mult;
  272. weights.z *= mult;
  273. weights.w *= mult;
  274. }
  275. private void ReadUVs(ref List<Vector2> uvs, GLTFAccessor.ImportResult[] accessors, int? texcoord, int vertCount) {
  276. // If there are no valid texcoords
  277. if (!texcoord.HasValue) {
  278. // If there are already uvs, add some empty filler uvs so it still matches the vertex array
  279. if (uvs != null) uvs.AddRange(new Vector2[vertCount - uvs.Count]);
  280. return;
  281. }
  282. Vector2[] _uvs = accessors[texcoord.Value].ReadVec2(true);
  283. FlipY(ref _uvs);
  284. if (uvs == null) uvs = new List<Vector2>(_uvs);
  285. else uvs.AddRange(_uvs);
  286. }
  287. public void FlipY(ref Vector2[] uv) {
  288. for (int i = 0; i < uv.Length; i++) {
  289. uv[i].y = 1 - uv[i].y;
  290. }
  291. }
  292. }
  293. private MeshData[] meshData;
  294. private List<GLTFMesh> meshes;
  295. private GLTFMaterial.ImportTask materialTask;
  296. public ImportTask(List<GLTFMesh> meshes, GLTFAccessor.ImportTask accessorTask, GLTFBufferView.ImportTask bufferViewTask, GLTFMaterial.ImportTask materialTask, ImportSettings importSettings) : base(accessorTask, materialTask) {
  297. this.meshes = meshes;
  298. this.materialTask = materialTask;
  299. task = new Task(() => {
  300. if (meshes == null) return;
  301. meshData = new MeshData[meshes.Count];
  302. for (int i = 0; i < meshData.Length; i++) {
  303. meshData[i] = new MeshData(meshes[i], accessorTask.Result, bufferViewTask.Result);
  304. }
  305. });
  306. }
  307. public override IEnumerator OnCoroutine(Action<float> onProgress = null) {
  308. // No mesh
  309. if (meshData == null) {
  310. if (onProgress != null) onProgress.Invoke(1f);
  311. IsCompleted = true;
  312. yield break;
  313. }
  314. Result = new ImportResult[meshData.Length];
  315. for (int i = 0; i < meshData.Length; i++) {
  316. if (meshData[i] == null) {
  317. Debug.LogWarning("Mesh " + i + " import error");
  318. continue;
  319. }
  320. Result[i] = new ImportResult();
  321. Result[i].mesh = meshData[i].ToMesh();
  322. Result[i].materials = new Material[meshes[i].primitives.Count];
  323. for (int k = 0; k < meshes[i].primitives.Count; k++) {
  324. int? matIndex = meshes[i].primitives[k].material;
  325. if (matIndex.HasValue && materialTask.Result != null && materialTask.Result.Count() > matIndex.Value) {
  326. GLTFMaterial.ImportResult matImport = materialTask.Result[matIndex.Value];
  327. if (matImport != null) Result[i].materials[k] = matImport.material;
  328. else {
  329. Debug.LogWarning("Mesh[" + i + "].matIndex points to null material (index " + matIndex.Value + ")");
  330. Result[i].materials[k] = GLTFMaterial.defaultMaterial;
  331. }
  332. } else {
  333. Result[i].materials[k] = GLTFMaterial.defaultMaterial;
  334. }
  335. }
  336. if (string.IsNullOrEmpty(Result[i].mesh.name)) Result[i].mesh.name = "mesh" + i;
  337. if (onProgress != null) onProgress.Invoke((float) (i + 1) / (float) meshData.Length);
  338. yield return null;
  339. }
  340. IsCompleted = true;
  341. }
  342. }
  343. #endregion
  344. #region Export
  345. public class ExportResult : GLTFMesh {
  346. [JsonIgnore] public Mesh mesh;
  347. }
  348. public static List<ExportResult> Export(List<GLTFNode.ExportResult> nodes) {
  349. List<ExportResult> results = new List<ExportResult>();
  350. for (int i = 0; i < nodes.Count; i++) {
  351. if (nodes[i].filter) {
  352. Mesh mesh = nodes[i].filter.sharedMesh;
  353. if (mesh) {
  354. nodes[i].mesh = results.Count;
  355. results.Add(Export(mesh));
  356. }
  357. }
  358. }
  359. return results;
  360. }
  361. public static ExportResult Export(Mesh mesh) {
  362. ExportResult result = new ExportResult();
  363. result.name = mesh.name;
  364. result.primitives = new List<GLTFPrimitive>();
  365. for (int i = 0; i < mesh.subMeshCount; i++) {
  366. GLTFPrimitive primitive = new GLTFPrimitive();
  367. result.primitives.Add(primitive);
  368. }
  369. return result;
  370. }
  371. #endregion
  372. }
  373. }