updateVersion.js 37 KB

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