ModelExperimentalSceneGraph.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. import buildDrawCommands from "./buildDrawCommands.js";
  2. import BoundingSphere from "../../Core/BoundingSphere.js";
  3. import Check from "../../Core/Check.js";
  4. import clone from "../../Core/clone.js";
  5. import defaultValue from "../../Core/defaultValue.js";
  6. import defined from "../../Core/defined.js";
  7. import ImageBasedLightingPipelineStage from "./ImageBasedLightingPipelineStage.js";
  8. import Matrix4 from "../../Core/Matrix4.js";
  9. import ModelColorPipelineStage from "./ModelColorPipelineStage.js";
  10. import ModelClippingPlanesPipelineStage from "./ModelClippingPlanesPipelineStage.js";
  11. import ModelExperimentalPrimitive from "./ModelExperimentalPrimitive.js";
  12. import ModelExperimentalNode from "./ModelExperimentalNode.js";
  13. import ModelExperimentalSkin from "./ModelExperimentalSkin.js";
  14. import ModelExperimentalUtility from "./ModelExperimentalUtility.js";
  15. import ModelRenderResources from "./ModelRenderResources.js";
  16. import ModelSplitterPipelineStage from "./ModelSplitterPipelineStage.js";
  17. import NodeRenderResources from "./NodeRenderResources.js";
  18. import PrimitiveRenderResources from "./PrimitiveRenderResources.js";
  19. import RenderState from "../../Renderer/RenderState.js";
  20. import ShadowMode from "../ShadowMode.js";
  21. import SplitDirection from "../SplitDirection.js";
  22. /**
  23. * An in memory representation of the scene graph for a {@link ModelExperimental}
  24. *
  25. * @param {Object} options An object containing the following options
  26. * @param {ModelExperimental} options.model The model this scene graph belongs to
  27. * @param {ModelComponents} options.modelComponents The model components describing the model
  28. *
  29. * @alias ModelExperimentalSceneGraph
  30. * @constructor
  31. *
  32. * @private
  33. */
  34. export default function ModelExperimentalSceneGraph(options) {
  35. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  36. const components = options.modelComponents;
  37. //>>includeStart('debug', pragmas.debug);
  38. Check.typeOf.object("options.model", options.model);
  39. Check.typeOf.object("options.modelComponents", components);
  40. //>>includeEnd('debug');
  41. /**
  42. * A reference to the {@link ModelExperimental} that owns this scene graph.
  43. *
  44. * @type {ModelExperimental}
  45. * @readonly
  46. *
  47. * @private
  48. */
  49. this._model = options.model;
  50. /**
  51. * The model components that represent the contents of the 3D model file.
  52. *
  53. * @type {ModelComponents}
  54. * @readonly
  55. *
  56. * @private
  57. */
  58. this._components = components;
  59. /**
  60. * Pipeline stages to apply across the model.
  61. *
  62. * @type {Object[]}
  63. * @readonly
  64. *
  65. * @private
  66. */
  67. this._pipelineStages = [];
  68. /**
  69. * Update stages to apply across the model.
  70. *
  71. * @type {Object[]}
  72. * @readonly
  73. *
  74. * @private
  75. */
  76. this._updateStages = [];
  77. /**
  78. * The runtime nodes that make up the scene graph
  79. *
  80. * @type {ModelExperimentalNode[]}
  81. * @readonly
  82. *
  83. * @private
  84. */
  85. this._runtimeNodes = [];
  86. /**
  87. * The indices of the root nodes in the runtime nodes array.
  88. *
  89. * @type {Number[]}
  90. * @readonly
  91. *
  92. * @private
  93. */
  94. this._rootNodes = [];
  95. /**
  96. * The indices of the skinned nodes in the runtime nodes array. These refer
  97. * to the nodes that will be manipulated by their skin, as opposed to the nodes
  98. * acting as joints for the skin.
  99. *
  100. * @type {Number[]}
  101. * @readonly
  102. *
  103. * @private
  104. */
  105. this._skinnedNodes = [];
  106. /**
  107. * The runtime skins that affect nodes in the scene graph.
  108. *
  109. * @type {ModelExperimentalSkin[]}
  110. * @readonly
  111. *
  112. * @private
  113. */
  114. this._runtimeSkins = [];
  115. /**
  116. * Once computed, the {@link DrawCommand}s that are used to render this
  117. * scene graph are stored here.
  118. *
  119. * @type {DrawCommand[]}
  120. * @readonly
  121. *
  122. * @private
  123. */
  124. this._drawCommands = [];
  125. /**
  126. * Pipeline stages to apply to this model. This
  127. * is an array of classes, each with a static method called
  128. * <code>process()</code>
  129. *
  130. * @type {Object[]}
  131. * @readonly
  132. *
  133. * @private
  134. */
  135. this.modelPipelineStages = [];
  136. this._boundingSphere = undefined;
  137. this._computedModelMatrix = Matrix4.clone(Matrix4.IDENTITY);
  138. this._axisCorrectionMatrix = ModelExperimentalUtility.getAxisCorrectionMatrix(
  139. components.upAxis,
  140. components.forwardAxis,
  141. new Matrix4()
  142. );
  143. initialize(this);
  144. }
  145. Object.defineProperties(ModelExperimentalSceneGraph.prototype, {
  146. /**
  147. * The model components this scene graph represents.
  148. *
  149. * @type {ModelComponents}
  150. * @readonly
  151. *
  152. * @private
  153. */
  154. components: {
  155. get: function () {
  156. return this._components;
  157. },
  158. },
  159. /**
  160. * The axis-corrected model matrix.
  161. *
  162. * @type {Matrix4}
  163. * @readonly
  164. *
  165. * @private
  166. */
  167. computedModelMatrix: {
  168. get: function () {
  169. return this._computedModelMatrix;
  170. },
  171. },
  172. /**
  173. * A matrix to correct from y-up in some model formats (e.g. glTF) to the
  174. * z-up coordinate system Cesium uses.
  175. *
  176. * @type {Matrix4}
  177. * @readonly
  178. *
  179. * @private
  180. */
  181. axisCorrectionMatrix: {
  182. get: function () {
  183. return this._axisCorrectionMatrix;
  184. },
  185. },
  186. /**
  187. * The bounding sphere containing all the primitives in the scene graph.
  188. *
  189. * @type {BoundingSphere}
  190. * @readonly
  191. *
  192. * @private
  193. */
  194. boundingSphere: {
  195. get: function () {
  196. return this._boundingSphere;
  197. },
  198. },
  199. });
  200. function initialize(sceneGraph) {
  201. const components = sceneGraph._components;
  202. const scene = components.scene;
  203. computeModelMatrix(sceneGraph);
  204. const nodes = components.nodes;
  205. const nodesLength = nodes.length;
  206. // Initialize this array to be the same size as the nodes array in the model's file.
  207. // This is so nodes can be stored by their index in the file, for future ease of access.
  208. sceneGraph._runtimeNodes = new Array(nodesLength);
  209. const rootNodes = scene.nodes;
  210. const rootNodesLength = rootNodes.length;
  211. const transformToRoot = Matrix4.IDENTITY;
  212. for (let i = 0; i < rootNodesLength; i++) {
  213. const rootNode = scene.nodes[i];
  214. const rootNodeIndex = traverseSceneGraph(
  215. sceneGraph,
  216. rootNode,
  217. transformToRoot
  218. );
  219. sceneGraph._rootNodes.push(rootNodeIndex);
  220. }
  221. // Handle skins after all runtime nodes are created
  222. const skins = components.skins;
  223. const runtimeSkins = sceneGraph._runtimeSkins;
  224. const skinsLength = skins.length;
  225. for (let i = 0; i < skinsLength; i++) {
  226. const skin = skins[i];
  227. runtimeSkins.push(
  228. new ModelExperimentalSkin({
  229. skin: skin,
  230. sceneGraph: sceneGraph,
  231. })
  232. );
  233. }
  234. const skinnedNodes = sceneGraph._skinnedNodes;
  235. const skinnedNodesLength = skinnedNodes.length;
  236. for (let i = 0; i < skinnedNodesLength; i++) {
  237. const skinnedNodeIndex = skinnedNodes[i];
  238. const skinnedNode = sceneGraph._runtimeNodes[skinnedNodeIndex];
  239. // Use the index of the skin in the model components to find
  240. // the corresponding runtime skin.
  241. const skin = nodes[skinnedNodeIndex].skin;
  242. const skinIndex = skin.index;
  243. skinnedNode._runtimeSkin = runtimeSkins[skinIndex];
  244. skinnedNode.updateJointMatrices();
  245. }
  246. }
  247. function computeModelMatrix(sceneGraph) {
  248. const components = sceneGraph._components;
  249. const model = sceneGraph._model;
  250. sceneGraph._computedModelMatrix = Matrix4.multiplyTransformation(
  251. model.modelMatrix,
  252. components.transform,
  253. sceneGraph._computedModelMatrix
  254. );
  255. sceneGraph._computedModelMatrix = Matrix4.multiplyTransformation(
  256. sceneGraph._computedModelMatrix,
  257. sceneGraph._axisCorrectionMatrix,
  258. sceneGraph._computedModelMatrix
  259. );
  260. sceneGraph._computedModelMatrix = Matrix4.multiplyByUniformScale(
  261. sceneGraph._computedModelMatrix,
  262. model.computedScale,
  263. sceneGraph._computedModelMatrix
  264. );
  265. }
  266. /**
  267. * Recursively traverse through the nodes in the scene graph, using depth-first
  268. * post-order traversal.
  269. *
  270. * @param {ModelSceneGraph} sceneGraph The scene graph
  271. * @param {ModelComponents.Node} node The current node
  272. * @param {Matrix4} transformToRoot The transforms of this node's ancestors.
  273. *
  274. * @returns {Number} The index of this node in the runtimeNodes array.
  275. *
  276. * @private
  277. */
  278. function traverseSceneGraph(sceneGraph, node, transformToRoot) {
  279. // The indices of the children of this node in the runtimeNodes array.
  280. const childrenIndices = [];
  281. const transform = ModelExperimentalUtility.getNodeTransform(node);
  282. // Traverse through scene graph.
  283. const childrenLength = node.children.length;
  284. for (let i = 0; i < childrenLength; i++) {
  285. const childNode = node.children[i];
  286. const childNodeTransformToRoot = Matrix4.multiplyTransformation(
  287. transformToRoot,
  288. transform,
  289. new Matrix4()
  290. );
  291. const childIndex = traverseSceneGraph(
  292. sceneGraph,
  293. childNode,
  294. childNodeTransformToRoot
  295. );
  296. childrenIndices.push(childIndex);
  297. }
  298. // Process node and mesh primitives.
  299. const runtimeNode = new ModelExperimentalNode({
  300. node: node,
  301. transform: transform,
  302. transformToRoot: transformToRoot,
  303. children: childrenIndices,
  304. sceneGraph: sceneGraph,
  305. });
  306. const primitivesLength = node.primitives.length;
  307. for (let i = 0; i < primitivesLength; i++) {
  308. runtimeNode.runtimePrimitives.push(
  309. new ModelExperimentalPrimitive({
  310. primitive: node.primitives[i],
  311. node: node,
  312. model: sceneGraph._model,
  313. })
  314. );
  315. }
  316. const index = node.index;
  317. sceneGraph._runtimeNodes[index] = runtimeNode;
  318. if (defined(node.skin)) {
  319. sceneGraph._skinnedNodes.push(index);
  320. }
  321. return index;
  322. }
  323. /**
  324. * Generates the draw commands for each primitive in the model.
  325. *
  326. * @param {FrameState} frameState The current frame state. This is needed to
  327. * allocate GPU resources as needed.
  328. *
  329. * @private
  330. */
  331. ModelExperimentalSceneGraph.prototype.buildDrawCommands = function (
  332. frameState
  333. ) {
  334. const model = this._model;
  335. const modelRenderResources = new ModelRenderResources(model);
  336. this.configurePipeline();
  337. const modelPipelineStages = this.modelPipelineStages;
  338. let i, j, k;
  339. for (i = 0; i < modelPipelineStages.length; i++) {
  340. const modelPipelineStage = modelPipelineStages[i];
  341. modelPipelineStage.process(modelRenderResources, model, frameState);
  342. }
  343. const boundingSpheres = [];
  344. for (i = 0; i < this._runtimeNodes.length; i++) {
  345. const runtimeNode = this._runtimeNodes[i];
  346. runtimeNode.configurePipeline();
  347. const nodePipelineStages = runtimeNode.pipelineStages;
  348. const nodeRenderResources = new NodeRenderResources(
  349. modelRenderResources,
  350. runtimeNode
  351. );
  352. for (j = 0; j < nodePipelineStages.length; j++) {
  353. const nodePipelineStage = nodePipelineStages[j];
  354. nodePipelineStage.process(
  355. nodeRenderResources,
  356. runtimeNode.node,
  357. frameState
  358. );
  359. }
  360. for (j = 0; j < runtimeNode.runtimePrimitives.length; j++) {
  361. const runtimePrimitive = runtimeNode.runtimePrimitives[j];
  362. runtimePrimitive.configurePipeline();
  363. const primitivePipelineStages = runtimePrimitive.pipelineStages;
  364. const primitiveRenderResources = new PrimitiveRenderResources(
  365. nodeRenderResources,
  366. runtimePrimitive
  367. );
  368. for (k = 0; k < primitivePipelineStages.length; k++) {
  369. const primitivePipelineStage = primitivePipelineStages[k];
  370. primitivePipelineStage.process(
  371. primitiveRenderResources,
  372. runtimePrimitive.primitive,
  373. frameState
  374. );
  375. }
  376. runtimePrimitive.boundingSphere = BoundingSphere.clone(
  377. primitiveRenderResources.boundingSphere
  378. );
  379. boundingSpheres.push(runtimePrimitive.boundingSphere);
  380. const drawCommands = buildDrawCommands(
  381. primitiveRenderResources,
  382. frameState
  383. );
  384. runtimePrimitive.drawCommands = drawCommands;
  385. }
  386. }
  387. this._boundingSphere = BoundingSphere.fromBoundingSpheres(boundingSpheres);
  388. model._boundingSphere = BoundingSphere.transform(
  389. this._boundingSphere,
  390. model.modelMatrix,
  391. model._boundingSphere
  392. );
  393. model._initialRadius = model._boundingSphere.radius;
  394. model._boundingSphere.radius *= model._clampedScale;
  395. };
  396. /**
  397. * Configure the model pipeline stages. If the pipeline needs to be re-run, call
  398. * this method again to ensure the correct sequence of pipeline stages are
  399. * used.
  400. *
  401. * @private
  402. */
  403. ModelExperimentalSceneGraph.prototype.configurePipeline = function () {
  404. const modelPipelineStages = this.modelPipelineStages;
  405. modelPipelineStages.length = 0;
  406. const model = this._model;
  407. if (defined(model.color)) {
  408. modelPipelineStages.push(ModelColorPipelineStage);
  409. }
  410. if (model.imageBasedLighting.enabled) {
  411. modelPipelineStages.push(ImageBasedLightingPipelineStage);
  412. }
  413. if (model.isClippingEnabled()) {
  414. modelPipelineStages.push(ModelClippingPlanesPipelineStage);
  415. }
  416. if (
  417. defined(model.splitDirection) &&
  418. model.splitDirection !== SplitDirection.NONE
  419. ) {
  420. modelPipelineStages.push(ModelSplitterPipelineStage);
  421. }
  422. };
  423. ModelExperimentalSceneGraph.prototype.update = function (
  424. frameState,
  425. updateForAnimations
  426. ) {
  427. let i, j, k;
  428. for (i = 0; i < this._runtimeNodes.length; i++) {
  429. const runtimeNode = this._runtimeNodes[i];
  430. for (j = 0; j < runtimeNode.updateStages.length; j++) {
  431. const nodeUpdateStage = runtimeNode.updateStages[j];
  432. nodeUpdateStage.update(runtimeNode, this, frameState);
  433. }
  434. if (updateForAnimations) {
  435. this.updateJointMatrices();
  436. }
  437. for (j = 0; j < runtimeNode.runtimePrimitives.length; j++) {
  438. const runtimePrimitive = runtimeNode.runtimePrimitives[j];
  439. for (k = 0; k < runtimePrimitive.updateStages.length; k++) {
  440. const stage = runtimePrimitive.updateStages[k];
  441. stage.update(runtimePrimitive);
  442. }
  443. }
  444. }
  445. };
  446. ModelExperimentalSceneGraph.prototype.updateModelMatrix = function () {
  447. computeModelMatrix(this);
  448. // Mark all root nodes as dirty. Any and all children will be
  449. // affected recursively in the update stage.
  450. const rootNodes = this._rootNodes;
  451. for (let i = 0; i < rootNodes.length; i++) {
  452. const node = this._runtimeNodes[rootNodes[i]];
  453. node._transformDirty = true;
  454. }
  455. };
  456. /**
  457. * Updates the joint matrices for the skins and nodes of the model.
  458. *
  459. * @private
  460. */
  461. ModelExperimentalSceneGraph.prototype.updateJointMatrices = function () {
  462. const skinnedNodes = this._skinnedNodes;
  463. const length = skinnedNodes.length;
  464. for (let i = 0; i < length; i++) {
  465. const nodeIndex = skinnedNodes[i];
  466. const runtimeNode = this._runtimeNodes[nodeIndex];
  467. runtimeNode.updateJointMatrices();
  468. }
  469. };
  470. function forEachRuntimePrimitive(sceneGraph, callback) {
  471. for (let i = 0; i < sceneGraph._runtimeNodes.length; i++) {
  472. const runtimeNode = sceneGraph._runtimeNodes[i];
  473. for (let j = 0; j < runtimeNode.runtimePrimitives.length; j++) {
  474. const runtimePrimitive = runtimeNode.runtimePrimitives[j];
  475. callback(runtimePrimitive);
  476. }
  477. }
  478. }
  479. /**
  480. * Traverses through all draw commands and changes the back-face culling setting.
  481. *
  482. * @param {Boolean} backFaceCulling The new value for the back-face culling setting.
  483. *
  484. * @private
  485. */
  486. ModelExperimentalSceneGraph.prototype.updateBackFaceCulling = function (
  487. backFaceCulling
  488. ) {
  489. const model = this._model;
  490. forEachRuntimePrimitive(this, function (runtimePrimitive) {
  491. for (let k = 0; k < runtimePrimitive.drawCommands.length; k++) {
  492. const drawCommand = runtimePrimitive.drawCommands[k];
  493. const renderState = clone(drawCommand.renderState, true);
  494. const doubleSided = runtimePrimitive.primitive.material.doubleSided;
  495. const translucent = defined(model.color) && model.color.alpha < 1.0;
  496. renderState.cull.enabled =
  497. backFaceCulling && !doubleSided && !translucent;
  498. drawCommand.renderState = RenderState.fromCache(renderState);
  499. }
  500. });
  501. };
  502. /**
  503. * Traverses through all draw commands and changes the shadow settings.
  504. *
  505. * @param {ShadowMode} shadowMode The new shadow settings.
  506. *
  507. * @private
  508. */
  509. ModelExperimentalSceneGraph.prototype.updateShadows = function (shadowMode) {
  510. const model = this._model;
  511. const castShadows = ShadowMode.castShadows(model.shadows);
  512. const receiveShadows = ShadowMode.receiveShadows(model.shadows);
  513. forEachRuntimePrimitive(this, function (runtimePrimitive) {
  514. for (let k = 0; k < runtimePrimitive.drawCommands.length; k++) {
  515. const drawCommand = runtimePrimitive.drawCommands[k];
  516. drawCommand.castShadows = castShadows;
  517. drawCommand.receiveShadows = receiveShadows;
  518. }
  519. });
  520. };
  521. /**
  522. * Returns an array of draw commands, obtained by traversing through the scene graph and collecting
  523. * the draw commands associated with each primitive.
  524. *
  525. * @private
  526. */
  527. ModelExperimentalSceneGraph.prototype.getDrawCommands = function () {
  528. const drawCommands = [];
  529. forEachRuntimePrimitive(this, function (runtimePrimitive) {
  530. drawCommands.push.apply(drawCommands, runtimePrimitive.drawCommands);
  531. });
  532. return drawCommands;
  533. };