removeUnusedElements.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. import ForEach from './ForEach.js'
  2. import hasExtension from './hasExtension.js'
  3. import defaultValue from '../../Core/defaultValue.js'
  4. import defined from '../../Core/defined.js'
  5. var allElementTypes = ['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer'];
  6. /**
  7. * Removes unused elements from gltf.
  8. *
  9. * @param {Object} gltf A javascript object containing a glTF asset.
  10. * @param {String[]} [elementTypes=['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer']] Element types to be removed. Needs to be a subset of ['mesh', 'node', 'material', 'accessor', 'bufferView', 'buffer'], other items will be ignored.
  11. *
  12. * @private
  13. */
  14. function removeUnusedElements(gltf, elementTypes) {
  15. elementTypes = defaultValue(elementTypes, allElementTypes);
  16. allElementTypes.forEach(function(type) {
  17. if (elementTypes.indexOf(type) > -1) {
  18. removeUnusedElementsByType(gltf, type);
  19. }
  20. });
  21. return gltf;
  22. }
  23. var TypeToGltfElementName = {
  24. accessor: 'accessors',
  25. buffer: 'buffers',
  26. bufferView: 'bufferViews',
  27. node: 'nodes',
  28. material: 'materials',
  29. mesh: 'meshes'
  30. };
  31. function removeUnusedElementsByType(gltf, type) {
  32. var name = TypeToGltfElementName[type];
  33. var arrayOfObjects = gltf[name];
  34. if (defined(arrayOfObjects)) {
  35. var removed = 0;
  36. var usedIds = getListOfElementsIdsInUse[type](gltf);
  37. var length = arrayOfObjects.length;
  38. for (var i = 0; i < length; ++i) {
  39. if (!usedIds[i]) {
  40. Remove[type](gltf, i - removed);
  41. removed++;
  42. }
  43. }
  44. }
  45. }
  46. /**
  47. * Contains functions for removing elements from a glTF hierarchy.
  48. * Since top-level glTF elements are arrays, when something is removed, referring
  49. * indices need to be updated.
  50. * @constructor
  51. *
  52. * @private
  53. */
  54. function Remove() {}
  55. Remove.accessor = function(gltf, accessorId) {
  56. var accessors = gltf.accessors;
  57. accessors.splice(accessorId, 1);
  58. ForEach.mesh(gltf, function(mesh) {
  59. ForEach.meshPrimitive(mesh, function(primitive) {
  60. // Update accessor ids for the primitives.
  61. ForEach.meshPrimitiveAttribute(primitive, function(attributeAccessorId, semantic) {
  62. if (attributeAccessorId > accessorId) {
  63. primitive.attributes[semantic]--;
  64. }
  65. });
  66. // Update accessor ids for the targets.
  67. ForEach.meshPrimitiveTarget(primitive, function(target) {
  68. ForEach.meshPrimitiveTargetAttribute(target, function(attributeAccessorId, semantic) {
  69. if (attributeAccessorId > accessorId) {
  70. target[semantic]--;
  71. }
  72. });
  73. });
  74. var indices = primitive.indices;
  75. if (defined(indices) && indices > accessorId) {
  76. primitive.indices--;
  77. }
  78. });
  79. });
  80. ForEach.skin(gltf, function(skin) {
  81. if (defined(skin.inverseBindMatrices) && skin.inverseBindMatrices > accessorId) {
  82. skin.inverseBindMatrices--;
  83. }
  84. });
  85. ForEach.animation(gltf, function(animation) {
  86. ForEach.animationSampler(animation, function(sampler) {
  87. if (defined(sampler.input) && sampler.input > accessorId) {
  88. sampler.input--;
  89. }
  90. if (defined(sampler.output) && sampler.output > accessorId) {
  91. sampler.output--;
  92. }
  93. });
  94. });
  95. };
  96. Remove.buffer = function(gltf, bufferId) {
  97. var buffers = gltf.buffers;
  98. buffers.splice(bufferId, 1);
  99. ForEach.bufferView(gltf, function(bufferView) {
  100. if (defined(bufferView.buffer) && bufferView.buffer > bufferId) {
  101. bufferView.buffer--;
  102. }
  103. });
  104. };
  105. Remove.bufferView = function(gltf, bufferViewId) {
  106. var bufferViews = gltf.bufferViews;
  107. bufferViews.splice(bufferViewId, 1);
  108. ForEach.accessor(gltf, function(accessor) {
  109. if (defined(accessor.bufferView) && accessor.bufferView > bufferViewId) {
  110. accessor.bufferView--;
  111. }
  112. });
  113. ForEach.shader(gltf, function(shader) {
  114. if (defined(shader.bufferView) && shader.bufferView > bufferViewId) {
  115. shader.bufferView--;
  116. }
  117. });
  118. ForEach.image(gltf, function(image) {
  119. if (defined(image.bufferView) && image.bufferView > bufferViewId) {
  120. image.bufferView--;
  121. }
  122. ForEach.compressedImage(image, function(compressedImage) {
  123. var compressedImageBufferView = compressedImage.bufferView;
  124. if (defined(compressedImageBufferView) && compressedImageBufferView > bufferViewId) {
  125. compressedImage.bufferView--;
  126. }
  127. });
  128. });
  129. if (hasExtension(gltf, 'KHR_draco_mesh_compression')) {
  130. ForEach.mesh(gltf, function(mesh) {
  131. ForEach.meshPrimitive(mesh, function(primitive) {
  132. if (defined(primitive.extensions) &&
  133. defined(primitive.extensions.KHR_draco_mesh_compression)) {
  134. if (primitive.extensions.KHR_draco_mesh_compression.bufferView > bufferViewId) {
  135. primitive.extensions.KHR_draco_mesh_compression.bufferView--;
  136. }
  137. }
  138. });
  139. });
  140. }
  141. };
  142. Remove.mesh = function(gltf, meshId) {
  143. var meshes = gltf.meshes;
  144. meshes.splice(meshId, 1);
  145. ForEach.node(gltf, function(node) {
  146. if (defined(node.mesh)) {
  147. if (node.mesh > meshId) {
  148. node.mesh--;
  149. } else if (node.mesh === meshId) {
  150. // Remove reference to deleted mesh
  151. delete node.mesh;
  152. }
  153. }
  154. });
  155. };
  156. Remove.node = function(gltf, nodeId) {
  157. var nodes = gltf.nodes;
  158. nodes.splice(nodeId, 1);
  159. // Shift all node references
  160. ForEach.skin(gltf, function(skin) {
  161. if (defined(skin.skeleton) && skin.skeleton > nodeId) {
  162. skin.skeleton--;
  163. }
  164. skin.joints = skin.joints.map(function(x) {
  165. return x > nodeId ? x - 1 : x;
  166. });
  167. });
  168. ForEach.animation(gltf, function(animation) {
  169. ForEach.animationChannel(animation, function(channel) {
  170. if (defined(channel.target) && defined(channel.target.node) && (channel.target.node > nodeId)) {
  171. channel.target.node--;
  172. }
  173. });
  174. });
  175. ForEach.technique(gltf, function(technique) {
  176. ForEach.techniqueUniform(technique, function(uniform) {
  177. if (defined(uniform.node) && uniform.node > nodeId) {
  178. uniform.node--;
  179. }
  180. });
  181. });
  182. ForEach.node(gltf, function(node) {
  183. if (!defined(node.children)) {
  184. return;
  185. }
  186. node.children = node.children
  187. .filter(function(x) {
  188. return x !== nodeId; // Remove
  189. })
  190. .map(function(x) {
  191. return x > nodeId ? x - 1 : x; // Shift indices
  192. });
  193. });
  194. ForEach.scene(gltf, function(scene) {
  195. scene.nodes = scene.nodes
  196. .filter(function(x) {
  197. return x !== nodeId; // Remove
  198. })
  199. .map(function(x) {
  200. return x > nodeId ? x - 1 : x; // Shift indices
  201. });
  202. });
  203. };
  204. Remove.material = function(gltf, materialId) {
  205. var materials = gltf.materials;
  206. materials.splice(materialId, 1);
  207. // Shift other material ids
  208. ForEach.mesh(gltf, function(mesh) {
  209. ForEach.meshPrimitive(mesh, function(primitive) {
  210. if (defined(primitive.material) && primitive.material > materialId) {
  211. primitive.material--;
  212. }
  213. });
  214. });
  215. };
  216. /**
  217. * Contains functions for getting a list of element ids in use by the glTF asset.
  218. * @constructor
  219. *
  220. * @private
  221. */
  222. function getListOfElementsIdsInUse() {}
  223. getListOfElementsIdsInUse.accessor = function(gltf) {
  224. // Calculate accessor's that are currently in use.
  225. var usedAccessorIds = {};
  226. ForEach.mesh(gltf, function(mesh) {
  227. ForEach.meshPrimitive(mesh, function(primitive) {
  228. ForEach.meshPrimitiveAttribute(primitive, function(accessorId) {
  229. usedAccessorIds[accessorId] = true;
  230. });
  231. ForEach.meshPrimitiveTarget(primitive, function(target) {
  232. ForEach.meshPrimitiveTargetAttribute(target, function(accessorId) {
  233. usedAccessorIds[accessorId] = true;
  234. });
  235. });
  236. var indices = primitive.indices;
  237. if (defined(indices)) {
  238. usedAccessorIds[indices] = true;
  239. }
  240. });
  241. });
  242. ForEach.skin(gltf, function(skin) {
  243. if (defined(skin.inverseBindMatrices)) {
  244. usedAccessorIds[skin.inverseBindMatrices] = true;
  245. }
  246. });
  247. ForEach.animation(gltf, function(animation) {
  248. ForEach.animationSampler(animation, function(sampler) {
  249. if (defined(sampler.input)) {
  250. usedAccessorIds[sampler.input] = true;
  251. }
  252. if (defined(sampler.output)) {
  253. usedAccessorIds[sampler.output] = true;
  254. }
  255. });
  256. });
  257. return usedAccessorIds;
  258. };
  259. getListOfElementsIdsInUse.buffer = function(gltf) {
  260. // Calculate buffer's that are currently in use.
  261. var usedBufferIds = {};
  262. ForEach.bufferView(gltf, function(bufferView) {
  263. if (defined(bufferView.buffer)) {
  264. usedBufferIds[bufferView.buffer] = true;
  265. }
  266. });
  267. return usedBufferIds;
  268. };
  269. getListOfElementsIdsInUse.bufferView = function(gltf) {
  270. // Calculate bufferView's that are currently in use.
  271. var usedBufferViewIds = {};
  272. ForEach.accessor(gltf, function(accessor) {
  273. if (defined(accessor.bufferView)) {
  274. usedBufferViewIds[accessor.bufferView] = true;
  275. }
  276. });
  277. ForEach.shader(gltf, function(shader) {
  278. if (defined(shader.bufferView)) {
  279. usedBufferViewIds[shader.bufferView] = true;
  280. }
  281. });
  282. ForEach.image(gltf, function(image) {
  283. if (defined(image.bufferView)) {
  284. usedBufferViewIds[image.bufferView] = true;
  285. }
  286. ForEach.compressedImage(image, function(compressedImage) {
  287. if (defined(compressedImage.bufferView)) {
  288. usedBufferViewIds[compressedImage.bufferView] = true;
  289. }
  290. });
  291. });
  292. if (hasExtension(gltf, 'KHR_draco_mesh_compression')) {
  293. ForEach.mesh(gltf, function(mesh) {
  294. ForEach.meshPrimitive(mesh, function(primitive) {
  295. if (defined(primitive.extensions) &&
  296. defined(primitive.extensions.KHR_draco_mesh_compression)) {
  297. usedBufferViewIds[primitive.extensions.KHR_draco_mesh_compression.bufferView] = true;
  298. }
  299. });
  300. });
  301. }
  302. return usedBufferViewIds;
  303. };
  304. getListOfElementsIdsInUse.mesh = function(gltf) {
  305. var usedMeshIds = {};
  306. ForEach.node(gltf, function(node) {
  307. if (defined(node.mesh && defined(gltf.meshes))) {
  308. var mesh = gltf.meshes[node.mesh];
  309. if (defined(mesh) && defined(mesh.primitives) && (mesh.primitives.length > 0)) {
  310. usedMeshIds[node.mesh] = true;
  311. }
  312. }
  313. });
  314. return usedMeshIds;
  315. };
  316. // Check if node is empty. It is considered empty if neither referencing
  317. // mesh, camera, extensions and has no children
  318. function nodeIsEmpty(gltf, node) {
  319. if (defined(node.mesh) || defined(node.camera) || defined(node.skin)
  320. || defined(node.weights) || defined(node.extras)
  321. || (defined(node.extensions) && node.extensions.length !== 0)) {
  322. return false;
  323. }
  324. // Empty if no children or children are all empty nodes
  325. return !defined(node.children)
  326. || node.children.filter(function(n) {
  327. return !nodeIsEmpty(gltf, gltf.nodes[n]);
  328. }).length === 0;
  329. }
  330. getListOfElementsIdsInUse.node = function(gltf) {
  331. var usedNodeIds = {};
  332. ForEach.node(gltf, function(node, nodeId) {
  333. if (!nodeIsEmpty(gltf, node)) {
  334. usedNodeIds[nodeId] = true;
  335. }
  336. });
  337. ForEach.skin(gltf, function(skin) {
  338. if (defined(skin.skeleton)) {
  339. usedNodeIds[skin.skeleton] = true;
  340. }
  341. ForEach.skinJoint(skin, function(joint) {
  342. usedNodeIds[joint] = true;
  343. });
  344. });
  345. ForEach.animation(gltf, function(animation) {
  346. ForEach.animationChannel(animation, function(channel) {
  347. if (defined(channel.target) && defined(channel.target.node)) {
  348. usedNodeIds[channel.target.node] = true;
  349. }
  350. });
  351. });
  352. ForEach.technique(gltf, function(technique) {
  353. ForEach.techniqueUniform(technique, function(uniform) {
  354. if (defined(uniform.node)) {
  355. usedNodeIds[uniform.node] = true;
  356. }
  357. });
  358. });
  359. return usedNodeIds;
  360. };
  361. getListOfElementsIdsInUse.material = function(gltf) {
  362. var usedMaterialIds = {};
  363. ForEach.mesh(gltf, function(mesh) {
  364. ForEach.meshPrimitive(mesh, function(primitive) {
  365. if (defined(primitive.material)) {
  366. usedMaterialIds[primitive.material] = true;
  367. }
  368. });
  369. });
  370. return usedMaterialIds;
  371. };
  372. export default removeUnusedElements;