ModelSceneGraph.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. import BoundingSphere from "../../Core/BoundingSphere.js";
  2. import Cartesian3 from "../../Core/Cartesian3.js";
  3. import Check from "../../Core/Check.js";
  4. import defaultValue from "../../Core/defaultValue.js";
  5. import defined from "../../Core/defined.js";
  6. import Matrix4 from "../../Core/Matrix4.js";
  7. import Transforms from "../../Core/Transforms.js";
  8. import SceneMode from "../SceneMode.js";
  9. import SplitDirection from "../SplitDirection.js";
  10. import buildDrawCommand from "./buildDrawCommand.js";
  11. import TilesetPipelineStage from "./TilesetPipelineStage.js";
  12. import ImageBasedLightingPipelineStage from "./ImageBasedLightingPipelineStage.js";
  13. import ModelArticulation from "./ModelArticulation.js";
  14. import ModelColorPipelineStage from "./ModelColorPipelineStage.js";
  15. import ModelClippingPlanesPipelineStage from "./ModelClippingPlanesPipelineStage.js";
  16. import ModelNode from "./ModelNode.js";
  17. import ModelRuntimeNode from "./ModelRuntimeNode.js";
  18. import ModelRuntimePrimitive from "./ModelRuntimePrimitive.js";
  19. import ModelSkin from "./ModelSkin.js";
  20. import ModelUtility from "./ModelUtility.js";
  21. import ModelRenderResources from "./ModelRenderResources.js";
  22. import ModelSilhouettePipelineStage from "./ModelSilhouettePipelineStage.js";
  23. import ModelSplitterPipelineStage from "./ModelSplitterPipelineStage.js";
  24. import ModelType from "./ModelType.js";
  25. import NodeRenderResources from "./NodeRenderResources.js";
  26. import PrimitiveRenderResources from "./PrimitiveRenderResources.js";
  27. /**
  28. * An in memory representation of the scene graph for a {@link Model}
  29. *
  30. * @param {object} options An object containing the following options
  31. * @param {Model} options.model The model this scene graph belongs to
  32. * @param {ModelComponents} options.modelComponents The model components describing the model
  33. *
  34. * @alias ModelSceneGraph
  35. * @constructor
  36. *
  37. * @private
  38. */
  39. function ModelSceneGraph(options) {
  40. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  41. const components = options.modelComponents;
  42. //>>includeStart('debug', pragmas.debug);
  43. Check.typeOf.object("options.model", options.model);
  44. Check.typeOf.object("options.modelComponents", components);
  45. //>>includeEnd('debug');
  46. /**
  47. * A reference to the {@link Model} that owns this scene graph.
  48. *
  49. * @type {Model}
  50. * @readonly
  51. *
  52. * @private
  53. */
  54. this._model = options.model;
  55. /**
  56. * The model components that represent the contents of the 3D model file.
  57. *
  58. * @type {ModelComponents}
  59. * @readonly
  60. *
  61. * @private
  62. */
  63. this._components = components;
  64. /**
  65. * Pipeline stages to apply across the model.
  66. *
  67. * @type {Object[]}
  68. * @readonly
  69. *
  70. * @private
  71. */
  72. this._pipelineStages = [];
  73. /**
  74. * Update stages to apply across the model.
  75. *
  76. * @type {Object[]}
  77. * @readonly
  78. *
  79. * @private
  80. */
  81. this._updateStages = [];
  82. /**
  83. * The runtime nodes that make up the scene graph
  84. *
  85. * @type {ModelRuntimeNode[]}
  86. * @readonly
  87. *
  88. * @private
  89. */
  90. this._runtimeNodes = [];
  91. /**
  92. * The indices of the root nodes in the runtime nodes array.
  93. *
  94. * @type {number[]}
  95. * @readonly
  96. *
  97. * @private
  98. */
  99. this._rootNodes = [];
  100. /**
  101. * The indices of the skinned nodes in the runtime nodes array. These refer
  102. * to the nodes that will be manipulated by their skin, as opposed to the nodes
  103. * acting as joints for the skin.
  104. *
  105. * @type {number[]}
  106. * @readonly
  107. *
  108. * @private
  109. */
  110. this._skinnedNodes = [];
  111. /**
  112. * The runtime skins that affect nodes in the scene graph.
  113. *
  114. * @type {ModelSkin[]}
  115. * @readonly
  116. *
  117. * @private
  118. */
  119. this._runtimeSkins = [];
  120. /**
  121. * Pipeline stages to apply to this model. This
  122. * is an array of classes, each with a static method called
  123. * <code>process()</code>
  124. *
  125. * @type {Object[]}
  126. * @readonly
  127. *
  128. * @private
  129. */
  130. this.modelPipelineStages = [];
  131. // The scene graph's bounding sphere is model space, so that
  132. // the model's bounding sphere can be recomputed when given a
  133. // new model matrix.
  134. this._boundingSphere = undefined;
  135. // The 2D bounding sphere is in world space. This is checked
  136. // by the draw commands to see if the model is over the IDL,
  137. // and if so, renders the primitives using extra commands.
  138. this._boundingSphere2D = undefined;
  139. this._computedModelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  140. this._computedModelMatrix2D = Matrix4.clone(Matrix4.IDENTITY);
  141. this._axisCorrectionMatrix = ModelUtility.getAxisCorrectionMatrix(
  142. components.upAxis,
  143. components.forwardAxis,
  144. new Matrix4()
  145. );
  146. // Store articulations from the AGI_articulations extension
  147. // by name in a dictionary for easy retrieval.
  148. this._runtimeArticulations = {};
  149. initialize(this);
  150. }
  151. Object.defineProperties(ModelSceneGraph.prototype, {
  152. /**
  153. * The model components this scene graph represents.
  154. *
  155. * @type {ModelComponents}
  156. * @readonly
  157. *
  158. * @private
  159. */
  160. components: {
  161. get: function () {
  162. return this._components;
  163. },
  164. },
  165. /**
  166. * The axis-corrected model matrix.
  167. *
  168. * @type {Matrix4}
  169. * @readonly
  170. *
  171. * @private
  172. */
  173. computedModelMatrix: {
  174. get: function () {
  175. return this._computedModelMatrix;
  176. },
  177. },
  178. /**
  179. * A matrix to correct from y-up in some model formats (e.g. glTF) to the
  180. * z-up coordinate system Cesium uses.
  181. *
  182. * @type {Matrix4}
  183. * @readonly
  184. *
  185. * @private
  186. */
  187. axisCorrectionMatrix: {
  188. get: function () {
  189. return this._axisCorrectionMatrix;
  190. },
  191. },
  192. /**
  193. * The bounding sphere containing all the primitives in the scene graph
  194. * in model space.
  195. *
  196. * @type {BoundingSphere}
  197. * @readonly
  198. *
  199. * @private
  200. */
  201. boundingSphere: {
  202. get: function () {
  203. return this._boundingSphere;
  204. },
  205. },
  206. });
  207. function initialize(sceneGraph) {
  208. const components = sceneGraph._components;
  209. const scene = components.scene;
  210. const model = sceneGraph._model;
  211. // If the model has a height reference that modifies the model matrix,
  212. // it will be accounted for in updateModelMatrix.
  213. const modelMatrix = model.modelMatrix;
  214. computeModelMatrix(sceneGraph, modelMatrix);
  215. const articulations = components.articulations;
  216. const articulationsLength = articulations.length;
  217. const runtimeArticulations = sceneGraph._runtimeArticulations;
  218. for (let i = 0; i < articulationsLength; i++) {
  219. const articulation = articulations[i];
  220. const runtimeArticulation = new ModelArticulation({
  221. articulation: articulation,
  222. sceneGraph: sceneGraph,
  223. });
  224. const name = runtimeArticulation.name;
  225. runtimeArticulations[name] = runtimeArticulation;
  226. }
  227. const nodes = components.nodes;
  228. const nodesLength = nodes.length;
  229. // Initialize this array to be the same size as the nodes array in
  230. // the model file. This is so the node indices remain the same. However,
  231. // only nodes reachable from the scene's root node will be populated, the
  232. // rest will be undefined
  233. sceneGraph._runtimeNodes = new Array(nodesLength);
  234. const rootNodes = scene.nodes;
  235. const rootNodesLength = rootNodes.length;
  236. const transformToRoot = Matrix4.IDENTITY;
  237. for (let i = 0; i < rootNodesLength; i++) {
  238. const rootNode = scene.nodes[i];
  239. const rootNodeIndex = traverseAndCreateSceneGraph(
  240. sceneGraph,
  241. rootNode,
  242. transformToRoot
  243. );
  244. sceneGraph._rootNodes.push(rootNodeIndex);
  245. }
  246. // Handle skins after all runtime nodes are created
  247. const skins = components.skins;
  248. const runtimeSkins = sceneGraph._runtimeSkins;
  249. const skinsLength = skins.length;
  250. for (let i = 0; i < skinsLength; i++) {
  251. const skin = skins[i];
  252. runtimeSkins.push(
  253. new ModelSkin({
  254. skin: skin,
  255. sceneGraph: sceneGraph,
  256. })
  257. );
  258. }
  259. const skinnedNodes = sceneGraph._skinnedNodes;
  260. const skinnedNodesLength = skinnedNodes.length;
  261. for (let i = 0; i < skinnedNodesLength; i++) {
  262. const skinnedNodeIndex = skinnedNodes[i];
  263. const skinnedNode = sceneGraph._runtimeNodes[skinnedNodeIndex];
  264. // Use the index of the skin in the model components to find
  265. // the corresponding runtime skin.
  266. const skin = nodes[skinnedNodeIndex].skin;
  267. const skinIndex = skin.index;
  268. skinnedNode._runtimeSkin = runtimeSkins[skinIndex];
  269. skinnedNode.updateJointMatrices();
  270. }
  271. // Ensure articulations are applied with their initial values to their target nodes.
  272. sceneGraph.applyArticulations();
  273. }
  274. function computeModelMatrix(sceneGraph, modelMatrix) {
  275. const components = sceneGraph._components;
  276. const model = sceneGraph._model;
  277. sceneGraph._computedModelMatrix = Matrix4.multiplyTransformation(
  278. modelMatrix,
  279. components.transform,
  280. sceneGraph._computedModelMatrix
  281. );
  282. sceneGraph._computedModelMatrix = Matrix4.multiplyTransformation(
  283. sceneGraph._computedModelMatrix,
  284. sceneGraph._axisCorrectionMatrix,
  285. sceneGraph._computedModelMatrix
  286. );
  287. sceneGraph._computedModelMatrix = Matrix4.multiplyByUniformScale(
  288. sceneGraph._computedModelMatrix,
  289. model.computedScale,
  290. sceneGraph._computedModelMatrix
  291. );
  292. }
  293. const scratchComputedTranslation = new Cartesian3();
  294. function computeModelMatrix2D(sceneGraph, frameState) {
  295. const computedModelMatrix = sceneGraph._computedModelMatrix;
  296. const translation = Matrix4.getTranslation(
  297. computedModelMatrix,
  298. scratchComputedTranslation
  299. );
  300. if (!Cartesian3.equals(translation, Cartesian3.ZERO)) {
  301. sceneGraph._computedModelMatrix2D = Transforms.basisTo2D(
  302. frameState.mapProjection,
  303. computedModelMatrix,
  304. sceneGraph._computedModelMatrix2D
  305. );
  306. } else {
  307. const center = sceneGraph.boundingSphere.center;
  308. const to2D = Transforms.wgs84To2DModelMatrix(
  309. frameState.mapProjection,
  310. center,
  311. sceneGraph._computedModelMatrix2D
  312. );
  313. sceneGraph._computedModelMatrix2D = Matrix4.multiply(
  314. to2D,
  315. computedModelMatrix,
  316. sceneGraph._computedModelMatrix2D
  317. );
  318. }
  319. sceneGraph._boundingSphere2D = BoundingSphere.transform(
  320. sceneGraph._boundingSphere,
  321. sceneGraph._computedModelMatrix2D,
  322. sceneGraph._boundingSphere2D
  323. );
  324. }
  325. /**
  326. * Recursively traverse through the nodes in the scene graph to create
  327. * their runtime versions, using a post-order depth-first traversal.
  328. *
  329. * @param {ModelSceneGraph} sceneGraph The scene graph
  330. * @param {ModelComponents.Node} node The current node
  331. * @param {Matrix4} transformToRoot The transforms of this node's ancestors.
  332. * @returns {number} The index of this node in the runtimeNodes array.
  333. *
  334. * @private
  335. */
  336. function traverseAndCreateSceneGraph(sceneGraph, node, transformToRoot) {
  337. // The indices of the children of this node in the runtimeNodes array.
  338. const childrenIndices = [];
  339. const transform = ModelUtility.getNodeTransform(node);
  340. // Traverse through scene graph.
  341. const childrenLength = node.children.length;
  342. for (let i = 0; i < childrenLength; i++) {
  343. const childNode = node.children[i];
  344. const childNodeTransformToRoot = Matrix4.multiplyTransformation(
  345. transformToRoot,
  346. transform,
  347. new Matrix4()
  348. );
  349. const childIndex = traverseAndCreateSceneGraph(
  350. sceneGraph,
  351. childNode,
  352. childNodeTransformToRoot
  353. );
  354. childrenIndices.push(childIndex);
  355. }
  356. // Process node and mesh primitives.
  357. const runtimeNode = new ModelRuntimeNode({
  358. node: node,
  359. transform: transform,
  360. transformToRoot: transformToRoot,
  361. children: childrenIndices,
  362. sceneGraph: sceneGraph,
  363. });
  364. const primitivesLength = node.primitives.length;
  365. for (let i = 0; i < primitivesLength; i++) {
  366. runtimeNode.runtimePrimitives.push(
  367. new ModelRuntimePrimitive({
  368. primitive: node.primitives[i],
  369. node: node,
  370. model: sceneGraph._model,
  371. })
  372. );
  373. }
  374. const index = node.index;
  375. sceneGraph._runtimeNodes[index] = runtimeNode;
  376. if (defined(node.skin)) {
  377. sceneGraph._skinnedNodes.push(index);
  378. }
  379. // Create and store the public version of the runtime node.
  380. const name = node.name;
  381. if (defined(name)) {
  382. const model = sceneGraph._model;
  383. const publicNode = new ModelNode(model, runtimeNode);
  384. model._nodesByName[name] = publicNode;
  385. }
  386. return index;
  387. }
  388. const scratchModelPositionMin = new Cartesian3();
  389. const scratchModelPositionMax = new Cartesian3();
  390. const scratchPrimitivePositionMin = new Cartesian3();
  391. const scratchPrimitivePositionMax = new Cartesian3();
  392. /**
  393. * Generates the {@link ModelDrawCommand} for each primitive in the model.
  394. * If the model is used for classification, a {@link ClassificationModelDrawCommand}
  395. * is generated for each primitive instead.
  396. *
  397. * @param {FrameState} frameState The current frame state. This is needed to
  398. * allocate GPU resources as needed.
  399. *
  400. * @private
  401. */
  402. ModelSceneGraph.prototype.buildDrawCommands = function (frameState) {
  403. const model = this._model;
  404. const modelRenderResources = new ModelRenderResources(model);
  405. // Reset the memory counts before running the pipeline
  406. model.statistics.clear();
  407. this.configurePipeline(frameState);
  408. const modelPipelineStages = this.modelPipelineStages;
  409. let i, j, k;
  410. for (i = 0; i < modelPipelineStages.length; i++) {
  411. const modelPipelineStage = modelPipelineStages[i];
  412. modelPipelineStage.process(modelRenderResources, model, frameState);
  413. }
  414. const modelPositionMin = Cartesian3.fromElements(
  415. Number.MAX_VALUE,
  416. Number.MAX_VALUE,
  417. Number.MAX_VALUE,
  418. scratchModelPositionMin
  419. );
  420. const modelPositionMax = Cartesian3.fromElements(
  421. -Number.MAX_VALUE,
  422. -Number.MAX_VALUE,
  423. -Number.MAX_VALUE,
  424. scratchModelPositionMax
  425. );
  426. for (i = 0; i < this._runtimeNodes.length; i++) {
  427. const runtimeNode = this._runtimeNodes[i];
  428. // If a node in the model was unreachable from the scene graph, there will
  429. // be no corresponding runtime node and therefore should be skipped.
  430. if (!defined(runtimeNode)) {
  431. continue;
  432. }
  433. runtimeNode.configurePipeline();
  434. const nodePipelineStages = runtimeNode.pipelineStages;
  435. const nodeRenderResources = new NodeRenderResources(
  436. modelRenderResources,
  437. runtimeNode
  438. );
  439. for (j = 0; j < nodePipelineStages.length; j++) {
  440. const nodePipelineStage = nodePipelineStages[j];
  441. nodePipelineStage.process(
  442. nodeRenderResources,
  443. runtimeNode.node,
  444. frameState
  445. );
  446. }
  447. const nodeTransform = runtimeNode.computedTransform;
  448. for (j = 0; j < runtimeNode.runtimePrimitives.length; j++) {
  449. const runtimePrimitive = runtimeNode.runtimePrimitives[j];
  450. runtimePrimitive.configurePipeline(frameState);
  451. const primitivePipelineStages = runtimePrimitive.pipelineStages;
  452. const primitiveRenderResources = new PrimitiveRenderResources(
  453. nodeRenderResources,
  454. runtimePrimitive
  455. );
  456. for (k = 0; k < primitivePipelineStages.length; k++) {
  457. const primitivePipelineStage = primitivePipelineStages[k];
  458. primitivePipelineStage.process(
  459. primitiveRenderResources,
  460. runtimePrimitive.primitive,
  461. frameState
  462. );
  463. }
  464. runtimePrimitive.boundingSphere = BoundingSphere.clone(
  465. primitiveRenderResources.boundingSphere,
  466. new BoundingSphere()
  467. );
  468. const primitivePositionMin = Matrix4.multiplyByPoint(
  469. nodeTransform,
  470. primitiveRenderResources.positionMin,
  471. scratchPrimitivePositionMin
  472. );
  473. const primitivePositionMax = Matrix4.multiplyByPoint(
  474. nodeTransform,
  475. primitiveRenderResources.positionMax,
  476. scratchPrimitivePositionMax
  477. );
  478. Cartesian3.minimumByComponent(
  479. modelPositionMin,
  480. primitivePositionMin,
  481. modelPositionMin
  482. );
  483. Cartesian3.maximumByComponent(
  484. modelPositionMax,
  485. primitivePositionMax,
  486. modelPositionMax
  487. );
  488. const drawCommand = buildDrawCommand(
  489. primitiveRenderResources,
  490. frameState
  491. );
  492. runtimePrimitive.drawCommand = drawCommand;
  493. }
  494. }
  495. this._boundingSphere = BoundingSphere.fromCornerPoints(
  496. modelPositionMin,
  497. modelPositionMax,
  498. new BoundingSphere()
  499. );
  500. this._boundingSphere = BoundingSphere.transformWithoutScale(
  501. this._boundingSphere,
  502. this._axisCorrectionMatrix,
  503. this._boundingSphere
  504. );
  505. this._boundingSphere = BoundingSphere.transform(
  506. this._boundingSphere,
  507. this._components.transform,
  508. this._boundingSphere
  509. );
  510. model._boundingSphere = BoundingSphere.transform(
  511. this._boundingSphere,
  512. model.modelMatrix,
  513. model._boundingSphere
  514. );
  515. model._initialRadius = model._boundingSphere.radius;
  516. model._boundingSphere.radius *= model._clampedScale;
  517. };
  518. /**
  519. * Configure the model pipeline stages. If the pipeline needs to be re-run, call
  520. * this method again to ensure the correct sequence of pipeline stages are
  521. * used.
  522. *
  523. * @private
  524. */
  525. ModelSceneGraph.prototype.configurePipeline = function (frameState) {
  526. const modelPipelineStages = this.modelPipelineStages;
  527. modelPipelineStages.length = 0;
  528. const model = this._model;
  529. if (defined(model.color)) {
  530. modelPipelineStages.push(ModelColorPipelineStage);
  531. }
  532. // Skip these pipeline stages for classification models.
  533. if (defined(model.classificationType)) {
  534. return;
  535. }
  536. if (model.imageBasedLighting.enabled) {
  537. modelPipelineStages.push(ImageBasedLightingPipelineStage);
  538. }
  539. if (model.isClippingEnabled()) {
  540. modelPipelineStages.push(ModelClippingPlanesPipelineStage);
  541. }
  542. if (model.hasSilhouette(frameState)) {
  543. modelPipelineStages.push(ModelSilhouettePipelineStage);
  544. }
  545. if (
  546. defined(model.splitDirection) &&
  547. model.splitDirection !== SplitDirection.NONE
  548. ) {
  549. modelPipelineStages.push(ModelSplitterPipelineStage);
  550. }
  551. if (ModelType.is3DTiles(model.type)) {
  552. modelPipelineStages.push(TilesetPipelineStage);
  553. }
  554. };
  555. ModelSceneGraph.prototype.update = function (frameState, updateForAnimations) {
  556. let i, j, k;
  557. for (i = 0; i < this._runtimeNodes.length; i++) {
  558. const runtimeNode = this._runtimeNodes[i];
  559. // If a node in the model was unreachable from the scene graph, there will
  560. // be no corresponding runtime node and therefore should be skipped.
  561. if (!defined(runtimeNode)) {
  562. continue;
  563. }
  564. for (j = 0; j < runtimeNode.updateStages.length; j++) {
  565. const nodeUpdateStage = runtimeNode.updateStages[j];
  566. nodeUpdateStage.update(runtimeNode, this, frameState);
  567. }
  568. const disableAnimations =
  569. frameState.mode !== SceneMode.SCENE3D && this._model._projectTo2D;
  570. if (updateForAnimations && !disableAnimations) {
  571. this.updateJointMatrices();
  572. }
  573. for (j = 0; j < runtimeNode.runtimePrimitives.length; j++) {
  574. const runtimePrimitive = runtimeNode.runtimePrimitives[j];
  575. for (k = 0; k < runtimePrimitive.updateStages.length; k++) {
  576. const stage = runtimePrimitive.updateStages[k];
  577. stage.update(runtimePrimitive, this);
  578. }
  579. }
  580. }
  581. };
  582. ModelSceneGraph.prototype.updateModelMatrix = function (
  583. modelMatrix,
  584. frameState
  585. ) {
  586. computeModelMatrix(this, modelMatrix);
  587. if (frameState.mode !== SceneMode.SCENE3D) {
  588. computeModelMatrix2D(this, frameState);
  589. }
  590. // Mark all root nodes as dirty. Any and all children will be
  591. // affected recursively in the update stage.
  592. const rootNodes = this._rootNodes;
  593. for (let i = 0; i < rootNodes.length; i++) {
  594. const node = this._runtimeNodes[rootNodes[i]];
  595. node._transformDirty = true;
  596. }
  597. };
  598. /**
  599. * Updates the joint matrices for the skins and nodes of the model.
  600. *
  601. * @private
  602. */
  603. ModelSceneGraph.prototype.updateJointMatrices = function () {
  604. const skinnedNodes = this._skinnedNodes;
  605. const length = skinnedNodes.length;
  606. for (let i = 0; i < length; i++) {
  607. const nodeIndex = skinnedNodes[i];
  608. const runtimeNode = this._runtimeNodes[nodeIndex];
  609. runtimeNode.updateJointMatrices();
  610. }
  611. };
  612. /**
  613. * A callback to be applied once at each runtime primitive in the
  614. * scene graph
  615. * @callback traverseSceneGraphCallback
  616. *
  617. * @param {ModelRuntimePrimitive} runtimePrimitive The runtime primitive for the current step of the traversal
  618. * @param {object} [options] A dictionary of additional options to be passed to the callback, or undefined if the callback does not need any additional information.
  619. *
  620. * @private
  621. */
  622. /**
  623. * Recursively traverse through the runtime nodes in the scene graph
  624. * using a post-order depth-first traversal to perform a callback on
  625. * their runtime primitives.
  626. *
  627. * @param {ModelSceneGraph} sceneGraph The scene graph.
  628. * @param {ModelRuntimeNode} runtimeNode The current runtime node.
  629. * @param {boolean} visibleNodesOnly Whether to only traverse nodes that are visible.
  630. * @param {traverseSceneGraphCallback} callback The callback to perform on the runtime primitives of the node.
  631. * @param {object} [callbackOptions] A dictionary of additional options to be passed to the callback, if needed.
  632. *
  633. * @private
  634. */
  635. function traverseSceneGraph(
  636. sceneGraph,
  637. runtimeNode,
  638. visibleNodesOnly,
  639. callback,
  640. callbackOptions
  641. ) {
  642. if (visibleNodesOnly && !runtimeNode.show) {
  643. return;
  644. }
  645. const childrenLength = runtimeNode.children.length;
  646. for (let i = 0; i < childrenLength; i++) {
  647. const childRuntimeNode = runtimeNode.getChild(i);
  648. traverseSceneGraph(
  649. sceneGraph,
  650. childRuntimeNode,
  651. visibleNodesOnly,
  652. callback,
  653. callbackOptions
  654. );
  655. }
  656. const runtimePrimitives = runtimeNode.runtimePrimitives;
  657. const runtimePrimitivesLength = runtimePrimitives.length;
  658. for (let j = 0; j < runtimePrimitivesLength; j++) {
  659. const runtimePrimitive = runtimePrimitives[j];
  660. callback(runtimePrimitive, callbackOptions);
  661. }
  662. }
  663. function forEachRuntimePrimitive(
  664. sceneGraph,
  665. visibleNodesOnly,
  666. callback,
  667. callbackOptions
  668. ) {
  669. const rootNodes = sceneGraph._rootNodes;
  670. const rootNodesLength = rootNodes.length;
  671. for (let i = 0; i < rootNodesLength; i++) {
  672. const rootNodeIndex = rootNodes[i];
  673. const runtimeNode = sceneGraph._runtimeNodes[rootNodeIndex];
  674. traverseSceneGraph(
  675. sceneGraph,
  676. runtimeNode,
  677. visibleNodesOnly,
  678. callback,
  679. callbackOptions
  680. );
  681. }
  682. }
  683. const scratchBackFaceCullingOptions = {
  684. backFaceCulling: undefined,
  685. };
  686. /**
  687. * Traverses through all draw commands and changes the back-face culling setting.
  688. *
  689. * @param {boolean} backFaceCulling The new value for the back-face culling setting.
  690. *
  691. * @private
  692. */
  693. ModelSceneGraph.prototype.updateBackFaceCulling = function (backFaceCulling) {
  694. const backFaceCullingOptions = scratchBackFaceCullingOptions;
  695. backFaceCullingOptions.backFaceCulling = backFaceCulling;
  696. forEachRuntimePrimitive(
  697. this,
  698. false,
  699. updatePrimitiveBackFaceCulling,
  700. backFaceCullingOptions
  701. );
  702. };
  703. // Callback is defined here to avoid allocating a closure in the render loop
  704. function updatePrimitiveBackFaceCulling(runtimePrimitive, options) {
  705. const drawCommand = runtimePrimitive.drawCommand;
  706. drawCommand.backFaceCulling = options.backFaceCulling;
  707. }
  708. const scratchShadowOptions = {
  709. shadowMode: undefined,
  710. };
  711. /**
  712. * Traverses through all draw commands and changes the shadow settings.
  713. *
  714. * @param {ShadowMode} shadowMode The new shadow settings.
  715. *
  716. * @private
  717. */
  718. ModelSceneGraph.prototype.updateShadows = function (shadowMode) {
  719. const shadowOptions = scratchShadowOptions;
  720. shadowOptions.shadowMode = shadowMode;
  721. forEachRuntimePrimitive(this, false, updatePrimitiveShadows, shadowOptions);
  722. };
  723. // Callback is defined here to avoid allocating a closure in the render loop
  724. function updatePrimitiveShadows(runtimePrimitive, options) {
  725. const drawCommand = runtimePrimitive.drawCommand;
  726. drawCommand.shadows = options.shadowMode;
  727. }
  728. const scratchShowBoundingVolumeOptions = {
  729. debugShowBoundingVolume: undefined,
  730. };
  731. /**
  732. * Traverses through all draw commands and changes whether to show the debug bounding volume.
  733. *
  734. * @param {boolean} debugShowBoundingVolume The new value for showing the debug bounding volume.
  735. *
  736. * @private
  737. */
  738. ModelSceneGraph.prototype.updateShowBoundingVolume = function (
  739. debugShowBoundingVolume
  740. ) {
  741. const showBoundingVolumeOptions = scratchShowBoundingVolumeOptions;
  742. showBoundingVolumeOptions.debugShowBoundingVolume = debugShowBoundingVolume;
  743. forEachRuntimePrimitive(
  744. this,
  745. false,
  746. updatePrimitiveShowBoundingVolume,
  747. showBoundingVolumeOptions
  748. );
  749. };
  750. // Callback is defined here to avoid allocating a closure in the render loop
  751. function updatePrimitiveShowBoundingVolume(runtimePrimitive, options) {
  752. const drawCommand = runtimePrimitive.drawCommand;
  753. drawCommand.debugShowBoundingVolume = options.debugShowBoundingVolume;
  754. }
  755. const scratchSilhouetteCommands = [];
  756. const scratchPushDrawCommandOptions = {
  757. frameState: undefined,
  758. hasSilhouette: undefined,
  759. };
  760. /**
  761. * Traverses through the scene graph and pushes the draw commands associated
  762. * with each primitive to the frame state's command list.
  763. *
  764. * @param {FrameState} frameState The frame state.
  765. *
  766. * @private
  767. */
  768. ModelSceneGraph.prototype.pushDrawCommands = function (frameState) {
  769. // If a model has silhouettes, the commands that draw the silhouettes for
  770. // each primitive can only be invoked after the entire model has drawn.
  771. // Otherwise, the silhouette may draw on top of the model. This requires
  772. // gathering the original commands and the silhouette commands separately.
  773. const silhouetteCommands = scratchSilhouetteCommands;
  774. silhouetteCommands.length = 0;
  775. // Since this function is called each frame, the options object is
  776. // preallocated in a scratch variable
  777. const pushDrawCommandOptions = scratchPushDrawCommandOptions;
  778. pushDrawCommandOptions.hasSilhouette = this._model.hasSilhouette(frameState);
  779. pushDrawCommandOptions.frameState = frameState;
  780. forEachRuntimePrimitive(
  781. this,
  782. true,
  783. pushPrimitiveDrawCommands,
  784. pushDrawCommandOptions
  785. );
  786. frameState.commandList.push.apply(frameState.commandList, silhouetteCommands);
  787. };
  788. // Callback is defined here to avoid allocating a closure in the render loop
  789. function pushPrimitiveDrawCommands(runtimePrimitive, options) {
  790. const frameState = options.frameState;
  791. const hasSilhouette = options.hasSilhouette;
  792. const passes = frameState.passes;
  793. const silhouetteCommands = scratchSilhouetteCommands;
  794. const primitiveDrawCommand = runtimePrimitive.drawCommand;
  795. primitiveDrawCommand.pushCommands(frameState, frameState.commandList);
  796. // If a model has silhouettes, the commands that draw the silhouettes for
  797. // each primitive can only be invoked after the entire model has drawn.
  798. // Otherwise, the silhouette may draw on top of the model. This requires
  799. // gathering the original commands and the silhouette commands separately.
  800. if (hasSilhouette && !passes.pick) {
  801. primitiveDrawCommand.pushSilhouetteCommands(frameState, silhouetteCommands);
  802. }
  803. }
  804. /**
  805. * Sets the current value of an articulation stage.
  806. *
  807. * @param {string} articulationStageKey The name of the articulation, a space, and the name of the stage.
  808. * @param {number} value The numeric value of this stage of the articulation.
  809. *
  810. * @private
  811. */
  812. ModelSceneGraph.prototype.setArticulationStage = function (
  813. articulationStageKey,
  814. value
  815. ) {
  816. const names = articulationStageKey.split(" ");
  817. if (names.length !== 2) {
  818. return;
  819. }
  820. const articulationName = names[0];
  821. const stageName = names[1];
  822. const runtimeArticulation = this._runtimeArticulations[articulationName];
  823. if (defined(runtimeArticulation)) {
  824. runtimeArticulation.setArticulationStage(stageName, value);
  825. }
  826. };
  827. /**
  828. * Applies any modified articulation stages to the matrix of each node that participates
  829. * in any articulation. Note that this will overwrite any nodeTransformations on participating nodes.
  830. *
  831. * @private
  832. */
  833. ModelSceneGraph.prototype.applyArticulations = function () {
  834. const runtimeArticulations = this._runtimeArticulations;
  835. for (const articulationName in runtimeArticulations) {
  836. if (runtimeArticulations.hasOwnProperty(articulationName)) {
  837. const articulation = runtimeArticulations[articulationName];
  838. articulation.apply();
  839. }
  840. }
  841. };
  842. export default ModelSceneGraph;