ModelExperimental.js 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686
  1. import BoundingSphere from "../../Core/BoundingSphere.js";
  2. import Cartesian3 from "../../Core/Cartesian3.js";
  3. import Check from "../../Core/Check.js";
  4. import ColorBlendMode from "../ColorBlendMode.js";
  5. import ClippingPlaneCollection from "../ClippingPlaneCollection.js";
  6. import defined from "../../Core/defined.js";
  7. import defer from "../../Core/defer.js";
  8. import defaultValue from "../../Core/defaultValue.js";
  9. import DeveloperError from "../../Core/DeveloperError.js";
  10. import GltfLoader from "../GltfLoader.js";
  11. import ImageBasedLighting from "../ImageBasedLighting.js";
  12. import ModelExperimentalAnimationCollection from "./ModelExperimentalAnimationCollection.js";
  13. import ModelExperimentalSceneGraph from "./ModelExperimentalSceneGraph.js";
  14. import ModelExperimentalType from "./ModelExperimentalType.js";
  15. import ModelExperimentalUtility from "./ModelExperimentalUtility.js";
  16. import Pass from "../../Renderer/Pass.js";
  17. import Resource from "../../Core/Resource.js";
  18. import destroyObject from "../../Core/destroyObject.js";
  19. import Matrix3 from "../../Core/Matrix3.js";
  20. import Matrix4 from "../../Core/Matrix4.js";
  21. import ModelFeatureTable from "./ModelFeatureTable.js";
  22. import PointCloudShading from "../PointCloudShading.js";
  23. import B3dmLoader from "./B3dmLoader.js";
  24. import PntsLoader from "./PntsLoader.js";
  25. import Color from "../../Core/Color.js";
  26. import I3dmLoader from "./I3dmLoader.js";
  27. import ShadowMode from "../ShadowMode.js";
  28. import SplitDirection from "../SplitDirection.js";
  29. /**
  30. * A 3D model. This is a new architecture that is more decoupled than the older {@link Model}. This class is still experimental.
  31. * <p>
  32. * Do not call this function directly, instead use the `from` functions to create
  33. * the Model from your source data type.
  34. * </p>
  35. *
  36. * @alias ModelExperimental
  37. * @constructor
  38. *
  39. * @param {Object} options Object with the following properties:
  40. * @param {Resource} options.resource The Resource to the 3D model.
  41. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
  42. * @param {Number} [options.scale=1.0] A uniform scale applied to this model.
  43. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
  44. * @param {Number} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize.
  45. * @param {Boolean} [options.clampAnimations=true] Determines if the model's animations should hold a pose over frames where no keyframes are specified.
  46. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
  47. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
  48. * @param {Boolean} [options.cull=true] Whether or not to cull the model using frustum/horizon culling. If the model is part of a 3D Tiles tileset, this property will always be false, since the 3D Tiles culling system is used.
  49. * @param {Boolean} [options.opaquePass=Pass.OPAQUE] The pass to use in the {@link DrawCommand} for the opaque portions of the model.
  50. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each primitive is pickable with {@link Scene#pick}.
  51. * @param {CustomShader} [options.customShader] A custom shader. This will add user-defined GLSL code to the vertex and fragment shaders. Using custom shaders with a {@link Cesium3DTileStyle} may lead to undefined behavior.
  52. * @param {Cesium3DTileContent} [options.content] The tile content this model belongs to. This property will be undefined if model is not loaded as part of a tileset.
  53. * @param {Boolean} [options.show=true] Whether or not to render the model.
  54. * @param {Color} [options.color] A color that blends with the model's rendered color.
  55. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model.
  56. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two.
  57. * @param {String|Number} [options.featureIdLabel="featureId_0"] Label of the feature ID set to use for picking and styling. For EXT_mesh_features, this is the feature ID's label property, or "featureId_N" (where N is the index in the featureIds array) when not specified. EXT_feature_metadata did not have a label field, so such feature ID sets are always labeled "featureId_N" where N is the index in the list of all feature Ids, where feature ID attributes are listed before feature ID textures. If featureIdLabel is an integer N, it is converted to the string "featureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
  58. * @param {String|Number} [options.instanceFeatureIdLabel="instanceFeatureId_0"] Label of the instance feature ID set used for picking and styling. If instanceFeatureIdLabel is set to an integer N, it is converted to the string "instanceFeatureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
  59. * @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation based on geometric error and lighting.
  60. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
  61. * @param {Cartesian3} [options.lightColor] The light color when shading the model. When <code>undefined</code> the scene's light color is used instead.
  62. * @param {ImageBasedLighting} [options.imageBasedLighting] The properties for managing image-based lighting on this model.
  63. * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the material's doubleSided property; when false, back face culling is disabled. Back faces are not culled if the model's color is translucent.
  64. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources.
  65. * @param {Boolean} [options.showCreditsOnScreen=false] Whether to display the credits of this model on screen.
  66. * @param {SplitDirection} [options.splitDirection=SplitDirection.NONE] The {@link SplitDirection} split to apply to this model.
  67. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  68. */
  69. export default function ModelExperimental(options) {
  70. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  71. //>>includeStart('debug', pragmas.debug);
  72. Check.typeOf.object("options.loader", options.loader);
  73. Check.typeOf.object("options.resource", options.resource);
  74. //>>includeEnd('debug');
  75. /**
  76. * The loader used to load resources for this model.
  77. * The corresponding constructor parameter is undocumented, since
  78. * ResourceLoader is part of the private API.
  79. *
  80. * @type {ResourceLoader}
  81. * @private
  82. */
  83. this._loader = options.loader;
  84. this._resource = options.resource;
  85. /**
  86. * Type of this model, to distinguish individual glTF files from 3D Tiles
  87. * internally. The corresponding constructor parameter is undocumented, since
  88. * ModelExperimentalType is part of the private API.
  89. *
  90. * @type {ModelExperimentalType}
  91. * @private
  92. * @readonly
  93. */
  94. this.type = defaultValue(options.type, ModelExperimentalType.GLTF);
  95. /**
  96. * The 4x4 transformation matrix that transforms the model from model to world coordinates.
  97. * When this is the identity matrix, the model is drawn in world coordinates, i.e., Earth's Cartesian WGS84 coordinates.
  98. * Local reference frames can be used by providing a different transformation matrix, like that returned
  99. * by {@link Transforms.eastNorthUpToFixedFrame}.
  100. *
  101. * @type {Matrix4}
  102. * @default {@link Matrix4.IDENTITY}
  103. *
  104. * @example
  105. * const origin = Cesium.Cartesian3.fromDegrees(-95.0, 40.0, 200000.0);
  106. * m.modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(origin);
  107. */
  108. this.modelMatrix = Matrix4.clone(
  109. defaultValue(options.modelMatrix, Matrix4.IDENTITY)
  110. );
  111. this._modelMatrix = Matrix4.clone(this.modelMatrix);
  112. this._scale = defaultValue(options.scale, 1.0);
  113. this._minimumPixelSize = defaultValue(options.minimumPixelSize, 0.0);
  114. this._maximumScale = options.maximumScale;
  115. /**
  116. * The scale value after being clamped by the maximum scale parameter.
  117. * Used to adjust bounding spheres without repeated calculation.
  118. *
  119. * @type {Number}
  120. * @private
  121. */
  122. this._clampedScale = defined(this._maximumScale)
  123. ? Math.min(this._scale, this._maximumScale)
  124. : this._scale;
  125. this._computedScale = this._clampedScale;
  126. /**
  127. * Whether or not the ModelExperimentalSceneGraph should call updateModelMatrix.
  128. * This will be true if any of the model matrix, scale, minimum pixel size, or maximum scale are dirty.
  129. *
  130. * @type {Number}
  131. * @private
  132. */
  133. this._updateModelMatrix = false;
  134. /**
  135. * If defined, this matrix is used to transform miscellaneous properties like
  136. * clipping planes and image-based lighting instead of the modelMatrix. This is
  137. * so that when models are part of a tileset, these properties get transformed
  138. * relative to a common reference (such as the root).
  139. *
  140. * @type {Matrix4}
  141. * @private
  142. */
  143. this.referenceMatrix = undefined;
  144. this._iblReferenceFrameMatrix = Matrix3.clone(Matrix3.IDENTITY); // Derived from reference matrix and the current view matrix
  145. this._resourcesLoaded = false;
  146. this._drawCommandsBuilt = false;
  147. this._ready = false;
  148. this._readyPromise = defer();
  149. this._customShader = options.customShader;
  150. this._content = options.content;
  151. this._texturesLoaded = false;
  152. this._defaultTexture = undefined;
  153. this._activeAnimations = new ModelExperimentalAnimationCollection(this);
  154. this._clampAnimations = defaultValue(options.clampAnimations, true);
  155. const color = options.color;
  156. this._color = defaultValue(color) ? Color.clone(color) : undefined;
  157. this._colorBlendMode = defaultValue(
  158. options.colorBlendMode,
  159. ColorBlendMode.HIGHLIGHT
  160. );
  161. this._colorBlendAmount = defaultValue(options.colorBlendAmount, 0.5);
  162. this._cull = defaultValue(options.cull, true);
  163. this._opaquePass = defaultValue(options.opaquePass, Pass.OPAQUE);
  164. this._allowPicking = defaultValue(options.allowPicking, true);
  165. this._show = defaultValue(options.show, true);
  166. this._style = undefined;
  167. let featureIdLabel = defaultValue(options.featureIdLabel, "featureId_0");
  168. if (typeof featureIdLabel === "number") {
  169. featureIdLabel = `featureId_${featureIdLabel}`;
  170. }
  171. this._featureIdLabel = featureIdLabel;
  172. let instanceFeatureIdLabel = defaultValue(
  173. options.instanceFeatureIdLabel,
  174. "instanceFeatureId_0"
  175. );
  176. if (typeof instanceFeatureIdLabel === "number") {
  177. instanceFeatureIdLabel = `instanceFeatureId_${instanceFeatureIdLabel}`;
  178. }
  179. this._instanceFeatureIdLabel = instanceFeatureIdLabel;
  180. this._featureTables = [];
  181. this._featureTableId = undefined;
  182. this._featureTableIdDirty = true;
  183. // Keeps track of resources that need to be destroyed when the Model is destroyed.
  184. this._resources = [];
  185. // Computation of the model's bounding sphere and its initial radius is done in ModelExperimentalSceneGraph
  186. this._boundingSphere = new BoundingSphere();
  187. this._initialRadius = undefined;
  188. const pointCloudShading = new PointCloudShading(options.pointCloudShading);
  189. this._attenuation = pointCloudShading.attenuation;
  190. this._pointCloudShading = pointCloudShading;
  191. // If the given clipping planes don't have an owner, make this model its owner.
  192. // Otherwise, the clipping planes are passed down from a tileset.
  193. const clippingPlanes = options.clippingPlanes;
  194. if (defined(clippingPlanes) && clippingPlanes.owner === undefined) {
  195. ClippingPlaneCollection.setOwner(clippingPlanes, this, "_clippingPlanes");
  196. } else {
  197. this._clippingPlanes = clippingPlanes;
  198. }
  199. this._clippingPlanesState = 0; // If this value changes, the shaders need to be regenerated.
  200. this._clippingPlanesMatrix = Matrix4.clone(Matrix4.IDENTITY); // Derived from reference matrix and the current view matrix
  201. this._lightColor = Cartesian3.clone(options.lightColor);
  202. this._imageBasedLighting = defined(options.imageBasedLighting)
  203. ? options.imageBasedLighting
  204. : new ImageBasedLighting();
  205. this._shouldDestroyImageBasedLighting = !defined(options.imageBasedLighting);
  206. this._backFaceCulling = defaultValue(options.backFaceCulling, true);
  207. this._backFaceCullingDirty = false;
  208. this._shadows = defaultValue(options.shadows, ShadowMode.ENABLED);
  209. this._shadowsDirty = false;
  210. this._debugShowBoundingVolumeDirty = false;
  211. this._debugShowBoundingVolume = defaultValue(
  212. options.debugShowBoundingVolume,
  213. false
  214. );
  215. this._debugWireframe = defaultValue(options.debugWireframe, false);
  216. this._showCreditsOnScreen = defaultValue(options.showCreditsOnScreen, false);
  217. this._splitDirection = defaultValue(
  218. options.splitDirection,
  219. SplitDirection.NONE
  220. );
  221. initialize(this);
  222. }
  223. function createModelFeatureTables(model, structuralMetadata) {
  224. const featureTables = model._featureTables;
  225. const propertyTables = structuralMetadata.propertyTables;
  226. for (let i = 0; i < propertyTables.length; i++) {
  227. const propertyTable = propertyTables[i];
  228. const modelFeatureTable = new ModelFeatureTable({
  229. model: model,
  230. propertyTable: propertyTable,
  231. });
  232. featureTables.push(modelFeatureTable);
  233. }
  234. return featureTables;
  235. }
  236. function selectFeatureTableId(components, model) {
  237. const featureIdLabel = model._featureIdLabel;
  238. const instanceFeatureIdLabel = model._instanceFeatureIdLabel;
  239. let i, j;
  240. let featureIdAttribute;
  241. let node;
  242. // Scan the nodes till we find one with instances, get the feature table ID
  243. // if the feature ID attribute of the user-selected index is present.
  244. for (i = 0; i < components.nodes.length; i++) {
  245. node = components.nodes[i];
  246. if (defined(node.instances)) {
  247. featureIdAttribute = ModelExperimentalUtility.getFeatureIdsByLabel(
  248. node.instances.featureIds,
  249. instanceFeatureIdLabel
  250. );
  251. if (
  252. defined(featureIdAttribute) &&
  253. defined(featureIdAttribute.propertyTableId)
  254. ) {
  255. return featureIdAttribute.propertyTableId;
  256. }
  257. }
  258. }
  259. // Scan the primitives till we find one with textures or attributes, get the feature table ID
  260. // if the feature ID attribute/texture of the user-selected index is present.
  261. for (i = 0; i < components.nodes.length; i++) {
  262. node = components.nodes[i];
  263. for (j = 0; j < node.primitives.length; j++) {
  264. const primitive = node.primitives[j];
  265. const featureIds = ModelExperimentalUtility.getFeatureIdsByLabel(
  266. primitive.featureIds,
  267. featureIdLabel
  268. );
  269. if (defined(featureIds)) {
  270. return featureIds.propertyTableId;
  271. }
  272. }
  273. }
  274. }
  275. function initialize(model) {
  276. const loader = model._loader;
  277. const resource = model._resource;
  278. loader.load();
  279. loader.promise
  280. .then(function (loader) {
  281. const components = loader.components;
  282. const structuralMetadata = components.structuralMetadata;
  283. if (
  284. defined(structuralMetadata) &&
  285. structuralMetadata.propertyTableCount > 0
  286. ) {
  287. createModelFeatureTables(model, structuralMetadata);
  288. }
  289. model._sceneGraph = new ModelExperimentalSceneGraph({
  290. model: model,
  291. modelComponents: components,
  292. });
  293. model._resourcesLoaded = true;
  294. })
  295. .catch(
  296. ModelExperimentalUtility.getFailedLoadFunction(model, "model", resource)
  297. );
  298. // Transcoded .pnts models do not have textures
  299. const texturesLoadedPromise = defaultValue(
  300. loader.texturesLoadedPromise,
  301. Promise.resolve()
  302. );
  303. texturesLoadedPromise
  304. .then(function () {
  305. model._texturesLoaded = true;
  306. })
  307. .catch(
  308. ModelExperimentalUtility.getFailedLoadFunction(model, "model", resource)
  309. );
  310. }
  311. Object.defineProperties(ModelExperimental.prototype, {
  312. /**
  313. * When <code>true</code>, this model is ready to render, i.e., the external binary, image,
  314. * and shader files were downloaded and the WebGL resources were created. This is set to
  315. * <code>true</code> right before {@link ModelExperimental#readyPromise} is resolved.
  316. *
  317. * @memberof ModelExperimental.prototype
  318. *
  319. * @type {Boolean}
  320. * @readonly
  321. *
  322. * @default false
  323. */
  324. ready: {
  325. get: function () {
  326. return this._ready;
  327. },
  328. },
  329. /**
  330. * Gets the promise that will be resolved when this model is ready to render, i.e. when the external resources
  331. * have been downloaded and the WebGL resources are created.
  332. * <p>
  333. * This promise is resolved at the end of the frame before the first frame the model is rendered in.
  334. * </p>
  335. *
  336. * @memberof ModelExperimental.prototype
  337. *
  338. * @type {Promise.<ModelExperimental>}
  339. * @readonly
  340. */
  341. readyPromise: {
  342. get: function () {
  343. return this._readyPromise.promise;
  344. },
  345. },
  346. /**
  347. * @private
  348. */
  349. loader: {
  350. get: function () {
  351. return this._loader;
  352. },
  353. },
  354. /**
  355. * The currently playing glTF animations.
  356. *
  357. * @memberof ModelExperimental.prototype
  358. * @type {ModelExperimentalAnimationCollection}
  359. * @readonly
  360. */
  361. activeAnimations: {
  362. get: function () {
  363. return this._activeAnimations;
  364. },
  365. },
  366. /**
  367. * Determines if the model's animations should hold a pose over frames where no keyframes are specified.
  368. *
  369. * @memberof ModelExperimental.prototype
  370. * @type {Boolean}
  371. *
  372. * @default true
  373. */
  374. clampAnimations: {
  375. get: function () {
  376. return this._clampAnimations;
  377. },
  378. set: function (value) {
  379. this._clampAnimations = value;
  380. },
  381. },
  382. /**
  383. * Whether or not to cull the model using frustum/horizon culling. If the model is part of a 3D Tiles tileset, this property
  384. * will always be false, since the 3D Tiles culling system is used.
  385. *
  386. * @type {Boolean}
  387. * @readonly
  388. *
  389. * @private
  390. */
  391. cull: {
  392. get: function () {
  393. return this._cull;
  394. },
  395. },
  396. /**
  397. * The pass to use in the {@link DrawCommand} for the opaque portions of the model.
  398. *
  399. * @type {Pass}
  400. * @readonly
  401. *
  402. * @private
  403. */
  404. opaquePass: {
  405. get: function () {
  406. return this._opaquePass;
  407. },
  408. },
  409. /**
  410. * Point cloud shading settings for controlling point cloud attenuation
  411. * and lighting. For 3D Tiles, this is inherited from the
  412. * {@link Cesium3DTileset}.
  413. *
  414. * @memberof ModelExperimental.prototype
  415. *
  416. * @type {PointCloudShading}
  417. */
  418. pointCloudShading: {
  419. get: function () {
  420. return this._pointCloudShading;
  421. },
  422. set: function (value) {
  423. //>>includeStart('debug', pragmas.debug);
  424. Check.defined("pointCloudShading", value);
  425. //>>includeEnd('debug');
  426. if (value !== this._pointCloudShading) {
  427. this.resetDrawCommands();
  428. }
  429. this._pointCloudShading = value;
  430. },
  431. },
  432. /**
  433. * The model's custom shader, if it exists. Using custom shaders with a {@link Cesium3DTileStyle}
  434. * may lead to undefined behavior.
  435. *
  436. * @memberof ModelExperimental.prototype
  437. *
  438. * @type {CustomShader}
  439. */
  440. customShader: {
  441. get: function () {
  442. return this._customShader;
  443. },
  444. set: function (value) {
  445. if (value !== this._customShader) {
  446. this.resetDrawCommands();
  447. }
  448. this._customShader = value;
  449. },
  450. },
  451. /**
  452. * The scene graph of this model.
  453. *
  454. * @memberof ModelExperimental.prototype
  455. *
  456. * @type {ModelExperimentalSceneGraph}
  457. * @private
  458. */
  459. sceneGraph: {
  460. get: function () {
  461. return this._sceneGraph;
  462. },
  463. },
  464. /**
  465. * The tile content this model belongs to, if it is loaded as part of a {@link Cesium3DTileset}.
  466. *
  467. * @memberof ModelExperimental.prototype
  468. *
  469. * @type {Cesium3DTileContent}
  470. * @readonly
  471. *
  472. * @private
  473. */
  474. content: {
  475. get: function () {
  476. return this._content;
  477. },
  478. },
  479. /**
  480. * The structural metadata from the EXT_structural_metadata extension
  481. *
  482. * @memberof ModelExperimental.prototype
  483. *
  484. * @type {StructuralMetadata}
  485. * @readonly
  486. * @private
  487. */
  488. structuralMetadata: {
  489. get: function () {
  490. return this._sceneGraph.components.structuralMetadata;
  491. },
  492. },
  493. /**
  494. * The ID for the feature table to use for picking and styling in this model.
  495. *
  496. * @memberof ModelExperimental.prototype
  497. *
  498. * @type {Number}
  499. *
  500. * @private
  501. */
  502. featureTableId: {
  503. get: function () {
  504. return this._featureTableId;
  505. },
  506. set: function (value) {
  507. this._featureTableId = value;
  508. },
  509. },
  510. /**
  511. * The feature tables for this model.
  512. *
  513. * @memberof ModelExperimental.prototype
  514. *
  515. * @type {Array}
  516. * @readonly
  517. *
  518. * @private
  519. */
  520. featureTables: {
  521. get: function () {
  522. return this._featureTables;
  523. },
  524. set: function (value) {
  525. this._featureTables = value;
  526. },
  527. },
  528. /**
  529. * When <code>true</code>, each primitive is pickable with {@link Scene#pick}. When <code>false</code>, GPU memory is saved.
  530. *
  531. * @type {Boolean}
  532. * @readonly
  533. *
  534. * @private
  535. */
  536. allowPicking: {
  537. get: function () {
  538. return this._allowPicking;
  539. },
  540. },
  541. /**
  542. * The style to apply the to the features in the model. Cannot be applied if a {@link CustomShader} is also applied.
  543. *
  544. * @type {Cesium3DTileStyle}
  545. */
  546. style: {
  547. get: function () {
  548. return this._style;
  549. },
  550. set: function (value) {
  551. if (value !== this._style) {
  552. this.applyStyle(value);
  553. }
  554. this._style = value;
  555. },
  556. },
  557. /**
  558. * The color to blend with the model's rendered color.
  559. *
  560. * @memberof ModelExperimental.prototype
  561. *
  562. * @type {Color}
  563. */
  564. color: {
  565. get: function () {
  566. return this._color;
  567. },
  568. set: function (value) {
  569. if (!Color.equals(this._color, value)) {
  570. this.resetDrawCommands();
  571. }
  572. this._color = Color.clone(value, this._color);
  573. },
  574. },
  575. /**
  576. * Defines how the color blends with the model.
  577. *
  578. * @memberof ModelExperimental.prototype
  579. *
  580. * @type {Cesium3DTileColorBlendMode|ColorBlendMode}
  581. * @default ColorBlendMode.HIGHLIGHT
  582. */
  583. colorBlendMode: {
  584. get: function () {
  585. return this._colorBlendMode;
  586. },
  587. set: function (value) {
  588. this._colorBlendMode = value;
  589. },
  590. },
  591. /**
  592. * Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two.
  593. *
  594. * @memberof ModelExperimental.prototype
  595. *
  596. * @type {Number}
  597. * @default 0.5
  598. */
  599. colorBlendAmount: {
  600. get: function () {
  601. return this._colorBlendAmount;
  602. },
  603. set: function (value) {
  604. this._colorBlendAmount = value;
  605. },
  606. },
  607. /**
  608. * Gets the model's bounding sphere.
  609. *
  610. * @memberof ModelExperimental.prototype
  611. *
  612. * @type {BoundingSphere}
  613. * @readonly
  614. */
  615. boundingSphere: {
  616. get: function () {
  617. //>>includeStart('debug', pragmas.debug);
  618. if (!this._ready) {
  619. throw new DeveloperError(
  620. "The model is not loaded. Use ModelExperimental.readyPromise or wait for ModelExperimental.ready to be true."
  621. );
  622. }
  623. //>>includeEnd('debug');
  624. return this._boundingSphere;
  625. },
  626. },
  627. /**
  628. * This property is for debugging only; it is not for production use nor is it optimized.
  629. * <p>
  630. * Draws the bounding sphere for each draw command in the model.
  631. * </p>
  632. *
  633. * @memberof ModelExperimental.prototype
  634. *
  635. * @type {Boolean}
  636. *
  637. * @default false
  638. */
  639. debugShowBoundingVolume: {
  640. get: function () {
  641. return this._debugShowBoundingVolume;
  642. },
  643. set: function (value) {
  644. if (this._debugShowBoundingVolume !== value) {
  645. this._debugShowBoundingVolumeDirty = true;
  646. }
  647. this._debugShowBoundingVolume = value;
  648. },
  649. },
  650. /**
  651. * This property is for debugging only; it is not for production use nor is it optimized.
  652. * <p>
  653. * Draws the model in wireframe.
  654. * </p>
  655. *
  656. * @memberof ModelExperimental.prototype
  657. *
  658. * @type {Boolean}
  659. *
  660. * @default false
  661. */
  662. debugWireframe: {
  663. get: function () {
  664. return this._debugWireframe;
  665. },
  666. set: function (value) {
  667. if (this._debugWireframe !== value) {
  668. this.resetDrawCommands();
  669. }
  670. this._debugWireframe = value;
  671. },
  672. },
  673. /**
  674. * Whether or not to render the model.
  675. *
  676. * @memberof ModelExperimental.prototype
  677. *
  678. * @type {Boolean}
  679. *
  680. * @default true
  681. */
  682. show: {
  683. get: function () {
  684. return this._show;
  685. },
  686. set: function (value) {
  687. this._show = value;
  688. },
  689. },
  690. /**
  691. * Label of the feature ID set to use for picking and styling.
  692. * <p>
  693. * For EXT_mesh_features, this is the feature ID's label property, or
  694. * "featureId_N" (where N is the index in the featureIds array) when not
  695. * specified. EXT_feature_metadata did not have a label field, so such
  696. * feature ID sets are always labeled "featureId_N" where N is the index in
  697. * the list of all feature Ids, where feature ID attributes are listed before
  698. * feature ID textures.
  699. * </p>
  700. * <p>
  701. * If featureIdLabel is set to an integer N, it is converted to
  702. * the string "featureId_N" automatically. If both per-primitive and
  703. * per-instance feature IDs are present, the instance feature IDs take
  704. * priority.
  705. * </p>
  706. *
  707. * @memberof ModelExperimental.prototype
  708. *
  709. * @type {String}
  710. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  711. */
  712. featureIdLabel: {
  713. get: function () {
  714. return this._featureIdLabel;
  715. },
  716. set: function (value) {
  717. // indices get converted into featureId_N
  718. if (typeof value === "number") {
  719. value = `featureId_${value}`;
  720. }
  721. //>>includeStart('debug', pragmas.debug);
  722. Check.typeOf.string("value", value);
  723. //>>includeEnd('debug');
  724. if (value !== this._featureIdLabel) {
  725. this._featureTableIdDirty = true;
  726. }
  727. this._featureIdLabel = value;
  728. },
  729. },
  730. /**
  731. * Label of the instance feature ID set used for picking and styling.
  732. * <p>
  733. * If instanceFeatureIdLabel is set to an integer N, it is converted to
  734. * the string "instanceFeatureId_N" automatically.
  735. * If both per-primitive and per-instance feature IDs are present, the
  736. * instance feature IDs take priority.
  737. * </p>
  738. *
  739. * @memberof ModelExperimental.prototype
  740. *
  741. * @type {String}
  742. * @experimental This feature is using part of the 3D Tiles spec that is not final and is subject to change without Cesium's standard deprecation policy.
  743. */
  744. instanceFeatureIdLabel: {
  745. get: function () {
  746. return this._instanceFeatureIdLabel;
  747. },
  748. set: function (value) {
  749. // indices get converted into instanceFeatureId_N
  750. if (typeof value === "number") {
  751. value = `instanceFeatureId_${value}`;
  752. }
  753. //>>includeStart('debug', pragmas.debug);
  754. Check.typeOf.string("value", value);
  755. //>>includeEnd('debug');
  756. if (value !== this._instanceFeatureIdLabel) {
  757. this._featureTableIdDirty = true;
  758. }
  759. this._instanceFeatureIdLabel = value;
  760. },
  761. },
  762. /**
  763. * The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
  764. *
  765. * @memberof ModelExperimental.prototype
  766. *
  767. * @type {ClippingPlaneCollection}
  768. */
  769. clippingPlanes: {
  770. get: function () {
  771. return this._clippingPlanes;
  772. },
  773. set: function (value) {
  774. if (value !== this._clippingPlanes) {
  775. // Handle destroying old clipping planes, new clipping planes ownership
  776. ClippingPlaneCollection.setOwner(value, this, "_clippingPlanes");
  777. this.resetDrawCommands();
  778. }
  779. },
  780. },
  781. /**
  782. * The light color when shading the model. When <code>undefined</code> the scene's light color is used instead.
  783. * <p>
  784. * Disabling additional light sources by setting <code>model.imageBasedLightingFactor = new Cartesian2(0.0, 0.0)</code> will make the
  785. * model much darker. Here, increasing the intensity of the light source will make the model brighter.
  786. * </p>
  787. * @memberof ModelExperimental.prototype
  788. *
  789. * @type {Cartesian3}
  790. * @default undefined
  791. */
  792. lightColor: {
  793. get: function () {
  794. return this._lightColor;
  795. },
  796. set: function (value) {
  797. if (defined(value) !== defined(this._lightColor)) {
  798. this.resetDrawCommands();
  799. }
  800. this._lightColor = Cartesian3.clone(value, this._lightColor);
  801. },
  802. },
  803. /**
  804. * The properties for managing image-based lighting on this model.
  805. *
  806. * @memberof ModelExperimental.prototype
  807. *
  808. * @type {ImageBasedLighting}
  809. */
  810. imageBasedLighting: {
  811. get: function () {
  812. return this._imageBasedLighting;
  813. },
  814. set: function (value) {
  815. //>>includeStart('debug', pragmas.debug);
  816. Check.typeOf.object("imageBasedLighting", this._imageBasedLighting);
  817. //>>includeEnd('debug');
  818. if (value !== this._imageBasedLighting) {
  819. if (
  820. this._shouldDestroyImageBasedLighting &&
  821. !this._imageBasedLighting.isDestroyed()
  822. ) {
  823. this._imageBasedLighting.destroy();
  824. }
  825. this._imageBasedLighting = value;
  826. this._shouldDestroyImageBasedLighting = false;
  827. this.resetDrawCommands();
  828. }
  829. },
  830. },
  831. /**
  832. * Whether to cull back-facing geometry. When true, back face culling is
  833. * determined by the material's doubleSided property; when false, back face
  834. * culling is disabled. Back faces are not culled if the model's color is
  835. * translucent.
  836. *
  837. * @memberof ModelExperimental.prototype
  838. *
  839. * @type {Boolean}
  840. *
  841. * @default true
  842. */
  843. backFaceCulling: {
  844. get: function () {
  845. return this._backFaceCulling;
  846. },
  847. set: function (value) {
  848. if (value !== this._backFaceCulling) {
  849. this._backFaceCullingDirty = true;
  850. }
  851. this._backFaceCulling = value;
  852. },
  853. },
  854. /**
  855. * A uniform scale applied to this model before the {@link Model#modelMatrix}.
  856. * Values greater than <code>1.0</code> increase the size of the model; values
  857. * less than <code>1.0</code> decrease.
  858. *
  859. * @memberof ModelExperimental.prototype
  860. *
  861. * @type {Number}
  862. *
  863. * @default 1.0
  864. */
  865. scale: {
  866. get: function () {
  867. return this._scale;
  868. },
  869. set: function (value) {
  870. if (value !== this._scale) {
  871. this._updateModelMatrix = true;
  872. }
  873. this._scale = value;
  874. },
  875. },
  876. /**
  877. * The true scale of the model after being affected by the model's scale,
  878. * minimum pixel size, and maximum scale parameters.
  879. *
  880. * @memberof ModelExperimental.prototype
  881. *
  882. * @type {Number}
  883. * @private
  884. */
  885. computedScale: {
  886. get: function () {
  887. return this._computedScale;
  888. },
  889. },
  890. /**
  891. * The approximate minimum pixel size of the model regardless of zoom.
  892. * This can be used to ensure that a model is visible even when the viewer
  893. * zooms out. When <code>0.0</code>, no minimum size is enforced.
  894. *
  895. * @memberof ModelExperimental.prototype
  896. *
  897. * @type {Number}
  898. *
  899. * @default 0.0
  900. */
  901. minimumPixelSize: {
  902. get: function () {
  903. return this._minimumPixelSize;
  904. },
  905. set: function (value) {
  906. if (value !== this._minimumPixelSize) {
  907. this._updateModelMatrix = true;
  908. }
  909. this._minimumPixelSize = value;
  910. },
  911. },
  912. /**
  913. * The maximum scale size for a model. This can be used to give
  914. * an upper limit to the {@link Model#minimumPixelSize}, ensuring that the model
  915. * is never an unreasonable scale.
  916. *
  917. * @memberof ModelExperimental.prototype
  918. *
  919. * @type {Number}
  920. */
  921. maximumScale: {
  922. get: function () {
  923. return this._maximumScale;
  924. },
  925. set: function (value) {
  926. if (value !== this._maximumScale) {
  927. this._updateModelMatrix = true;
  928. }
  929. this._maximumScale = value;
  930. },
  931. },
  932. /**
  933. * Determines whether the model casts or receives shadows from light sources.
  934. * @memberof ModelExperimental.prototype
  935. *
  936. * @type {ShadowMode}
  937. *
  938. * @default ShadowMode.ENABLED
  939. */
  940. shadows: {
  941. get: function () {
  942. return this._shadows;
  943. },
  944. set: function (value) {
  945. if (value !== this._shadows) {
  946. this._shadowsDirty = true;
  947. }
  948. this._shadows = value;
  949. },
  950. },
  951. /**
  952. * Gets or sets whether the credits of the model will be displayed on the screen
  953. *
  954. * @memberof ModelExperimental.prototype
  955. *
  956. * @type {Boolean}
  957. *
  958. * @default false
  959. */
  960. showCreditsOnScreen: {
  961. get: function () {
  962. return this._showCreditsOnScreen;
  963. },
  964. set: function (value) {
  965. this._showCreditsOnScreen = value;
  966. },
  967. },
  968. /**
  969. * The {@link SplitDirection} to apply to this model.
  970. *
  971. * @memberof ModelExperimental.prototype
  972. *
  973. * @type {SplitDirection}
  974. * @default {@link SplitDirection.NONE}
  975. */
  976. splitDirection: {
  977. get: function () {
  978. return this._splitDirection;
  979. },
  980. set: function (value) {
  981. if (this._splitDirection !== value) {
  982. this.resetDrawCommands();
  983. }
  984. this._splitDirection = value;
  985. },
  986. },
  987. });
  988. /**
  989. * Resets the draw commands for this model.
  990. *
  991. * @private
  992. */
  993. ModelExperimental.prototype.resetDrawCommands = function () {
  994. if (!this._drawCommandsBuilt) {
  995. return;
  996. }
  997. this.destroyResources();
  998. this._drawCommandsBuilt = false;
  999. };
  1000. const scratchIBLReferenceFrameMatrix4 = new Matrix4();
  1001. const scratchIBLReferenceFrameMatrix3 = new Matrix3();
  1002. const scratchClippingPlanesMatrix = new Matrix4();
  1003. /**
  1004. * Called when {@link Viewer} or {@link CesiumWidget} render the scene to
  1005. * get the draw commands needed to render this primitive.
  1006. * <p>
  1007. * Do not call this function directly. This is documented just to
  1008. * list the exceptions that may be propagated when the scene is rendered:
  1009. * </p>
  1010. *
  1011. * @exception {RuntimeError} Failed to load external reference.
  1012. */
  1013. ModelExperimental.prototype.update = function (frameState) {
  1014. // Keep processing the model every frame until the main resources
  1015. // (buffer views) and textures (which may be loaded asynchronously)
  1016. // are processed.
  1017. if (!this._resourcesLoaded || !this._texturesLoaded) {
  1018. this._loader.process(frameState);
  1019. }
  1020. // A custom shader may have to load texture uniforms.
  1021. if (defined(this._customShader)) {
  1022. this._customShader.update(frameState);
  1023. }
  1024. // Check if the shader needs to be updated for point cloud attenuation
  1025. // settings.
  1026. if (this.pointCloudShading.attenuation !== this._attenuation) {
  1027. this.resetDrawCommands();
  1028. this._attenuation = this.pointCloudShading.attenuation;
  1029. }
  1030. const context = frameState.context;
  1031. const referenceMatrix = defaultValue(this.referenceMatrix, this.modelMatrix);
  1032. // Update the image-based lighting for this model to detect any changes in parameters.
  1033. this._imageBasedLighting.update(frameState);
  1034. if (
  1035. this._imageBasedLighting.useSphericalHarmonicCoefficients ||
  1036. this._imageBasedLighting.useSpecularEnvironmentMaps
  1037. ) {
  1038. let iblReferenceFrameMatrix3 = scratchIBLReferenceFrameMatrix3;
  1039. let iblReferenceFrameMatrix4 = scratchIBLReferenceFrameMatrix4;
  1040. iblReferenceFrameMatrix4 = Matrix4.multiply(
  1041. context.uniformState.view3D,
  1042. referenceMatrix,
  1043. iblReferenceFrameMatrix4
  1044. );
  1045. iblReferenceFrameMatrix3 = Matrix4.getMatrix3(
  1046. iblReferenceFrameMatrix4,
  1047. iblReferenceFrameMatrix3
  1048. );
  1049. iblReferenceFrameMatrix3 = Matrix3.getRotation(
  1050. iblReferenceFrameMatrix3,
  1051. iblReferenceFrameMatrix3
  1052. );
  1053. this._iblReferenceFrameMatrix = Matrix3.transpose(
  1054. iblReferenceFrameMatrix3,
  1055. this._iblReferenceFrameMatrix
  1056. );
  1057. }
  1058. if (this._imageBasedLighting.shouldRegenerateShaders) {
  1059. this.resetDrawCommands();
  1060. }
  1061. // Update the clipping planes collection for this model to detect any changes.
  1062. let currentClippingPlanesState = 0;
  1063. if (this.isClippingEnabled()) {
  1064. if (this._clippingPlanes.owner === this) {
  1065. this._clippingPlanes.update(frameState);
  1066. }
  1067. let clippingPlanesMatrix = scratchClippingPlanesMatrix;
  1068. clippingPlanesMatrix = Matrix4.multiply(
  1069. context.uniformState.view3D,
  1070. referenceMatrix,
  1071. clippingPlanesMatrix
  1072. );
  1073. clippingPlanesMatrix = Matrix4.multiply(
  1074. clippingPlanesMatrix,
  1075. this._clippingPlanes.modelMatrix,
  1076. clippingPlanesMatrix
  1077. );
  1078. this._clippingPlanesMatrix = Matrix4.inverseTranspose(
  1079. clippingPlanesMatrix,
  1080. this._clippingPlanesMatrix
  1081. );
  1082. currentClippingPlanesState = this._clippingPlanes.clippingPlanesState;
  1083. }
  1084. if (currentClippingPlanesState !== this._clippingPlanesState) {
  1085. this.resetDrawCommands();
  1086. this._clippingPlanesState = currentClippingPlanesState;
  1087. }
  1088. this._defaultTexture = context.defaultTexture;
  1089. // short-circuit if the model resources aren't ready.
  1090. if (!this._resourcesLoaded) {
  1091. return;
  1092. }
  1093. if (this._featureTableIdDirty) {
  1094. updateFeatureTableId(this);
  1095. this._featureTableIdDirty = false;
  1096. }
  1097. const featureTables = this._featureTables;
  1098. for (let i = 0; i < featureTables.length; i++) {
  1099. featureTables[i].update(frameState);
  1100. // Check if the types of style commands needed have changed and trigger a reset of the draw commands
  1101. // to ensure that translucent and opaque features are handled in the correct passes.
  1102. if (featureTables[i].styleCommandsNeededDirty) {
  1103. this.resetDrawCommands();
  1104. }
  1105. }
  1106. if (!this._drawCommandsBuilt) {
  1107. this._sceneGraph.buildDrawCommands(frameState);
  1108. this._drawCommandsBuilt = true;
  1109. const model = this;
  1110. if (!model._ready) {
  1111. // Set the model as ready after the first frame render since the user might set up events subscribed to
  1112. // the post render event, and the model may not be ready for those past the first frame.
  1113. frameState.afterRender.push(function () {
  1114. model._ready = true;
  1115. model._readyPromise.resolve(model);
  1116. });
  1117. // Don't render until the next frame after the ready promise is resolved
  1118. return;
  1119. }
  1120. }
  1121. if (this._debugShowBoundingVolumeDirty) {
  1122. updateShowBoundingVolume(this._sceneGraph, this._debugShowBoundingVolume);
  1123. this._debugShowBoundingVolumeDirty = false;
  1124. }
  1125. // This is done without a dirty flag so that the model matrix can be update in-place
  1126. // without needing to use a setter.
  1127. if (!Matrix4.equals(this.modelMatrix, this._modelMatrix)) {
  1128. this._updateModelMatrix = true;
  1129. this._modelMatrix = Matrix4.clone(this.modelMatrix, this._modelMatrix);
  1130. this._boundingSphere = BoundingSphere.transform(
  1131. this._sceneGraph.boundingSphere,
  1132. this.modelMatrix,
  1133. this._boundingSphere
  1134. );
  1135. }
  1136. if (this._updateModelMatrix || this._minimumPixelSize !== 0.0) {
  1137. this._clampedScale = defined(this._maximumScale)
  1138. ? Math.min(this._scale, this._maximumScale)
  1139. : this._scale;
  1140. this._boundingSphere.radius = this._initialRadius * this._clampedScale;
  1141. this._computedScale = getScale(this, frameState);
  1142. this._sceneGraph.updateModelMatrix();
  1143. this._updateModelMatrix = false;
  1144. }
  1145. if (this._backFaceCullingDirty) {
  1146. this.sceneGraph.updateBackFaceCulling(this._backFaceCulling);
  1147. this._backFaceCullingDirty = false;
  1148. }
  1149. if (this._shadowsDirty) {
  1150. this.sceneGraph.updateShadows(this._shadows);
  1151. this._shadowsDirty = false;
  1152. }
  1153. const updateForAnimations = this._activeAnimations.update(frameState);
  1154. this._sceneGraph.update(frameState, updateForAnimations);
  1155. // Check for show here because we still want the draw commands to be built so user can instantly see the model
  1156. // when show is set to true.
  1157. if (this._show && this._computedScale !== 0) {
  1158. const asset = this._sceneGraph.components.asset;
  1159. const credits = asset.credits;
  1160. const length = credits.length;
  1161. for (let i = 0; i < length; i++) {
  1162. const credit = credits[i];
  1163. credit.showOnScreen = this._showCreditsOnScreen;
  1164. frameState.creditDisplay.addCredit(credit);
  1165. }
  1166. const drawCommands = this._sceneGraph.getDrawCommands();
  1167. frameState.commandList.push.apply(frameState.commandList, drawCommands);
  1168. }
  1169. };
  1170. function updateFeatureTableId(model) {
  1171. const components = model._sceneGraph.components;
  1172. const structuralMetadata = components.structuralMetadata;
  1173. if (
  1174. defined(structuralMetadata) &&
  1175. structuralMetadata.propertyTableCount > 0
  1176. ) {
  1177. model.featureTableId = selectFeatureTableId(components, model);
  1178. // Re-apply the style to reflect the new feature ID table.
  1179. // This in turn triggers a rebuild of the draw commands.
  1180. model.applyStyle(model._style);
  1181. }
  1182. }
  1183. const scratchBoundingSphere = new BoundingSphere();
  1184. function scaleInPixels(positionWC, radius, frameState) {
  1185. scratchBoundingSphere.center = positionWC;
  1186. scratchBoundingSphere.radius = radius;
  1187. return frameState.camera.getPixelSize(
  1188. scratchBoundingSphere,
  1189. frameState.context.drawingBufferWidth,
  1190. frameState.context.drawingBufferHeight
  1191. );
  1192. }
  1193. const scratchPosition = new Cartesian3();
  1194. function getScale(model, frameState) {
  1195. let scale = model.scale;
  1196. if (model.minimumPixelSize !== 0.0) {
  1197. // Compute size of bounding sphere in pixels
  1198. const context = frameState.context;
  1199. const maxPixelSize = Math.max(
  1200. context.drawingBufferWidth,
  1201. context.drawingBufferHeight
  1202. );
  1203. const m = model.modelMatrix;
  1204. scratchPosition.x = m[12];
  1205. scratchPosition.y = m[13];
  1206. scratchPosition.z = m[14];
  1207. const radius = model.boundingSphere.radius;
  1208. const metersPerPixel = scaleInPixels(scratchPosition, radius, frameState);
  1209. // metersPerPixel is always > 0.0
  1210. const pixelsPerMeter = 1.0 / metersPerPixel;
  1211. const diameterInPixels = Math.min(
  1212. pixelsPerMeter * (2.0 * radius),
  1213. maxPixelSize
  1214. );
  1215. // Maintain model's minimum pixel size
  1216. if (diameterInPixels < model.minimumPixelSize) {
  1217. scale =
  1218. (model.minimumPixelSize * metersPerPixel) /
  1219. (2.0 * model._initialRadius);
  1220. }
  1221. }
  1222. return defined(model.maximumScale)
  1223. ? Math.min(model.maximumScale, scale)
  1224. : scale;
  1225. }
  1226. /**
  1227. * Gets whether or not clipping planes are enabled for this model.
  1228. *
  1229. * @returns {Boolean} <code>true</code> if clipping planes are enabled for this model, <code>false</code>.
  1230. * @private
  1231. */
  1232. ModelExperimental.prototype.isClippingEnabled = function () {
  1233. const clippingPlanes = this._clippingPlanes;
  1234. return (
  1235. defined(clippingPlanes) &&
  1236. clippingPlanes.enabled &&
  1237. clippingPlanes.length !== 0
  1238. );
  1239. };
  1240. /**
  1241. * Returns true if this object was destroyed; otherwise, false.
  1242. * <br /><br />
  1243. * If this object was destroyed, it should not be used; calling any function other than
  1244. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  1245. *
  1246. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  1247. *
  1248. * @see ModelExperimental#destroy
  1249. */
  1250. ModelExperimental.prototype.isDestroyed = function () {
  1251. return false;
  1252. };
  1253. /**
  1254. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  1255. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  1256. * <br /><br />
  1257. * Once an object is destroyed, it should not be used; calling any function other than
  1258. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  1259. * assign the return value (<code>undefined</code>) to the object as done in the example.
  1260. *
  1261. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  1262. *
  1263. *
  1264. * @example
  1265. * model = model && model.destroy();
  1266. *
  1267. * @see ModelExperimental#isDestroyed
  1268. */
  1269. ModelExperimental.prototype.destroy = function () {
  1270. const loader = this._loader;
  1271. if (defined(loader)) {
  1272. loader.destroy();
  1273. }
  1274. const featureTables = this._featureTables;
  1275. if (defined(featureTables)) {
  1276. for (let i = 0; i < featureTables.length; i++) {
  1277. featureTables[i].destroy();
  1278. }
  1279. }
  1280. this.destroyResources();
  1281. // Only destroy the ClippingPlaneCollection if this is the owner.
  1282. const clippingPlaneCollection = this._clippingPlanes;
  1283. if (
  1284. defined(clippingPlaneCollection) &&
  1285. !clippingPlaneCollection.isDestroyed() &&
  1286. clippingPlaneCollection.owner === this
  1287. ) {
  1288. clippingPlaneCollection.destroy();
  1289. }
  1290. this._clippingPlanes = undefined;
  1291. // Only destroy the ImageBasedLighting if this is the owner.
  1292. if (
  1293. this._shouldDestroyImageBasedLighting &&
  1294. !this._imageBasedLighting.isDestroyed()
  1295. ) {
  1296. this._imageBasedLighting.destroy();
  1297. }
  1298. this._imageBasedLighting = undefined;
  1299. destroyObject(this);
  1300. };
  1301. /**
  1302. * Destroys resources generated in the pipeline stages.
  1303. * @private
  1304. */
  1305. ModelExperimental.prototype.destroyResources = function () {
  1306. const resources = this._resources;
  1307. for (let i = 0; i < resources.length; i++) {
  1308. resources[i].destroy();
  1309. }
  1310. this._resources = [];
  1311. };
  1312. /**
  1313. * <p>
  1314. * Creates a model from a glTF asset. When the model is ready to render, i.e., when the external binary, image,
  1315. * and shader files are downloaded and the WebGL resources are created, the {@link Model#readyPromise} is resolved.
  1316. * </p>
  1317. * <p>
  1318. * The model can be a traditional glTF asset with a .gltf extension or a Binary glTF using the .glb extension.
  1319. *
  1320. * @param {Object} options Object with the following properties:
  1321. * @param {String|Resource|Uint8Array|Object} options.gltf A Resource/URL to a glTF/glb file, a binary glTF buffer, or a JSON object containing the glTF contents
  1322. * @param {String|Resource} [options.basePath=''] The base path that paths in the glTF JSON are relative to.
  1323. * @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 transformation matrix that transforms the model from model to world coordinates.
  1324. * @param {Number} [options.scale=1.0] A uniform scale applied to this model.
  1325. * @param {Number} [options.minimumPixelSize=0.0] The approximate minimum pixel size of the model regardless of zoom.
  1326. * @param {Number} [options.maximumScale] The maximum scale size of a model. An upper limit for minimumPixelSize.
  1327. * @param {Boolean} [options.incrementallyLoadTextures=true] Determine if textures may continue to stream in after the model is loaded.
  1328. * @param {Boolean} [options.releaseGltfJson=false] When true, the glTF JSON is released once the glTF is loaded. This is is especially useful for cases like 3D Tiles, where each .gltf model is unique and caching the glTF JSON is not effective.
  1329. * @param {Boolean} [options.debugShowBoundingVolume=false] For debugging only. Draws the bounding sphere for each draw command in the model.
  1330. * @param {Boolean} [options.debugWireframe=false] For debugging only. Draws the model in wireframe.
  1331. * @param {Boolean} [options.cull=true] Whether or not to cull the model using frustum/horizon culling. If the model is part of a 3D Tiles tileset, this property will always be false, since the 3D Tiles culling system is used.
  1332. * @param {Boolean} [options.opaquePass=Pass.OPAQUE] The pass to use in the {@link DrawCommand} for the opaque portions of the model.
  1333. * @param {Axis} [options.upAxis=Axis.Y] The up-axis of the glTF model.
  1334. * @param {Axis} [options.forwardAxis=Axis.Z] The forward-axis of the glTF model.
  1335. * @param {Boolean} [options.allowPicking=true] When <code>true</code>, each primitive is pickable with {@link Scene#pick}.
  1336. * @param {CustomShader} [options.customShader] A custom shader. This will add user-defined GLSL code to the vertex and fragment shaders. Using custom shaders with a {@link Cesium3DTileStyle} may lead to undefined behavior.
  1337. * @param {Cesium3DTileContent} [options.content] The tile content this model belongs to. This property will be undefined if model is not loaded as part of a tileset.
  1338. * @param {Boolean} [options.show=true] Whether or not to render the model.
  1339. * @param {Color} [options.color] A color that blends with the model's rendered color.
  1340. * @param {ColorBlendMode} [options.colorBlendMode=ColorBlendMode.HIGHLIGHT] Defines how the color blends with the model.
  1341. * @param {Number} [options.colorBlendAmount=0.5] Value used to determine the color strength when the <code>colorBlendMode</code> is <code>MIX</code>. A value of 0.0 results in the model's rendered color while a value of 1.0 results in a solid color, with any value in-between resulting in a mix of the two.
  1342. * @param {String|Number} [options.featureIdLabel="featureId_0"] Label of the feature ID set to use for picking and styling. For EXT_mesh_features, this is the feature ID's label property, or "featureId_N" (where N is the index in the featureIds array) when not specified. EXT_feature_metadata did not have a label field, so such feature ID sets are always labeled "featureId_N" where N is the index in the list of all feature Ids, where feature ID attributes are listed before feature ID textures. If featureIdLabel is an integer N, it is converted to the string "featureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
  1343. * @param {String|Number} [options.instanceFeatureIdLabel="instanceFeatureId_0"] Label of the instance feature ID set used for picking and styling. If instanceFeatureIdLabel is set to an integer N, it is converted to the string "instanceFeatureId_N" automatically. If both per-primitive and per-instance feature IDs are present, the instance feature IDs take priority.
  1344. * @param {Object} [options.pointCloudShading] Options for constructing a {@link PointCloudShading} object to control point attenuation and lighting.
  1345. * @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model.
  1346. * @param {Cartesian3} [options.lightColor] The light color when shading the model. When <code>undefined</code> the scene's light color is used instead.
  1347. * @param {ImageBasedLighting} [options.imageBasedLighting] The properties for managing image-based lighting on this model.
  1348. * @param {Boolean} [options.backFaceCulling=true] Whether to cull back-facing geometry. When true, back face culling is determined by the material's doubleSided property; when false, back face culling is disabled. Back faces are not culled if the model's color is translucent.
  1349. * @param {ShadowMode} [options.shadows=ShadowMode.ENABLED] Determines whether the model casts or receives shadows from light sources.
  1350. * @param {Boolean} [options.showCreditsOnScreen=false] Whether to display the credits of this model on screen.
  1351. * @param {SplitDirection} [options.splitDirection=SplitDirection.NONE] The {@link SplitDirection} split to apply to this model.
  1352. * @returns {ModelExperimental} The newly created model.
  1353. */
  1354. ModelExperimental.fromGltf = function (options) {
  1355. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  1356. //>>includeStart('debug', pragmas.debug);
  1357. Check.defined("options.gltf", options.gltf);
  1358. //>>includeEnd('debug');
  1359. const loaderOptions = {
  1360. releaseGltfJson: options.releaseGltfJson,
  1361. incrementallyLoadTextures: options.incrementallyLoadTextures,
  1362. upAxis: options.upAxis,
  1363. forwardAxis: options.forwardAxis,
  1364. };
  1365. const gltf = options.gltf;
  1366. const basePath = defaultValue(options.basePath, "");
  1367. const baseResource = Resource.createIfNeeded(basePath);
  1368. if (defined(gltf.asset)) {
  1369. loaderOptions.gltfJson = gltf;
  1370. loaderOptions.baseResource = baseResource;
  1371. loaderOptions.gltfResource = baseResource;
  1372. } else if (gltf instanceof Uint8Array) {
  1373. loaderOptions.typedArray = gltf;
  1374. loaderOptions.baseResource = baseResource;
  1375. loaderOptions.gltfResource = baseResource;
  1376. } else {
  1377. loaderOptions.gltfResource = Resource.createIfNeeded(options.gltf);
  1378. }
  1379. const loader = new GltfLoader(loaderOptions);
  1380. const is3DTiles = defined(options.content);
  1381. const type = is3DTiles
  1382. ? ModelExperimentalType.TILE_GLTF
  1383. : ModelExperimentalType.GLTF;
  1384. const modelOptions = makeModelOptions(loader, type, options);
  1385. modelOptions.resource = loaderOptions.gltfResource;
  1386. const model = new ModelExperimental(modelOptions);
  1387. return model;
  1388. };
  1389. /*
  1390. * @private
  1391. */
  1392. ModelExperimental.fromB3dm = function (options) {
  1393. const loaderOptions = {
  1394. b3dmResource: options.resource,
  1395. arrayBuffer: options.arrayBuffer,
  1396. byteOffset: options.byteOffset,
  1397. releaseGltfJson: options.releaseGltfJson,
  1398. incrementallyLoadTextures: options.incrementallyLoadTextures,
  1399. upAxis: options.upAxis,
  1400. forwardAxis: options.forwardAxis,
  1401. };
  1402. const loader = new B3dmLoader(loaderOptions);
  1403. const modelOptions = makeModelOptions(
  1404. loader,
  1405. ModelExperimentalType.TILE_B3DM,
  1406. options
  1407. );
  1408. const model = new ModelExperimental(modelOptions);
  1409. return model;
  1410. };
  1411. /**
  1412. * @private
  1413. */
  1414. ModelExperimental.fromPnts = function (options) {
  1415. const loaderOptions = {
  1416. arrayBuffer: options.arrayBuffer,
  1417. byteOffset: options.byteOffset,
  1418. };
  1419. const loader = new PntsLoader(loaderOptions);
  1420. const modelOptions = makeModelOptions(
  1421. loader,
  1422. ModelExperimentalType.TILE_PNTS,
  1423. options
  1424. );
  1425. const model = new ModelExperimental(modelOptions);
  1426. return model;
  1427. };
  1428. /*
  1429. * @private
  1430. */
  1431. ModelExperimental.fromI3dm = function (options) {
  1432. const loaderOptions = {
  1433. i3dmResource: options.resource,
  1434. arrayBuffer: options.arrayBuffer,
  1435. byteOffset: options.byteOffset,
  1436. releaseGltfJson: options.releaseGltfJson,
  1437. incrementallyLoadTextures: options.incrementallyLoadTextures,
  1438. upAxis: options.upAxis,
  1439. forwardAxis: options.forwardAxis,
  1440. };
  1441. const loader = new I3dmLoader(loaderOptions);
  1442. const modelOptions = makeModelOptions(
  1443. loader,
  1444. ModelExperimentalType.TILE_I3DM,
  1445. options
  1446. );
  1447. const model = new ModelExperimental(modelOptions);
  1448. return model;
  1449. };
  1450. function updateShowBoundingVolume(sceneGraph, debugShowBoundingVolume) {
  1451. const drawCommands = sceneGraph._drawCommands;
  1452. for (let i = 0; i < drawCommands.length; i++) {
  1453. drawCommands[i].debugShowBoundingVolume = debugShowBoundingVolume;
  1454. }
  1455. }
  1456. /**
  1457. * @private
  1458. */
  1459. ModelExperimental.prototype.applyColorAndShow = function (style) {
  1460. const hasColorStyle = defined(style) && defined(style.color);
  1461. const hasShowStyle = defined(style) && defined(style.show);
  1462. this._color = hasColorStyle
  1463. ? style.color.evaluateColor(undefined, this._color)
  1464. : Color.clone(Color.WHITE, this._color);
  1465. this._show = hasShowStyle ? style.show.evaluate(undefined) : true;
  1466. };
  1467. /**
  1468. * @private
  1469. */
  1470. ModelExperimental.prototype.applyStyle = function (style) {
  1471. // The style is only set by the ModelFeatureTable. If there are no features,
  1472. // the color and show from the style are directly applied.
  1473. if (
  1474. defined(this.featureTableId) &&
  1475. this.featureTables[this.featureTableId].featuresLength > 0
  1476. ) {
  1477. const featureTable = this.featureTables[this.featureTableId];
  1478. featureTable.applyStyle(style);
  1479. } else {
  1480. this.applyColorAndShow(style);
  1481. }
  1482. this.resetDrawCommands();
  1483. };
  1484. function makeModelOptions(loader, modelType, options) {
  1485. return {
  1486. loader: loader,
  1487. type: modelType,
  1488. resource: options.resource,
  1489. modelMatrix: options.modelMatrix,
  1490. scale: options.scale,
  1491. minimumPixelSize: options.minimumPixelSize,
  1492. maximumScale: options.maximumScale,
  1493. debugShowBoundingVolume: options.debugShowBoundingVolume,
  1494. debugWireframe: options.debugWireframe,
  1495. cull: options.cull,
  1496. opaquePass: options.opaquePass,
  1497. allowPicking: options.allowPicking,
  1498. customShader: options.customShader,
  1499. content: options.content,
  1500. show: options.show,
  1501. color: options.color,
  1502. colorBlendAmount: options.colorBlendAmount,
  1503. colorBlendMode: options.colorBlendMode,
  1504. featureIdLabel: options.featureIdLabel,
  1505. instanceFeatureIdLabel: options.instanceFeatureIdLabel,
  1506. pointCloudShading: options.pointCloudShading,
  1507. clippingPlanes: options.clippingPlanes,
  1508. lightColor: options.lightColor,
  1509. imageBasedLighting: options.imageBasedLighting,
  1510. backFaceCulling: options.backFaceCulling,
  1511. shadows: options.shadows,
  1512. showCreditsOnScreen: options.showCreditsOnScreen,
  1513. splitDirection: options.splitDirection,
  1514. };
  1515. }