Source: Scene/Model.js

  1. /*global define*/
  2. define([
  3. '../Core/BoundingSphere',
  4. '../Core/Cartesian2',
  5. '../Core/Cartesian3',
  6. '../Core/Cartesian4',
  7. '../Core/Cartographic',
  8. '../Core/clone',
  9. '../Core/combine',
  10. '../Core/ComponentDatatype',
  11. '../Core/defaultValue',
  12. '../Core/defined',
  13. '../Core/defineProperties',
  14. '../Core/destroyObject',
  15. '../Core/DeveloperError',
  16. '../Core/DistanceDisplayCondition',
  17. '../Core/FeatureDetection',
  18. '../Core/getAbsoluteUri',
  19. '../Core/getBaseUri',
  20. '../Core/getMagic',
  21. '../Core/getStringFromTypedArray',
  22. '../Core/IndexDatatype',
  23. '../Core/loadArrayBuffer',
  24. '../Core/loadImage',
  25. '../Core/loadImageFromTypedArray',
  26. '../Core/loadText',
  27. '../Core/Math',
  28. '../Core/Matrix2',
  29. '../Core/Matrix3',
  30. '../Core/Matrix4',
  31. '../Core/PrimitiveType',
  32. '../Core/Quaternion',
  33. '../Core/Queue',
  34. '../Core/RuntimeError',
  35. '../Core/Transforms',
  36. '../Renderer/Buffer',
  37. '../Renderer/BufferUsage',
  38. '../Renderer/DrawCommand',
  39. '../Renderer/RenderState',
  40. '../Renderer/Sampler',
  41. '../Renderer/ShaderProgram',
  42. '../Renderer/ShaderSource',
  43. '../Renderer/Texture',
  44. '../Renderer/TextureMinificationFilter',
  45. '../Renderer/TextureWrap',
  46. '../Renderer/VertexArray',
  47. '../Renderer/WebGLConstants',
  48. '../ThirdParty/gltfDefaults',
  49. '../ThirdParty/Uri',
  50. '../ThirdParty/when',
  51. './getBinaryAccessor',
  52. './HeightReference',
  53. './ModelAnimationCache',
  54. './ModelAnimationCollection',
  55. './ModelMaterial',
  56. './modelMaterialsCommon',
  57. './ModelMesh',
  58. './ModelNode',
  59. './Pass',
  60. './SceneMode',
  61. './ShadowMode'
  62. ], function(
  63. BoundingSphere,
  64. Cartesian2,
  65. Cartesian3,
  66. Cartesian4,
  67. Cartographic,
  68. clone,
  69. combine,
  70. ComponentDatatype,
  71. defaultValue,
  72. defined,
  73. defineProperties,
  74. destroyObject,
  75. DeveloperError,
  76. DistanceDisplayCondition,
  77. FeatureDetection,
  78. getAbsoluteUri,
  79. getBaseUri,
  80. getMagic,
  81. getStringFromTypedArray,
  82. IndexDatatype,
  83. loadArrayBuffer,
  84. loadImage,
  85. loadImageFromTypedArray,
  86. loadText,
  87. CesiumMath,
  88. Matrix2,
  89. Matrix3,
  90. Matrix4,
  91. PrimitiveType,
  92. Quaternion,
  93. Queue,
  94. RuntimeError,
  95. Transforms,
  96. Buffer,
  97. BufferUsage,
  98. DrawCommand,
  99. RenderState,
  100. Sampler,
  101. ShaderProgram,
  102. ShaderSource,
  103. Texture,
  104. TextureMinificationFilter,
  105. TextureWrap,
  106. VertexArray,
  107. WebGLConstants,
  108. gltfDefaults,
  109. Uri,
  110. when,
  111. getBinaryAccessor,
  112. HeightReference,
  113. ModelAnimationCache,
  114. ModelAnimationCollection,
  115. ModelMaterial,
  116. modelMaterialsCommon,
  117. ModelMesh,
  118. ModelNode,
  119. Pass,
  120. SceneMode,
  121. ShadowMode) {
  122. 'use strict';
  123. // Bail out if the browser doesn't support typed arrays, to prevent the setup function
  124. // from failing, since we won't be able to create a WebGL context anyway.
  125. if (!FeatureDetection.supportsTypedArrays()) {
  126. return {};
  127. }
  128. var yUpToZUp = Matrix4.fromRotationTranslation(Matrix3.fromRotationX(CesiumMath.PI_OVER_TWO));
  129. var boundingSphereCartesian3Scratch = new Cartesian3();
  130. var ModelState = {
  131. NEEDS_LOAD : 0,
  132. LOADING : 1,
  133. LOADED : 2, // Renderable, but textures can still be pending when incrementallyLoadTextures is true.
  134. FAILED : 3
  135. };
  136. // GLTF_SPEC: Figure out correct mime types (https://github.com/KhronosGroup/glTF/issues/412)
  137. var defaultModelAccept = 'model/vnd.gltf.binary,model/vnd.gltf+json,model/gltf.binary,model/gltf+json;q=0.8,application/json;q=0.2,*/*;q=0.01';
  138. function LoadResources() {
  139. this.buffersToCreate = new Queue();
  140. this.buffers = {};
  141. this.pendingBufferLoads = 0;
  142. this.programsToCreate = new Queue();
  143. this.shaders = {};
  144. this.pendingShaderLoads = 0;
  145. this.texturesToCreate = new Queue();
  146. this.pendingTextureLoads = 0;
  147. this.texturesToCreateFromBufferView = new Queue();
  148. this.pendingBufferViewToImage = 0;
  149. this.createSamplers = true;
  150. this.createSkins = true;
  151. this.createRuntimeAnimations = true;
  152. this.createVertexArrays = true;
  153. this.createRenderStates = true;
  154. this.createUniformMaps = true;
  155. this.createRuntimeNodes = true;
  156. this.skinnedNodesIds = [];
  157. }
  158. LoadResources.prototype.getBuffer = function(bufferView) {
  159. return getSubarray(this.buffers[bufferView.buffer], bufferView.byteOffset, bufferView.byteLength);
  160. };
  161. LoadResources.prototype.finishedPendingBufferLoads = function() {
  162. return (this.pendingBufferLoads === 0);
  163. };
  164. LoadResources.prototype.finishedBuffersCreation = function() {
  165. return ((this.pendingBufferLoads === 0) && (this.buffersToCreate.length === 0));
  166. };
  167. LoadResources.prototype.finishedProgramCreation = function() {
  168. return ((this.pendingShaderLoads === 0) && (this.programsToCreate.length === 0));
  169. };
  170. LoadResources.prototype.finishedTextureCreation = function() {
  171. var finishedPendingLoads = (this.pendingTextureLoads === 0);
  172. var finishedResourceCreation =
  173. (this.texturesToCreate.length === 0) &&
  174. (this.texturesToCreateFromBufferView.length === 0);
  175. return finishedPendingLoads && finishedResourceCreation;
  176. };
  177. LoadResources.prototype.finishedEverythingButTextureCreation = function() {
  178. var finishedPendingLoads =
  179. (this.pendingBufferLoads === 0) &&
  180. (this.pendingShaderLoads === 0);
  181. var finishedResourceCreation =
  182. (this.buffersToCreate.length === 0) &&
  183. (this.programsToCreate.length === 0) &&
  184. (this.pendingBufferViewToImage === 0);
  185. return finishedPendingLoads && finishedResourceCreation;
  186. };
  187. LoadResources.prototype.finished = function() {
  188. return this.finishedTextureCreation() && this.finishedEverythingButTextureCreation();
  189. };
  190. ///////////////////////////////////////////////////////////////////////////
  191. function setCachedGltf(model, cachedGltf) {
  192. model._cachedGltf = cachedGltf;
  193. model._animationIds = getAnimationIds(cachedGltf);
  194. }
  195. // glTF JSON can be big given embedded geometry, textures, and animations, so we
  196. // cache it across all models using the same url/cache-key. This also reduces the
  197. // slight overhead in assigning defaults to missing values.
  198. //
  199. // Note that this is a global cache, compared to renderer resources, which
  200. // are cached per context.
  201. function CachedGltf(options) {
  202. this._gltf = modelMaterialsCommon(gltfDefaults(options.gltf));
  203. this._bgltf = options.bgltf;
  204. this.ready = options.ready;
  205. this.modelsToLoad = [];
  206. this.count = 0;
  207. }
  208. defineProperties(CachedGltf.prototype, {
  209. gltf : {
  210. set : function(value) {
  211. this._gltf = modelMaterialsCommon(gltfDefaults(value));
  212. },
  213. get : function() {
  214. return this._gltf;
  215. }
  216. },
  217. bgltf : {
  218. get : function() {
  219. return this._bgltf;
  220. }
  221. }
  222. });
  223. CachedGltf.prototype.makeReady = function(gltfJson, bgltf) {
  224. this.gltf = gltfJson;
  225. this._bgltf = bgltf;
  226. var models = this.modelsToLoad;
  227. var length = models.length;
  228. for (var i = 0; i < length; ++i) {
  229. var m = models[i];
  230. if (!m.isDestroyed()) {
  231. setCachedGltf(m, this);
  232. }
  233. }
  234. this.modelsToLoad = undefined;
  235. this.ready = true;
  236. };
  237. function getAnimationIds(cachedGltf) {
  238. var animationIds = [];
  239. if (defined(cachedGltf) && defined(cachedGltf.gltf)) {
  240. var animations = cachedGltf.gltf.animations;
  241. for (var id in animations) {
  242. if (animations.hasOwnProperty(id)) {
  243. animationIds.push(id);
  244. }
  245. }
  246. }
  247. return animationIds;
  248. }
  249. var gltfCache = {};
  250. ///////////////////////////////////////////////////////////////////////////
  251. /**
  252. * A 3D model based on glTF, the runtime asset format for WebGL, OpenGL ES, and OpenGL.
  253. * <p>
  254. * Cesium includes support for geometry and materials, glTF animations, and glTF skinning.
  255. * In addition, individual glTF nodes are pickable with {@link Scene#pick} and animatable
  256. * with {@link Model#getNode}. glTF cameras and lights are not currently supported.
  257. * </p>
  258. * <p>
  259. * An external glTF asset is created with {@link Model.fromGltf}. glTF JSON can also be
  260. * created at runtime and passed to this constructor function. In either case, the
  261. * {@link Model#readyPromise} is resolved when the model is ready to render, i.e.,
  262. * when the external binary, image, and shader files are downloaded and the WebGL
  263. * resources are created.
  264. * </p>
  265. * <p>
  266. * For high-precision rendering, Cesium supports the CESIUM_RTC extension, which introduces the
  267. * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated
  268. * relative to a local origin.
  269. * </p>
  270. *
  271. * @alias Model
  272. * @constructor
  273. *
  274. * @param {Object} [options] Object with the following properties:
  275. * @param {Object|ArrayBuffer|Uint8Array} [options.gltf] The object for the glTF JSON or an arraybuffer of Binary glTF defined by the KHR_binary_glTF extension.
  276. * @param {String} [options.basePath=''] The base path that paths in the glTF JSON are relative to.
  277. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown.
  278. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
  279. * @param {Number} [options.scale=1.0] A uniform scale applied to this model.
  280. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
  281. * @param {Number} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize.
  282. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}.
  283. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
  284. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
  285. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded.
  286. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from each light source.
  287. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
  288. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
  289. * @param {HeightReference} [options.heightReference] Determines how the model is drawn relative to terrain.
  290. * @param {Scene} [options.scene] Must be passed in for models that use the height reference property.
  291. * @param {DistanceDisplayCondition} [options.istanceDisplayCondition] The condition specifying at what distance from the camera that this model will be displayed.
  292. *
  293. * @exception {DeveloperError} bgltf is not a valid Binary glTF file.
  294. * @exception {DeveloperError} Only glTF Binary version 1 is supported.
  295. *
  296. * @see Model.fromGltf
  297. *
  298. * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=3D%20Models.html|Cesium Sandcastle Models Demo}
  299. */
  300. function Model(options) {
  301. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  302. var cacheKey = options.cacheKey;
  303. this._cacheKey = cacheKey;
  304. this._cachedGltf = undefined;
  305. this._releaseGltfJson = defaultValue(options.releaseGltfJson, false);
  306. this._animationIds = undefined;
  307. var cachedGltf;
  308. if (defined(cacheKey) && defined(gltfCache[cacheKey]) && gltfCache[cacheKey].ready) {
  309. // glTF JSON is in cache and ready
  310. cachedGltf = gltfCache[cacheKey];
  311. ++cachedGltf.count;
  312. } else {
  313. // glTF was explicitly provided, e.g., when a user uses the Model constructor directly
  314. var gltf = options.gltf;
  315. if (defined(gltf)) {
  316. if (gltf instanceof ArrayBuffer) {
  317. gltf = new Uint8Array(gltf);
  318. }
  319. if (gltf instanceof Uint8Array) {
  320. // Binary glTF
  321. var result = parseBinaryGltfHeader(gltf);
  322. // KHR_binary_glTF is from the beginning of the binary section
  323. if (result.binaryOffset !== 0) {
  324. gltf = gltf.subarray(result.binaryOffset);
  325. }
  326. cachedGltf = new CachedGltf({
  327. gltf : result.glTF,
  328. bgltf : gltf,
  329. ready : true
  330. });
  331. } else {
  332. // Normal glTF (JSON)
  333. cachedGltf = new CachedGltf({
  334. gltf : options.gltf,
  335. ready : true
  336. });
  337. }
  338. cachedGltf.count = 1;
  339. if (defined(cacheKey)) {
  340. gltfCache[cacheKey] = cachedGltf;
  341. }
  342. }
  343. }
  344. setCachedGltf(this, cachedGltf);
  345. this._basePath = defaultValue(options.basePath, '');
  346. var docUri = new Uri(document.location.href);
  347. var modelUri = new Uri(this._basePath);
  348. this._baseUri = modelUri.resolve(docUri);
  349. /**
  350. * Determines if the model primitive will be shown.
  351. *
  352. * @type {Boolean}
  353. *
  354. * @default true
  355. */
  356. this.show = defaultValue(options.show, true);
  357. /**
  358. * The 4x4 transformation matrix that transforms the model from model to world coordinates.
  359. * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  360. * Local reference frames can be used by providing a different transformation matrix, like that returned
  361. * by {@link Transforms.eastNorthUpToFixedFrame}.
  362. *
  363. * @type {Matrix4}
  364. *
  365. * @default {@link Matrix4.IDENTITY}
  366. *
  367. * @example
  368. * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  369. * m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  370. */
  371. this.modelMatrix = Matrix4.clone(defaultValue(options.modelMatrix, Matrix4.IDENTITY));
  372. this._modelMatrix = Matrix4.clone(this.modelMatrix);
  373. this._clampedModelMatrix = undefined;
  374. /**
  375. * A uniform scale applied to this model before the {@link Model#modelMatrix}.
  376. * Values greater than <code>1.0</code> increase the size of the model; values
  377. * less than <code>1.0</code> decrease.
  378. *
  379. * @type {Number}
  380. *
  381. * @default 1.0
  382. */
  383. this.scale = defaultValue(options.scale, 1.0);
  384. this._scale = this.scale;
  385. /**
  386. * The approximate minimum pixel size of the model regardless of zoom.
  387. * This can be used to ensure that a model is visible even when the viewer
  388. * zooms out. When <code>0.0</code>, no minimum size is enforced.
  389. *
  390. * @type {Number}
  391. *
  392. * @default 0.0
  393. */
  394. this.minimumPixelSize = defaultValue(options.minimumPixelSize, 0.0);
  395. this._minimumPixelSize = this.minimumPixelSize;
  396. /**
  397. * The maximum scale size for a model. This can be used to give
  398. * an upper limit to the {@link Model#minimumPixelSize}, ensuring that the model
  399. * is never an unreasonable scale.
  400. *
  401. * @type {Number}
  402. */
  403. this.maximumScale = options.maximumScale;
  404. this._maximumScale = this.maximumScale;
  405. /**
  406. * User-defined object returned when the model is picked.
  407. *
  408. * @type Object
  409. *
  410. * @default undefined
  411. *
  412. * @see Scene#pick
  413. */
  414. this.id = options.id;
  415. this._id = options.id;
  416. /**
  417. * Returns the height reference of the model
  418. *
  419. * @memberof Model.prototype
  420. *
  421. * @type {HeightReference}
  422. *
  423. * @default HeightReference.NONE
  424. */
  425. this.heightReference = defaultValue(options.heightReference, HeightReference.NONE);
  426. this._heightReference = this.heightReference;
  427. this._heightChanged = false;
  428. this._removeUpdateHeightCallback = undefined;
  429. var scene = options.scene;
  430. this._scene = scene;
  431. if (defined(scene)) {
  432. scene.terrainProviderChanged.addEventListener(function() {
  433. this._heightChanged = true;
  434. }, this);
  435. }
  436. /**
  437. * Used for picking primitives that wrap a model.
  438. *
  439. * @private
  440. */
  441. this.pickPrimitive = options.pickPrimitive;
  442. this._allowPicking = defaultValue(options.allowPicking, true);
  443. this._ready = false;
  444. this._readyPromise = when.defer();
  445. /**
  446. * The currently playing glTF animations.
  447. *
  448. * @type {ModelAnimationCollection}
  449. */
  450. this.activeAnimations = new ModelAnimationCollection(this);
  451. this._defaultTexture = undefined;
  452. this._incrementallyLoadTextures = defaultValue(options.incrementallyLoadTextures, true);
  453. this._asynchronous = defaultValue(options.asynchronous, true);
  454. /**
  455. * Determines whether the model casts or receives shadows from each light source.
  456. *
  457. * @type {ShadowMode}
  458. *
  459. * @default ShadowMode.ENABLED
  460. */
  461. this.shadows = defaultValue(options.shadows, ShadowMode.ENABLED);
  462. this._shadows = this.shadows;
  463. /**
  464. * This property is for debugging only; it is not for production use nor is it optimized.
  465. * <p>
  466. * Draws the bounding sphere for each draw command in the model. A glTF primitive corresponds
  467. * to one draw command. A glTF mesh has an array of primitives, often of length one.
  468. * </p>
  469. *
  470. * @type {Boolean}
  471. *
  472. * @default false
  473. */
  474. this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false);
  475. this._debugShowBoundingVolume = false;
  476. /**
  477. * This property is for debugging only; it is not for production use nor is it optimized.
  478. * <p>
  479. * Draws the model in wireframe.
  480. * </p>
  481. *
  482. * @type {Boolean}
  483. *
  484. * @default false
  485. */
  486. this.debugWireframe = defaultValue(options.debugWireframe, false);
  487. this._debugWireframe = false;
  488. this._distanceDisplayCondition = options.distanceDisplayCondition;
  489. // Undocumented options
  490. this._precreatedAttributes = options.precreatedAttributes;
  491. this._vertexShaderLoaded = options.vertexShaderLoaded;
  492. this._fragmentShaderLoaded = options.fragmentShaderLoaded;
  493. this._uniformMapLoaded = options.uniformMapLoaded;
  494. this._pickVertexShaderLoaded = options.pickVertexShaderLoaded;
  495. this._pickFragmentShaderLoaded = options.pickFragmentShaderLoaded;
  496. this._pickUniformMapLoaded = options.pickUniformMapLoaded;
  497. this._ignoreCommands = defaultValue(options.ignoreCommands, false);
  498. /**
  499. * @private
  500. * @readonly
  501. */
  502. this.cull = defaultValue(options.cull, true);
  503. this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and scale
  504. this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins
  505. this._boundingSphere = undefined;
  506. this._scaledBoundingSphere = new BoundingSphere();
  507. this._state = ModelState.NEEDS_LOAD;
  508. this._loadResources = undefined;
  509. this._mode = undefined;
  510. this._perNodeShowDirty = false; // true when the Cesium API was used to change a node's show property
  511. this._cesiumAnimationsDirty = false; // true when the Cesium API, not a glTF animation, changed a node transform
  512. this._dirty = false; // true when the model was transformed this frame
  513. this._maxDirtyNumber = 0; // Used in place of a dirty boolean flag to avoid an extra graph traversal
  514. this._runtime = {
  515. animations : undefined,
  516. rootNodes : undefined,
  517. nodes : undefined, // Indexed with the node property's name, i.e., glTF id
  518. nodesByName : undefined, // Indexed with name property in the node
  519. skinnedNodes : undefined,
  520. meshesByName : undefined, // Indexed with the name property in the mesh
  521. materialsByName : undefined, // Indexed with the name property in the material
  522. materialsById : undefined // Indexed with the material's property name
  523. };
  524. this._uniformMaps = {}; // Not cached since it can be targeted by glTF animation
  525. this._extensionsUsed = undefined; // Cached used extensions in a hash-map so we don't have to search the gltf array
  526. this._quantizedUniforms = {}; // Quantized uniforms for each program for WEB3D_quantized_attributes
  527. this._programPrimitives = {};
  528. this._rendererResources = { // Cached between models with the same url/cache-key
  529. buffers : {},
  530. vertexArrays : {},
  531. programs : {},
  532. pickPrograms : {},
  533. textures : {},
  534. samplers : {},
  535. renderStates : {}
  536. };
  537. this._cachedRendererResources = undefined;
  538. this._loadRendererResourcesFromCache = false;
  539. this._nodeCommands = [];
  540. this._pickIds = [];
  541. // CESIUM_RTC extension
  542. this._rtcCenter = undefined; // in world coordinates
  543. this._rtcCenterEye = undefined; // in eye coordinates
  544. }
  545. defineProperties(Model.prototype, {
  546. /**
  547. * The object for the glTF JSON, including properties with default values omitted
  548. * from the JSON provided to this model.
  549. *
  550. * @memberof Model.prototype
  551. *
  552. * @type {Object}
  553. * @readonly
  554. *
  555. * @default undefined
  556. */
  557. gltf : {
  558. get : function() {
  559. return defined(this._cachedGltf) ? this._cachedGltf.gltf : undefined;
  560. }
  561. },
  562. /**
  563. * When <code>true</code>, the glTF JSON is not stored with the model once the model is
  564. * loaded (when {@link Model#ready} is <code>true</code>). This saves memory when
  565. * geometry, textures, and animations are embedded in the .gltf file, which is the
  566. * default for the {@link http://cesiumjs.org/convertmodel.html|Cesium model converter}.
  567. * This is especially useful for cases like 3D buildings, where each .gltf model is unique
  568. * and caching the glTF JSON is not effective.
  569. *
  570. * @memberof Model.prototype
  571. *
  572. * @type {Boolean}
  573. * @readonly
  574. *
  575. * @default false
  576. *
  577. * @private
  578. */
  579. releaseGltfJson : {
  580. get : function() {
  581. return this._releaseGltfJson;
  582. }
  583. },
  584. /**
  585. * The key identifying this model in the model cache for glTF JSON, renderer resources, and animations.
  586. * Caching saves memory and improves loading speed when several models with the same url are created.
  587. * <p>
  588. * This key is automatically generated when the model is created with {@link Model.fromGltf}. If the model
  589. * is created directly from glTF JSON using the {@link Model} constructor, this key can be manually
  590. * provided; otherwise, the model will not be changed.
  591. * </p>
  592. *
  593. * @memberof Model.prototype
  594. *
  595. * @type {String}
  596. * @readonly
  597. *
  598. * @private
  599. */
  600. cacheKey : {
  601. get : function() {
  602. return this._cacheKey;
  603. }
  604. },
  605. /**
  606. * The base path that paths in the glTF JSON are relative to. The base
  607. * path is the same path as the path containing the .gltf file
  608. * minus the .gltf file, when binary, image, and shader files are
  609. * in the same directory as the .gltf. When this is <code>''</code>,
  610. * the app's base path is used.
  611. *
  612. * @memberof Model.prototype
  613. *
  614. * @type {String}
  615. * @readonly
  616. *
  617. * @default ''
  618. */
  619. basePath : {
  620. get : function() {
  621. return this._basePath;
  622. }
  623. },
  624. /**
  625. * The model's bounding sphere in its local coordinate system. This does not take into
  626. * account glTF animations and skins nor does it take into account {@link Model#minimumPixelSize}.
  627. *
  628. * @memberof Model.prototype
  629. *
  630. * @type {BoundingSphere}
  631. * @readonly
  632. *
  633. * @default undefined
  634. *
  635. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  636. *
  637. * @example
  638. * // Center in WGS84 coordinates
  639. * var center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3());
  640. */
  641. boundingSphere : {
  642. get : function() {
  643. //>>includeStart('debug', pragmas.debug);
  644. if (this._state !== ModelState.LOADED) {
  645. throw new DeveloperError('The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.');
  646. }
  647. //>>includeEnd('debug');
  648. var modelMatrix = this.modelMatrix;
  649. if ((this.heightReference !== HeightReference.NONE) && this._clampedModelMatrix) {
  650. modelMatrix = this._clampedModelMatrix;
  651. }
  652. var nonUniformScale = Matrix4.getScale(modelMatrix, boundingSphereCartesian3Scratch);
  653. var scale = defined(this.maximumScale) ? Math.min(this.maximumScale, this.scale) : this.scale;
  654. Cartesian3.multiplyByScalar(nonUniformScale, scale, nonUniformScale);
  655. var scaledBoundingSphere = this._scaledBoundingSphere;
  656. scaledBoundingSphere.center = Cartesian3.multiplyComponents(this._boundingSphere.center, nonUniformScale, scaledBoundingSphere.center);
  657. scaledBoundingSphere.radius = Cartesian3.maximumComponent(nonUniformScale) * this._initialRadius;
  658. if (defined(this._rtcCenter)) {
  659. Cartesian3.add(this._rtcCenter, scaledBoundingSphere.center, scaledBoundingSphere.center);
  660. }
  661. return scaledBoundingSphere;
  662. }
  663. },
  664. /**
  665. * When <code>true</code>, this model is ready to render, i.e., the external binary, image,
  666. * and shader files were downloaded and the WebGL resources were created. This is set to
  667. * <code>true</code> right before {@link Model#readyPromise} is resolved.
  668. *
  669. * @memberof Model.prototype
  670. *
  671. * @type {Boolean}
  672. * @readonly
  673. *
  674. * @default false
  675. */
  676. ready : {
  677. get : function() {
  678. return this._ready;
  679. }
  680. },
  681. /**
  682. * Gets the promise that will be resolved when this model is ready to render, i.e., when the external binary, image,
  683. * and shader files were downloaded and the WebGL resources were created.
  684. * <p>
  685. * This promise is resolved at the end of the frame before the first frame the model is rendered in.
  686. * </p>
  687. *
  688. * @memberof Model.prototype
  689. * @type {Promise.<Model>}
  690. * @readonly
  691. *
  692. * @example
  693. * // Play all animations at half-speed when the model is ready to render
  694. * Cesium.when(model.readyPromise).then(function(model) {
  695. * model.activeAnimations.addAll({
  696. * speedup : 0.5
  697. * });
  698. * }).otherwise(function(error){
  699. * window.alert(error);
  700. * });
  701. *
  702. * @see Model#ready
  703. */
  704. readyPromise : {
  705. get : function() {
  706. return this._readyPromise.promise;
  707. }
  708. },
  709. /**
  710. * Determines if model WebGL resource creation will be spread out over several frames or
  711. * block until completion once all glTF files are loaded.
  712. *
  713. * @memberof Model.prototype
  714. *
  715. * @type {Boolean}
  716. * @readonly
  717. *
  718. * @default true
  719. */
  720. asynchronous : {
  721. get : function() {
  722. return this._asynchronous;
  723. }
  724. },
  725. /**
  726. * When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
  727. *
  728. * @memberof Model.prototype
  729. *
  730. * @type {Boolean}
  731. * @readonly
  732. *
  733. * @default true
  734. */
  735. allowPicking : {
  736. get : function() {
  737. return this._allowPicking;
  738. }
  739. },
  740. /**
  741. * Determine if textures may continue to stream in after the model is loaded.
  742. *
  743. * @memberof Model.prototype
  744. *
  745. * @type {Boolean}
  746. * @readonly
  747. *
  748. * @default true
  749. */
  750. incrementallyLoadTextures : {
  751. get : function() {
  752. return this._incrementallyLoadTextures;
  753. }
  754. },
  755. /**
  756. * Return the number of pending texture loads.
  757. *
  758. * @memberof Model.prototype
  759. *
  760. * @type {Number}
  761. * @readonly
  762. */
  763. pendingTextureLoads : {
  764. get : function() {
  765. return defined(this._loadResources) ? this._loadResources.pendingTextureLoads : 0;
  766. }
  767. },
  768. /**
  769. * Returns true if the model was transformed this frame
  770. *
  771. * @memberof Model.prototype
  772. *
  773. * @type {Boolean}
  774. * @readonly
  775. *
  776. * @private
  777. */
  778. dirty : {
  779. get : function() {
  780. return this._dirty;
  781. }
  782. },
  783. /**
  784. * Gets or sets the condition specifying at what distance from the camera that this model will be displayed.
  785. * @memberof Model.prototype
  786. * @type {DistanceDisplayCondition}
  787. * @default undefined
  788. */
  789. distanceDisplayCondition : {
  790. get : function() {
  791. return this._distanceDisplayCondition;
  792. },
  793. set : function(value) {
  794. //>>includeStart('debug', pragmas.debug);
  795. if (defined(value) && value.far <= value.near) {
  796. throw new DeveloperError('far must be greater than near');
  797. }
  798. //>>includeEnd('debug');
  799. this._distanceDisplayCondition = DistanceDisplayCondition.clone(value, this._distanceDisplayCondition);
  800. }
  801. }
  802. });
  803. var sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;
  804. /**
  805. * This function differs from the normal subarray function
  806. * because it takes offset and length, rather than begin and end.
  807. */
  808. function getSubarray(array, offset, length) {
  809. return array.subarray(offset, offset + length);
  810. }
  811. function containsGltfMagic(uint8Array) {
  812. var magic = getMagic(uint8Array);
  813. return magic === 'glTF';
  814. }
  815. function parseBinaryGltfHeader(uint8Array) {
  816. //>>includeStart('debug', pragmas.debug);
  817. if (!containsGltfMagic(uint8Array)) {
  818. throw new DeveloperError('bgltf is not a valid Binary glTF file.');
  819. }
  820. //>>includeEnd('debug');
  821. var view = new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength);
  822. var byteOffset = 0;
  823. byteOffset += sizeOfUint32; // Skip magic number
  824. //>>includeStart('debug', pragmas.debug);
  825. var version = view.getUint32(byteOffset, true);
  826. if (version !== 1) {
  827. throw new DeveloperError('Only Binary glTF version 1 is supported. Version ' + version + ' is not.');
  828. }
  829. //>>includeEnd('debug');
  830. byteOffset += sizeOfUint32;
  831. byteOffset += sizeOfUint32; // Skip length
  832. var sceneLength = view.getUint32(byteOffset, true);
  833. byteOffset += sizeOfUint32 + sizeOfUint32; // Skip sceneFormat
  834. var sceneOffset = byteOffset;
  835. var binOffset = sceneOffset + sceneLength;
  836. var json = getStringFromTypedArray(uint8Array, sceneOffset, sceneLength);
  837. return {
  838. glTF: JSON.parse(json),
  839. binaryOffset: binOffset
  840. };
  841. }
  842. /**
  843. * <p>
  844. * Creates a model from a glTF asset. When the model is ready to render, i.e., when the external binary, image,
  845. * and shader files are downloaded and the WebGL resources are created, the {@link Model#readyPromise} is resolved.
  846. * </p>
  847. * <p>
  848. * The model can be a traditional glTF asset with a .gltf extension or a Binary glTF using the
  849. * KHR_binary_glTF extension with a .glb extension.
  850. * </p>
  851. * <p>
  852. * For high-precision rendering, Cesium supports the CESIUM_RTC extension, which introduces the
  853. * CESIUM_RTC_MODELVIEW parameter semantic that says the node is in WGS84 coordinates translated
  854. * relative to a local origin.
  855. * </p>
  856. *
  857. * @param {Object} options Object with the following properties:
  858. * @param {String} options.url The url to the .gltf file.
  859. * @param {Object} [options.headers] HTTP headers to send with the request.
  860. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown.
  861. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
  862. * @param {Number} [options.scale=1.0] A uniform scale applied to this model.
  863. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
  864. * @param {Number} [options.maximumScale] The maximum scale for the model.
  865. * @param {Object} [options.id] A user-defined object to return when the model is picked with {@link Scene#pick}.
  866. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each glTF mesh and primitive is pickable with {@link Scene#pick}.
  867. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
  868. * @param {Boolean} [options.asynchronous=true] Determines if model WebGL resource creation will be spread out over several frames or block until completion once all glTF files are loaded.
  869. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from each light source.
  870. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each {@link DrawCommand} in the model.
  871. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
  872. *
  873. * @returns {Model} The newly created model.
  874. *
  875. * @exception {DeveloperError} bgltf is not a valid Binary glTF file.
  876. * @exception {DeveloperError} Only glTF Binary version 1 is supported.
  877. *
  878. * @example
  879. * // Example 1. Create a model from a glTF asset
  880. * var model = scene.primitives.add(Cesium.Model.fromGltf({
  881. * url : './duck/duck.gltf'
  882. * }));
  883. *
  884. * @example
  885. * // Example 2. Create model and provide all properties and events
  886. * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  887. * var modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  888. *
  889. * var model = scene.primitives.add(Cesium.Model.fromGltf({
  890. * url : './duck/duck.gltf',
  891. * show : true, // default
  892. * modelMatrix : modelMatrix,
  893. * scale : 2.0, // double size
  894. * minimumPixelSize : 128, // never smaller than 128 pixels
  895. * maximumScale: 20000, // never larger than 20000 * model size (overrides minimumPixelSize)
  896. * allowPicking : false, // not pickable
  897. * debugShowBoundingVolume : false, // default
  898. * debugWireframe : false
  899. * }));
  900. *
  901. * model.readyPromise.then(function(model) {
  902. * // Play all animations when the model is ready to render
  903. * model.activeAnimations.addAll();
  904. * });
  905. */
  906. Model.fromGltf = function(options) {
  907. //>>includeStart('debug', pragmas.debug);
  908. if (!defined(options) || !defined(options.url)) {
  909. throw new DeveloperError('options.url is required');
  910. }
  911. //>>includeEnd('debug');
  912. var url = options.url;
  913. // If no cache key is provided, use the absolute URL, since two URLs with
  914. // different relative paths could point to the same model.
  915. var cacheKey = defaultValue(options.cacheKey, getAbsoluteUri(url));
  916. options = clone(options);
  917. options.basePath = getBaseUri(url);
  918. options.cacheKey = cacheKey;
  919. var model = new Model(options);
  920. options.headers = defined(options.headers) ? clone(options.headers) : {};
  921. if (!defined(options.headers.Accept)) {
  922. options.headers.Accept = defaultModelAccept;
  923. }
  924. var cachedGltf = gltfCache[cacheKey];
  925. if (!defined(cachedGltf)) {
  926. cachedGltf = new CachedGltf({
  927. ready : false
  928. });
  929. cachedGltf.count = 1;
  930. cachedGltf.modelsToLoad.push(model);
  931. setCachedGltf(model, cachedGltf);
  932. gltfCache[cacheKey] = cachedGltf;
  933. loadArrayBuffer(url, options.headers).then(function(arrayBuffer) {
  934. var array = new Uint8Array(arrayBuffer);
  935. if (containsGltfMagic(array)) {
  936. // Load binary glTF
  937. var result = parseBinaryGltfHeader(array);
  938. // KHR_binary_glTF is from the beginning of the binary section
  939. if (result.binaryOffset !== 0) {
  940. array = array.subarray(result.binaryOffset);
  941. }
  942. cachedGltf.makeReady(result.glTF, array);
  943. } else {
  944. // Load text (JSON) glTF
  945. var json = getStringFromTypedArray(array);
  946. cachedGltf.makeReady(JSON.parse(json));
  947. }
  948. }).otherwise(getFailedLoadFunction(model, 'model', url));
  949. } else if (!cachedGltf.ready) {
  950. // Cache hit but the loadArrayBuffer() or loadText() request is still pending
  951. ++cachedGltf.count;
  952. cachedGltf.modelsToLoad.push(model);
  953. }
  954. // else if the cached glTF is defined and ready, the
  955. // model constructor will pick it up using the cache key.
  956. return model;
  957. };
  958. /**
  959. * For the unit tests to verify model caching.
  960. *
  961. * @private
  962. */
  963. Model._gltfCache = gltfCache;
  964. function getRuntime(model, runtimeName, name) {
  965. //>>includeStart('debug', pragmas.debug);
  966. if (model._state !== ModelState.LOADED) {
  967. throw new DeveloperError('The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.');
  968. }
  969. if (!defined(name)) {
  970. throw new DeveloperError('name is required.');
  971. }
  972. //>>includeEnd('debug');
  973. return (model._runtime[runtimeName])[name];
  974. }
  975. /**
  976. * Returns the glTF node with the given <code>name</code> property. This is used to
  977. * modify a node's transform for animation outside of glTF animations.
  978. *
  979. * @param {String} name The glTF name of the node.
  980. * @returns {ModelNode} The node or <code>undefined</code> if no node with <code>name</code> exists.
  981. *
  982. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  983. *
  984. * @example
  985. * // Apply non-uniform scale to node LOD3sp
  986. * var node = model.getNode('LOD3sp');
  987. * node.matrix = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(5.0, 1.0, 1.0), node.matrix);
  988. */
  989. Model.prototype.getNode = function(name) {
  990. var node = getRuntime(this, 'nodesByName', name);
  991. return defined(node) ? node.publicNode : undefined;
  992. };
  993. /**
  994. * Returns the glTF mesh with the given <code>name</code> property.
  995. *
  996. * @param {String} name The glTF name of the mesh.
  997. *
  998. * @returns {ModelMesh} The mesh or <code>undefined</code> if no mesh with <code>name</code> exists.
  999. *
  1000. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  1001. */
  1002. Model.prototype.getMesh = function(name) {
  1003. return getRuntime(this, 'meshesByName', name);
  1004. };
  1005. /**
  1006. * Returns the glTF material with the given <code>name</code> property.
  1007. *
  1008. * @param {String} name The glTF name of the material.
  1009. * @returns {ModelMaterial} The material or <code>undefined</code> if no material with <code>name</code> exists.
  1010. *
  1011. * @exception {DeveloperError} The model is not loaded. Use Model.readyPromise or wait for Model.ready to be true.
  1012. */
  1013. Model.prototype.getMaterial = function(name) {
  1014. return getRuntime(this, 'materialsByName', name);
  1015. };
  1016. var aMinScratch = new Cartesian3();
  1017. var aMaxScratch = new Cartesian3();
  1018. function getAccessorMinMax(gltf, accessorId) {
  1019. var accessor = gltf.accessors[accessorId];
  1020. var extensions = accessor.extensions;
  1021. var accessorMin = accessor.min;
  1022. var accessorMax = accessor.max;
  1023. // If this accessor is quantized, we should use the decoded min and max
  1024. if (defined(extensions)) {
  1025. var quantizedAttributes = extensions.WEB3D_quantized_attributes;
  1026. if (defined(quantizedAttributes)) {
  1027. accessorMin = quantizedAttributes.decodedMin;
  1028. accessorMax = quantizedAttributes.decodedMax;
  1029. }
  1030. }
  1031. return {
  1032. min : accessorMin,
  1033. max : accessorMax
  1034. };
  1035. }
  1036. function computeBoundingSphere(gltf) {
  1037. var gltfNodes = gltf.nodes;
  1038. var gltfMeshes = gltf.meshes;
  1039. var rootNodes = gltf.scenes[gltf.scene].nodes;
  1040. var rootNodesLength = rootNodes.length;
  1041. var nodeStack = [];
  1042. var min = new Cartesian3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  1043. var max = new Cartesian3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
  1044. for (var i = 0; i < rootNodesLength; ++i) {
  1045. var n = gltfNodes[rootNodes[i]];
  1046. n._transformToRoot = getTransform(n);
  1047. nodeStack.push(n);
  1048. while (nodeStack.length > 0) {
  1049. n = nodeStack.pop();
  1050. var transformToRoot = n._transformToRoot;
  1051. var meshes = n.meshes;
  1052. if (defined(meshes)) {
  1053. var meshesLength = meshes.length;
  1054. for (var j = 0; j < meshesLength; ++j) {
  1055. var primitives = gltfMeshes[meshes[j]].primitives;
  1056. var primitivesLength = primitives.length;
  1057. for (var m = 0; m < primitivesLength; ++m) {
  1058. var positionAccessor = primitives[m].attributes.POSITION;
  1059. if (defined(positionAccessor)) {
  1060. var minMax = getAccessorMinMax(gltf, positionAccessor);
  1061. var aMin = Cartesian3.fromArray(minMax.min, 0, aMinScratch);
  1062. var aMax = Cartesian3.fromArray(minMax.max, 0, aMaxScratch);
  1063. if (defined(min) && defined(max)) {
  1064. Matrix4.multiplyByPoint(transformToRoot, aMin, aMin);
  1065. Matrix4.multiplyByPoint(transformToRoot, aMax, aMax);
  1066. Cartesian3.minimumByComponent(min, aMin, min);
  1067. Cartesian3.maximumByComponent(max, aMax, max);
  1068. }
  1069. }
  1070. }
  1071. }
  1072. }
  1073. var children = n.children;
  1074. var childrenLength = children.length;
  1075. for (var k = 0; k < childrenLength; ++k) {
  1076. var child = gltfNodes[children[k]];
  1077. child._transformToRoot = getTransform(child);
  1078. Matrix4.multiplyTransformation(transformToRoot, child._transformToRoot, child._transformToRoot);
  1079. nodeStack.push(child);
  1080. }
  1081. delete n._transformToRoot;
  1082. }
  1083. }
  1084. var boundingSphere = BoundingSphere.fromCornerPoints(min, max);
  1085. return BoundingSphere.transformWithoutScale(boundingSphere, yUpToZUp, boundingSphere);
  1086. }
  1087. ///////////////////////////////////////////////////////////////////////////
  1088. function getFailedLoadFunction(model, type, path) {
  1089. return function() {
  1090. model._state = ModelState.FAILED;
  1091. model._readyPromise.reject(new RuntimeError('Failed to load ' + type + ': ' + path));
  1092. };
  1093. }
  1094. function bufferLoad(model, id) {
  1095. return function(arrayBuffer) {
  1096. var loadResources = model._loadResources;
  1097. loadResources.buffers[id] = new Uint8Array(arrayBuffer);
  1098. --loadResources.pendingBufferLoads;
  1099. };
  1100. }
  1101. function parseBuffers(model) {
  1102. var buffers = model.gltf.buffers;
  1103. for (var id in buffers) {
  1104. if (buffers.hasOwnProperty(id)) {
  1105. var buffer = buffers[id];
  1106. // The extension 'KHR_binary_glTF' uses a special buffer entitled just 'binary_glTF'.
  1107. // The 'KHR_binary_glTF' check is for backwards compatibility for the Cesium model converter
  1108. // circa Cesium 1.15-1.20 when the converter incorrectly used the buffer name 'KHR_binary_glTF'.
  1109. if ((id === 'binary_glTF') || (id === 'KHR_binary_glTF')) {
  1110. // Buffer is the binary glTF file itself that is already loaded
  1111. var loadResources = model._loadResources;
  1112. loadResources.buffers[id] = model._cachedGltf.bgltf;
  1113. }
  1114. else if (buffer.type === 'arraybuffer') {
  1115. ++model._loadResources.pendingBufferLoads;
  1116. var uri = new Uri(buffer.uri);
  1117. var bufferPath = uri.resolve(model._baseUri).toString();
  1118. loadArrayBuffer(bufferPath).then(bufferLoad(model, id)).otherwise(getFailedLoadFunction(model, 'buffer', bufferPath));
  1119. }
  1120. }
  1121. }
  1122. }
  1123. function parseBufferViews(model) {
  1124. var bufferViews = model.gltf.bufferViews;
  1125. for (var id in bufferViews) {
  1126. if (bufferViews.hasOwnProperty(id)) {
  1127. if (bufferViews[id].target === WebGLConstants.ARRAY_BUFFER) {
  1128. model._loadResources.buffersToCreate.enqueue(id);
  1129. }
  1130. }
  1131. }
  1132. }
  1133. function shaderLoad(model, id) {
  1134. return function(source) {
  1135. var loadResources = model._loadResources;
  1136. loadResources.shaders[id] = {
  1137. source : source,
  1138. bufferView : undefined
  1139. };
  1140. --loadResources.pendingShaderLoads;
  1141. };
  1142. }
  1143. function parseShaders(model) {
  1144. var shaders = model.gltf.shaders;
  1145. for (var id in shaders) {
  1146. if (shaders.hasOwnProperty(id)) {
  1147. var shader = shaders[id];
  1148. // Shader references either uri (external or base64-encoded) or bufferView
  1149. if (defined(shader.extras) && defined(shader.extras.source)) {
  1150. model._loadResources.shaders[id] = {
  1151. source : shader.extras.source,
  1152. bufferView : undefined
  1153. };
  1154. }
  1155. else if (defined(shader.extensions) && defined(shader.extensions.KHR_binary_glTF)) {
  1156. var binary = shader.extensions.KHR_binary_glTF;
  1157. model._loadResources.shaders[id] = {
  1158. source : undefined,
  1159. bufferView : binary.bufferView
  1160. };
  1161. } else {
  1162. ++model._loadResources.pendingShaderLoads;
  1163. var uri = new Uri(shader.uri);
  1164. var shaderPath = uri.resolve(model._baseUri).toString();
  1165. loadText(shaderPath).then(shaderLoad(model, id)).otherwise(getFailedLoadFunction(model, 'shader', shaderPath));
  1166. }
  1167. }
  1168. }
  1169. }
  1170. function parsePrograms(model) {
  1171. var programs = model.gltf.programs;
  1172. for (var id in programs) {
  1173. if (programs.hasOwnProperty(id)) {
  1174. model._loadResources.programsToCreate.enqueue(id);
  1175. }
  1176. }
  1177. }
  1178. function imageLoad(model, id) {
  1179. return function(image) {
  1180. var loadResources = model._loadResources;
  1181. --loadResources.pendingTextureLoads;
  1182. loadResources.texturesToCreate.enqueue({
  1183. id : id,
  1184. image : image,
  1185. bufferView : undefined
  1186. });
  1187. };
  1188. }
  1189. function parseTextures(model) {
  1190. var images = model.gltf.images;
  1191. var textures = model.gltf.textures;
  1192. for (var id in textures) {
  1193. if (textures.hasOwnProperty(id)) {
  1194. var gltfImage = images[textures[id].source];
  1195. // Image references either uri (external or base64-encoded) or bufferView
  1196. if (defined(gltfImage.extensions) && defined(gltfImage.extensions.KHR_binary_glTF)) {
  1197. var binary = gltfImage.extensions.KHR_binary_glTF;
  1198. model._loadResources.texturesToCreateFromBufferView.enqueue({
  1199. id : id,
  1200. image : undefined,
  1201. bufferView : binary.bufferView,
  1202. mimeType : binary.mimeType
  1203. });
  1204. } else {
  1205. ++model._loadResources.pendingTextureLoads;
  1206. var uri = new Uri(gltfImage.uri);
  1207. var imagePath = uri.resolve(model._baseUri).toString();
  1208. loadImage(imagePath).then(imageLoad(model, id)).otherwise(getFailedLoadFunction(model, 'image', imagePath));
  1209. }
  1210. }
  1211. }
  1212. }
  1213. var nodeTranslationScratch = new Cartesian3();
  1214. var nodeQuaternionScratch = new Quaternion();
  1215. var nodeScaleScratch = new Cartesian3();
  1216. function getTransform(node) {
  1217. if (defined(node.matrix)) {
  1218. return Matrix4.fromArray(node.matrix);
  1219. }
  1220. return Matrix4.fromTranslationQuaternionRotationScale(
  1221. Cartesian3.fromArray(node.translation, 0, nodeTranslationScratch),
  1222. Quaternion.unpack(node.rotation, 0, nodeQuaternionScratch),
  1223. Cartesian3.fromArray(node.scale, 0 , nodeScaleScratch));
  1224. }
  1225. function parseNodes(model) {
  1226. var runtimeNodes = {};
  1227. var runtimeNodesByName = {};
  1228. var skinnedNodes = [];
  1229. var skinnedNodesIds = model._loadResources.skinnedNodesIds;
  1230. var nodes = model.gltf.nodes;
  1231. for (var id in nodes) {
  1232. if (nodes.hasOwnProperty(id)) {
  1233. var node = nodes[id];
  1234. var runtimeNode = {
  1235. // Animation targets
  1236. matrix : undefined,
  1237. translation : undefined,
  1238. rotation : undefined,
  1239. scale : undefined,
  1240. // Per-node show inherited from parent
  1241. computedShow : true,
  1242. // Computed transforms
  1243. transformToRoot : new Matrix4(),
  1244. computedMatrix : new Matrix4(),
  1245. dirtyNumber : 0, // The frame this node was made dirty by an animation; for graph traversal
  1246. // Rendering
  1247. commands : [], // empty for transform, light, and camera nodes
  1248. // Skinned node
  1249. inverseBindMatrices : undefined, // undefined when node is not skinned
  1250. bindShapeMatrix : undefined, // undefined when node is not skinned or identity
  1251. joints : [], // empty when node is not skinned
  1252. computedJointMatrices : [], // empty when node is not skinned
  1253. // Joint node
  1254. jointName : node.jointName, // undefined when node is not a joint
  1255. // Graph pointers
  1256. children : [], // empty for leaf nodes
  1257. parents : [], // empty for root nodes
  1258. // Publicly-accessible ModelNode instance to modify animation targets
  1259. publicNode : undefined
  1260. };
  1261. runtimeNode.publicNode = new ModelNode(model, node, runtimeNode, id, getTransform(node));
  1262. runtimeNodes[id] = runtimeNode;
  1263. runtimeNodesByName[node.name] = runtimeNode;
  1264. if (defined(node.skin)) {
  1265. skinnedNodesIds.push(id);
  1266. skinnedNodes.push(runtimeNode);
  1267. }
  1268. }
  1269. }
  1270. model._runtime.nodes = runtimeNodes;
  1271. model._runtime.nodesByName = runtimeNodesByName;
  1272. model._runtime.skinnedNodes = skinnedNodes;
  1273. }
  1274. function parseMaterials(model) {
  1275. var runtimeMaterialsByName = {};
  1276. var runtimeMaterialsById = {};
  1277. var materials = model.gltf.materials;
  1278. var uniformMaps = model._uniformMaps;
  1279. for (var id in materials) {
  1280. if (materials.hasOwnProperty(id)) {
  1281. // Allocated now so ModelMaterial can keep a reference to it.
  1282. uniformMaps[id] = {
  1283. uniformMap : undefined,
  1284. values : undefined,
  1285. jointMatrixUniformName : undefined
  1286. };
  1287. var material = materials[id];
  1288. var modelMaterial = new ModelMaterial(model, material, id);
  1289. runtimeMaterialsByName[material.name] = modelMaterial;
  1290. runtimeMaterialsById[id] = modelMaterial;
  1291. }
  1292. }
  1293. model._runtime.materialsByName = runtimeMaterialsByName;
  1294. model._runtime.materialsById = runtimeMaterialsById;
  1295. }
  1296. function parseMeshes(model) {
  1297. var runtimeMeshesByName = {};
  1298. var runtimeMaterialsById = model._runtime.materialsById;
  1299. var meshes = model.gltf.meshes;
  1300. var usesQuantizedAttributes = usesExtension(model, 'WEB3D_quantized_attributes');
  1301. for (var id in meshes) {
  1302. if (meshes.hasOwnProperty(id)) {
  1303. var mesh = meshes[id];
  1304. runtimeMeshesByName[mesh.name] = new ModelMesh(mesh, runtimeMaterialsById, id);
  1305. if (usesQuantizedAttributes) {
  1306. // Cache primitives according to their program
  1307. var primitives = mesh.primitives;
  1308. var primitivesLength = primitives.length;
  1309. for (var i = 0; i < primitivesLength; i++) {
  1310. var primitive = primitives[i];
  1311. var programId = getProgramForPrimitive(model, primitive);
  1312. var programPrimitives = model._programPrimitives[programId];
  1313. if (!defined(programPrimitives)) {
  1314. programPrimitives = [];
  1315. model._programPrimitives[programId] = programPrimitives;
  1316. }
  1317. programPrimitives.push(primitive);
  1318. }
  1319. }
  1320. }
  1321. }
  1322. model._runtime.meshesByName = runtimeMeshesByName;
  1323. }
  1324. function parse(model) {
  1325. if (!model._loadRendererResourcesFromCache) {
  1326. parseBuffers(model);
  1327. parseBufferViews(model);
  1328. parseShaders(model);
  1329. parsePrograms(model);
  1330. parseTextures(model);
  1331. }
  1332. parseMaterials(model);
  1333. parseMeshes(model);
  1334. parseNodes(model);
  1335. }
  1336. function usesExtension(model, extension) {
  1337. var cachedExtensionsUsed = model._extensionsUsed;
  1338. if (!defined(cachedExtensionsUsed)) {
  1339. var extensionsUsed = model.gltf.extensionsUsed;
  1340. cachedExtensionsUsed = {};
  1341. var extensionsLength = extensionsUsed.length;
  1342. for (var i = 0; i < extensionsLength; i++) {
  1343. cachedExtensionsUsed[extensionsUsed[i]] = true;
  1344. }
  1345. }
  1346. return defined(cachedExtensionsUsed[extension]);
  1347. }
  1348. ///////////////////////////////////////////////////////////////////////////
  1349. function createBuffers(model, context) {
  1350. var loadResources = model._loadResources;
  1351. if (loadResources.pendingBufferLoads !== 0) {
  1352. return;
  1353. }
  1354. var bufferView;
  1355. var bufferViews = model.gltf.bufferViews;
  1356. var rendererBuffers = model._rendererResources.buffers;
  1357. while (loadResources.buffersToCreate.length > 0) {
  1358. var bufferViewId = loadResources.buffersToCreate.dequeue();
  1359. bufferView = bufferViews[bufferViewId];
  1360. // Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below.
  1361. var vertexBuffer = Buffer.createVertexBuffer({
  1362. context : context,
  1363. typedArray : loadResources.getBuffer(bufferView),
  1364. usage : BufferUsage.STATIC_DRAW
  1365. });
  1366. vertexBuffer.vertexArrayDestroyable = false;
  1367. rendererBuffers[bufferViewId] = vertexBuffer;
  1368. }
  1369. // The Cesium Renderer requires knowing the datatype for an index buffer
  1370. // at creation type, which is not part of the glTF bufferview so loop
  1371. // through glTF accessors to create the bufferview's index buffer.
  1372. var accessors = model.gltf.accessors;
  1373. for (var id in accessors) {
  1374. if (accessors.hasOwnProperty(id)) {
  1375. var accessor = accessors[id];
  1376. bufferView = bufferViews[accessor.bufferView];
  1377. if ((bufferView.target === WebGLConstants.ELEMENT_ARRAY_BUFFER) && !defined(rendererBuffers[accessor.bufferView])) {
  1378. var indexBuffer = Buffer.createIndexBuffer({
  1379. context : context,
  1380. typedArray : loadResources.getBuffer(bufferView),
  1381. usage : BufferUsage.STATIC_DRAW,
  1382. indexDatatype : accessor.componentType
  1383. });
  1384. indexBuffer.vertexArrayDestroyable = false;
  1385. rendererBuffers[accessor.bufferView] = indexBuffer;
  1386. // In theory, several glTF accessors with different componentTypes could
  1387. // point to the same glTF bufferView, which would break this.
  1388. // In practice, it is unlikely as it will be UNSIGNED_SHORT.
  1389. }
  1390. }
  1391. }
  1392. }
  1393. function createAttributeLocations(model, attributes) {
  1394. var attributeLocations = {};
  1395. var length = attributes.length;
  1396. var i;
  1397. // Set the position attribute to the 0th index. In some WebGL implementations the shader
  1398. // will not work correctly if the 0th attribute is not active. For example, some glTF models
  1399. // list the normal attribute first but derived shaders like the cast-shadows shader do not use
  1400. // the normal attribute.
  1401. for (i = 1; i < length; ++i) {
  1402. var attribute = attributes[i];
  1403. if (/position/i.test(attribute)) {
  1404. attributes[i] = attributes[0];
  1405. attributes[0] = attribute;
  1406. break;
  1407. }
  1408. }
  1409. for (i = 0; i < length; ++i) {
  1410. attributeLocations[attributes[i]] = i;
  1411. }
  1412. return attributeLocations;
  1413. }
  1414. function getShaderSource(model, shader) {
  1415. if (defined(shader.source)) {
  1416. return shader.source;
  1417. }
  1418. var loadResources = model._loadResources;
  1419. var gltf = model.gltf;
  1420. var bufferView = gltf.bufferViews[shader.bufferView];
  1421. return getStringFromTypedArray(loadResources.getBuffer(bufferView));
  1422. }
  1423. function replaceAllButFirstInString(string, find, replace) {
  1424. var index = string.indexOf(find);
  1425. return string.replace(new RegExp(find, 'g'), function(match, offset, all) {
  1426. return index === offset ? match : replace;
  1427. });
  1428. }
  1429. function getProgramForPrimitive(model, primitive) {
  1430. var gltf = model.gltf;
  1431. var materialId = primitive.material;
  1432. var material = gltf.materials[materialId];
  1433. var techniqueId = material.technique;
  1434. var technique = gltf.techniques[techniqueId];
  1435. return technique.program;
  1436. }
  1437. function getQuantizedAttributes(model, accessorId) {
  1438. var gltf = model.gltf;
  1439. var accessor = gltf.accessors[accessorId];
  1440. var extensions = accessor.extensions;
  1441. if (defined(extensions)) {
  1442. return extensions.WEB3D_quantized_attributes;
  1443. }
  1444. return undefined;
  1445. }
  1446. function getAttributeVariableName(model, primitive, attributeSemantic) {
  1447. var gltf = model.gltf;
  1448. var materialId = primitive.material;
  1449. var material = gltf.materials[materialId];
  1450. var techniqueId = material.technique;
  1451. var technique = gltf.techniques[techniqueId];
  1452. for (var parameter in technique.parameters) {
  1453. if (technique.parameters.hasOwnProperty(parameter)) {
  1454. var semantic = technique.parameters[parameter].semantic;
  1455. if (semantic === attributeSemantic) {
  1456. var attributes = technique.attributes;
  1457. for (var attributeVarName in attributes) {
  1458. if (attributes.hasOwnProperty(attributeVarName)) {
  1459. var name = attributes[attributeVarName];
  1460. if (name === parameter) {
  1461. return attributeVarName;
  1462. }
  1463. }
  1464. }
  1465. }
  1466. }
  1467. }
  1468. return undefined;
  1469. }
  1470. function modifyShaderForQuantizedAttributes(shader, programName, model, context) {
  1471. var quantizedUniforms = {};
  1472. model._quantizedUniforms[programName] = quantizedUniforms;
  1473. var primitives = model._programPrimitives[programName];
  1474. for (var i = 0; i < primitives.length; i++) {
  1475. var primitive = primitives[i];
  1476. if (getProgramForPrimitive(model, primitive) === programName) {
  1477. for (var attributeSemantic in primitive.attributes) {
  1478. if (primitive.attributes.hasOwnProperty(attributeSemantic)) {
  1479. var decodeUniformVarName = 'czm_u_dec_' + attributeSemantic.toLowerCase();
  1480. var decodeUniformVarNameScale = decodeUniformVarName + '_scale';
  1481. var decodeUniformVarNameTranslate = decodeUniformVarName + '_translate';
  1482. if (!defined(quantizedUniforms[decodeUniformVarName]) && !defined(quantizedUniforms[decodeUniformVarNameScale])) {
  1483. var accessorId = primitive.attributes[attributeSemantic];
  1484. var quantizedAttributes = getQuantizedAttributes(model, accessorId);
  1485. if (defined(quantizedAttributes)) {
  1486. var attributeVarName = getAttributeVariableName(model, primitive, attributeSemantic);
  1487. var decodeMatrix = quantizedAttributes.decodeMatrix;
  1488. var newMain = 'czm_decoded_' + attributeSemantic;
  1489. var decodedAttributeVarName = attributeVarName.replace('a_', 'czm_a_dec_');
  1490. var size = Math.floor(Math.sqrt(decodeMatrix.length));
  1491. // replace usages of the original attribute with the decoded version, but not the declaration
  1492. shader = replaceAllButFirstInString(shader, attributeVarName, decodedAttributeVarName);
  1493. // declare decoded attribute
  1494. var variableType;
  1495. if (size > 2) {
  1496. variableType = 'vec' + (size - 1);
  1497. } else {
  1498. variableType = 'float';
  1499. }
  1500. shader = variableType + ' ' + decodedAttributeVarName + ';\n' + shader;
  1501. // splice decode function into the shader - attributes are pre-multiplied with the decode matrix
  1502. // uniform in the shader (32-bit floating point)
  1503. var decode = '';
  1504. if (size === 5) {
  1505. // separate scale and translate since glsl doesn't have mat5
  1506. shader = 'uniform mat4 ' + decodeUniformVarNameScale + ';\n' + shader;
  1507. shader = 'uniform vec4 ' + decodeUniformVarNameTranslate + ';\n' + shader;
  1508. decode = '\n' +
  1509. 'void main() {\n' +
  1510. ' ' + decodedAttributeVarName + ' = ' + decodeUniformVarNameScale + ' * ' + attributeVarName + ' + ' + decodeUniformVarNameTranslate + ';\n' +
  1511. ' ' + newMain + '();\n' +
  1512. '}\n';
  1513. quantizedUniforms[decodeUniformVarNameScale] = {mat : 4};
  1514. quantizedUniforms[decodeUniformVarNameTranslate] = {vec : 4};
  1515. }
  1516. else {
  1517. shader = 'uniform mat' + size + ' ' + decodeUniformVarName + ';\n' + shader;
  1518. decode = '\n' +
  1519. 'void main() {\n' +
  1520. ' ' + decodedAttributeVarName + ' = ' + variableType + '(' + decodeUniformVarName + ' * vec' + size + '(' + attributeVarName + ',1.0));\n' +
  1521. ' ' + newMain + '();\n' +
  1522. '}\n';
  1523. quantizedUniforms[decodeUniformVarName] = {mat : size};
  1524. }
  1525. shader = ShaderSource.replaceMain(shader, newMain);
  1526. shader += decode;
  1527. }
  1528. }
  1529. }
  1530. }
  1531. }
  1532. }
  1533. // This is not needed after the program is processed, free the memory
  1534. model._programPrimitives[programName] = undefined;
  1535. return shader;
  1536. }
  1537. function modifyShader(shader, programName, callback) {
  1538. if (defined(callback)) {
  1539. shader = callback(shader, programName);
  1540. }
  1541. return shader;
  1542. }
  1543. function createProgram(id, model, context) {
  1544. var programs = model.gltf.programs;
  1545. var shaders = model._loadResources.shaders;
  1546. var program = programs[id];
  1547. var attributeLocations = createAttributeLocations(model, program.attributes);
  1548. var vs = getShaderSource(model, shaders[program.vertexShader]);
  1549. var fs = getShaderSource(model, shaders[program.fragmentShader]);
  1550. // Add pre-created attributes to attributeLocations
  1551. var attributesLength = program.attributes.length;
  1552. var precreatedAttributes = model._precreatedAttributes;
  1553. if (defined(precreatedAttributes)) {
  1554. for (var attrName in precreatedAttributes) {
  1555. if (precreatedAttributes.hasOwnProperty(attrName)) {
  1556. attributeLocations[attrName] = attributesLength++;
  1557. }
  1558. }
  1559. }
  1560. if (usesExtension(model, 'WEB3D_quantized_attributes')) {
  1561. vs = modifyShaderForQuantizedAttributes(vs, id, model, context);
  1562. }
  1563. var drawVS = modifyShader(vs, id, model._vertexShaderLoaded);
  1564. var drawFS = modifyShader(fs, id, model._fragmentShaderLoaded);
  1565. model._rendererResources.programs[id] = ShaderProgram.fromCache({
  1566. context : context,
  1567. vertexShaderSource : drawVS,
  1568. fragmentShaderSource : drawFS,
  1569. attributeLocations : attributeLocations
  1570. });
  1571. if (model.allowPicking) {
  1572. // PERFORMANCE_IDEA: Can optimize this shader with a glTF hint. https://github.com/KhronosGroup/glTF/issues/181
  1573. var pickVS = modifyShader(vs, id, model._pickVertexShaderLoaded);
  1574. var pickFS = modifyShader(fs, id, model._pickFragmentShaderLoaded);
  1575. if (!model._pickFragmentShaderLoaded) {
  1576. pickFS = ShaderSource.createPickFragmentShaderSource(fs, 'uniform');
  1577. }
  1578. model._rendererResources.pickPrograms[id] = ShaderProgram.fromCache({
  1579. context : context,
  1580. vertexShaderSource : pickVS,
  1581. fragmentShaderSource : pickFS,
  1582. attributeLocations : attributeLocations
  1583. });
  1584. }
  1585. }
  1586. function createPrograms(model, context) {
  1587. var loadResources = model._loadResources;
  1588. var id;
  1589. if (loadResources.pendingShaderLoads !== 0) {
  1590. return;
  1591. }
  1592. // PERFORMANCE_IDEA: this could be more fine-grained by looking
  1593. // at the shader's bufferView's to determine the buffer dependencies.
  1594. if (loadResources.pendingBufferLoads !== 0) {
  1595. return;
  1596. }
  1597. if (model.asynchronous) {
  1598. // Create one program per frame
  1599. if (loadResources.programsToCreate.length > 0) {
  1600. id = loadResources.programsToCreate.dequeue();
  1601. createProgram(id, model, context);
  1602. }
  1603. } else {
  1604. // Create all loaded programs this frame
  1605. while (loadResources.programsToCreate.length > 0) {
  1606. id = loadResources.programsToCreate.dequeue();
  1607. createProgram(id, model, context);
  1608. }
  1609. }
  1610. }
  1611. function getOnImageCreatedFromTypedArray(loadResources, gltfTexture) {
  1612. return function(image) {
  1613. loadResources.texturesToCreate.enqueue({
  1614. id : gltfTexture.id,
  1615. image : image,
  1616. bufferView : undefined
  1617. });
  1618. --loadResources.pendingBufferViewToImage;
  1619. };
  1620. }
  1621. function loadTexturesFromBufferViews(model) {
  1622. var loadResources = model._loadResources;
  1623. if (loadResources.pendingBufferLoads !== 0) {
  1624. return;
  1625. }
  1626. while (loadResources.texturesToCreateFromBufferView.length > 0) {
  1627. var gltfTexture = loadResources.texturesToCreateFromBufferView.dequeue();
  1628. var gltf = model.gltf;
  1629. var bufferView = gltf.bufferViews[gltfTexture.bufferView];
  1630. var onload = getOnImageCreatedFromTypedArray(loadResources, gltfTexture);
  1631. var onerror = getFailedLoadFunction(model, 'image', 'id: ' + gltfTexture.id + ', bufferView: ' + gltfTexture.bufferView);
  1632. loadImageFromTypedArray(loadResources.getBuffer(bufferView), gltfTexture.mimeType).
  1633. then(onload).otherwise(onerror);
  1634. ++loadResources.pendingBufferViewToImage;
  1635. }
  1636. }
  1637. function createSamplers(model, context) {
  1638. var loadResources = model._loadResources;
  1639. if (loadResources.createSamplers) {
  1640. loadResources.createSamplers = false;
  1641. var rendererSamplers = model._rendererResources.samplers;
  1642. var samplers = model.gltf.samplers;
  1643. for (var id in samplers) {
  1644. if (samplers.hasOwnProperty(id)) {
  1645. var sampler = samplers[id];
  1646. rendererSamplers[id] = new Sampler({
  1647. wrapS : sampler.wrapS,
  1648. wrapT : sampler.wrapT,
  1649. minificationFilter : sampler.minFilter,
  1650. magnificationFilter : sampler.magFilter
  1651. });
  1652. }
  1653. }
  1654. }
  1655. }
  1656. function createTexture(gltfTexture, model, context) {
  1657. var textures = model.gltf.textures;
  1658. var texture = textures[gltfTexture.id];
  1659. var rendererSamplers = model._rendererResources.samplers;
  1660. var sampler = rendererSamplers[texture.sampler];
  1661. var mipmap =
  1662. (sampler.minificationFilter === TextureMinificationFilter.NEAREST_MIPMAP_NEAREST) ||
  1663. (sampler.minificationFilter === TextureMinificationFilter.NEAREST_MIPMAP_LINEAR) ||
  1664. (sampler.minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_NEAREST) ||
  1665. (sampler.minificationFilter === TextureMinificationFilter.LINEAR_MIPMAP_LINEAR);
  1666. var requiresNpot = mipmap ||
  1667. (sampler.wrapS === TextureWrap.REPEAT) ||
  1668. (sampler.wrapS === TextureWrap.MIRRORED_REPEAT) ||
  1669. (sampler.wrapT === TextureWrap.REPEAT) ||
  1670. (sampler.wrapT === TextureWrap.MIRRORED_REPEAT);
  1671. var source = gltfTexture.image;
  1672. var npot = !CesiumMath.isPowerOfTwo(source.width) || !CesiumMath.isPowerOfTwo(source.height);
  1673. if (requiresNpot && npot) {
  1674. // WebGL requires power-of-two texture dimensions for mipmapping and REPEAT/MIRRORED_REPEAT wrap modes.
  1675. var canvas = document.createElement('canvas');
  1676. canvas.width = CesiumMath.nextPowerOfTwo(source.width);
  1677. canvas.height = CesiumMath.nextPowerOfTwo(source.height);
  1678. var canvasContext = canvas.getContext('2d');
  1679. canvasContext.drawImage(source, 0, 0, source.width, source.height, 0, 0, canvas.width, canvas.height);
  1680. source = canvas;
  1681. }
  1682. var tx;
  1683. if (texture.target === WebGLConstants.TEXTURE_2D) {
  1684. tx = new Texture({
  1685. context : context,
  1686. source : source,
  1687. pixelFormat : texture.internalFormat,
  1688. pixelDatatype : texture.type,
  1689. sampler : sampler,
  1690. flipY : false
  1691. });
  1692. }
  1693. // GLTF_SPEC: Support TEXTURE_CUBE_MAP. https://github.com/KhronosGroup/glTF/issues/40
  1694. if (mipmap) {
  1695. tx.generateMipmap();
  1696. }
  1697. model._rendererResources.textures[gltfTexture.id] = tx;
  1698. }
  1699. function createTextures(model, context) {
  1700. var loadResources = model._loadResources;
  1701. var gltfTexture;
  1702. if (model.asynchronous) {
  1703. // Create one texture per frame
  1704. if (loadResources.texturesToCreate.length > 0) {
  1705. gltfTexture = loadResources.texturesToCreate.dequeue();
  1706. createTexture(gltfTexture, model, context);
  1707. }
  1708. } else {
  1709. // Create all loaded textures this frame
  1710. while (loadResources.texturesToCreate.length > 0) {
  1711. gltfTexture = loadResources.texturesToCreate.dequeue();
  1712. createTexture(gltfTexture, model, context);
  1713. }
  1714. }
  1715. }
  1716. function getAttributeLocations(model, primitive) {
  1717. var gltf = model.gltf;
  1718. var techniques = gltf.techniques;
  1719. var materials = gltf.materials;
  1720. // Retrieve the compiled shader program to assign index values to attributes
  1721. var attributeLocations = {};
  1722. var technique = techniques[materials[primitive.material].technique];
  1723. var parameters = technique.parameters;
  1724. var attributes = technique.attributes;
  1725. var programAttributeLocations = model._rendererResources.programs[technique.program].vertexAttributes;
  1726. // Note: WebGL shader compiler may have optimized and removed some attributes from programAttributeLocations
  1727. for (var location in programAttributeLocations){
  1728. if (programAttributeLocations.hasOwnProperty(location)) {
  1729. var attribute = attributes[location];
  1730. var index = programAttributeLocations[location].index;
  1731. if (defined(attribute)) {
  1732. var parameter = parameters[attribute];
  1733. attributeLocations[parameter.semantic] = index;
  1734. } else {
  1735. // Pre-created attributes
  1736. attributeLocations[location] = index;
  1737. }
  1738. }
  1739. }
  1740. return attributeLocations;
  1741. }
  1742. function searchForest(forest, jointName, nodes) {
  1743. var length = forest.length;
  1744. for (var i = 0; i < length; ++i) {
  1745. var stack = [forest[i]]; // Push root node of tree
  1746. while (stack.length > 0) {
  1747. var id = stack.pop();
  1748. var n = nodes[id];
  1749. if (n.jointName === jointName) {
  1750. return id;
  1751. }
  1752. var children = n.children;
  1753. var childrenLength = children.length;
  1754. for (var k = 0; k < childrenLength; ++k) {
  1755. stack.push(children[k]);
  1756. }
  1757. }
  1758. }
  1759. // This should never happen; the skeleton should have a node for all joints in the skin.
  1760. return undefined;
  1761. }
  1762. function createJoints(model, runtimeSkins) {
  1763. var gltf = model.gltf;
  1764. var skins = gltf.skins;
  1765. var nodes = gltf.nodes;
  1766. var runtimeNodes = model._runtime.nodes;
  1767. var skinnedNodesIds = model._loadResources.skinnedNodesIds;
  1768. var length = skinnedNodesIds.length;
  1769. for (var j = 0; j < length; ++j) {
  1770. var id = skinnedNodesIds[j];
  1771. var skinnedNode = runtimeNodes[id];
  1772. var node = nodes[id];
  1773. var runtimeSkin = runtimeSkins[node.skin];
  1774. skinnedNode.inverseBindMatrices = runtimeSkin.inverseBindMatrices;
  1775. skinnedNode.bindShapeMatrix = runtimeSkin.bindShapeMatrix;
  1776. // 1. Find nodes with the names in node.skeletons (the node's skeletons)
  1777. // 2. These nodes form the root nodes of the forest to search for each joint in skin.jointNames. This search uses jointName, not the node's name.
  1778. // 3. Search for the joint name among the gltf node hierarchy instead of the runtime node hierarchy. Child links aren't set up yet for runtime nodes.
  1779. var forest = [];
  1780. var gltfSkeletons = node.skeletons;
  1781. var skeletonsLength = gltfSkeletons.length;
  1782. for (var k = 0; k < skeletonsLength; ++k) {
  1783. forest.push(gltfSkeletons[k]);
  1784. }
  1785. var gltfJointNames = skins[node.skin].jointNames;
  1786. var jointNamesLength = gltfJointNames.length;
  1787. for (var i = 0; i < jointNamesLength; ++i) {
  1788. var jointName = gltfJointNames[i];
  1789. var jointNode = runtimeNodes[searchForest(forest, jointName, nodes)];
  1790. skinnedNode.joints.push(jointNode);
  1791. }
  1792. }
  1793. }
  1794. function createSkins(model) {
  1795. var loadResources = model._loadResources;
  1796. if (loadResources.pendingBufferLoads !== 0) {
  1797. return;
  1798. }
  1799. if (!loadResources.createSkins) {
  1800. return;
  1801. }
  1802. loadResources.createSkins = false;
  1803. var gltf = model.gltf;
  1804. var accessors = gltf.accessors;
  1805. var skins = gltf.skins;
  1806. var runtimeSkins = {};
  1807. for (var id in skins) {
  1808. if (skins.hasOwnProperty(id)) {
  1809. var skin = skins[id];
  1810. var accessor = accessors[skin.inverseBindMatrices];
  1811. var bindShapeMatrix;
  1812. if (!Matrix4.equals(skin.bindShapeMatrix, Matrix4.IDENTITY)) {
  1813. bindShapeMatrix = Matrix4.clone(skin.bindShapeMatrix);
  1814. }
  1815. runtimeSkins[id] = {
  1816. inverseBindMatrices : ModelAnimationCache.getSkinInverseBindMatrices(model, accessor),
  1817. bindShapeMatrix : bindShapeMatrix // not used when undefined
  1818. };
  1819. }
  1820. }
  1821. createJoints(model, runtimeSkins);
  1822. }
  1823. function getChannelEvaluator(model, runtimeNode, targetPath, spline) {
  1824. return function(localAnimationTime) {
  1825. // Workaround for https://github.com/KhronosGroup/glTF/issues/219
  1826. //if (targetPath === 'translation') {
  1827. // return;
  1828. //}
  1829. runtimeNode[targetPath] = spline.evaluate(localAnimationTime, runtimeNode[targetPath]);
  1830. runtimeNode.dirtyNumber = model._maxDirtyNumber;
  1831. };
  1832. }
  1833. function createRuntimeAnimations(model) {
  1834. var loadResources = model._loadResources;
  1835. if (!loadResources.finishedPendingBufferLoads()) {
  1836. return;
  1837. }
  1838. if (!loadResources.createRuntimeAnimations) {
  1839. return;
  1840. }
  1841. loadResources.createRuntimeAnimations = false;
  1842. model._runtime.animations = {
  1843. };
  1844. var runtimeNodes = model._runtime.nodes;
  1845. var animations = model.gltf.animations;
  1846. var accessors = model.gltf.accessors;
  1847. for (var animationId in animations) {
  1848. if (animations.hasOwnProperty(animationId)) {
  1849. var animation = animations[animationId];
  1850. var channels = animation.channels;
  1851. var parameters = animation.parameters;
  1852. var samplers = animation.samplers;
  1853. var parameterValues = {};
  1854. for (var name in parameters) {
  1855. if (parameters.hasOwnProperty(name)) {
  1856. parameterValues[name] = ModelAnimationCache.getAnimationParameterValues(model, accessors[parameters[name]]);
  1857. }
  1858. }
  1859. // Find start and stop time for the entire animation
  1860. var startTime = Number.MAX_VALUE;
  1861. var stopTime = -Number.MAX_VALUE;
  1862. var length = channels.length;
  1863. var channelEvaluators = new Array(length);
  1864. for (var i = 0; i < length; ++i) {
  1865. var channel = channels[i];
  1866. var target = channel.target;
  1867. var sampler = samplers[channel.sampler];
  1868. var times = parameterValues[sampler.input];
  1869. startTime = Math.min(startTime, times[0]);
  1870. stopTime = Math.max(stopTime, times[times.length - 1]);
  1871. var spline = ModelAnimationCache.getAnimationSpline(model, animationId, animation, channel.sampler, sampler, parameterValues);
  1872. // GLTF_SPEC: Support more targets like materials. https://github.com/KhronosGroup/glTF/issues/142
  1873. channelEvaluators[i] = getChannelEvaluator(model, runtimeNodes[target.id], target.path, spline);
  1874. }
  1875. model._runtime.animations[animationId] = {
  1876. startTime : startTime,
  1877. stopTime : stopTime,
  1878. channelEvaluators : channelEvaluators
  1879. };
  1880. }
  1881. }
  1882. }
  1883. function createVertexArrays(model, context) {
  1884. var loadResources = model._loadResources;
  1885. if (!loadResources.finishedBuffersCreation() || !loadResources.finishedProgramCreation()) {
  1886. return;
  1887. }
  1888. if (!loadResources.createVertexArrays) {
  1889. return;
  1890. }
  1891. loadResources.createVertexArrays = false;
  1892. var rendererBuffers = model._rendererResources.buffers;
  1893. var rendererVertexArrays = model._rendererResources.vertexArrays;
  1894. var gltf = model.gltf;
  1895. var accessors = gltf.accessors;
  1896. var meshes = gltf.meshes;
  1897. for (var meshId in meshes) {
  1898. if (meshes.hasOwnProperty(meshId)) {
  1899. var primitives = meshes[meshId].primitives;
  1900. var primitivesLength = primitives.length;
  1901. for (var i = 0; i < primitivesLength; ++i) {
  1902. var primitive = primitives[i];
  1903. // GLTF_SPEC: This does not take into account attribute arrays,
  1904. // indicated by when an attribute points to a parameter with a
  1905. // count property.
  1906. //
  1907. // https://github.com/KhronosGroup/glTF/issues/258
  1908. var attributeLocations = getAttributeLocations(model, primitive);
  1909. var attributeName;
  1910. var attributeLocation;
  1911. var attribute;
  1912. var attributes = [];
  1913. var primitiveAttributes = primitive.attributes;
  1914. for (attributeName in primitiveAttributes) {
  1915. if (primitiveAttributes.hasOwnProperty(attributeName)) {
  1916. attributeLocation = attributeLocations[attributeName];
  1917. // Skip if the attribute is not used by the material, e.g., because the asset was exported
  1918. // with an attribute that wasn't used and the asset wasn't optimized.
  1919. if (defined(attributeLocation)) {
  1920. var a = accessors[primitiveAttributes[attributeName]];
  1921. attributes.push({
  1922. index : attributeLocation,
  1923. vertexBuffer : rendererBuffers[a.bufferView],
  1924. componentsPerAttribute : getBinaryAccessor(a).componentsPerAttribute,
  1925. componentDatatype : a.componentType,
  1926. normalize : false,
  1927. offsetInBytes : a.byteOffset,
  1928. strideInBytes : a.byteStride
  1929. });
  1930. }
  1931. }
  1932. }
  1933. // Add pre-created attributes
  1934. var precreatedAttributes = model._precreatedAttributes;
  1935. if (defined(precreatedAttributes)) {
  1936. for (attributeName in precreatedAttributes) {
  1937. if (precreatedAttributes.hasOwnProperty(attributeName)) {
  1938. attributeLocation = attributeLocations[attributeName];
  1939. if (defined(attributeLocation)) {
  1940. attribute = precreatedAttributes[attributeName];
  1941. attribute.index = attributeLocation;
  1942. attributes.push(attribute);
  1943. }
  1944. }
  1945. }
  1946. }
  1947. var indexBuffer;
  1948. if (defined(primitive.indices)) {
  1949. var accessor = accessors[primitive.indices];
  1950. indexBuffer = rendererBuffers[accessor.bufferView];
  1951. }
  1952. rendererVertexArrays[meshId + '.primitive.' + i] = new VertexArray({
  1953. context : context,
  1954. attributes : attributes,
  1955. indexBuffer : indexBuffer
  1956. });
  1957. }
  1958. }
  1959. }
  1960. }
  1961. function getBooleanStates(states) {
  1962. // GLTF_SPEC: SAMPLE_ALPHA_TO_COVERAGE not used by Cesium
  1963. var booleanStates = {};
  1964. booleanStates[WebGLConstants.BLEND] = false;
  1965. booleanStates[WebGLConstants.CULL_FACE] = false;
  1966. booleanStates[WebGLConstants.DEPTH_TEST] = false;
  1967. booleanStates[WebGLConstants.POLYGON_OFFSET_FILL] = false;
  1968. booleanStates[WebGLConstants.SCISSOR_TEST] = false;
  1969. var enable = states.enable;
  1970. var length = enable.length;
  1971. var i;
  1972. for (i = 0; i < length; ++i) {
  1973. booleanStates[enable[i]] = true;
  1974. }
  1975. return booleanStates;
  1976. }
  1977. function createRenderStates(model, context) {
  1978. var loadResources = model._loadResources;
  1979. var techniques = model.gltf.techniques;
  1980. if (loadResources.createRenderStates) {
  1981. loadResources.createRenderStates = false;
  1982. for (var id in techniques) {
  1983. if (techniques.hasOwnProperty(id)) {
  1984. createRenderStateForTechnique(model, id, context);
  1985. }
  1986. }
  1987. }
  1988. }
  1989. function createRenderStateForTechnique(model, id, context) {
  1990. var rendererRenderStates = model._rendererResources.renderStates;
  1991. var techniques = model.gltf.techniques;
  1992. var technique = techniques[id];
  1993. var states = technique.states;
  1994. var booleanStates = getBooleanStates(states);
  1995. var statesFunctions = defaultValue(states.functions, defaultValue.EMPTY_OBJECT);
  1996. var blendColor = defaultValue(statesFunctions.blendColor, [0.0, 0.0, 0.0, 0.0]);
  1997. var blendEquationSeparate = defaultValue(statesFunctions.blendEquationSeparate, [
  1998. WebGLConstants.FUNC_ADD,
  1999. WebGLConstants.FUNC_ADD]);
  2000. var blendFuncSeparate = defaultValue(statesFunctions.blendFuncSeparate, [
  2001. WebGLConstants.ONE,
  2002. WebGLConstants.ZERO,
  2003. WebGLConstants.ONE,
  2004. WebGLConstants.ZERO]);
  2005. var colorMask = defaultValue(statesFunctions.colorMask, [true, true, true, true]);
  2006. var depthRange = defaultValue(statesFunctions.depthRange, [0.0, 1.0]);
  2007. var polygonOffset = defaultValue(statesFunctions.polygonOffset, [0.0, 0.0]);
  2008. var scissor = defaultValue(statesFunctions.scissor, [0.0, 0.0, 0.0, 0.0]);
  2009. rendererRenderStates[id] = RenderState.fromCache({
  2010. frontFace : defined(statesFunctions.frontFace) ? statesFunctions.frontFace[0] : WebGLConstants.CCW,
  2011. cull : {
  2012. enabled : booleanStates[WebGLConstants.CULL_FACE],
  2013. face : defined(statesFunctions.cullFace) ? statesFunctions.cullFace[0] : WebGLConstants.BACK
  2014. },
  2015. lineWidth : defined(statesFunctions.lineWidth) ? statesFunctions.lineWidth[0] : 1.0,
  2016. polygonOffset : {
  2017. enabled : booleanStates[WebGLConstants.POLYGON_OFFSET_FILL],
  2018. factor : polygonOffset[0],
  2019. units : polygonOffset[1]
  2020. },
  2021. scissorTest : {
  2022. enabled : booleanStates[WebGLConstants.SCISSOR_TEST],
  2023. rectangle : {
  2024. x : scissor[0],
  2025. y : scissor[1],
  2026. width : scissor[2],
  2027. height : scissor[3]
  2028. }
  2029. },
  2030. depthRange : {
  2031. near : depthRange[0],
  2032. far : depthRange[1]
  2033. },
  2034. depthTest : {
  2035. enabled : booleanStates[WebGLConstants.DEPTH_TEST],
  2036. func : defined(statesFunctions.depthFunc) ? statesFunctions.depthFunc[0] : WebGLConstants.LESS
  2037. },
  2038. colorMask : {
  2039. red : colorMask[0],
  2040. green : colorMask[1],
  2041. blue : colorMask[2],
  2042. alpha : colorMask[3]
  2043. },
  2044. depthMask : defined(statesFunctions.depthMask) ? statesFunctions.depthMask[0] : true,
  2045. blending : {
  2046. enabled : booleanStates[WebGLConstants.BLEND],
  2047. color : {
  2048. red : blendColor[0],
  2049. green : blendColor[1],
  2050. blue : blendColor[2],
  2051. alpha : blendColor[3]
  2052. },
  2053. equationRgb : blendEquationSeparate[0],
  2054. equationAlpha : blendEquationSeparate[1],
  2055. functionSourceRgb : blendFuncSeparate[0],
  2056. functionDestinationRgb : blendFuncSeparate[1],
  2057. functionSourceAlpha : blendFuncSeparate[2],
  2058. functionDestinationAlpha : blendFuncSeparate[3]
  2059. }
  2060. });
  2061. }
  2062. // This doesn't support LOCAL, which we could add if it is ever used.
  2063. var scratchTranslationRtc = new Cartesian3();
  2064. var gltfSemanticUniforms = {
  2065. MODEL : function(uniformState, model) {
  2066. return function() {
  2067. return uniformState.model;
  2068. };
  2069. },
  2070. VIEW : function(uniformState, model) {
  2071. return function() {
  2072. return uniformState.view;
  2073. };
  2074. },
  2075. PROJECTION : function(uniformState, model) {
  2076. return function() {
  2077. return uniformState.projection;
  2078. };
  2079. },
  2080. MODELVIEW : function(uniformState, model) {
  2081. return function() {
  2082. return uniformState.modelView;
  2083. };
  2084. },
  2085. CESIUM_RTC_MODELVIEW : function(uniformState, model) {
  2086. // CESIUM_RTC extension
  2087. var mvRtc = new Matrix4();
  2088. return function() {
  2089. Matrix4.getTranslation(uniformState.model, scratchTranslationRtc);
  2090. Cartesian3.add(scratchTranslationRtc, model._rtcCenter, scratchTranslationRtc);
  2091. Matrix4.multiplyByPoint(uniformState.view, scratchTranslationRtc, scratchTranslationRtc);
  2092. return Matrix4.setTranslation(uniformState.modelView, scratchTranslationRtc, mvRtc);
  2093. };
  2094. },
  2095. MODELVIEWPROJECTION : function(uniformState, model) {
  2096. return function() {
  2097. return uniformState.modelViewProjection;
  2098. };
  2099. },
  2100. MODELINVERSE : function(uniformState, model) {
  2101. return function() {
  2102. return uniformState.inverseModel;
  2103. };
  2104. },
  2105. VIEWINVERSE : function(uniformState, model) {
  2106. return function() {
  2107. return uniformState.inverseView;
  2108. };
  2109. },
  2110. PROJECTIONINVERSE : function(uniformState, model) {
  2111. return function() {
  2112. return uniformState.inverseProjection;
  2113. };
  2114. },
  2115. MODELVIEWINVERSE : function(uniformState, model) {
  2116. return function() {
  2117. return uniformState.inverseModelView;
  2118. };
  2119. },
  2120. MODELVIEWPROJECTIONINVERSE : function(uniformState, model) {
  2121. return function() {
  2122. return uniformState.inverseModelViewProjection;
  2123. };
  2124. },
  2125. MODELINVERSETRANSPOSE : function(uniformState, model) {
  2126. return function() {
  2127. return uniformState.inverseTransposeModel;
  2128. };
  2129. },
  2130. MODELVIEWINVERSETRANSPOSE : function(uniformState, model) {
  2131. return function() {
  2132. return uniformState.normal;
  2133. };
  2134. },
  2135. VIEWPORT : function(uniformState, model) {
  2136. return function() {
  2137. return uniformState.viewportCartesian4;
  2138. };
  2139. }
  2140. // JOINTMATRIX created in createCommand()
  2141. };
  2142. ///////////////////////////////////////////////////////////////////////////
  2143. function getScalarUniformFunction(value, model) {
  2144. var that = {
  2145. value : value,
  2146. clone : function(source, result) {
  2147. return source;
  2148. },
  2149. func : function() {
  2150. return that.value;
  2151. }
  2152. };
  2153. return that;
  2154. }
  2155. function getVec2UniformFunction(value, model) {
  2156. var that = {
  2157. value : Cartesian2.fromArray(value),
  2158. clone : Cartesian2.clone,
  2159. func : function() {
  2160. return that.value;
  2161. }
  2162. };
  2163. return that;
  2164. }
  2165. function getVec3UniformFunction(value, model) {
  2166. var that = {
  2167. value : Cartesian3.fromArray(value),
  2168. clone : Cartesian3.clone,
  2169. func : function() {
  2170. return that.value;
  2171. }
  2172. };
  2173. return that;
  2174. }
  2175. function getVec4UniformFunction(value, model) {
  2176. var that = {
  2177. value : Cartesian4.fromArray(value),
  2178. clone : Cartesian4.clone,
  2179. func : function() {
  2180. return that.value;
  2181. }
  2182. };
  2183. return that;
  2184. }
  2185. function getMat2UniformFunction(value, model) {
  2186. var that = {
  2187. value : Matrix2.fromColumnMajorArray(value),
  2188. clone : Matrix2.clone,
  2189. func : function() {
  2190. return that.value;
  2191. }
  2192. };
  2193. return that;
  2194. }
  2195. function getMat3UniformFunction(value, model) {
  2196. var that = {
  2197. value : Matrix3.fromColumnMajorArray(value),
  2198. clone : Matrix3.clone,
  2199. func : function() {
  2200. return that.value;
  2201. }
  2202. };
  2203. return that;
  2204. }
  2205. function getMat4UniformFunction(value, model) {
  2206. var that = {
  2207. value : Matrix4.fromColumnMajorArray(value),
  2208. clone : Matrix4.clone,
  2209. func : function() {
  2210. return that.value;
  2211. }
  2212. };
  2213. return that;
  2214. }
  2215. ///////////////////////////////////////////////////////////////////////////
  2216. function DelayLoadedTextureUniform(value, model) {
  2217. this._value = undefined;
  2218. this._textureId = value;
  2219. this._model = model;
  2220. }
  2221. defineProperties(DelayLoadedTextureUniform.prototype, {
  2222. value : {
  2223. get : function() {
  2224. // Use the default texture (1x1 white) until the model's texture is loaded
  2225. if (!defined(this._value)) {
  2226. var texture = this._model._rendererResources.textures[this._textureId];
  2227. if (defined(texture)) {
  2228. this._value = texture;
  2229. } else {
  2230. return this._model._defaultTexture;
  2231. }
  2232. }
  2233. return this._value;
  2234. },
  2235. set : function(value) {
  2236. this._value = value;
  2237. }
  2238. }
  2239. });
  2240. DelayLoadedTextureUniform.prototype.clone = function(source, result) {
  2241. return source;
  2242. };
  2243. DelayLoadedTextureUniform.prototype.func = undefined;
  2244. ///////////////////////////////////////////////////////////////////////////
  2245. function getTextureUniformFunction(value, model) {
  2246. var uniform = new DelayLoadedTextureUniform(value, model);
  2247. // Define function here to access closure since 'this' can't be
  2248. // used when the Renderer sets uniforms.
  2249. uniform.func = function() {
  2250. return uniform.value;
  2251. };
  2252. return uniform;
  2253. }
  2254. var gltfUniformFunctions = {};
  2255. gltfUniformFunctions[WebGLConstants.FLOAT] = getScalarUniformFunction;
  2256. gltfUniformFunctions[WebGLConstants.FLOAT_VEC2] = getVec2UniformFunction;
  2257. gltfUniformFunctions[WebGLConstants.FLOAT_VEC3] = getVec3UniformFunction;
  2258. gltfUniformFunctions[WebGLConstants.FLOAT_VEC4] = getVec4UniformFunction;
  2259. gltfUniformFunctions[WebGLConstants.INT] = getScalarUniformFunction;
  2260. gltfUniformFunctions[WebGLConstants.INT_VEC2] = getVec2UniformFunction;
  2261. gltfUniformFunctions[WebGLConstants.INT_VEC3] = getVec3UniformFunction;
  2262. gltfUniformFunctions[WebGLConstants.INT_VEC4] = getVec4UniformFunction;
  2263. gltfUniformFunctions[WebGLConstants.BOOL] = getScalarUniformFunction;
  2264. gltfUniformFunctions[WebGLConstants.BOOL_VEC2] = getVec2UniformFunction;
  2265. gltfUniformFunctions[WebGLConstants.BOOL_VEC3] = getVec3UniformFunction;
  2266. gltfUniformFunctions[WebGLConstants.BOOL_VEC4] = getVec4UniformFunction;
  2267. gltfUniformFunctions[WebGLConstants.FLOAT_MAT2] = getMat2UniformFunction;
  2268. gltfUniformFunctions[WebGLConstants.FLOAT_MAT3] = getMat3UniformFunction;
  2269. gltfUniformFunctions[WebGLConstants.FLOAT_MAT4] = getMat4UniformFunction;
  2270. gltfUniformFunctions[WebGLConstants.SAMPLER_2D] = getTextureUniformFunction;
  2271. // GLTF_SPEC: Support SAMPLER_CUBE. https://github.com/KhronosGroup/glTF/issues/40
  2272. var gltfUniformsFromNode = {
  2273. MODEL : function(uniformState, model, runtimeNode) {
  2274. return function() {
  2275. return runtimeNode.computedMatrix;
  2276. };
  2277. },
  2278. VIEW : function(uniformState, model, runtimeNode) {
  2279. return function() {
  2280. return uniformState.view;
  2281. };
  2282. },
  2283. PROJECTION : function(uniformState, model, runtimeNode) {
  2284. return function() {
  2285. return uniformState.projection;
  2286. };
  2287. },
  2288. MODELVIEW : function(uniformState, model, runtimeNode) {
  2289. var mv = new Matrix4();
  2290. return function() {
  2291. return Matrix4.multiplyTransformation(uniformState.view, runtimeNode.computedMatrix, mv);
  2292. };
  2293. },
  2294. CESIUM_RTC_MODELVIEW : function(uniformState, model, runtimeNode) {
  2295. // CESIUM_RTC extension
  2296. var mvRtc = new Matrix4();
  2297. return function() {
  2298. Matrix4.multiplyTransformation(uniformState.view, runtimeNode.computedMatrix, mvRtc);
  2299. return Matrix4.setTranslation(mvRtc, model._rtcCenterEye, mvRtc);
  2300. };
  2301. },
  2302. MODELVIEWPROJECTION : function(uniformState, model, runtimeNode) {
  2303. var mvp = new Matrix4();
  2304. return function() {
  2305. Matrix4.multiplyTransformation(uniformState.view, runtimeNode.computedMatrix, mvp);
  2306. return Matrix4.multiply(uniformState._projection, mvp, mvp);
  2307. };
  2308. },
  2309. MODELINVERSE : function(uniformState, model, runtimeNode) {
  2310. var mInverse = new Matrix4();
  2311. return function() {
  2312. return Matrix4.inverse(runtimeNode.computedMatrix, mInverse);
  2313. };
  2314. },
  2315. VIEWINVERSE : function(uniformState, model) {
  2316. return function() {
  2317. return uniformState.inverseView;
  2318. };
  2319. },
  2320. PROJECTIONINVERSE : function(uniformState, model, runtimeNode) {
  2321. return function() {
  2322. return uniformState.inverseProjection;
  2323. };
  2324. },
  2325. MODELVIEWINVERSE : function(uniformState, model, runtimeNode) {
  2326. var mv = new Matrix4();
  2327. var mvInverse = new Matrix4();
  2328. return function() {
  2329. Matrix4.multiplyTransformation(uniformState.view, runtimeNode.computedMatrix, mv);
  2330. return Matrix4.inverse(mv, mvInverse);
  2331. };
  2332. },
  2333. MODELVIEWPROJECTIONINVERSE : function(uniformState, model, runtimeNode) {
  2334. var mvp = new Matrix4();
  2335. var mvpInverse = new Matrix4();
  2336. return function() {
  2337. Matrix4.multiplyTransformation(uniformState.view, runtimeNode.computedMatrix, mvp);
  2338. Matrix4.multiply(uniformState._projection, mvp, mvp);
  2339. return Matrix4.inverse(mvp, mvpInverse);
  2340. };
  2341. },
  2342. MODELINVERSETRANSPOSE : function(uniformState, model, runtimeNode) {
  2343. var mInverse = new Matrix4();
  2344. var mInverseTranspose = new Matrix3();
  2345. return function() {
  2346. Matrix4.inverse(runtimeNode.computedMatrix, mInverse);
  2347. Matrix4.getRotation(mInverse, mInverseTranspose);
  2348. return Matrix3.transpose(mInverseTranspose, mInverseTranspose);
  2349. };
  2350. },
  2351. MODELVIEWINVERSETRANSPOSE : function(uniformState, model, runtimeNode) {
  2352. var mv = new Matrix4();
  2353. var mvInverse = new Matrix4();
  2354. var mvInverseTranspose = new Matrix3();
  2355. return function() {
  2356. Matrix4.multiplyTransformation(uniformState.view, runtimeNode.computedMatrix, mv);
  2357. Matrix4.inverse(mv, mvInverse);
  2358. Matrix4.getRotation(mvInverse, mvInverseTranspose);
  2359. return Matrix3.transpose(mvInverseTranspose, mvInverseTranspose);
  2360. };
  2361. },
  2362. VIEWPORT : function(uniformState, model, runtimeNode) {
  2363. return function() {
  2364. return uniformState.viewportCartesian4;
  2365. };
  2366. }
  2367. };
  2368. function getUniformFunctionFromSource(source, model, semantic, uniformState) {
  2369. var runtimeNode = model._runtime.nodes[source];
  2370. return gltfUniformsFromNode[semantic](uniformState, model, runtimeNode);
  2371. }
  2372. function createUniformMaps(model, context) {
  2373. var loadResources = model._loadResources;
  2374. if (!loadResources.finishedProgramCreation()) {
  2375. return;
  2376. }
  2377. if (!loadResources.createUniformMaps) {
  2378. return;
  2379. }
  2380. loadResources.createUniformMaps = false;
  2381. var gltf = model.gltf;
  2382. var materials = gltf.materials;
  2383. var techniques = gltf.techniques;
  2384. var uniformMaps = model._uniformMaps;
  2385. for (var materialId in materials) {
  2386. if (materials.hasOwnProperty(materialId)) {
  2387. var material = materials[materialId];
  2388. var instanceParameters = material.values;
  2389. var technique = techniques[material.technique];
  2390. var parameters = technique.parameters;
  2391. var uniforms = technique.uniforms;
  2392. var uniformMap = {};
  2393. var uniformValues = {};
  2394. var jointMatrixUniformName;
  2395. // Uniform parameters
  2396. for (var name in uniforms) {
  2397. if (uniforms.hasOwnProperty(name)) {
  2398. var parameterName = uniforms[name];
  2399. var parameter = parameters[parameterName];
  2400. // GLTF_SPEC: This does not take into account uniform arrays,
  2401. // indicated by parameters with a count property.
  2402. //
  2403. // https://github.com/KhronosGroup/glTF/issues/258
  2404. // GLTF_SPEC: In this implementation, material parameters with a
  2405. // semantic or targeted via a source (for animation) are not
  2406. // targetable for material animations. Is this too strict?
  2407. //
  2408. // https://github.com/KhronosGroup/glTF/issues/142
  2409. if (defined(instanceParameters[parameterName])) {
  2410. // Parameter overrides by the instance technique
  2411. var uv = gltfUniformFunctions[parameter.type](instanceParameters[parameterName], model);
  2412. uniformMap[name] = uv.func;
  2413. uniformValues[parameterName] = uv;
  2414. } else if (defined(parameter.node)) {
  2415. uniformMap[name] = getUniformFunctionFromSource(parameter.node, model, parameter.semantic, context.uniformState);
  2416. } else if (defined(parameter.semantic)) {
  2417. if (parameter.semantic !== 'JOINTMATRIX') {
  2418. // Map glTF semantic to Cesium automatic uniform
  2419. uniformMap[name] = gltfSemanticUniforms[parameter.semantic](context.uniformState, model);
  2420. } else {
  2421. jointMatrixUniformName = name;
  2422. }
  2423. } else if (defined(parameter.value)) {
  2424. // Technique value that isn't overridden by a material
  2425. var uv2 = gltfUniformFunctions[parameter.type](parameter.value, model);
  2426. uniformMap[name] = uv2.func;
  2427. uniformValues[parameterName] = uv2;
  2428. }
  2429. }
  2430. }
  2431. var u = uniformMaps[materialId];
  2432. u.uniformMap = uniformMap; // uniform name -> function for the renderer
  2433. u.values = uniformValues; // material parameter name -> ModelMaterial for modifying the parameter at runtime
  2434. u.jointMatrixUniformName = jointMatrixUniformName;
  2435. }
  2436. }
  2437. }
  2438. function scaleFromMatrix5Array(matrix) {
  2439. return [matrix[0], matrix[1], matrix[2], matrix[3],
  2440. matrix[5], matrix[6], matrix[7], matrix[8],
  2441. matrix[10], matrix[11], matrix[12], matrix[13],
  2442. matrix[15], matrix[16], matrix[17], matrix[18]];
  2443. }
  2444. function translateFromMatrix5Array(matrix) {
  2445. return [matrix[20], matrix[21], matrix[22], matrix[23]];
  2446. }
  2447. function createUniformsForQuantizedAttributes(model, primitive, context) {
  2448. var gltf = model.gltf;
  2449. var accessors = gltf.accessors;
  2450. var programId = getProgramForPrimitive(model, primitive);
  2451. var quantizedUniforms = model._quantizedUniforms[programId];
  2452. var setUniforms = {};
  2453. var uniformMap = {};
  2454. for (var attribute in primitive.attributes) {
  2455. if (primitive.attributes.hasOwnProperty(attribute)) {
  2456. var accessorId = primitive.attributes[attribute];
  2457. var a = accessors[accessorId];
  2458. var extensions = a.extensions;
  2459. if (defined(extensions)) {
  2460. var quantizedAttributes = extensions.WEB3D_quantized_attributes;
  2461. if (defined(quantizedAttributes)) {
  2462. var decodeMatrix = quantizedAttributes.decodeMatrix;
  2463. var uniformVariable = 'czm_u_dec_' + attribute.toLowerCase();
  2464. switch (a.type) {
  2465. case 'SCALAR':
  2466. uniformMap[uniformVariable] = getMat2UniformFunction(decodeMatrix, model).func;
  2467. setUniforms[uniformVariable] = true;
  2468. break;
  2469. case 'VEC2':
  2470. uniformMap[uniformVariable] = getMat3UniformFunction(decodeMatrix, model).func;
  2471. setUniforms[uniformVariable] = true;
  2472. break;
  2473. case 'VEC3':
  2474. uniformMap[uniformVariable] = getMat4UniformFunction(decodeMatrix, model).func;
  2475. setUniforms[uniformVariable] = true;
  2476. break;
  2477. case 'VEC4':
  2478. // VEC4 attributes are split into scale and translate because there is no mat5 in GLSL
  2479. var uniformVariableScale = uniformVariable + '_scale';
  2480. var uniformVariableTranslate = uniformVariable + '_translate';
  2481. uniformMap[uniformVariableScale] = getMat4UniformFunction(scaleFromMatrix5Array(decodeMatrix), model).func;
  2482. uniformMap[uniformVariableTranslate] = getVec4UniformFunction(translateFromMatrix5Array(decodeMatrix), model).func;
  2483. setUniforms[uniformVariableScale] = true;
  2484. setUniforms[uniformVariableTranslate] = true;
  2485. break;
  2486. }
  2487. }
  2488. }
  2489. }
  2490. }
  2491. // If there are any unset quantized uniforms in this program, they should be set to the identity
  2492. for (var quantizedUniform in quantizedUniforms) {
  2493. if (quantizedUniforms.hasOwnProperty(quantizedUniform)) {
  2494. if (!setUniforms[quantizedUniform]) {
  2495. var properties = quantizedUniforms[quantizedUniform];
  2496. if (defined(properties.mat)) {
  2497. if (properties.mat === 2) {
  2498. uniformMap[quantizedUniform] = getMat2UniformFunction(Matrix2.IDENTITY, model).func;
  2499. } else if (properties.mat === 3) {
  2500. uniformMap[quantizedUniform] = getMat3UniformFunction(Matrix3.IDENTITY, model).func;
  2501. } else if (properties.mat === 4) {
  2502. uniformMap[quantizedUniform] = getMat4UniformFunction(Matrix4.IDENTITY, model).func;
  2503. }
  2504. }
  2505. if (defined(properties.vec)) {
  2506. if (properties.vec === 4) {
  2507. uniformMap[quantizedUniform] = getVec4UniformFunction([0, 0, 0, 0], model).func;
  2508. }
  2509. }
  2510. }
  2511. }
  2512. }
  2513. return uniformMap;
  2514. }
  2515. function createPickColorFunction(color) {
  2516. return function() {
  2517. return color;
  2518. };
  2519. }
  2520. function createJointMatricesFunction(runtimeNode) {
  2521. return function() {
  2522. return runtimeNode.computedJointMatrices;
  2523. };
  2524. }
  2525. function createCommand(model, gltfNode, runtimeNode, context, scene3DOnly) {
  2526. var nodeCommands = model._nodeCommands;
  2527. var pickIds = model._pickIds;
  2528. var allowPicking = model.allowPicking;
  2529. var runtimeMeshesByName = model._runtime.meshesByName;
  2530. var resources = model._rendererResources;
  2531. var rendererVertexArrays = resources.vertexArrays;
  2532. var rendererPrograms = resources.programs;
  2533. var rendererPickPrograms = resources.pickPrograms;
  2534. var rendererRenderStates = resources.renderStates;
  2535. var uniformMaps = model._uniformMaps;
  2536. var gltf = model.gltf;
  2537. var accessors = gltf.accessors;
  2538. var gltfMeshes = gltf.meshes;
  2539. var techniques = gltf.techniques;
  2540. var materials = gltf.materials;
  2541. var meshes = gltfNode.meshes;
  2542. var meshesLength = meshes.length;
  2543. for (var j = 0; j < meshesLength; ++j) {
  2544. var id = meshes[j];
  2545. var mesh = gltfMeshes[id];
  2546. var primitives = mesh.primitives;
  2547. var length = primitives.length;
  2548. // The glTF node hierarchy is a DAG so a node can have more than one
  2549. // parent, so a node may already have commands. If so, append more
  2550. // since they will have a different model matrix.
  2551. for (var i = 0; i < length; ++i) {
  2552. var primitive = primitives[i];
  2553. var ix = accessors[primitive.indices];
  2554. var material = materials[primitive.material];
  2555. var technique = techniques[material.technique];
  2556. var programId = technique.program;
  2557. var boundingSphere;
  2558. var positionAccessor = primitive.attributes.POSITION;
  2559. if (defined(positionAccessor)) {
  2560. var minMax = getAccessorMinMax(gltf, positionAccessor);
  2561. boundingSphere = BoundingSphere.fromCornerPoints(Cartesian3.fromArray(minMax.min), Cartesian3.fromArray(minMax.max));
  2562. }
  2563. var vertexArray = rendererVertexArrays[id + '.primitive.' + i];
  2564. var offset;
  2565. var count;
  2566. if (defined(ix)) {
  2567. count = ix.count;
  2568. offset = (ix.byteOffset / IndexDatatype.getSizeInBytes(ix.componentType)); // glTF has offset in bytes. Cesium has offsets in indices
  2569. }
  2570. else {
  2571. var positions = accessors[primitive.attributes.POSITION];
  2572. count = positions.count;
  2573. offset = 0;
  2574. }
  2575. var um = uniformMaps[primitive.material];
  2576. var uniformMap = um.uniformMap;
  2577. if (defined(um.jointMatrixUniformName)) {
  2578. var jointUniformMap = {};
  2579. jointUniformMap[um.jointMatrixUniformName] = createJointMatricesFunction(runtimeNode);
  2580. uniformMap = combine(uniformMap, jointUniformMap);
  2581. }
  2582. // Allow callback to modify the uniformMap
  2583. if (defined(model._uniformMapLoaded)) {
  2584. uniformMap = model._uniformMapLoaded(uniformMap, programId, runtimeNode);
  2585. }
  2586. // Add uniforms for decoding quantized attributes if used
  2587. if (usesExtension(model, 'WEB3D_quantized_attributes')) {
  2588. var quantizedUniformMap = createUniformsForQuantizedAttributes(model, primitive, context);
  2589. uniformMap = combine(uniformMap, quantizedUniformMap);
  2590. }
  2591. var rs = rendererRenderStates[material.technique];
  2592. // GLTF_SPEC: Offical means to determine translucency. https://github.com/KhronosGroup/glTF/issues/105
  2593. var isTranslucent = rs.blending.enabled;
  2594. var owner = {
  2595. primitive : defaultValue(model.pickPrimitive, model),
  2596. id : model.id,
  2597. node : runtimeNode.publicNode,
  2598. mesh : runtimeMeshesByName[mesh.name]
  2599. };
  2600. var castShadows = ShadowMode.castShadows(model._shadows);
  2601. var receiveShadows = ShadowMode.receiveShadows(model._shadows);
  2602. var command = new DrawCommand({
  2603. boundingVolume : new BoundingSphere(), // updated in update()
  2604. cull : model.cull,
  2605. modelMatrix : new Matrix4(), // computed in update()
  2606. primitiveType : primitive.mode,
  2607. vertexArray : vertexArray,
  2608. count : count,
  2609. offset : offset,
  2610. shaderProgram : rendererPrograms[technique.program],
  2611. castShadows : castShadows,
  2612. receiveShadows : receiveShadows,
  2613. uniformMap : uniformMap,
  2614. renderState : rs,
  2615. owner : owner,
  2616. pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE
  2617. });
  2618. var pickCommand;
  2619. if (allowPicking) {
  2620. var pickUniformMap;
  2621. // Callback to override default model picking
  2622. if (defined(model._pickFragmentShaderLoaded)) {
  2623. if (defined(model._pickUniformMapLoaded)) {
  2624. pickUniformMap = model._pickUniformMapLoaded(uniformMap);
  2625. } else {
  2626. // This is unlikely, but could happen if the override shader does not
  2627. // need new uniforms since, for example, its pick ids are coming from
  2628. // a vertex attribute or are baked into the shader source.
  2629. pickUniformMap = combine(uniformMap);
  2630. }
  2631. } else {
  2632. var pickId = context.createPickId(owner);
  2633. pickIds.push(pickId);
  2634. var pickUniforms = {
  2635. czm_pickColor : createPickColorFunction(pickId.color)
  2636. };
  2637. pickUniformMap = combine(uniformMap, pickUniforms);
  2638. }
  2639. pickCommand = new DrawCommand({
  2640. boundingVolume : new BoundingSphere(), // updated in update()
  2641. cull : model.cull,
  2642. modelMatrix : new Matrix4(), // computed in update()
  2643. primitiveType : primitive.mode,
  2644. vertexArray : vertexArray,
  2645. count : count,
  2646. offset : offset,
  2647. shaderProgram : rendererPickPrograms[technique.program],
  2648. uniformMap : pickUniformMap,
  2649. renderState : rs,
  2650. owner : owner,
  2651. pass : isTranslucent ? Pass.TRANSLUCENT : Pass.OPAQUE
  2652. });
  2653. }
  2654. var command2D;
  2655. var pickCommand2D;
  2656. if (!scene3DOnly) {
  2657. command2D = DrawCommand.shallowClone(command);
  2658. command2D.boundingVolume = new BoundingSphere();
  2659. command2D.modelMatrix = new Matrix4();
  2660. if (allowPicking) {
  2661. pickCommand2D = DrawCommand.shallowClone(pickCommand);
  2662. pickCommand2D.boundingVolume = new BoundingSphere();
  2663. pickCommand2D.modelMatrix = new Matrix4();
  2664. }
  2665. }
  2666. var nodeCommand = {
  2667. show : true,
  2668. boundingSphere : boundingSphere,
  2669. command : command,
  2670. pickCommand : pickCommand,
  2671. command2D : command2D,
  2672. pickCommand2D : pickCommand2D
  2673. };
  2674. runtimeNode.commands.push(nodeCommand);
  2675. nodeCommands.push(nodeCommand);
  2676. }
  2677. }
  2678. }
  2679. function createRuntimeNodes(model, context, scene3DOnly) {
  2680. var loadResources = model._loadResources;
  2681. if (!loadResources.finishedEverythingButTextureCreation()) {
  2682. return;
  2683. }
  2684. if (!loadResources.createRuntimeNodes) {
  2685. return;
  2686. }
  2687. loadResources.createRuntimeNodes = false;
  2688. var rootNodes = [];
  2689. var runtimeNodes = model._runtime.nodes;
  2690. var gltf = model.gltf;
  2691. var nodes = gltf.nodes;
  2692. var scene = gltf.scenes[gltf.scene];
  2693. var sceneNodes = scene.nodes;
  2694. var length = sceneNodes.length;
  2695. var stack = [];
  2696. for (var i = 0; i < length; ++i) {
  2697. stack.push({
  2698. parentRuntimeNode : undefined,
  2699. gltfNode : nodes[sceneNodes[i]],
  2700. id : sceneNodes[i]
  2701. });
  2702. while (stack.length > 0) {
  2703. var n = stack.pop();
  2704. var parentRuntimeNode = n.parentRuntimeNode;
  2705. var gltfNode = n.gltfNode;
  2706. // Node hierarchy is a DAG so a node can have more than one parent so it may already exist
  2707. var runtimeNode = runtimeNodes[n.id];
  2708. if (runtimeNode.parents.length === 0) {
  2709. if (defined(gltfNode.matrix)) {
  2710. runtimeNode.matrix = Matrix4.fromColumnMajorArray(gltfNode.matrix);
  2711. } else {
  2712. // TRS converted to Cesium types
  2713. var rotation = gltfNode.rotation;
  2714. runtimeNode.translation = Cartesian3.fromArray(gltfNode.translation);
  2715. runtimeNode.rotation = Quaternion.unpack(rotation);
  2716. runtimeNode.scale = Cartesian3.fromArray(gltfNode.scale);
  2717. }
  2718. }
  2719. if (defined(parentRuntimeNode)) {
  2720. parentRuntimeNode.children.push(runtimeNode);
  2721. runtimeNode.parents.push(parentRuntimeNode);
  2722. } else {
  2723. rootNodes.push(runtimeNode);
  2724. }
  2725. if (defined(gltfNode.meshes)) {
  2726. createCommand(model, gltfNode, runtimeNode, context, scene3DOnly);
  2727. }
  2728. var children = gltfNode.children;
  2729. var childrenLength = children.length;
  2730. for (var k = 0; k < childrenLength; ++k) {
  2731. stack.push({
  2732. parentRuntimeNode : runtimeNode,
  2733. gltfNode : nodes[children[k]],
  2734. id : children[k]
  2735. });
  2736. }
  2737. }
  2738. }
  2739. model._runtime.rootNodes = rootNodes;
  2740. model._runtime.nodes = runtimeNodes;
  2741. }
  2742. function createResources(model, frameState) {
  2743. var context = frameState.context;
  2744. var scene3DOnly = frameState.scene3DOnly;
  2745. if (model._loadRendererResourcesFromCache) {
  2746. var resources = model._rendererResources;
  2747. var cachedResources = model._cachedRendererResources;
  2748. resources.buffers = cachedResources.buffers;
  2749. resources.vertexArrays = cachedResources.vertexArrays;
  2750. resources.programs = cachedResources.programs;
  2751. resources.pickPrograms = cachedResources.pickPrograms;
  2752. resources.textures = cachedResources.textures;
  2753. resources.samplers = cachedResources.samplers;
  2754. resources.renderStates = cachedResources.renderStates;
  2755. // Vertex arrays are unique to this model, create instead of using the cache.
  2756. if (defined(model._precreatedAttributes)) {
  2757. createVertexArrays(model, context);
  2758. }
  2759. } else {
  2760. createBuffers(model, context); // using glTF bufferViews
  2761. createPrograms(model, context);
  2762. createSamplers(model, context);
  2763. loadTexturesFromBufferViews(model);
  2764. createTextures(model, context);
  2765. }
  2766. createSkins(model);
  2767. createRuntimeAnimations(model);
  2768. if (!model._loadRendererResourcesFromCache) {
  2769. createVertexArrays(model, context); // using glTF meshes
  2770. createRenderStates(model, context); // using glTF materials/techniques/states
  2771. // Long-term, we might not cache render states if they could change
  2772. // due to an animation, e.g., a uniform going from opaque to transparent.
  2773. // Could use copy-on-write if it is worth it. Probably overkill.
  2774. }
  2775. createUniformMaps(model, context); // using glTF materials/techniques
  2776. createRuntimeNodes(model, context, scene3DOnly); // using glTF scene
  2777. }
  2778. ///////////////////////////////////////////////////////////////////////////
  2779. function getNodeMatrix(node, result) {
  2780. var publicNode = node.publicNode;
  2781. var publicMatrix = publicNode.matrix;
  2782. if (publicNode.useMatrix && defined(publicMatrix)) {
  2783. // Public matrix overrides orginial glTF matrix and glTF animations
  2784. Matrix4.clone(publicMatrix, result);
  2785. } else if (defined(node.matrix)) {
  2786. Matrix4.clone(node.matrix, result);
  2787. } else {
  2788. Matrix4.fromTranslationQuaternionRotationScale(node.translation, node.rotation, node.scale, result);
  2789. // Keep matrix returned by the node in-sync if the node is targeted by an animation. Only TRS nodes can be targeted.
  2790. publicNode.setMatrix(result);
  2791. }
  2792. }
  2793. var scratchNodeStack = [];
  2794. var scratchComputedMatrixIn2D = new Matrix4();
  2795. function updateNodeHierarchyModelMatrix(model, modelTransformChanged, justLoaded, projection) {
  2796. var maxDirtyNumber = model._maxDirtyNumber;
  2797. var allowPicking = model.allowPicking;
  2798. var rootNodes = model._runtime.rootNodes;
  2799. var length = rootNodes.length;
  2800. var nodeStack = scratchNodeStack;
  2801. var computedModelMatrix = model._computedModelMatrix;
  2802. if (model._mode !== SceneMode.SCENE3D) {
  2803. computedModelMatrix = Transforms.basisTo2D(projection, computedModelMatrix, scratchComputedMatrixIn2D);
  2804. }
  2805. for (var i = 0; i < length; ++i) {
  2806. var n = rootNodes[i];
  2807. getNodeMatrix(n, n.transformToRoot);
  2808. nodeStack.push(n);
  2809. while (nodeStack.length > 0) {
  2810. n = nodeStack.pop();
  2811. var transformToRoot = n.transformToRoot;
  2812. var commands = n.commands;
  2813. if ((n.dirtyNumber === maxDirtyNumber) || modelTransformChanged || justLoaded) {
  2814. var nodeMatrix = Matrix4.multiplyTransformation(computedModelMatrix, transformToRoot, n.computedMatrix);
  2815. var commandsLength = commands.length;
  2816. if (commandsLength > 0) {
  2817. // Node has meshes, which has primitives. Update their commands.
  2818. for (var j = 0 ; j < commandsLength; ++j) {
  2819. var primitiveCommand = commands[j];
  2820. var command = primitiveCommand.command;
  2821. Matrix4.clone(nodeMatrix, command.modelMatrix);
  2822. // PERFORMANCE_IDEA: Can use transformWithoutScale if no node up to the root has scale (including animation)
  2823. BoundingSphere.transform(primitiveCommand.boundingSphere, command.modelMatrix, command.boundingVolume);
  2824. if (defined(model._rtcCenter)) {
  2825. Cartesian3.add(model._rtcCenter, command.boundingVolume.center, command.boundingVolume.center);
  2826. }
  2827. if (allowPicking) {
  2828. var pickCommand = primitiveCommand.pickCommand;
  2829. Matrix4.clone(command.modelMatrix, pickCommand.modelMatrix);
  2830. BoundingSphere.clone(command.boundingVolume, pickCommand.boundingVolume);
  2831. }
  2832. // If the model crosses the IDL in 2D, it will be drawn in one viewport, but part of it
  2833. // will be clipped by the viewport. We create a second command that translates the model
  2834. // model matrix to the opposite side of the map so the part that was clipped in one viewport
  2835. // is drawn in the other.
  2836. command = primitiveCommand.command2D;
  2837. if (defined(command) && model._mode === SceneMode.SCENE2D) {
  2838. Matrix4.clone(nodeMatrix, command.modelMatrix);
  2839. command.modelMatrix[13] -= CesiumMath.sign(command.modelMatrix[13]) * 2.0 * CesiumMath.PI * projection.ellipsoid.maximumRadius;
  2840. BoundingSphere.transform(primitiveCommand.boundingSphere, command.modelMatrix, command.boundingVolume);
  2841. if (allowPicking) {
  2842. var pickCommand2D = primitiveCommand.pickCommand2D;
  2843. Matrix4.clone(command.modelMatrix, pickCommand2D.modelMatrix);
  2844. BoundingSphere.clone(command.boundingVolume, pickCommand2D.boundingVolume);
  2845. }
  2846. }
  2847. }
  2848. }
  2849. }
  2850. var children = n.children;
  2851. var childrenLength = children.length;
  2852. for (var k = 0; k < childrenLength; ++k) {
  2853. var child = children[k];
  2854. // A node's transform needs to be updated if
  2855. // - It was targeted for animation this frame, or
  2856. // - Any of its ancestors were targeted for animation this frame
  2857. // PERFORMANCE_IDEA: if a child has multiple parents and only one of the parents
  2858. // is dirty, all the subtrees for each child instance will be dirty; we probably
  2859. // won't see this in the wild often.
  2860. child.dirtyNumber = Math.max(child.dirtyNumber, n.dirtyNumber);
  2861. if ((child.dirtyNumber === maxDirtyNumber) || justLoaded) {
  2862. // Don't check for modelTransformChanged since if only the model's model matrix changed,
  2863. // we do not need to rebuild the local transform-to-root, only the final
  2864. // [model's-model-matrix][transform-to-root] above.
  2865. getNodeMatrix(child, child.transformToRoot);
  2866. Matrix4.multiplyTransformation(transformToRoot, child.transformToRoot, child.transformToRoot);
  2867. }
  2868. nodeStack.push(child);
  2869. }
  2870. }
  2871. }
  2872. ++model._maxDirtyNumber;
  2873. }
  2874. var scratchObjectSpace = new Matrix4();
  2875. function applySkins(model) {
  2876. var skinnedNodes = model._runtime.skinnedNodes;
  2877. var length = skinnedNodes.length;
  2878. for (var i = 0; i < length; ++i) {
  2879. var node = skinnedNodes[i];
  2880. scratchObjectSpace = Matrix4.inverseTransformation(node.transformToRoot, scratchObjectSpace);
  2881. var computedJointMatrices = node.computedJointMatrices;
  2882. var joints = node.joints;
  2883. var bindShapeMatrix = node.bindShapeMatrix;
  2884. var inverseBindMatrices = node.inverseBindMatrices;
  2885. var inverseBindMatricesLength = inverseBindMatrices.length;
  2886. for (var m = 0; m < inverseBindMatricesLength; ++m) {
  2887. // [joint-matrix] = [node-to-root^-1][joint-to-root][inverse-bind][bind-shape]
  2888. if (!defined(computedJointMatrices[m])) {
  2889. computedJointMatrices[m] = new Matrix4();
  2890. }
  2891. computedJointMatrices[m] = Matrix4.multiplyTransformation(scratchObjectSpace, joints[m].transformToRoot, computedJointMatrices[m]);
  2892. computedJointMatrices[m] = Matrix4.multiplyTransformation(computedJointMatrices[m], inverseBindMatrices[m], computedJointMatrices[m]);
  2893. if (defined(bindShapeMatrix)) {
  2894. // Optimization for when bind shape matrix is the identity.
  2895. computedJointMatrices[m] = Matrix4.multiplyTransformation(computedJointMatrices[m], bindShapeMatrix, computedJointMatrices[m]);
  2896. }
  2897. }
  2898. }
  2899. }
  2900. function updatePerNodeShow(model) {
  2901. // Totally not worth it, but we could optimize this:
  2902. // http://blogs.agi.com/insight3d/index.php/2008/02/13/deletion-in-bounding-volume-hierarchies/
  2903. var rootNodes = model._runtime.rootNodes;
  2904. var length = rootNodes.length;
  2905. var nodeStack = scratchNodeStack;
  2906. for (var i = 0; i < length; ++i) {
  2907. var n = rootNodes[i];
  2908. n.computedShow = n.publicNode.show;
  2909. nodeStack.push(n);
  2910. while (nodeStack.length > 0) {
  2911. n = nodeStack.pop();
  2912. var show = n.computedShow;
  2913. var nodeCommands = n.commands;
  2914. var nodeCommandsLength = nodeCommands.length;
  2915. for (var j = 0 ; j < nodeCommandsLength; ++j) {
  2916. nodeCommands[j].show = show;
  2917. }
  2918. // if commandsLength is zero, the node has a light or camera
  2919. var children = n.children;
  2920. var childrenLength = children.length;
  2921. for (var k = 0; k < childrenLength; ++k) {
  2922. var child = children[k];
  2923. // Parent needs to be shown for child to be shown.
  2924. child.computedShow = show && child.publicNode.show;
  2925. nodeStack.push(child);
  2926. }
  2927. }
  2928. }
  2929. }
  2930. function updatePickIds(model, context) {
  2931. var id = model.id;
  2932. if (model._id !== id) {
  2933. model._id = id;
  2934. var pickIds = model._pickIds;
  2935. var length = pickIds.length;
  2936. for (var i = 0; i < length; ++i) {
  2937. pickIds[i].object.id = id;
  2938. }
  2939. }
  2940. }
  2941. function updateWireframe(model) {
  2942. if (model._debugWireframe !== model.debugWireframe) {
  2943. model._debugWireframe = model.debugWireframe;
  2944. // This assumes the original primitive was TRIANGLES and that the triangles
  2945. // are connected for the wireframe to look perfect.
  2946. var primitiveType = model.debugWireframe ? PrimitiveType.LINES : PrimitiveType.TRIANGLES;
  2947. var nodeCommands = model._nodeCommands;
  2948. var length = nodeCommands.length;
  2949. for (var i = 0; i < length; ++i) {
  2950. nodeCommands[i].command.primitiveType = primitiveType;
  2951. }
  2952. }
  2953. }
  2954. function updateShowBoundingVolume(model) {
  2955. if (model.debugShowBoundingVolume !== model._debugShowBoundingVolume) {
  2956. model._debugShowBoundingVolume = model.debugShowBoundingVolume;
  2957. var debugShowBoundingVolume = model.debugShowBoundingVolume;
  2958. var nodeCommands = model._nodeCommands;
  2959. var length = nodeCommands.length;
  2960. for (var i = 0; i < length; ++i) {
  2961. nodeCommands[i].command.debugShowBoundingVolume = debugShowBoundingVolume;
  2962. }
  2963. }
  2964. }
  2965. function updateShadows(model) {
  2966. if (model.shadows !== model._shadows) {
  2967. model._shadows = model.shadows;
  2968. var castShadows = ShadowMode.castShadows(model.shadows);
  2969. var receiveShadows = ShadowMode.receiveShadows(model.shadows);
  2970. var nodeCommands = model._nodeCommands;
  2971. var length = nodeCommands.length;
  2972. for (var i = 0; i < length; i++) {
  2973. var nodeCommand = nodeCommands[i];
  2974. nodeCommand.command.castShadows = castShadows;
  2975. nodeCommand.command.receiveShadows = receiveShadows;
  2976. }
  2977. }
  2978. }
  2979. var scratchBoundingSphere = new BoundingSphere();
  2980. function scaleInPixels(positionWC, radius, frameState) {
  2981. scratchBoundingSphere.center = positionWC;
  2982. scratchBoundingSphere.radius = radius;
  2983. return frameState.camera.getPixelSize(scratchBoundingSphere, frameState.context.drawingBufferWidth, frameState.context.drawingBufferHeight);
  2984. }
  2985. var scratchPosition = new Cartesian3();
  2986. var scratchCartographic = new Cartographic();
  2987. function getScale(model, frameState) {
  2988. var scale = model.scale;
  2989. if (model.minimumPixelSize !== 0.0) {
  2990. // Compute size of bounding sphere in pixels
  2991. var context = frameState.context;
  2992. var maxPixelSize = Math.max(context.drawingBufferWidth, context.drawingBufferHeight);
  2993. var m = defined(model._clampedModelMatrix) ? model._clampedModelMatrix : model.modelMatrix;
  2994. scratchPosition.x = m[12];
  2995. scratchPosition.y = m[13];
  2996. scratchPosition.z = m[14];
  2997. if (defined(model._rtcCenter)) {
  2998. Cartesian3.add(model._rtcCenter, scratchPosition, scratchPosition);
  2999. }
  3000. if (model._mode !== SceneMode.SCENE3D) {
  3001. var projection = frameState.mapProjection;
  3002. var cartographic = projection.ellipsoid.cartesianToCartographic(scratchPosition, scratchCartographic);
  3003. projection.project(cartographic, scratchPosition);
  3004. Cartesian3.fromElements(scratchPosition.z, scratchPosition.x, scratchPosition.y, scratchPosition);
  3005. }
  3006. var radius = model.boundingSphere.radius;
  3007. var metersPerPixel = scaleInPixels(scratchPosition, radius, frameState);
  3008. // metersPerPixel is always > 0.0
  3009. var pixelsPerMeter = 1.0 / metersPerPixel;
  3010. var diameterInPixels = Math.min(pixelsPerMeter * (2.0 * radius), maxPixelSize);
  3011. // Maintain model's minimum pixel size
  3012. if (diameterInPixels < model.minimumPixelSize) {
  3013. scale = (model.minimumPixelSize * metersPerPixel) / (2.0 * model._initialRadius);
  3014. }
  3015. }
  3016. return defined(model.maximumScale) ? Math.min(model.maximumScale, scale) : scale;
  3017. }
  3018. function releaseCachedGltf(model) {
  3019. if (defined(model._cacheKey) && defined(model._cachedGltf) && (--model._cachedGltf.count === 0)) {
  3020. delete gltfCache[model._cacheKey];
  3021. }
  3022. model._cachedGltf = undefined;
  3023. }
  3024. function checkSupportedExtensions(model) {
  3025. var extensionsUsed = model.gltf.extensionsUsed;
  3026. if (defined(extensionsUsed)) {
  3027. var extensionsUsedCount = extensionsUsed.length;
  3028. for (var index=0;index<extensionsUsedCount;++index) {
  3029. var extension = extensionsUsed[index];
  3030. if (extension !== 'CESIUM_RTC' && extension !== 'KHR_binary_glTF' &&
  3031. extension !== 'KHR_materials_common' && extension !== 'WEB3D_quantized_attributes') {
  3032. throw new RuntimeError('Unsupported glTF Extension: ' + extension);
  3033. }
  3034. }
  3035. }
  3036. }
  3037. ///////////////////////////////////////////////////////////////////////////
  3038. function CachedRendererResources(context, cacheKey) {
  3039. this.buffers = undefined;
  3040. this.vertexArrays = undefined;
  3041. this.programs = undefined;
  3042. this.pickPrograms = undefined;
  3043. this.textures = undefined;
  3044. this.samplers = undefined;
  3045. this.renderStates = undefined;
  3046. this.ready = false;
  3047. this.context = context;
  3048. this.cacheKey = cacheKey;
  3049. this.count = 0;
  3050. }
  3051. function destroy(property) {
  3052. for (var name in property) {
  3053. if (property.hasOwnProperty(name)) {
  3054. property[name].destroy();
  3055. }
  3056. }
  3057. }
  3058. function destroyCachedRendererResources(resources) {
  3059. destroy(resources.buffers);
  3060. destroy(resources.vertexArrays);
  3061. destroy(resources.programs);
  3062. destroy(resources.pickPrograms);
  3063. destroy(resources.textures);
  3064. }
  3065. CachedRendererResources.prototype.release = function() {
  3066. if (--this.count === 0) {
  3067. if (defined(this.cacheKey)) {
  3068. // Remove if this was cached
  3069. delete this.context.cache.modelRendererResourceCache[this.cacheKey];
  3070. }
  3071. destroyCachedRendererResources(this);
  3072. return destroyObject(this);
  3073. }
  3074. return undefined;
  3075. };
  3076. ///////////////////////////////////////////////////////////////////////////
  3077. function getUpdateHeightCallback(model, ellipsoid, cartoPosition) {
  3078. return function (clampedPosition) {
  3079. if (model.heightReference === HeightReference.RELATIVE_TO_GROUND) {
  3080. var clampedCart = ellipsoid.cartesianToCartographic(clampedPosition, scratchCartographic);
  3081. clampedCart.height += cartoPosition.height;
  3082. ellipsoid.cartographicToCartesian(clampedCart, clampedPosition);
  3083. }
  3084. var clampedModelMatrix = model._clampedModelMatrix;
  3085. // Modify clamped model matrix to use new height
  3086. Matrix4.clone(model.modelMatrix, clampedModelMatrix);
  3087. clampedModelMatrix[12] = clampedPosition.x;
  3088. clampedModelMatrix[13] = clampedPosition.y;
  3089. clampedModelMatrix[14] = clampedPosition.z;
  3090. model._heightChanged = true;
  3091. };
  3092. }
  3093. function updateClamping(model) {
  3094. if (defined(model._removeUpdateHeightCallback)) {
  3095. model._removeUpdateHeightCallback();
  3096. model._removeUpdateHeightCallback = undefined;
  3097. }
  3098. var scene = model._scene;
  3099. if (!defined(scene) || (model.heightReference === HeightReference.NONE)) {
  3100. //>>includeStart('debug', pragmas.debug);
  3101. if (model.heightReference !== HeightReference.NONE) {
  3102. throw new DeveloperError('Height reference is not supported without a scene.');
  3103. }
  3104. //>>includeEnd('debug');
  3105. model._clampedModelMatrix = undefined;
  3106. return;
  3107. }
  3108. var globe = scene.globe;
  3109. var ellipsoid = globe.ellipsoid;
  3110. // Compute cartographic position so we don't recompute every update
  3111. var modelMatrix = model.modelMatrix;
  3112. scratchPosition.x = modelMatrix[12];
  3113. scratchPosition.y = modelMatrix[13];
  3114. scratchPosition.z = modelMatrix[14];
  3115. var cartoPosition = ellipsoid.cartesianToCartographic(scratchPosition);
  3116. if (!defined(model._clampedModelMatrix)) {
  3117. model._clampedModelMatrix = Matrix4.clone(modelMatrix, new Matrix4());
  3118. }
  3119. // Install callback to handle updating of terrain tiles
  3120. var surface = globe._surface;
  3121. model._removeUpdateHeightCallback = surface.updateHeight(cartoPosition, getUpdateHeightCallback(model, ellipsoid, cartoPosition));
  3122. // Set the correct height now
  3123. var height = globe.getHeight(cartoPosition);
  3124. if (defined(height)) {
  3125. // Get callback with cartoPosition being the non-clamped position
  3126. var cb = getUpdateHeightCallback(model, ellipsoid, cartoPosition);
  3127. // Compute the clamped cartesian and call updateHeight callback
  3128. Cartographic.clone(cartoPosition, scratchCartographic);
  3129. scratchCartographic.height = height;
  3130. ellipsoid.cartographicToCartesian(scratchCartographic, scratchPosition);
  3131. cb(scratchPosition);
  3132. }
  3133. }
  3134. var scratchDisplayConditionCartesian = new Cartesian3();
  3135. var scratchDistanceDisplayConditionCartographic = new Cartographic();
  3136. function distanceDisplayConditionVisible(model, frameState) {
  3137. var distance2;
  3138. var ddc = model.distanceDisplayCondition;
  3139. var nearSquared = ddc.near * ddc.near;
  3140. var farSquared = ddc.far * ddc.far;
  3141. if (frameState.mode === SceneMode.SCENE2D) {
  3142. var frustum2DWidth = frameState.camera.frustum.right - frameState.camera.frustum.left;
  3143. distance2 = frustum2DWidth * 0.5;
  3144. distance2 = distance2 * distance2;
  3145. } else {
  3146. // Distance to center of primitive's reference frame
  3147. var position = Matrix4.getTranslation(model.modelMatrix, scratchDisplayConditionCartesian);
  3148. if (frameState.mode === SceneMode.COLUMBUS_VIEW) {
  3149. var projection = frameState.mapProjection;
  3150. var ellipsoid = projection.ellipsoid;
  3151. var cartographic = ellipsoid.cartesianToCartographic(position, scratchDistanceDisplayConditionCartographic);
  3152. position = projection.project(cartographic, position);
  3153. Cartesian3.fromElements(position.z, position.x, position.y, position);
  3154. }
  3155. distance2 = Cartesian3.distanceSquared(position, frameState.camera.positionWC);
  3156. }
  3157. return (distance2 >= nearSquared) && (distance2 <= farSquared);
  3158. }
  3159. /**
  3160. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  3161. * get the draw commands needed to render this primitive.
  3162. * <p>
  3163. * Do not call this function directly. This is documented just to
  3164. * list the exceptions that may be propagated when the scene is rendered:
  3165. * </p>
  3166. *
  3167. * @exception {RuntimeError} Failed to load external reference.
  3168. */
  3169. Model.prototype.update = function(frameState) {
  3170. if (frameState.mode === SceneMode.MORPHING) {
  3171. return;
  3172. }
  3173. var context = frameState.context;
  3174. this._defaultTexture = context.defaultTexture;
  3175. if ((this._state === ModelState.NEEDS_LOAD) && defined(this.gltf)) {
  3176. // Use renderer resources from cache instead of loading/creating them?
  3177. var cachedRendererResources;
  3178. var cacheKey = this.cacheKey;
  3179. if (defined(cacheKey)) {
  3180. context.cache.modelRendererResourceCache = defaultValue(context.cache.modelRendererResourceCache, {});
  3181. var modelCaches = context.cache.modelRendererResourceCache;
  3182. cachedRendererResources = modelCaches[this.cacheKey];
  3183. if (defined(cachedRendererResources)) {
  3184. if (!cachedRendererResources.ready) {
  3185. // Cached resources for the model are not loaded yet. We'll
  3186. // try again every frame until they are.
  3187. return;
  3188. }
  3189. ++cachedRendererResources.count;
  3190. this._loadRendererResourcesFromCache = true;
  3191. } else {
  3192. cachedRendererResources = new CachedRendererResources(context, cacheKey);
  3193. cachedRendererResources.count = 1;
  3194. modelCaches[this.cacheKey] = cachedRendererResources;
  3195. }
  3196. this._cachedRendererResources = cachedRendererResources;
  3197. } else {
  3198. cachedRendererResources = new CachedRendererResources(context);
  3199. cachedRendererResources.count = 1;
  3200. this._cachedRendererResources = cachedRendererResources;
  3201. }
  3202. this._state = ModelState.LOADING;
  3203. this._boundingSphere = computeBoundingSphere(this.gltf);
  3204. this._initialRadius = this._boundingSphere.radius;
  3205. checkSupportedExtensions(this);
  3206. if (this._state !== ModelState.FAILED) {
  3207. var extensions = this.gltf.extensions;
  3208. if (defined(extensions) && defined(extensions.CESIUM_RTC)) {
  3209. this._rtcCenter = Cartesian3.fromArray(extensions.CESIUM_RTC.center);
  3210. this._rtcCenterEye = new Cartesian3();
  3211. }
  3212. this._loadResources = new LoadResources();
  3213. parse(this);
  3214. }
  3215. }
  3216. var loadResources = this._loadResources;
  3217. var incrementallyLoadTextures = this._incrementallyLoadTextures;
  3218. var justLoaded = false;
  3219. if (this._state === ModelState.LOADING) {
  3220. // Create WebGL resources as buffers/shaders/textures are downloaded
  3221. createResources(this, frameState);
  3222. // Transition from LOADING -> LOADED once resources are downloaded and created.
  3223. // Textures may continue to stream in while in the LOADED state.
  3224. if (loadResources.finished() ||
  3225. (incrementallyLoadTextures && loadResources.finishedEverythingButTextureCreation())) {
  3226. this._state = ModelState.LOADED;
  3227. justLoaded = true;
  3228. }
  3229. }
  3230. // Incrementally stream textures.
  3231. if (defined(loadResources) && (this._state === ModelState.LOADED)) {
  3232. // Also check justLoaded so we don't process twice during the transition frame
  3233. if (incrementallyLoadTextures && !justLoaded) {
  3234. createResources(this, frameState);
  3235. }
  3236. if (loadResources.finished()) {
  3237. this._loadResources = undefined; // Clear CPU memory since WebGL resources were created.
  3238. var resources = this._rendererResources;
  3239. var cachedResources = this._cachedRendererResources;
  3240. cachedResources.buffers = resources.buffers;
  3241. cachedResources.vertexArrays = resources.vertexArrays;
  3242. cachedResources.programs = resources.programs;
  3243. cachedResources.pickPrograms = resources.pickPrograms;
  3244. cachedResources.textures = resources.textures;
  3245. cachedResources.samplers = resources.samplers;
  3246. cachedResources.renderStates = resources.renderStates;
  3247. cachedResources.ready = true;
  3248. // Vertex arrays are unique to this model, do not store in cache.
  3249. if (defined(this._precreatedAttributes)) {
  3250. cachedResources.vertexArrays = {};
  3251. }
  3252. if (this.releaseGltfJson) {
  3253. releaseCachedGltf(this);
  3254. }
  3255. }
  3256. }
  3257. var displayConditionPassed = defined(this.distanceDisplayCondition) ? distanceDisplayConditionVisible(this, frameState) : true;
  3258. var show = this.show && displayConditionPassed && (this.scale !== 0.0);
  3259. if ((show && this._state === ModelState.LOADED) || justLoaded) {
  3260. var animated = this.activeAnimations.update(frameState) || this._cesiumAnimationsDirty;
  3261. this._cesiumAnimationsDirty = false;
  3262. this._dirty = false;
  3263. var modelMatrix = this.modelMatrix;
  3264. var modeChanged = frameState.mode !== this._mode;
  3265. this._mode = frameState.mode;
  3266. // Model's model matrix needs to be updated
  3267. var modelTransformChanged = !Matrix4.equals(this._modelMatrix, modelMatrix) ||
  3268. (this._scale !== this.scale) ||
  3269. (this._minimumPixelSize !== this.minimumPixelSize) || (this.minimumPixelSize !== 0.0) || // Minimum pixel size changed or is enabled
  3270. (this._maximumScale !== this.maximumScale) ||
  3271. (this._heightReference !== this.heightReference) || this._heightChanged ||
  3272. modeChanged;
  3273. if (modelTransformChanged || justLoaded) {
  3274. Matrix4.clone(modelMatrix, this._modelMatrix);
  3275. updateClamping(this);
  3276. if (defined(this._clampedModelMatrix)) {
  3277. modelMatrix = this._clampedModelMatrix;
  3278. }
  3279. this._scale = this.scale;
  3280. this._minimumPixelSize = this.minimumPixelSize;
  3281. this._maximumScale = this.maximumScale;
  3282. this._heightReference = this.heightReference;
  3283. this._heightChanged = false;
  3284. var scale = getScale(this, frameState);
  3285. var computedModelMatrix = this._computedModelMatrix;
  3286. Matrix4.multiplyByUniformScale(modelMatrix, scale, computedModelMatrix);
  3287. Matrix4.multiplyTransformation(computedModelMatrix, yUpToZUp, computedModelMatrix);
  3288. }
  3289. // Update modelMatrix throughout the graph as needed
  3290. if (animated || modelTransformChanged || justLoaded) {
  3291. updateNodeHierarchyModelMatrix(this, modelTransformChanged, justLoaded, frameState.mapProjection);
  3292. this._dirty = true;
  3293. if (animated || justLoaded) {
  3294. // Apply skins if animation changed any node transforms
  3295. applySkins(this);
  3296. }
  3297. }
  3298. if (this._perNodeShowDirty) {
  3299. this._perNodeShowDirty = false;
  3300. updatePerNodeShow(this);
  3301. }
  3302. updatePickIds(this, context);
  3303. updateWireframe(this);
  3304. updateShowBoundingVolume(this);
  3305. updateShadows(this);
  3306. }
  3307. if (justLoaded) {
  3308. // Called after modelMatrix update.
  3309. var model = this;
  3310. frameState.afterRender.push(function() {
  3311. model._ready = true;
  3312. model._readyPromise.resolve(model);
  3313. });
  3314. return;
  3315. }
  3316. // We don't check show at the top of the function since we
  3317. // want to be able to progressively load models when they are not shown,
  3318. // and then have them visible immediately when show is set to true.
  3319. if (show && !this._ignoreCommands) {
  3320. // PERFORMANCE_IDEA: This is terrible
  3321. var commandList = frameState.commandList;
  3322. var passes = frameState.passes;
  3323. var nodeCommands = this._nodeCommands;
  3324. var length = nodeCommands.length;
  3325. var i;
  3326. var nc;
  3327. var idl2D = frameState.mapProjection.ellipsoid.maximumRadius * CesiumMath.PI;
  3328. var boundingVolume;
  3329. if (passes.render) {
  3330. for (i = 0; i < length; ++i) {
  3331. nc = nodeCommands[i];
  3332. if (nc.show) {
  3333. var command = nc.command;
  3334. commandList.push(command);
  3335. boundingVolume = command.boundingVolume;
  3336. if (frameState.mode === SceneMode.SCENE2D &&
  3337. (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) {
  3338. commandList.push(nc.command2D);
  3339. }
  3340. }
  3341. }
  3342. }
  3343. if (passes.pick && this.allowPicking) {
  3344. for (i = 0; i < length; ++i) {
  3345. nc = nodeCommands[i];
  3346. if (nc.show) {
  3347. var pickCommand = nc.pickCommand;
  3348. commandList.push(pickCommand);
  3349. boundingVolume = pickCommand.boundingVolume;
  3350. if (frameState.mode === SceneMode.SCENE2D &&
  3351. (boundingVolume.center.y + boundingVolume.radius > idl2D || boundingVolume.center.y - boundingVolume.radius < idl2D)) {
  3352. commandList.push(nc.pickCommand2D);
  3353. }
  3354. }
  3355. }
  3356. }
  3357. }
  3358. };
  3359. /**
  3360. * Returns true if this object was destroyed; otherwise, false.
  3361. * <br /><br />
  3362. * If this object was destroyed, it should not be used; calling any function other than
  3363. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  3364. *
  3365. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  3366. *
  3367. * @see Model#destroy
  3368. */
  3369. Model.prototype.isDestroyed = function() {
  3370. return false;
  3371. };
  3372. /**
  3373. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  3374. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  3375. * <br /><br />
  3376. * Once an object is destroyed, it should not be used; calling any function other than
  3377. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  3378. * assign the return value (<code>undefined</code>) to the object as done in the example.
  3379. *
  3380. * @returns {undefined}
  3381. *
  3382. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  3383. *
  3384. *
  3385. * @example
  3386. * model = model && model.destroy();
  3387. *
  3388. * @see Model#isDestroyed
  3389. */
  3390. Model.prototype.destroy = function() {
  3391. // Vertex arrays are unique to this model, destroy here.
  3392. if (defined(this._precreatedAttributes)) {
  3393. destroy(this._rendererResources.vertexArrays);
  3394. }
  3395. this._rendererResources = undefined;
  3396. this._cachedRendererResources = this._cachedRendererResources && this._cachedRendererResources.release();
  3397. var pickIds = this._pickIds;
  3398. var length = pickIds.length;
  3399. for (var i = 0; i < length; ++i) {
  3400. pickIds[i].destroy();
  3401. }
  3402. releaseCachedGltf(this);
  3403. return destroyObject(this);
  3404. };
  3405. return Model;
  3406. });