ClassificationModel.js 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  1. import arraySlice from '../Core/arraySlice.js';
  2. import BoundingSphere from '../Core/BoundingSphere.js';
  3. import Cartesian3 from '../Core/Cartesian3.js';
  4. import Cartesian4 from '../Core/Cartesian4.js';
  5. import Color from '../Core/Color.js';
  6. import combine from '../Core/combine.js';
  7. import ComponentDatatype from '../Core/ComponentDatatype.js';
  8. import defaultValue from '../Core/defaultValue.js';
  9. import defined from '../Core/defined.js';
  10. import defineProperties from '../Core/defineProperties.js';
  11. import destroyObject from '../Core/destroyObject.js';
  12. import DeveloperError from '../Core/DeveloperError.js';
  13. import FeatureDetection from '../Core/FeatureDetection.js';
  14. import IndexDatatype from '../Core/IndexDatatype.js';
  15. import Matrix4 from '../Core/Matrix4.js';
  16. import PrimitiveType from '../Core/PrimitiveType.js';
  17. import RuntimeError from '../Core/RuntimeError.js';
  18. import Transforms from '../Core/Transforms.js';
  19. import WebGLConstants from '../Core/WebGLConstants.js';
  20. import addDefaults from '../ThirdParty/GltfPipeline/addDefaults.js';
  21. import ForEach from '../ThirdParty/GltfPipeline/ForEach.js';
  22. import getAccessorByteStride from '../ThirdParty/GltfPipeline/getAccessorByteStride.js';
  23. import numberOfComponentsForType from '../ThirdParty/GltfPipeline/numberOfComponentsForType.js';
  24. import parseGlb from '../ThirdParty/GltfPipeline/parseGlb.js';
  25. import updateVersion from '../ThirdParty/GltfPipeline/updateVersion.js';
  26. import when from '../ThirdParty/when.js';
  27. import Axis from './Axis.js';
  28. import ModelLoadResources from './ModelLoadResources.js';
  29. import ModelUtility from './ModelUtility.js';
  30. import processModelMaterialsCommon from './processModelMaterialsCommon.js';
  31. import processPbrMaterials from './processPbrMaterials.js';
  32. import SceneMode from './SceneMode.js';
  33. import Vector3DTileBatch from './Vector3DTileBatch.js';
  34. import Vector3DTilePrimitive from './Vector3DTilePrimitive.js';
  35. var boundingSphereCartesian3Scratch = new Cartesian3();
  36. var ModelState = ModelUtility.ModelState;
  37. ///////////////////////////////////////////////////////////////////////////
  38. /**
  39. * A 3D model for classifying other 3D assets based on glTF, the runtime 3D asset format.
  40. * This is a special case when a model of a 3D tileset becomes a classifier when setting {@link Cesium3DTileset#classificationType}.
  41. *
  42. * @alias ClassificationModel
  43. * @constructor
  44. *
  45. * @private
  46. *
  47. * @param {Object} options Object with the following properties:
  48. * @param {ArrayBuffer|Uint8Array} options.gltf A binary glTF buffer.
  49. * @param {Boolean} [options.show=true] Determines if the model primitive will be shown.
  50. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
  51. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
  52. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
  53. * @param {ClassificationType} [options.classificationType] What this model will classify.
  54. *
  55. * @exception {RuntimeError} Only binary glTF is supported.
  56. * @exception {RuntimeError} Buffer data must be embedded in the binary glTF.
  57. * @exception {RuntimeError} Only one node is supported for classification and it must have a mesh.
  58. * @exception {RuntimeError} Only one mesh is supported when using b3dm for classification.
  59. * @exception {RuntimeError} Only one primitive per mesh is supported when using b3dm for classification.
  60. * @exception {RuntimeError} The mesh must have a position attribute.
  61. * @exception {RuntimeError} The mesh must have a batch id attribute.
  62. */
  63. function ClassificationModel(options) {
  64. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  65. var gltf = options.gltf;
  66. if (gltf instanceof ArrayBuffer) {
  67. gltf = new Uint8Array(gltf);
  68. }
  69. if (gltf instanceof Uint8Array) {
  70. // Parse and update binary glTF
  71. gltf = parseGlb(gltf);
  72. updateVersion(gltf);
  73. addDefaults(gltf);
  74. processModelMaterialsCommon(gltf);
  75. processPbrMaterials(gltf);
  76. } else {
  77. throw new RuntimeError('Only binary glTF is supported as a classifier.');
  78. }
  79. ForEach.buffer(gltf, function(buffer) {
  80. if (!defined(buffer.extras._pipeline.source)) {
  81. throw new RuntimeError('Buffer data must be embedded in the binary gltf.');
  82. }
  83. });
  84. var gltfNodes = gltf.nodes;
  85. var gltfMeshes = gltf.meshes;
  86. var gltfNode = gltfNodes[0];
  87. var meshId = gltfNode.mesh;
  88. if (gltfNodes.length !== 1 || !defined(meshId)) {
  89. throw new RuntimeError('Only one node is supported for classification and it must have a mesh.');
  90. }
  91. if (gltfMeshes.length !== 1) {
  92. throw new RuntimeError('Only one mesh is supported when using b3dm for classification.');
  93. }
  94. var gltfPrimitives = gltfMeshes[0].primitives;
  95. if (gltfPrimitives.length !== 1) {
  96. throw new RuntimeError('Only one primitive per mesh is supported when using b3dm for classification.');
  97. }
  98. var gltfPositionAttribute = gltfPrimitives[0].attributes.POSITION;
  99. if (!defined(gltfPositionAttribute)) {
  100. throw new RuntimeError('The mesh must have a position attribute.');
  101. }
  102. var gltfBatchIdAttribute = gltfPrimitives[0].attributes._BATCHID;
  103. if (!defined(gltfBatchIdAttribute)) {
  104. throw new RuntimeError('The mesh must have a batch id attribute.');
  105. }
  106. this._gltf = gltf;
  107. /**
  108. * Determines if the model primitive will be shown.
  109. *
  110. * @type {Boolean}
  111. *
  112. * @default true
  113. */
  114. this.show = defaultValue(options.show, true);
  115. /**
  116. * The 4x4 transformation matrix that transforms the model from model to world coordinates.
  117. * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's WGS84 coordinates.
  118. * Local reference frames can be used by providing a different transformation matrix, like that returned
  119. * by {@link Transforms.eastNorthUpToFixedFrame}.
  120. *
  121. * @type {Matrix4}
  122. *
  123. * @default {@link Matrix4.IDENTITY}
  124. *
  125. * @example
  126. * var origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  127. * m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  128. */
  129. this.modelMatrix = Matrix4.clone(defaultValue(options.modelMatrix, Matrix4.IDENTITY));
  130. this._modelMatrix = Matrix4.clone(this.modelMatrix);
  131. this._ready = false;
  132. this._readyPromise = when.defer();
  133. /**
  134. * This property is for debugging only; it is not for production use nor is it optimized.
  135. * <p>
  136. * Draws the bounding sphere for each draw command in the model. A glTF primitive corresponds
  137. * to one draw command. A glTF mesh has an array of primitives, often of length one.
  138. * </p>
  139. *
  140. * @type {Boolean}
  141. *
  142. * @default false
  143. */
  144. this.debugShowBoundingVolume = defaultValue(options.debugShowBoundingVolume, false);
  145. this._debugShowBoundingVolume = false;
  146. /**
  147. * This property is for debugging only; it is not for production use nor is it optimized.
  148. * <p>
  149. * Draws the model in wireframe.
  150. * </p>
  151. *
  152. * @type {Boolean}
  153. *
  154. * @default false
  155. */
  156. this.debugWireframe = defaultValue(options.debugWireframe, false);
  157. this._debugWireframe = false;
  158. this._classificationType = options.classificationType;
  159. // Undocumented options
  160. this._vertexShaderLoaded = options.vertexShaderLoaded;
  161. this._classificationShaderLoaded = options.classificationShaderLoaded;
  162. this._uniformMapLoaded = options.uniformMapLoaded;
  163. this._pickIdLoaded = options.pickIdLoaded;
  164. this._ignoreCommands = defaultValue(options.ignoreCommands, false);
  165. this._upAxis = defaultValue(options.upAxis, Axis.Y);
  166. this._batchTable = options.batchTable;
  167. this._computedModelMatrix = new Matrix4(); // Derived from modelMatrix and axis
  168. this._initialRadius = undefined; // Radius without model's scale property, model-matrix scale, animations, or skins
  169. this._boundingSphere = undefined;
  170. this._scaledBoundingSphere = new BoundingSphere();
  171. this._state = ModelState.NEEDS_LOAD;
  172. this._loadResources = undefined;
  173. this._mode = undefined;
  174. this._dirty = false; // true when the model was transformed this frame
  175. this._nodeMatrix = new Matrix4();
  176. this._primitive = undefined;
  177. this._extensionsUsed = undefined; // Cached used glTF extensions
  178. this._extensionsRequired = undefined; // Cached required glTF extensions
  179. this._quantizedUniforms = undefined; // Quantized uniforms for WEB3D_quantized_attributes
  180. this._buffers = {};
  181. this._vertexArray = undefined;
  182. this._shaderProgram = undefined;
  183. this._uniformMap = undefined;
  184. this._geometryByteLength = 0;
  185. this._trianglesLength = 0;
  186. // CESIUM_RTC extension
  187. this._rtcCenter = undefined; // reference to either 3D or 2D
  188. this._rtcCenterEye = undefined; // in eye coordinates
  189. this._rtcCenter3D = undefined; // in world coordinates
  190. this._rtcCenter2D = undefined; // in projected world coordinates
  191. }
  192. defineProperties(ClassificationModel.prototype, {
  193. /**
  194. * The object for the glTF JSON, including properties with default values omitted
  195. * from the JSON provided to this model.
  196. *
  197. * @memberof ClassificationModel.prototype
  198. *
  199. * @type {Object}
  200. * @readonly
  201. *
  202. * @default undefined
  203. */
  204. gltf : {
  205. get : function() {
  206. return this._gltf;
  207. }
  208. },
  209. /**
  210. * The model's bounding sphere in its local coordinate system.
  211. *
  212. * @memberof ClassificationModel.prototype
  213. *
  214. * @type {BoundingSphere}
  215. * @readonly
  216. *
  217. * @default undefined
  218. *
  219. * @exception {DeveloperError} The model is not loaded. Use ClassificationModel.readyPromise or wait for ClassificationModel.ready to be true.
  220. *
  221. * @example
  222. * // Center in WGS84 coordinates
  223. * var center = Cesium.Matrix4.multiplyByPoint(model.modelMatrix, model.boundingSphere.center, new Cesium.Cartesian3());
  224. */
  225. boundingSphere : {
  226. get : function() {
  227. //>>includeStart('debug', pragmas.debug);
  228. if (this._state !== ModelState.LOADED) {
  229. throw new DeveloperError('The model is not loaded. Use ClassificationModel.readyPromise or wait for ClassificationModel.ready to be true.');
  230. }
  231. //>>includeEnd('debug');
  232. var modelMatrix = this.modelMatrix;
  233. var nonUniformScale = Matrix4.getScale(modelMatrix, boundingSphereCartesian3Scratch);
  234. var scaledBoundingSphere = this._scaledBoundingSphere;
  235. scaledBoundingSphere.center = Cartesian3.multiplyComponents(this._boundingSphere.center, nonUniformScale, scaledBoundingSphere.center);
  236. scaledBoundingSphere.radius = Cartesian3.maximumComponent(nonUniformScale) * this._initialRadius;
  237. if (defined(this._rtcCenter)) {
  238. Cartesian3.add(this._rtcCenter, scaledBoundingSphere.center, scaledBoundingSphere.center);
  239. }
  240. return scaledBoundingSphere;
  241. }
  242. },
  243. /**
  244. * When <code>true</code>, this model is ready to render, i.e., the external binary, image,
  245. * and shader files were downloaded and the WebGL resources were created. This is set to
  246. * <code>true</code> right before {@link ClassificationModel#readyPromise} is resolved.
  247. *
  248. * @memberof ClassificationModel.prototype
  249. *
  250. * @type {Boolean}
  251. * @readonly
  252. *
  253. * @default false
  254. */
  255. ready : {
  256. get : function() {
  257. return this._ready;
  258. }
  259. },
  260. /**
  261. * Gets the promise that will be resolved when this model is ready to render, i.e., when the external binary, image,
  262. * and shader files were downloaded and the WebGL resources were created.
  263. * <p>
  264. * This promise is resolved at the end of the frame before the first frame the model is rendered in.
  265. * </p>
  266. *
  267. * @memberof ClassificationModel.prototype
  268. * @type {Promise.<ClassificationModel>}
  269. * @readonly
  270. *
  271. * @see ClassificationModel#ready
  272. */
  273. readyPromise : {
  274. get : function() {
  275. return this._readyPromise.promise;
  276. }
  277. },
  278. /**
  279. * Returns true if the model was transformed this frame
  280. *
  281. * @memberof ClassificationModel.prototype
  282. *
  283. * @type {Boolean}
  284. * @readonly
  285. *
  286. * @private
  287. */
  288. dirty : {
  289. get : function() {
  290. return this._dirty;
  291. }
  292. },
  293. /**
  294. * Returns an object with all of the glTF extensions used.
  295. *
  296. * @memberof ClassificationModel.prototype
  297. *
  298. * @type {Object}
  299. * @readonly
  300. */
  301. extensionsUsed : {
  302. get : function() {
  303. if (!defined(this._extensionsUsed)) {
  304. this._extensionsUsed = ModelUtility.getUsedExtensions(this.gltf);
  305. }
  306. return this._extensionsUsed;
  307. }
  308. },
  309. /**
  310. * Returns an object with all of the glTF extensions required.
  311. *
  312. * @memberof ClassificationModel.prototype
  313. *
  314. * @type {Object}
  315. * @readonly
  316. */
  317. extensionsRequired : {
  318. get : function() {
  319. if (!defined(this._extensionsRequired)) {
  320. this._extensionsRequired = ModelUtility.getRequiredExtensions(this.gltf);
  321. }
  322. return this._extensionsRequired;
  323. }
  324. },
  325. /**
  326. * Gets the model's up-axis.
  327. * By default models are y-up according to the glTF spec, however geo-referenced models will typically be z-up.
  328. *
  329. * @memberof ClassificationModel.prototype
  330. *
  331. * @type {Number}
  332. * @default Axis.Y
  333. * @readonly
  334. *
  335. * @private
  336. */
  337. upAxis : {
  338. get : function() {
  339. return this._upAxis;
  340. }
  341. },
  342. /**
  343. * Gets the model's triangle count.
  344. *
  345. * @private
  346. */
  347. trianglesLength : {
  348. get : function() {
  349. return this._trianglesLength;
  350. }
  351. },
  352. /**
  353. * Gets the model's geometry memory in bytes. This includes all vertex and index buffers.
  354. *
  355. * @private
  356. */
  357. geometryByteLength : {
  358. get : function() {
  359. return this._geometryByteLength;
  360. }
  361. },
  362. /**
  363. * Gets the model's texture memory in bytes.
  364. *
  365. * @private
  366. */
  367. texturesByteLength : {
  368. get : function() {
  369. return 0;
  370. }
  371. },
  372. /**
  373. * Gets the model's classification type.
  374. * @memberof ClassificationModel.prototype
  375. * @type {ClassificationType}
  376. */
  377. classificationType : {
  378. get : function() {
  379. return this._classificationType;
  380. }
  381. }
  382. });
  383. ///////////////////////////////////////////////////////////////////////////
  384. function addBuffersToLoadResources(model) {
  385. var gltf = model.gltf;
  386. var loadResources = model._loadResources;
  387. ForEach.buffer(gltf, function(buffer, id) {
  388. loadResources.buffers[id] = buffer.extras._pipeline.source;
  389. });
  390. }
  391. function parseBufferViews(model) {
  392. var bufferViews = model.gltf.bufferViews;
  393. var vertexBuffersToCreate = model._loadResources.vertexBuffersToCreate;
  394. // Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below.
  395. ForEach.bufferView(model.gltf, function(bufferView, id) {
  396. if (bufferView.target === WebGLConstants.ARRAY_BUFFER) {
  397. vertexBuffersToCreate.enqueue(id);
  398. }
  399. });
  400. var indexBuffersToCreate = model._loadResources.indexBuffersToCreate;
  401. var indexBufferIds = {};
  402. // The Cesium Renderer requires knowing the datatype for an index buffer
  403. // at creation type, which is not part of the glTF bufferview so loop
  404. // through glTF accessors to create the bufferview's index buffer.
  405. ForEach.accessor(model.gltf, function(accessor) {
  406. var bufferViewId = accessor.bufferView;
  407. var bufferView = bufferViews[bufferViewId];
  408. if ((bufferView.target === WebGLConstants.ELEMENT_ARRAY_BUFFER) && !defined(indexBufferIds[bufferViewId])) {
  409. indexBufferIds[bufferViewId] = true;
  410. indexBuffersToCreate.enqueue({
  411. id : bufferViewId,
  412. componentType : accessor.componentType
  413. });
  414. }
  415. });
  416. }
  417. function createVertexBuffer(bufferViewId, model) {
  418. var loadResources = model._loadResources;
  419. var bufferViews = model.gltf.bufferViews;
  420. var bufferView = bufferViews[bufferViewId];
  421. var vertexBuffer = loadResources.getBuffer(bufferView);
  422. model._buffers[bufferViewId] = vertexBuffer;
  423. model._geometryByteLength += vertexBuffer.byteLength;
  424. }
  425. function createIndexBuffer(bufferViewId, componentType, model) {
  426. var loadResources = model._loadResources;
  427. var bufferViews = model.gltf.bufferViews;
  428. var bufferView = bufferViews[bufferViewId];
  429. var indexBuffer = {
  430. typedArray : loadResources.getBuffer(bufferView),
  431. indexDatatype : componentType
  432. };
  433. model._buffers[bufferViewId] = indexBuffer;
  434. model._geometryByteLength += indexBuffer.typedArray.byteLength;
  435. }
  436. function createBuffers(model) {
  437. var loadResources = model._loadResources;
  438. if (loadResources.pendingBufferLoads !== 0) {
  439. return;
  440. }
  441. var vertexBuffersToCreate = loadResources.vertexBuffersToCreate;
  442. var indexBuffersToCreate = loadResources.indexBuffersToCreate;
  443. while (vertexBuffersToCreate.length > 0) {
  444. createVertexBuffer(vertexBuffersToCreate.dequeue(), model);
  445. }
  446. while (indexBuffersToCreate.length > 0) {
  447. var i = indexBuffersToCreate.dequeue();
  448. createIndexBuffer(i.id, i.componentType, model);
  449. }
  450. }
  451. function modifyShaderForQuantizedAttributes(shader, model) {
  452. var primitive = model.gltf.meshes[0].primitives[0];
  453. var result = ModelUtility.modifyShaderForQuantizedAttributes(model.gltf, primitive, shader);
  454. model._quantizedUniforms = result.uniforms;
  455. return result.shader;
  456. }
  457. function modifyShader(shader, callback) {
  458. if (defined(callback)) {
  459. shader = callback(shader);
  460. }
  461. return shader;
  462. }
  463. function createProgram(model) {
  464. var gltf = model.gltf;
  465. var positionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'POSITION');
  466. var batchIdName = ModelUtility.getAttributeOrUniformBySemantic(gltf, '_BATCHID');
  467. var attributeLocations = {};
  468. attributeLocations[positionName] = 0;
  469. attributeLocations[batchIdName] = 1;
  470. var modelViewProjectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEWPROJECTION');
  471. var uniformDecl;
  472. var toClip;
  473. if (!defined(modelViewProjectionName)) {
  474. var projectionName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'PROJECTION');
  475. var modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'MODELVIEW');
  476. if (!defined(modelViewName)) {
  477. modelViewName = ModelUtility.getAttributeOrUniformBySemantic(gltf, 'CESIUM_RTC_MODELVIEW');
  478. }
  479. uniformDecl =
  480. 'uniform mat4 ' + modelViewName + ';\n' +
  481. 'uniform mat4 ' + projectionName + ';\n';
  482. toClip = projectionName + ' * ' + modelViewName + ' * vec4(' + positionName + ', 1.0)';
  483. } else {
  484. uniformDecl = 'uniform mat4 ' + modelViewProjectionName + ';\n';
  485. toClip = modelViewProjectionName + ' * vec4(' + positionName + ', 1.0)';
  486. }
  487. var computePosition = ' vec4 positionInClipCoords = ' + toClip + ';\n';
  488. var vs =
  489. 'attribute vec3 ' + positionName + ';\n' +
  490. 'attribute float ' + batchIdName + ';\n' +
  491. uniformDecl +
  492. 'void main() {\n' +
  493. computePosition +
  494. ' gl_Position = czm_depthClampFarPlane(positionInClipCoords);\n' +
  495. '}\n';
  496. var fs =
  497. '#ifdef GL_EXT_frag_depth\n' +
  498. '#extension GL_EXT_frag_depth : enable\n' +
  499. '#endif\n' +
  500. 'void main() \n' +
  501. '{ \n' +
  502. ' gl_FragColor = vec4(1.0); \n' +
  503. ' czm_writeDepthClampedToFarPlane();\n' +
  504. '}\n';
  505. if (model.extensionsUsed.WEB3D_quantized_attributes) {
  506. vs = modifyShaderForQuantizedAttributes(vs, model);
  507. }
  508. var drawVS = modifyShader(vs, model._vertexShaderLoaded);
  509. var drawFS = modifyShader(fs, model._classificationShaderLoaded);
  510. drawVS = ModelUtility.modifyVertexShaderForLogDepth(drawVS, toClip);
  511. drawFS = ModelUtility.modifyFragmentShaderForLogDepth(drawFS);
  512. model._shaderProgram = {
  513. vertexShaderSource : drawVS,
  514. fragmentShaderSource : drawFS,
  515. attributeLocations : attributeLocations
  516. };
  517. }
  518. function getAttributeLocations() {
  519. return {
  520. POSITION : 0,
  521. _BATCHID : 1
  522. };
  523. }
  524. function createVertexArray(model) {
  525. var loadResources = model._loadResources;
  526. if (!loadResources.finishedBuffersCreation() || defined(model._vertexArray)) {
  527. return;
  528. }
  529. var rendererBuffers = model._buffers;
  530. var gltf = model.gltf;
  531. var accessors = gltf.accessors;
  532. var meshes = gltf.meshes;
  533. var primitives = meshes[0].primitives;
  534. var primitive = primitives[0];
  535. var attributeLocations = getAttributeLocations();
  536. var attributes = {};
  537. ForEach.meshPrimitiveAttribute(primitive, function(accessorId, attributeName) {
  538. // Skip if the attribute is not used by the material, e.g., because the asset
  539. // was exported with an attribute that wasn't used and the asset wasn't optimized.
  540. var attributeLocation = attributeLocations[attributeName];
  541. if (defined(attributeLocation)) {
  542. var a = accessors[accessorId];
  543. attributes[attributeName] = {
  544. index: attributeLocation,
  545. vertexBuffer: rendererBuffers[a.bufferView],
  546. componentsPerAttribute: numberOfComponentsForType(a.type),
  547. componentDatatype: a.componentType,
  548. offsetInBytes: a.byteOffset,
  549. strideInBytes: getAccessorByteStride(gltf, a)
  550. };
  551. }
  552. });
  553. var indexBuffer;
  554. if (defined(primitive.indices)) {
  555. var accessor = accessors[primitive.indices];
  556. indexBuffer = rendererBuffers[accessor.bufferView];
  557. }
  558. model._vertexArray = {
  559. attributes : attributes,
  560. indexBuffer : indexBuffer
  561. };
  562. }
  563. var gltfSemanticUniforms = {
  564. PROJECTION : function(uniformState, model) {
  565. return ModelUtility.getGltfSemanticUniforms().PROJECTION(uniformState, model);
  566. },
  567. MODELVIEW : function(uniformState, model) {
  568. return ModelUtility.getGltfSemanticUniforms().MODELVIEW(uniformState, model);
  569. },
  570. CESIUM_RTC_MODELVIEW : function(uniformState, model) {
  571. return ModelUtility.getGltfSemanticUniforms().CESIUM_RTC_MODELVIEW(uniformState, model);
  572. },
  573. MODELVIEWPROJECTION : function(uniformState, model) {
  574. return ModelUtility.getGltfSemanticUniforms().MODELVIEWPROJECTION(uniformState, model);
  575. }
  576. };
  577. function createUniformMap(model, context) {
  578. if (defined(model._uniformMap)) {
  579. return;
  580. }
  581. var uniformMap = {};
  582. ForEach.technique(model.gltf, function(technique) {
  583. ForEach.techniqueUniform(technique, function(uniform, uniformName) {
  584. if (!defined(uniform.semantic) || !defined(gltfSemanticUniforms[uniform.semantic])) {
  585. return;
  586. }
  587. uniformMap[uniformName] = gltfSemanticUniforms[uniform.semantic](context.uniformState, model);
  588. });
  589. });
  590. model._uniformMap = uniformMap;
  591. }
  592. function createUniformsForQuantizedAttributes(model, primitive) {
  593. return ModelUtility.createUniformsForQuantizedAttributes(model.gltf, primitive, model._quantizedUniforms);
  594. }
  595. function triangleCountFromPrimitiveIndices(primitive, indicesCount) {
  596. switch (primitive.mode) {
  597. case PrimitiveType.TRIANGLES:
  598. return (indicesCount / 3);
  599. case PrimitiveType.TRIANGLE_STRIP:
  600. case PrimitiveType.TRIANGLE_FAN:
  601. return Math.max(indicesCount - 2, 0);
  602. default:
  603. return 0;
  604. }
  605. }
  606. function createPrimitive(model) {
  607. var batchTable = model._batchTable;
  608. var uniformMap = model._uniformMap;
  609. var vertexArray = model._vertexArray;
  610. var gltf = model.gltf;
  611. var accessors = gltf.accessors;
  612. var gltfMeshes = gltf.meshes;
  613. var primitive = gltfMeshes[0].primitives[0];
  614. var ix = accessors[primitive.indices];
  615. var positionAccessor = primitive.attributes.POSITION;
  616. var minMax = ModelUtility.getAccessorMinMax(gltf, positionAccessor);
  617. var boundingSphere = BoundingSphere.fromCornerPoints(Cartesian3.fromArray(minMax.min), Cartesian3.fromArray(minMax.max));
  618. var offset;
  619. var count;
  620. if (defined(ix)) {
  621. count = ix.count;
  622. offset = (ix.byteOffset / IndexDatatype.getSizeInBytes(ix.componentType)); // glTF has offset in bytes. Cesium has offsets in indices
  623. }
  624. else {
  625. var positions = accessors[primitive.attributes.POSITION];
  626. count = positions.count;
  627. offset = 0;
  628. }
  629. // Update model triangle count using number of indices
  630. model._trianglesLength += triangleCountFromPrimitiveIndices(primitive, count);
  631. // Allow callback to modify the uniformMap
  632. if (defined(model._uniformMapLoaded)) {
  633. uniformMap = model._uniformMapLoaded(uniformMap);
  634. }
  635. // Add uniforms for decoding quantized attributes if used
  636. if (model.extensionsUsed.WEB3D_quantized_attributes) {
  637. var quantizedUniformMap = createUniformsForQuantizedAttributes(model, primitive);
  638. uniformMap = combine(uniformMap, quantizedUniformMap);
  639. }
  640. var attribute = vertexArray.attributes.POSITION;
  641. var componentDatatype = attribute.componentDatatype;
  642. var typedArray = attribute.vertexBuffer;
  643. var byteOffset = typedArray.byteOffset;
  644. var bufferLength = typedArray.byteLength / ComponentDatatype.getSizeInBytes(componentDatatype);
  645. var positionsBuffer = ComponentDatatype.createArrayBufferView(componentDatatype, typedArray.buffer, byteOffset, bufferLength);
  646. attribute = vertexArray.attributes._BATCHID;
  647. componentDatatype = attribute.componentDatatype;
  648. typedArray = attribute.vertexBuffer;
  649. byteOffset = typedArray.byteOffset;
  650. bufferLength = typedArray.byteLength / ComponentDatatype.getSizeInBytes(componentDatatype);
  651. var vertexBatchIds = ComponentDatatype.createArrayBufferView(componentDatatype, typedArray.buffer, byteOffset, bufferLength);
  652. var buffer = vertexArray.indexBuffer.typedArray;
  653. var indices;
  654. if (vertexArray.indexBuffer.indexDatatype === IndexDatatype.UNSIGNED_SHORT) {
  655. indices = new Uint16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / Uint16Array.BYTES_PER_ELEMENT);
  656. } else {
  657. indices = new Uint32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / Uint32Array.BYTES_PER_ELEMENT);
  658. }
  659. positionsBuffer = arraySlice(positionsBuffer);
  660. vertexBatchIds = arraySlice(vertexBatchIds);
  661. indices = arraySlice(indices, offset, offset + count);
  662. var batchIds = [];
  663. var indexCounts = [];
  664. var indexOffsets = [];
  665. var batchedIndices = [];
  666. var currentId = vertexBatchIds[indices[0]];
  667. batchIds.push(currentId);
  668. indexOffsets.push(0);
  669. var batchId;
  670. var indexOffset;
  671. var indexCount;
  672. var indicesLength = indices.length;
  673. for (var j = 1; j < indicesLength; ++j) {
  674. batchId = vertexBatchIds[indices[j]];
  675. if (batchId !== currentId) {
  676. indexOffset = indexOffsets[indexOffsets.length - 1];
  677. indexCount = j - indexOffset;
  678. batchIds.push(batchId);
  679. indexCounts.push(indexCount);
  680. indexOffsets.push(j);
  681. batchedIndices.push(new Vector3DTileBatch({
  682. offset : indexOffset,
  683. count : indexCount,
  684. batchIds : [currentId],
  685. color : Color.WHITE
  686. }));
  687. currentId = batchId;
  688. }
  689. }
  690. indexOffset = indexOffsets[indexOffsets.length - 1];
  691. indexCount = indicesLength - indexOffset;
  692. indexCounts.push(indexCount);
  693. batchedIndices.push(new Vector3DTileBatch({
  694. offset : indexOffset,
  695. count : indexCount,
  696. batchIds : [currentId],
  697. color : Color.WHITE
  698. }));
  699. var shader = model._shaderProgram;
  700. var vertexShaderSource = shader.vertexShaderSource;
  701. var fragmentShaderSource = shader.fragmentShaderSource;
  702. var attributeLocations = shader.attributeLocations;
  703. var pickId = defined(model._pickIdLoaded) ? model._pickIdLoaded() : undefined;
  704. model._primitive = new Vector3DTilePrimitive({
  705. classificationType : model._classificationType,
  706. positions : positionsBuffer,
  707. indices : indices,
  708. indexOffsets : indexOffsets,
  709. indexCounts : indexCounts,
  710. batchIds : batchIds,
  711. vertexBatchIds : vertexBatchIds,
  712. batchedIndices : batchedIndices,
  713. batchTable : batchTable,
  714. boundingVolume : new BoundingSphere(), // updated in update()
  715. _vertexShaderSource : vertexShaderSource,
  716. _fragmentShaderSource : fragmentShaderSource,
  717. _attributeLocations : attributeLocations,
  718. _uniformMap : uniformMap,
  719. _pickId : pickId,
  720. _modelMatrix : new Matrix4(), // updated in update()
  721. _boundingSphere : boundingSphere // used to update boundingVolume
  722. });
  723. // Release CPU resources
  724. model._buffers = undefined;
  725. model._vertexArray = undefined;
  726. model._shaderProgram = undefined;
  727. model._uniformMap = undefined;
  728. }
  729. function createRuntimeNodes(model) {
  730. var loadResources = model._loadResources;
  731. if (!loadResources.finished()) {
  732. return;
  733. }
  734. if (defined(model._primitive)) {
  735. return;
  736. }
  737. var gltf = model.gltf;
  738. var nodes = gltf.nodes;
  739. var gltfNode = nodes[0];
  740. model._nodeMatrix = ModelUtility.getTransform(gltfNode, model._nodeMatrix);
  741. createPrimitive(model);
  742. }
  743. function createResources(model, frameState) {
  744. var context = frameState.context;
  745. ModelUtility.checkSupportedGlExtensions(model.gltf.glExtensionsUsed, context);
  746. createBuffers(model); // using glTF bufferViews
  747. createProgram(model);
  748. createVertexArray(model); // using glTF meshes
  749. createUniformMap(model, context); // using glTF materials/techniques
  750. createRuntimeNodes(model); // using glTF scene
  751. }
  752. ///////////////////////////////////////////////////////////////////////////
  753. var scratchComputedTranslation = new Cartesian4();
  754. var scratchComputedMatrixIn2D = new Matrix4();
  755. function updateNodeModelMatrix(model, modelTransformChanged, justLoaded, projection) {
  756. var computedModelMatrix = model._computedModelMatrix;
  757. if ((model._mode !== SceneMode.SCENE3D) && !model._ignoreCommands) {
  758. var translation = Matrix4.getColumn(computedModelMatrix, 3, scratchComputedTranslation);
  759. if (!Cartesian4.equals(translation, Cartesian4.UNIT_W)) {
  760. computedModelMatrix = Transforms.basisTo2D(projection, computedModelMatrix, scratchComputedMatrixIn2D);
  761. model._rtcCenter = model._rtcCenter3D;
  762. } else {
  763. var center = model.boundingSphere.center;
  764. var to2D = Transforms.wgs84To2DModelMatrix(projection, center, scratchComputedMatrixIn2D);
  765. computedModelMatrix = Matrix4.multiply(to2D, computedModelMatrix, scratchComputedMatrixIn2D);
  766. if (defined(model._rtcCenter)) {
  767. Matrix4.setTranslation(computedModelMatrix, Cartesian4.UNIT_W, computedModelMatrix);
  768. model._rtcCenter = model._rtcCenter2D;
  769. }
  770. }
  771. }
  772. var primitive = model._primitive;
  773. if (modelTransformChanged || justLoaded) {
  774. Matrix4.multiplyTransformation(computedModelMatrix, model._nodeMatrix, primitive._modelMatrix);
  775. BoundingSphere.transform(primitive._boundingSphere, primitive._modelMatrix, primitive._boundingVolume);
  776. if (defined(model._rtcCenter)) {
  777. Cartesian3.add(model._rtcCenter, primitive._boundingVolume.center, primitive._boundingVolume.center);
  778. }
  779. }
  780. }
  781. ///////////////////////////////////////////////////////////////////////////
  782. ClassificationModel.prototype.updateCommands = function(batchId, color) {
  783. this._primitive.updateCommands(batchId, color);
  784. };
  785. ClassificationModel.prototype.update = function(frameState) {
  786. if (frameState.mode === SceneMode.MORPHING) {
  787. return;
  788. }
  789. if (!FeatureDetection.supportsWebP.initialized) {
  790. FeatureDetection.supportsWebP.initialize();
  791. return;
  792. }
  793. var supportsWebP = FeatureDetection.supportsWebP();
  794. if ((this._state === ModelState.NEEDS_LOAD) && defined(this.gltf)) {
  795. this._state = ModelState.LOADING;
  796. if (this._state !== ModelState.FAILED) {
  797. var extensions = this.gltf.extensions;
  798. if (defined(extensions) && defined(extensions.CESIUM_RTC)) {
  799. var center = Cartesian3.fromArray(extensions.CESIUM_RTC.center);
  800. if (!Cartesian3.equals(center, Cartesian3.ZERO)) {
  801. this._rtcCenter3D = center;
  802. var projection = frameState.mapProjection;
  803. var ellipsoid = projection.ellipsoid;
  804. var cartographic = ellipsoid.cartesianToCartographic(this._rtcCenter3D);
  805. var projectedCart = projection.project(cartographic);
  806. Cartesian3.fromElements(projectedCart.z, projectedCart.x, projectedCart.y, projectedCart);
  807. this._rtcCenter2D = projectedCart;
  808. this._rtcCenterEye = new Cartesian3();
  809. this._rtcCenter = this._rtcCenter3D;
  810. }
  811. }
  812. this._loadResources = new ModelLoadResources();
  813. ModelUtility.parseBuffers(this);
  814. }
  815. }
  816. var loadResources = this._loadResources;
  817. var justLoaded = false;
  818. if (this._state === ModelState.LOADING) {
  819. // Transition from LOADING -> LOADED once resources are downloaded and created.
  820. // Textures may continue to stream in while in the LOADED state.
  821. if (loadResources.pendingBufferLoads === 0) {
  822. ModelUtility.checkSupportedExtensions(this.extensionsRequired, supportsWebP);
  823. addBuffersToLoadResources(this);
  824. parseBufferViews(this);
  825. this._boundingSphere = ModelUtility.computeBoundingSphere(this);
  826. this._initialRadius = this._boundingSphere.radius;
  827. createResources(this, frameState);
  828. }
  829. if (loadResources.finished()) {
  830. this._state = ModelState.LOADED;
  831. justLoaded = true;
  832. }
  833. }
  834. if (defined(loadResources) && (this._state === ModelState.LOADED)) {
  835. if (!justLoaded) {
  836. createResources(this, frameState);
  837. }
  838. if (loadResources.finished()) {
  839. this._loadResources = undefined; // Clear CPU memory since WebGL resources were created.
  840. }
  841. }
  842. var show = this.show;
  843. if ((show && this._state === ModelState.LOADED) || justLoaded) {
  844. this._dirty = false;
  845. var modelMatrix = this.modelMatrix;
  846. var modeChanged = frameState.mode !== this._mode;
  847. this._mode = frameState.mode;
  848. // ClassificationModel's model matrix needs to be updated
  849. var modelTransformChanged = !Matrix4.equals(this._modelMatrix, modelMatrix) || modeChanged;
  850. if (modelTransformChanged || justLoaded) {
  851. Matrix4.clone(modelMatrix, this._modelMatrix);
  852. var computedModelMatrix = this._computedModelMatrix;
  853. Matrix4.clone(modelMatrix, computedModelMatrix);
  854. if (this._upAxis === Axis.Y) {
  855. Matrix4.multiplyTransformation(computedModelMatrix, Axis.Y_UP_TO_Z_UP, computedModelMatrix);
  856. } else if (this._upAxis === Axis.X) {
  857. Matrix4.multiplyTransformation(computedModelMatrix, Axis.X_UP_TO_Z_UP, computedModelMatrix);
  858. }
  859. }
  860. // Update modelMatrix throughout the graph as needed
  861. if (modelTransformChanged || justLoaded) {
  862. updateNodeModelMatrix(this, modelTransformChanged, justLoaded, frameState.mapProjection);
  863. this._dirty = true;
  864. }
  865. }
  866. if (justLoaded) {
  867. // Called after modelMatrix update.
  868. var model = this;
  869. frameState.afterRender.push(function() {
  870. model._ready = true;
  871. model._readyPromise.resolve(model);
  872. });
  873. return;
  874. }
  875. if (show && !this._ignoreCommands) {
  876. this._primitive.debugShowBoundingVolume = this.debugShowBoundingVolume;
  877. this._primitive.debugWireframe = this.debugWireframe;
  878. this._primitive.update(frameState);
  879. }
  880. };
  881. ClassificationModel.prototype.isDestroyed = function() {
  882. return false;
  883. };
  884. ClassificationModel.prototype.destroy = function() {
  885. this._primitive = this._primitive && this._primitive.destroy();
  886. return destroyObject(this);
  887. };
  888. export default ClassificationModel;