updateVersion.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. import addExtensionsUsed from "./addExtensionsUsed.js";
  2. import addToArray from "./addToArray.js";
  3. import findAccessorMinMax from "./findAccessorMinMax.js";
  4. import ForEach from "./ForEach.js";
  5. import getAccessorByteStride from "./getAccessorByteStride.js";
  6. import numberOfComponentsForType from "./numberOfComponentsForType.js";
  7. import moveTechniqueRenderStates from "./moveTechniqueRenderStates.js";
  8. import moveTechniquesToExtension from "./moveTechniquesToExtension.js";
  9. import removeUnusedElements from "./removeUnusedElements.js";
  10. import updateAccessorComponentTypes from "./updateAccessorComponentTypes.js";
  11. import Cartesian3 from "../../Core/Cartesian3.js";
  12. import Cartesian4 from "../../Core/Cartesian4.js";
  13. import clone from "../../Core/clone.js";
  14. import ComponentDatatype from "../../Core/ComponentDatatype.js";
  15. import defaultValue from "../../Core/defaultValue.js";
  16. import defined from "../../Core/defined.js";
  17. import Matrix4 from "../../Core/Matrix4.js";
  18. import Quaternion from "../../Core/Quaternion.js";
  19. import WebGLConstants from "../../Core/WebGLConstants.js";
  20. const updateFunctions = {
  21. 0.8: glTF08to10,
  22. "1.0": glTF10to20,
  23. "2.0": undefined,
  24. };
  25. /**
  26. * Update the glTF version to the latest version (2.0), or targetVersion if specified.
  27. * Applies changes made to the glTF spec between revisions so that the core library
  28. * only has to handle the latest version.
  29. *
  30. * @param {Object} gltf A javascript object containing a glTF asset.
  31. * @param {Object} [options] Options for updating the glTF.
  32. * @param {String} [options.targetVersion] The glTF will be upgraded until it hits the specified version.
  33. * @returns {Object} The updated glTF asset.
  34. *
  35. * @private
  36. */
  37. function updateVersion(gltf, options) {
  38. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  39. const targetVersion = options.targetVersion;
  40. let version = gltf.version;
  41. gltf.asset = defaultValue(gltf.asset, {
  42. version: "1.0",
  43. });
  44. gltf.asset.version = defaultValue(gltf.asset.version, "1.0");
  45. version = defaultValue(version, gltf.asset.version).toString();
  46. // Invalid version
  47. if (!Object.prototype.hasOwnProperty.call(updateFunctions, version)) {
  48. // Try truncating trailing version numbers, could be a number as well if it is 0.8
  49. if (defined(version)) {
  50. version = version.substring(0, 3);
  51. }
  52. // Default to 1.0 if it cannot be determined
  53. if (!Object.prototype.hasOwnProperty.call(updateFunctions, version)) {
  54. version = "1.0";
  55. }
  56. }
  57. let updateFunction = updateFunctions[version];
  58. while (defined(updateFunction)) {
  59. if (version === targetVersion) {
  60. break;
  61. }
  62. updateFunction(gltf, options);
  63. version = gltf.asset.version;
  64. updateFunction = updateFunctions[version];
  65. }
  66. return gltf;
  67. }
  68. function updateInstanceTechniques(gltf) {
  69. const materials = gltf.materials;
  70. for (const materialId in materials) {
  71. if (Object.prototype.hasOwnProperty.call(materials, materialId)) {
  72. const material = materials[materialId];
  73. const instanceTechnique = material.instanceTechnique;
  74. if (defined(instanceTechnique)) {
  75. material.technique = instanceTechnique.technique;
  76. material.values = instanceTechnique.values;
  77. delete material.instanceTechnique;
  78. }
  79. }
  80. }
  81. }
  82. function setPrimitiveModes(gltf) {
  83. const meshes = gltf.meshes;
  84. for (const meshId in meshes) {
  85. if (Object.prototype.hasOwnProperty.call(meshes, meshId)) {
  86. const mesh = meshes[meshId];
  87. const primitives = mesh.primitives;
  88. if (defined(primitives)) {
  89. const primitivesLength = primitives.length;
  90. for (let i = 0; i < primitivesLength; ++i) {
  91. const primitive = primitives[i];
  92. const defaultMode = defaultValue(
  93. primitive.primitive,
  94. WebGLConstants.TRIANGLES
  95. );
  96. primitive.mode = defaultValue(primitive.mode, defaultMode);
  97. delete primitive.primitive;
  98. }
  99. }
  100. }
  101. }
  102. }
  103. function updateNodes(gltf) {
  104. const nodes = gltf.nodes;
  105. const axis = new Cartesian3();
  106. const quat = new Quaternion();
  107. for (const nodeId in nodes) {
  108. if (Object.prototype.hasOwnProperty.call(nodes, nodeId)) {
  109. const node = nodes[nodeId];
  110. if (defined(node.rotation)) {
  111. const rotation = node.rotation;
  112. Cartesian3.fromArray(rotation, 0, axis);
  113. Quaternion.fromAxisAngle(axis, rotation[3], quat);
  114. node.rotation = [quat.x, quat.y, quat.z, quat.w];
  115. }
  116. const instanceSkin = node.instanceSkin;
  117. if (defined(instanceSkin)) {
  118. node.skeletons = instanceSkin.skeletons;
  119. node.skin = instanceSkin.skin;
  120. node.meshes = instanceSkin.meshes;
  121. delete node.instanceSkin;
  122. }
  123. }
  124. }
  125. }
  126. function updateAnimations(gltf) {
  127. const animations = gltf.animations;
  128. const accessors = gltf.accessors;
  129. const bufferViews = gltf.bufferViews;
  130. const buffers = gltf.buffers;
  131. const updatedAccessors = {};
  132. const axis = new Cartesian3();
  133. const quat = new Quaternion();
  134. for (const animationId in animations) {
  135. if (Object.prototype.hasOwnProperty.call(animations, animationId)) {
  136. const animation = animations[animationId];
  137. const channels = animation.channels;
  138. const parameters = animation.parameters;
  139. const samplers = animation.samplers;
  140. if (defined(channels)) {
  141. const channelsLength = channels.length;
  142. for (let i = 0; i < channelsLength; ++i) {
  143. const channel = channels[i];
  144. if (channel.target.path === "rotation") {
  145. const accessorId = parameters[samplers[channel.sampler].output];
  146. if (defined(updatedAccessors[accessorId])) {
  147. continue;
  148. }
  149. updatedAccessors[accessorId] = true;
  150. const accessor = accessors[accessorId];
  151. const bufferView = bufferViews[accessor.bufferView];
  152. const buffer = buffers[bufferView.buffer];
  153. const source = buffer.extras._pipeline.source;
  154. const byteOffset =
  155. source.byteOffset + bufferView.byteOffset + accessor.byteOffset;
  156. const componentType = accessor.componentType;
  157. const count = accessor.count;
  158. const componentsLength = numberOfComponentsForType(accessor.type);
  159. const length = accessor.count * componentsLength;
  160. const typedArray = ComponentDatatype.createArrayBufferView(
  161. componentType,
  162. source.buffer,
  163. byteOffset,
  164. length
  165. );
  166. for (let j = 0; j < count; j++) {
  167. const offset = j * componentsLength;
  168. Cartesian3.unpack(typedArray, offset, axis);
  169. const angle = typedArray[offset + 3];
  170. Quaternion.fromAxisAngle(axis, angle, quat);
  171. Quaternion.pack(quat, typedArray, offset);
  172. }
  173. }
  174. }
  175. }
  176. }
  177. }
  178. }
  179. function removeTechniquePasses(gltf) {
  180. const techniques = gltf.techniques;
  181. for (const techniqueId in techniques) {
  182. if (Object.prototype.hasOwnProperty.call(techniques, techniqueId)) {
  183. const technique = techniques[techniqueId];
  184. const passes = technique.passes;
  185. if (defined(passes)) {
  186. const passName = defaultValue(technique.pass, "defaultPass");
  187. if (Object.prototype.hasOwnProperty.call(passes, passName)) {
  188. const pass = passes[passName];
  189. const instanceProgram = pass.instanceProgram;
  190. technique.attributes = defaultValue(
  191. technique.attributes,
  192. instanceProgram.attributes
  193. );
  194. technique.program = defaultValue(
  195. technique.program,
  196. instanceProgram.program
  197. );
  198. technique.uniforms = defaultValue(
  199. technique.uniforms,
  200. instanceProgram.uniforms
  201. );
  202. technique.states = defaultValue(technique.states, pass.states);
  203. }
  204. delete technique.passes;
  205. delete technique.pass;
  206. }
  207. }
  208. }
  209. }
  210. function glTF08to10(gltf) {
  211. if (!defined(gltf.asset)) {
  212. gltf.asset = {};
  213. }
  214. const asset = gltf.asset;
  215. asset.version = "1.0";
  216. // Profile should be an object, not a string
  217. if (typeof asset.profile === "string") {
  218. const split = asset.profile.split(" ");
  219. asset.profile = {
  220. api: split[0],
  221. version: split[1],
  222. };
  223. } else {
  224. asset.profile = {};
  225. }
  226. // Version property should be in asset, not on the root element
  227. if (defined(gltf.version)) {
  228. delete gltf.version;
  229. }
  230. // material.instanceTechnique properties should be directly on the material
  231. updateInstanceTechniques(gltf);
  232. // primitive.primitive should be primitive.mode
  233. setPrimitiveModes(gltf);
  234. // Node rotation should be quaternion, not axis-angle
  235. // node.instanceSkin is deprecated
  236. updateNodes(gltf);
  237. // Animations that target rotations should be quaternion, not axis-angle
  238. updateAnimations(gltf);
  239. // technique.pass and techniques.passes are deprecated
  240. removeTechniquePasses(gltf);
  241. // gltf.allExtensions -> extensionsUsed
  242. if (defined(gltf.allExtensions)) {
  243. gltf.extensionsUsed = gltf.allExtensions;
  244. delete gltf.allExtensions;
  245. }
  246. // gltf.lights -> khrMaterialsCommon.lights
  247. if (defined(gltf.lights)) {
  248. const extensions = defaultValue(gltf.extensions, {});
  249. gltf.extensions = extensions;
  250. const materialsCommon = defaultValue(extensions.KHR_materials_common, {});
  251. extensions.KHR_materials_common = materialsCommon;
  252. materialsCommon.lights = gltf.lights;
  253. delete gltf.lights;
  254. addExtensionsUsed(gltf, "KHR_materials_common");
  255. }
  256. }
  257. function removeAnimationSamplersIndirection(gltf) {
  258. const animations = gltf.animations;
  259. for (const animationId in animations) {
  260. if (Object.prototype.hasOwnProperty.call(animations, animationId)) {
  261. const animation = animations[animationId];
  262. const parameters = animation.parameters;
  263. if (defined(parameters)) {
  264. const samplers = animation.samplers;
  265. for (const samplerId in samplers) {
  266. if (Object.prototype.hasOwnProperty.call(samplers, samplerId)) {
  267. const sampler = samplers[samplerId];
  268. sampler.input = parameters[sampler.input];
  269. sampler.output = parameters[sampler.output];
  270. }
  271. }
  272. delete animation.parameters;
  273. }
  274. }
  275. }
  276. }
  277. function objectToArray(object, mapping) {
  278. const array = [];
  279. for (const id in object) {
  280. if (Object.prototype.hasOwnProperty.call(object, id)) {
  281. const value = object[id];
  282. mapping[id] = array.length;
  283. array.push(value);
  284. if (!defined(value.name)) {
  285. value.name = id;
  286. }
  287. }
  288. }
  289. return array;
  290. }
  291. function objectsToArrays(gltf) {
  292. let i;
  293. const globalMapping = {
  294. accessors: {},
  295. animations: {},
  296. buffers: {},
  297. bufferViews: {},
  298. cameras: {},
  299. images: {},
  300. materials: {},
  301. meshes: {},
  302. nodes: {},
  303. programs: {},
  304. samplers: {},
  305. scenes: {},
  306. shaders: {},
  307. skins: {},
  308. textures: {},
  309. techniques: {},
  310. };
  311. // Map joint names to id names
  312. let jointName;
  313. const jointNameToId = {};
  314. const nodes = gltf.nodes;
  315. for (const id in nodes) {
  316. if (Object.prototype.hasOwnProperty.call(nodes, id)) {
  317. jointName = nodes[id].jointName;
  318. if (defined(jointName)) {
  319. jointNameToId[jointName] = id;
  320. }
  321. }
  322. }
  323. // Convert top level objects to arrays
  324. for (const topLevelId in gltf) {
  325. if (
  326. Object.prototype.hasOwnProperty.call(gltf, topLevelId) &&
  327. defined(globalMapping[topLevelId])
  328. ) {
  329. const objectMapping = {};
  330. const object = gltf[topLevelId];
  331. gltf[topLevelId] = objectToArray(object, objectMapping);
  332. globalMapping[topLevelId] = objectMapping;
  333. }
  334. }
  335. // Remap joint names to array indexes
  336. for (jointName in jointNameToId) {
  337. if (Object.prototype.hasOwnProperty.call(jointNameToId, jointName)) {
  338. jointNameToId[jointName] = globalMapping.nodes[jointNameToId[jointName]];
  339. }
  340. }
  341. // Fix references
  342. if (defined(gltf.scene)) {
  343. gltf.scene = globalMapping.scenes[gltf.scene];
  344. }
  345. ForEach.bufferView(gltf, function (bufferView) {
  346. if (defined(bufferView.buffer)) {
  347. bufferView.buffer = globalMapping.buffers[bufferView.buffer];
  348. }
  349. });
  350. ForEach.accessor(gltf, function (accessor) {
  351. if (defined(accessor.bufferView)) {
  352. accessor.bufferView = globalMapping.bufferViews[accessor.bufferView];
  353. }
  354. });
  355. ForEach.shader(gltf, function (shader) {
  356. const extensions = shader.extensions;
  357. if (defined(extensions)) {
  358. const binaryGltf = extensions.KHR_binary_glTF;
  359. if (defined(binaryGltf)) {
  360. shader.bufferView = globalMapping.bufferViews[binaryGltf.bufferView];
  361. delete extensions.KHR_binary_glTF;
  362. }
  363. if (Object.keys(extensions).length === 0) {
  364. delete shader.extensions;
  365. }
  366. }
  367. });
  368. ForEach.program(gltf, function (program) {
  369. if (defined(program.vertexShader)) {
  370. program.vertexShader = globalMapping.shaders[program.vertexShader];
  371. }
  372. if (defined(program.fragmentShader)) {
  373. program.fragmentShader = globalMapping.shaders[program.fragmentShader];
  374. }
  375. });
  376. ForEach.technique(gltf, function (technique) {
  377. if (defined(technique.program)) {
  378. technique.program = globalMapping.programs[technique.program];
  379. }
  380. ForEach.techniqueParameter(technique, function (parameter) {
  381. if (defined(parameter.node)) {
  382. parameter.node = globalMapping.nodes[parameter.node];
  383. }
  384. const value = parameter.value;
  385. if (typeof value === "string") {
  386. parameter.value = {
  387. index: globalMapping.textures[value],
  388. };
  389. }
  390. });
  391. });
  392. ForEach.mesh(gltf, function (mesh) {
  393. ForEach.meshPrimitive(mesh, function (primitive) {
  394. if (defined(primitive.indices)) {
  395. primitive.indices = globalMapping.accessors[primitive.indices];
  396. }
  397. ForEach.meshPrimitiveAttribute(
  398. primitive,
  399. function (accessorId, semantic) {
  400. primitive.attributes[semantic] = globalMapping.accessors[accessorId];
  401. }
  402. );
  403. if (defined(primitive.material)) {
  404. primitive.material = globalMapping.materials[primitive.material];
  405. }
  406. });
  407. });
  408. ForEach.node(gltf, function (node) {
  409. let children = node.children;
  410. if (defined(children)) {
  411. const childrenLength = children.length;
  412. for (i = 0; i < childrenLength; ++i) {
  413. children[i] = globalMapping.nodes[children[i]];
  414. }
  415. }
  416. if (defined(node.meshes)) {
  417. // Split out meshes on nodes
  418. const meshes = node.meshes;
  419. const meshesLength = meshes.length;
  420. if (meshesLength > 0) {
  421. node.mesh = globalMapping.meshes[meshes[0]];
  422. for (i = 1; i < meshesLength; ++i) {
  423. const meshNode = {
  424. mesh: globalMapping.meshes[meshes[i]],
  425. };
  426. const meshNodeId = addToArray(gltf.nodes, meshNode);
  427. if (!defined(children)) {
  428. children = [];
  429. node.children = children;
  430. }
  431. children.push(meshNodeId);
  432. }
  433. }
  434. delete node.meshes;
  435. }
  436. if (defined(node.camera)) {
  437. node.camera = globalMapping.cameras[node.camera];
  438. }
  439. if (defined(node.skin)) {
  440. node.skin = globalMapping.skins[node.skin];
  441. }
  442. if (defined(node.skeletons)) {
  443. // Assign skeletons to skins
  444. const skeletons = node.skeletons;
  445. const skeletonsLength = skeletons.length;
  446. if (skeletonsLength > 0 && defined(node.skin)) {
  447. const skin = gltf.skins[node.skin];
  448. skin.skeleton = globalMapping.nodes[skeletons[0]];
  449. }
  450. delete node.skeletons;
  451. }
  452. if (defined(node.jointName)) {
  453. delete node.jointName;
  454. }
  455. });
  456. ForEach.skin(gltf, function (skin) {
  457. if (defined(skin.inverseBindMatrices)) {
  458. skin.inverseBindMatrices =
  459. globalMapping.accessors[skin.inverseBindMatrices];
  460. }
  461. const jointNames = skin.jointNames;
  462. if (defined(jointNames)) {
  463. const joints = [];
  464. const jointNamesLength = jointNames.length;
  465. for (i = 0; i < jointNamesLength; ++i) {
  466. joints[i] = jointNameToId[jointNames[i]];
  467. }
  468. skin.joints = joints;
  469. delete skin.jointNames;
  470. }
  471. });
  472. ForEach.scene(gltf, function (scene) {
  473. const sceneNodes = scene.nodes;
  474. if (defined(sceneNodes)) {
  475. const sceneNodesLength = sceneNodes.length;
  476. for (i = 0; i < sceneNodesLength; ++i) {
  477. sceneNodes[i] = globalMapping.nodes[sceneNodes[i]];
  478. }
  479. }
  480. });
  481. ForEach.animation(gltf, function (animation) {
  482. const samplerMapping = {};
  483. animation.samplers = objectToArray(animation.samplers, samplerMapping);
  484. ForEach.animationSampler(animation, function (sampler) {
  485. sampler.input = globalMapping.accessors[sampler.input];
  486. sampler.output = globalMapping.accessors[sampler.output];
  487. });
  488. ForEach.animationChannel(animation, function (channel) {
  489. channel.sampler = samplerMapping[channel.sampler];
  490. const target = channel.target;
  491. if (defined(target)) {
  492. target.node = globalMapping.nodes[target.id];
  493. delete target.id;
  494. }
  495. });
  496. });
  497. ForEach.material(gltf, function (material) {
  498. if (defined(material.technique)) {
  499. material.technique = globalMapping.techniques[material.technique];
  500. }
  501. ForEach.materialValue(material, function (value, name) {
  502. if (typeof value === "string") {
  503. material.values[name] = {
  504. index: globalMapping.textures[value],
  505. };
  506. }
  507. });
  508. const extensions = material.extensions;
  509. if (defined(extensions)) {
  510. const materialsCommon = extensions.KHR_materials_common;
  511. if (defined(materialsCommon)) {
  512. ForEach.materialValue(materialsCommon, function (value, name) {
  513. if (typeof value === "string") {
  514. materialsCommon.values[name] = {
  515. index: globalMapping.textures[value],
  516. };
  517. }
  518. });
  519. }
  520. }
  521. });
  522. ForEach.image(gltf, function (image) {
  523. const extensions = image.extensions;
  524. if (defined(extensions)) {
  525. const binaryGltf = extensions.KHR_binary_glTF;
  526. if (defined(binaryGltf)) {
  527. image.bufferView = globalMapping.bufferViews[binaryGltf.bufferView];
  528. image.mimeType = binaryGltf.mimeType;
  529. delete extensions.KHR_binary_glTF;
  530. }
  531. if (Object.keys(extensions).length === 0) {
  532. delete image.extensions;
  533. }
  534. }
  535. });
  536. ForEach.texture(gltf, function (texture) {
  537. if (defined(texture.sampler)) {
  538. texture.sampler = globalMapping.samplers[texture.sampler];
  539. }
  540. if (defined(texture.source)) {
  541. texture.source = globalMapping.images[texture.source];
  542. }
  543. });
  544. }
  545. function removeAnimationSamplerNames(gltf) {
  546. ForEach.animation(gltf, function (animation) {
  547. ForEach.animationSampler(animation, function (sampler) {
  548. delete sampler.name;
  549. });
  550. });
  551. }
  552. function removeEmptyArrays(gltf) {
  553. for (const topLevelId in gltf) {
  554. if (Object.prototype.hasOwnProperty.call(gltf, topLevelId)) {
  555. const array = gltf[topLevelId];
  556. if (Array.isArray(array) && array.length === 0) {
  557. delete gltf[topLevelId];
  558. }
  559. }
  560. }
  561. ForEach.node(gltf, function (node) {
  562. if (defined(node.children) && node.children.length === 0) {
  563. delete node.children;
  564. }
  565. });
  566. }
  567. function stripAsset(gltf) {
  568. const asset = gltf.asset;
  569. delete asset.profile;
  570. delete asset.premultipliedAlpha;
  571. }
  572. const knownExtensions = {
  573. CESIUM_RTC: true,
  574. KHR_materials_common: true,
  575. WEB3D_quantized_attributes: true,
  576. };
  577. function requireKnownExtensions(gltf) {
  578. const extensionsUsed = gltf.extensionsUsed;
  579. gltf.extensionsRequired = defaultValue(gltf.extensionsRequired, []);
  580. if (defined(extensionsUsed)) {
  581. const extensionsUsedLength = extensionsUsed.length;
  582. for (let i = 0; i < extensionsUsedLength; ++i) {
  583. const extension = extensionsUsed[i];
  584. if (defined(knownExtensions[extension])) {
  585. gltf.extensionsRequired.push(extension);
  586. }
  587. }
  588. }
  589. }
  590. function removeBufferType(gltf) {
  591. ForEach.buffer(gltf, function (buffer) {
  592. delete buffer.type;
  593. });
  594. }
  595. function removeTextureProperties(gltf) {
  596. ForEach.texture(gltf, function (texture) {
  597. delete texture.format;
  598. delete texture.internalFormat;
  599. delete texture.target;
  600. delete texture.type;
  601. });
  602. }
  603. function requireAttributeSetIndex(gltf) {
  604. ForEach.mesh(gltf, function (mesh) {
  605. ForEach.meshPrimitive(mesh, function (primitive) {
  606. ForEach.meshPrimitiveAttribute(
  607. primitive,
  608. function (accessorId, semantic) {
  609. if (semantic === "TEXCOORD") {
  610. primitive.attributes.TEXCOORD_0 = accessorId;
  611. } else if (semantic === "COLOR") {
  612. primitive.attributes.COLOR_0 = accessorId;
  613. }
  614. }
  615. );
  616. delete primitive.attributes.TEXCOORD;
  617. delete primitive.attributes.COLOR;
  618. });
  619. });
  620. ForEach.technique(gltf, function (technique) {
  621. ForEach.techniqueParameter(technique, function (parameter) {
  622. const semantic = parameter.semantic;
  623. if (defined(semantic)) {
  624. if (semantic === "TEXCOORD") {
  625. parameter.semantic = "TEXCOORD_0";
  626. } else if (semantic === "COLOR") {
  627. parameter.semantic = "COLOR_0";
  628. }
  629. }
  630. });
  631. });
  632. }
  633. const knownSemantics = {
  634. POSITION: true,
  635. NORMAL: true,
  636. TANGENT: true,
  637. };
  638. const indexedSemantics = {
  639. COLOR: "COLOR",
  640. JOINT: "JOINTS",
  641. JOINTS: "JOINTS",
  642. TEXCOORD: "TEXCOORD",
  643. WEIGHT: "WEIGHTS",
  644. WEIGHTS: "WEIGHTS",
  645. };
  646. function underscoreApplicationSpecificSemantics(gltf) {
  647. const mappedSemantics = {};
  648. ForEach.mesh(gltf, function (mesh) {
  649. ForEach.meshPrimitive(mesh, function (primitive) {
  650. /*eslint-disable no-unused-vars*/
  651. ForEach.meshPrimitiveAttribute(
  652. primitive,
  653. function (accessorId, semantic) {
  654. if (semantic.charAt(0) !== "_") {
  655. const setIndex = semantic.search(/_[0-9]+/g);
  656. let strippedSemantic = semantic;
  657. let suffix = "_0";
  658. if (setIndex >= 0) {
  659. strippedSemantic = semantic.substring(0, setIndex);
  660. suffix = semantic.substring(setIndex);
  661. }
  662. let newSemantic;
  663. const indexedSemantic = indexedSemantics[strippedSemantic];
  664. if (defined(indexedSemantic)) {
  665. newSemantic = indexedSemantic + suffix;
  666. mappedSemantics[semantic] = newSemantic;
  667. } else if (!defined(knownSemantics[strippedSemantic])) {
  668. newSemantic = "_" + semantic;
  669. mappedSemantics[semantic] = newSemantic;
  670. }
  671. }
  672. }
  673. );
  674. for (const semantic in mappedSemantics) {
  675. if (Object.prototype.hasOwnProperty.call(mappedSemantics, semantic)) {
  676. const mappedSemantic = mappedSemantics[semantic];
  677. const accessorId = primitive.attributes[semantic];
  678. if (defined(accessorId)) {
  679. delete primitive.attributes[semantic];
  680. primitive.attributes[mappedSemantic] = accessorId;
  681. }
  682. }
  683. }
  684. });
  685. });
  686. ForEach.technique(gltf, function (technique) {
  687. ForEach.techniqueParameter(technique, function (parameter) {
  688. const mappedSemantic = mappedSemantics[parameter.semantic];
  689. if (defined(mappedSemantic)) {
  690. parameter.semantic = mappedSemantic;
  691. }
  692. });
  693. });
  694. }
  695. function clampCameraParameters(gltf) {
  696. ForEach.camera(gltf, function (camera) {
  697. const perspective = camera.perspective;
  698. if (defined(perspective)) {
  699. const aspectRatio = perspective.aspectRatio;
  700. if (defined(aspectRatio) && aspectRatio === 0.0) {
  701. delete perspective.aspectRatio;
  702. }
  703. const yfov = perspective.yfov;
  704. if (defined(yfov) && yfov === 0.0) {
  705. perspective.yfov = 1.0;
  706. }
  707. }
  708. });
  709. }
  710. function computeAccessorByteStride(gltf, accessor) {
  711. return defined(accessor.byteStride) && accessor.byteStride !== 0
  712. ? accessor.byteStride
  713. : getAccessorByteStride(gltf, accessor);
  714. }
  715. function requireByteLength(gltf) {
  716. ForEach.buffer(gltf, function (buffer) {
  717. if (!defined(buffer.byteLength)) {
  718. buffer.byteLength = buffer.extras._pipeline.source.length;
  719. }
  720. });
  721. ForEach.accessor(gltf, function (accessor) {
  722. const bufferViewId = accessor.bufferView;
  723. if (defined(bufferViewId)) {
  724. const bufferView = gltf.bufferViews[bufferViewId];
  725. const accessorByteStride = computeAccessorByteStride(gltf, accessor);
  726. const accessorByteEnd =
  727. accessor.byteOffset + accessor.count * accessorByteStride;
  728. bufferView.byteLength = Math.max(
  729. defaultValue(bufferView.byteLength, 0),
  730. accessorByteEnd
  731. );
  732. }
  733. });
  734. }
  735. function moveByteStrideToBufferView(gltf) {
  736. let i;
  737. let j;
  738. let bufferView;
  739. const bufferViews = gltf.bufferViews;
  740. const bufferViewHasVertexAttributes = {};
  741. ForEach.accessorContainingVertexAttributeData(gltf, function (accessorId) {
  742. const accessor = gltf.accessors[accessorId];
  743. if (defined(accessor.bufferView)) {
  744. bufferViewHasVertexAttributes[accessor.bufferView] = true;
  745. }
  746. });
  747. // Map buffer views to a list of accessors
  748. const bufferViewMap = {};
  749. ForEach.accessor(gltf, function (accessor) {
  750. if (defined(accessor.bufferView)) {
  751. bufferViewMap[accessor.bufferView] = defaultValue(
  752. bufferViewMap[accessor.bufferView],
  753. []
  754. );
  755. bufferViewMap[accessor.bufferView].push(accessor);
  756. }
  757. });
  758. // Split accessors with different byte strides
  759. for (const bufferViewId in bufferViewMap) {
  760. if (Object.prototype.hasOwnProperty.call(bufferViewMap, bufferViewId)) {
  761. bufferView = bufferViews[bufferViewId];
  762. const accessors = bufferViewMap[bufferViewId];
  763. accessors.sort(function (a, b) {
  764. return a.byteOffset - b.byteOffset;
  765. });
  766. let currentByteOffset = 0;
  767. let currentIndex = 0;
  768. const accessorsLength = accessors.length;
  769. for (i = 0; i < accessorsLength; ++i) {
  770. let accessor = accessors[i];
  771. const accessorByteStride = computeAccessorByteStride(gltf, accessor);
  772. const accessorByteOffset = accessor.byteOffset;
  773. const accessorByteLength = accessor.count * accessorByteStride;
  774. delete accessor.byteStride;
  775. const hasNextAccessor = i < accessorsLength - 1;
  776. const nextAccessorByteStride = hasNextAccessor
  777. ? computeAccessorByteStride(gltf, accessors[i + 1])
  778. : undefined;
  779. if (accessorByteStride !== nextAccessorByteStride) {
  780. const newBufferView = clone(bufferView, true);
  781. if (bufferViewHasVertexAttributes[bufferViewId]) {
  782. newBufferView.byteStride = accessorByteStride;
  783. }
  784. newBufferView.byteOffset += currentByteOffset;
  785. newBufferView.byteLength =
  786. accessorByteOffset + accessorByteLength - currentByteOffset;
  787. const newBufferViewId = addToArray(bufferViews, newBufferView);
  788. for (j = currentIndex; j <= i; ++j) {
  789. accessor = accessors[j];
  790. accessor.bufferView = newBufferViewId;
  791. accessor.byteOffset = accessor.byteOffset - currentByteOffset;
  792. }
  793. // Set current byte offset to next accessor's byte offset
  794. currentByteOffset = hasNextAccessor
  795. ? accessors[i + 1].byteOffset
  796. : undefined;
  797. currentIndex = i + 1;
  798. }
  799. }
  800. }
  801. }
  802. // Remove unused buffer views
  803. removeUnusedElements(gltf, ["accessor", "bufferView", "buffer"]);
  804. }
  805. function requirePositionAccessorMinMax(gltf) {
  806. ForEach.accessorWithSemantic(gltf, "POSITION", function (accessorId) {
  807. const accessor = gltf.accessors[accessorId];
  808. if (!defined(accessor.min) || !defined(accessor.max)) {
  809. const minMax = findAccessorMinMax(gltf, accessor);
  810. accessor.min = minMax.min;
  811. accessor.max = minMax.max;
  812. }
  813. });
  814. }
  815. function isNodeEmpty(node) {
  816. return (
  817. (!defined(node.children) || node.children.length === 0) &&
  818. (!defined(node.meshes) || node.meshes.length === 0) &&
  819. !defined(node.camera) &&
  820. !defined(node.skin) &&
  821. !defined(node.skeletons) &&
  822. !defined(node.jointName) &&
  823. (!defined(node.translation) ||
  824. Cartesian3.fromArray(node.translation).equals(Cartesian3.ZERO)) &&
  825. (!defined(node.scale) ||
  826. Cartesian3.fromArray(node.scale).equals(new Cartesian3(1.0, 1.0, 1.0))) &&
  827. (!defined(node.rotation) ||
  828. Cartesian4.fromArray(node.rotation).equals(
  829. new Cartesian4(0.0, 0.0, 0.0, 1.0)
  830. )) &&
  831. (!defined(node.matrix) ||
  832. Matrix4.fromColumnMajorArray(node.matrix).equals(Matrix4.IDENTITY)) &&
  833. !defined(node.extensions) &&
  834. !defined(node.extras)
  835. );
  836. }
  837. function deleteNode(gltf, nodeId) {
  838. // Remove from list of nodes in scene
  839. ForEach.scene(gltf, function (scene) {
  840. const sceneNodes = scene.nodes;
  841. if (defined(sceneNodes)) {
  842. const sceneNodesLength = sceneNodes.length;
  843. for (let i = sceneNodesLength; i >= 0; --i) {
  844. if (sceneNodes[i] === nodeId) {
  845. sceneNodes.splice(i, 1);
  846. return;
  847. }
  848. }
  849. }
  850. });
  851. // Remove parent node's reference to this node, and delete the parent if also empty
  852. ForEach.node(gltf, function (parentNode, parentNodeId) {
  853. if (defined(parentNode.children)) {
  854. const index = parentNode.children.indexOf(nodeId);
  855. if (index > -1) {
  856. parentNode.children.splice(index, 1);
  857. if (isNodeEmpty(parentNode)) {
  858. deleteNode(gltf, parentNodeId);
  859. }
  860. }
  861. }
  862. });
  863. delete gltf.nodes[nodeId];
  864. }
  865. function removeEmptyNodes(gltf) {
  866. ForEach.node(gltf, function (node, nodeId) {
  867. if (isNodeEmpty(node)) {
  868. deleteNode(gltf, nodeId);
  869. }
  870. });
  871. return gltf;
  872. }
  873. function requireAnimationAccessorMinMax(gltf) {
  874. ForEach.animation(gltf, function (animation) {
  875. ForEach.animationSampler(animation, function (sampler) {
  876. const accessor = gltf.accessors[sampler.input];
  877. if (!defined(accessor.min) || !defined(accessor.max)) {
  878. const minMax = findAccessorMinMax(gltf, accessor);
  879. accessor.min = minMax.min;
  880. accessor.max = minMax.max;
  881. }
  882. });
  883. });
  884. }
  885. function glTF10to20(gltf) {
  886. gltf.asset = defaultValue(gltf.asset, {});
  887. gltf.asset.version = "2.0";
  888. // material.instanceTechnique properties should be directly on the material. instanceTechnique is a gltf 0.8 property but is seen in some 1.0 models.
  889. updateInstanceTechniques(gltf);
  890. // animation.samplers now refers directly to accessors and animation.parameters should be removed
  891. removeAnimationSamplersIndirection(gltf);
  892. // Remove empty nodes and re-assign referencing indices
  893. removeEmptyNodes(gltf);
  894. // Top-level objects are now arrays referenced by index instead of id
  895. objectsToArrays(gltf);
  896. // Animation.sampler objects cannot have names
  897. removeAnimationSamplerNames(gltf);
  898. // asset.profile no longer exists
  899. stripAsset(gltf);
  900. // Move known extensions from extensionsUsed to extensionsRequired
  901. requireKnownExtensions(gltf);
  902. // bufferView.byteLength and buffer.byteLength are required
  903. requireByteLength(gltf);
  904. // byteStride moved from accessor to bufferView
  905. moveByteStrideToBufferView(gltf);
  906. // accessor.min and accessor.max must be defined for accessors containing POSITION attributes
  907. requirePositionAccessorMinMax(gltf);
  908. // An animation sampler's input accessor must have min and max properties defined
  909. requireAnimationAccessorMinMax(gltf);
  910. // buffer.type is unnecessary and should be removed
  911. removeBufferType(gltf);
  912. // Remove format, internalFormat, target, and type
  913. removeTextureProperties(gltf);
  914. // TEXCOORD and COLOR attributes must be written with a set index (TEXCOORD_#)
  915. requireAttributeSetIndex(gltf);
  916. // Add underscores to application-specific parameters
  917. underscoreApplicationSpecificSemantics(gltf);
  918. // Accessors referenced by JOINTS_0 and WEIGHTS_0 attributes must have correct component types
  919. updateAccessorComponentTypes(gltf);
  920. // Clamp camera parameters
  921. clampCameraParameters(gltf);
  922. // Move legacy technique render states to material properties and add KHR_blend extension blending functions
  923. moveTechniqueRenderStates(gltf);
  924. // Add material techniques to KHR_techniques_webgl extension, removing shaders, programs, and techniques
  925. moveTechniquesToExtension(gltf);
  926. // Remove empty arrays
  927. removeEmptyArrays(gltf);
  928. }
  929. export default updateVersion;