ClassificationModel.js 35 KB

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