ModelRuntimeNode.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. import Cartesian3 from "../../Core/Cartesian3.js";
  2. import Check from "../../Core/Check.js";
  3. import defaultValue from "../../Core/defaultValue.js";
  4. import defined from "../../Core/defined.js";
  5. import DeveloperError from "../../Core/DeveloperError.js";
  6. import Matrix4 from "../../Core/Matrix4.js";
  7. import TranslationRotationScale from "../../Core/TranslationRotationScale.js";
  8. import Quaternion from "../../Core/Quaternion.js";
  9. import InstancingPipelineStage from "./InstancingPipelineStage.js";
  10. import ModelMatrixUpdateStage from "./ModelMatrixUpdateStage.js";
  11. import NodeStatisticsPipelineStage from "./NodeStatisticsPipelineStage.js";
  12. /**
  13. * An in-memory representation of a node as part of the {@link ModelSceneGraph}.
  14. *
  15. * @param {object} options An object containing the following options:
  16. * @param {ModelComponents.Node} options.node The corresponding node components from the 3D model.
  17. * @param {Matrix4} options.transform The transform of this node, excluding transforms from the node's ancestors or children.
  18. * @param {Matrix4} options.transformToRoot The product of the transforms of all the node's ancestors, excluding the node's own transform.
  19. * @param {ModelSceneGraph} options.sceneGraph The scene graph this node belongs to.
  20. * @param {number[]} options.children The indices of the children of this node in the runtime nodes array of the scene graph.
  21. *
  22. * @alias ModelRuntimeNode
  23. * @constructor
  24. *
  25. * @private
  26. */
  27. function ModelRuntimeNode(options) {
  28. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  29. const node = options.node;
  30. const transform = options.transform;
  31. const transformToRoot = options.transformToRoot;
  32. const sceneGraph = options.sceneGraph;
  33. const children = options.children;
  34. //>>includeStart('debug', pragmas.debug);
  35. Check.typeOf.object("options.node", node);
  36. Check.typeOf.object("options.transform", transform);
  37. Check.typeOf.object("options.transformToRoot", transformToRoot);
  38. Check.typeOf.object("options.sceneGraph", sceneGraph);
  39. Check.typeOf.object("options.children", children);
  40. //>>includeEnd('debug');
  41. this._node = node;
  42. this._name = node.name;
  43. this._id = node.index;
  44. this._sceneGraph = sceneGraph;
  45. this._children = children;
  46. this._originalTransform = Matrix4.clone(transform, this._originalTransform);
  47. this._transform = Matrix4.clone(transform, this._transform);
  48. this._transformToRoot = Matrix4.clone(transformToRoot, this._transformToRoot);
  49. this._computedTransform = new Matrix4(); // Computed in initialize()
  50. this._transformDirty = false;
  51. // Used for animation
  52. this._transformParameters = undefined;
  53. this._morphWeights = [];
  54. // Will be set by the scene graph after the skins have been created
  55. this._runtimeSkin = undefined;
  56. this._computedJointMatrices = [];
  57. /**
  58. * Whether or not to show this node and its children. This can be toggled
  59. * by the user through {@link ModelNode}.
  60. *
  61. * @type {boolean}
  62. *
  63. * @default true
  64. *
  65. * @private
  66. */
  67. this.show = true;
  68. /**
  69. * Whether or not this node is animated by the user. This is set by the
  70. * corresponding {@link ModelNode} when the user supplies their
  71. * own transform. If this is true, the node will ignore animations in the
  72. * model's asset.
  73. *
  74. * @type {boolean}
  75. *
  76. * @private
  77. */
  78. this.userAnimated = false;
  79. /**
  80. * Pipeline stages to apply across all the mesh primitives of this node.
  81. * This is an array of classes, each with a static method called
  82. * <code>process()</code>.
  83. *
  84. * @type {Object[]}
  85. * @readonly
  86. *
  87. * @private
  88. */
  89. this.pipelineStages = [];
  90. /**
  91. * The mesh primitives that belong to this node.
  92. *
  93. * @type {ModelRuntimePrimitive[]}
  94. * @readonly
  95. *
  96. * @private
  97. */
  98. this.runtimePrimitives = [];
  99. /**
  100. * Update stages to apply to this node.
  101. *
  102. * @type {Object[]}
  103. * @readonly
  104. *
  105. * @private
  106. */
  107. this.updateStages = [];
  108. /**
  109. * The component-wise minimum value of the translations of the instances.
  110. * This value is set by InstancingPipelineStage.
  111. *
  112. * @type {Cartesian3}
  113. *
  114. * @private
  115. */
  116. this.instancingTranslationMin = undefined;
  117. /**
  118. * The component-wise maximum value of the translations of the instances.
  119. * This value is set by InstancingPipelineStage.
  120. *
  121. * @type {Cartesian3}
  122. *
  123. * @private
  124. */
  125. this.instancingTranslationMax = undefined;
  126. /**
  127. * A buffer containing the instanced transforms. The memory is managed
  128. * by Model; this is just a reference.
  129. *
  130. * @type {Buffer}
  131. *
  132. * @private
  133. */
  134. this.instancingTransformsBuffer = undefined;
  135. /**
  136. * A buffer containing the instanced transforms projected to 2D world
  137. * coordinates. Used for rendering in 2D / CV mode. The memory is managed
  138. * by Model; this is just a reference.
  139. *
  140. * @type {Buffer}
  141. *
  142. * @private
  143. */
  144. this.instancingTransformsBuffer2D = undefined;
  145. /**
  146. * A buffer containing the instanced translation values for the node if
  147. * it is instanced. Used for rendering in 2D / CV mode. The memory is
  148. * managed by Model; this is just a reference.
  149. *
  150. * @type {Buffer}
  151. *
  152. * @private
  153. */
  154. this.instancingTranslationBuffer2D = undefined;
  155. /**
  156. * If the model is instanced and projected to 2D, the reference point is the
  157. * average of the instancing translation max and min. The 2D translations are
  158. * defined relative to this point to avoid precision issues on the GPU.
  159. * <p>
  160. * This value is set by InstancingPipelineStage.
  161. * </p>
  162. *
  163. * @type {Cartesian3}
  164. *
  165. * @private
  166. */
  167. this.instancingReferencePoint2D = undefined;
  168. initialize(this);
  169. }
  170. Object.defineProperties(ModelRuntimeNode.prototype, {
  171. /**
  172. * The internal node this runtime node represents.
  173. *
  174. * @memberof ModelRuntimeNode.prototype
  175. * @type {ModelComponents.Node}
  176. * @readonly
  177. *
  178. * @private
  179. */
  180. node: {
  181. get: function () {
  182. return this._node;
  183. },
  184. },
  185. /**
  186. * The scene graph this node belongs to.
  187. *
  188. * @memberof ModelRuntimeNode.prototype
  189. * @type {ModelSceneGraph}
  190. * @readonly
  191. *
  192. * @private
  193. */
  194. sceneGraph: {
  195. get: function () {
  196. return this._sceneGraph;
  197. },
  198. },
  199. /**
  200. * The indices of the children of this node in the scene graph.
  201. *
  202. * @memberof ModelRuntimeNode.prototype
  203. * @type {number[]}
  204. * @readonly
  205. *
  206. * @private
  207. */
  208. children: {
  209. get: function () {
  210. return this._children;
  211. },
  212. },
  213. /**
  214. * The node's local space transform. This can be changed externally via
  215. * the corresponding {@link ModelNode}, such that animation can be
  216. * driven by another source, not just an animation in the model's asset.
  217. *
  218. * @memberof ModelRuntimeNode.prototype
  219. * @type {Matrix4}
  220. *
  221. * @private
  222. */
  223. transform: {
  224. get: function () {
  225. return this._transform;
  226. },
  227. set: function (value) {
  228. this._transformDirty = true;
  229. this._transform = Matrix4.clone(value, this._transform);
  230. },
  231. },
  232. /**
  233. * The transforms of all the node's ancestors, not including this node's
  234. * transform.
  235. *
  236. * @see ModelRuntimeNode#computedTransform
  237. *
  238. * @memberof ModelRuntimeNode.prototype
  239. * @type {Matrix4}
  240. * @readonly
  241. *
  242. * @private
  243. */
  244. transformToRoot: {
  245. get: function () {
  246. return this._transformToRoot;
  247. },
  248. },
  249. /**
  250. * A transform from the node's local space to the model's scene graph space.
  251. * This is the product of transformToRoot * transform.
  252. *
  253. * @memberof ModelRuntimeNode.prototype
  254. * @type {Matrix4}
  255. * @readonly
  256. *
  257. * @private
  258. */
  259. computedTransform: {
  260. get: function () {
  261. return this._computedTransform;
  262. },
  263. },
  264. /**
  265. * The node's original transform, as specified in the model.
  266. * Does not include transformations from the node's ancestors.
  267. *
  268. * @memberof ModelRuntimeNode.prototype
  269. * @type {Matrix4}
  270. * @readonly
  271. *
  272. * @private
  273. */
  274. originalTransform: {
  275. get: function () {
  276. return this._originalTransform;
  277. },
  278. },
  279. /**
  280. * The node's local space translation. This is used internally to allow
  281. * animations in the model's asset to affect the node's properties.
  282. *
  283. * If the node's transformation was originally described using a matrix
  284. * in the model, then this will return undefined.
  285. *
  286. * @memberof ModelRuntimeNode.prototype
  287. * @type {Cartesian3}
  288. *
  289. * @exception {DeveloperError} The translation of a node cannot be set if it was defined using a matrix in the model's asset.
  290. *
  291. * @private
  292. */
  293. translation: {
  294. get: function () {
  295. return defined(this._transformParameters)
  296. ? this._transformParameters.translation
  297. : undefined;
  298. },
  299. set: function (value) {
  300. const transformParameters = this._transformParameters;
  301. //>>includeStart('debug', pragmas.debug);
  302. if (!defined(transformParameters)) {
  303. throw new DeveloperError(
  304. "The translation of a node cannot be set if it was defined using a matrix in the model."
  305. );
  306. }
  307. //>>includeEnd('debug');
  308. const currentTranslation = transformParameters.translation;
  309. if (Cartesian3.equals(currentTranslation, value)) {
  310. return;
  311. }
  312. transformParameters.translation = Cartesian3.clone(
  313. value,
  314. transformParameters.translation
  315. );
  316. updateTransformFromParameters(this, transformParameters);
  317. },
  318. },
  319. /**
  320. * The node's local space rotation. This is used internally to allow
  321. * animations in the model's asset to affect the node's properties.
  322. *
  323. * If the node's transformation was originally described using a matrix
  324. * in the model, then this will return undefined.
  325. *
  326. * @memberof ModelRuntimeNode.prototype
  327. * @type {Quaternion}
  328. *
  329. * @exception {DeveloperError} The rotation of a node cannot be set if it was defined using a matrix in the model's asset.
  330. *
  331. * @private
  332. */
  333. rotation: {
  334. get: function () {
  335. return defined(this._transformParameters)
  336. ? this._transformParameters.rotation
  337. : undefined;
  338. },
  339. set: function (value) {
  340. const transformParameters = this._transformParameters;
  341. //>>includeStart('debug', pragmas.debug);
  342. if (!defined(transformParameters)) {
  343. throw new DeveloperError(
  344. "The rotation of a node cannot be set if it was defined using a matrix in the model."
  345. );
  346. }
  347. //>>includeEnd('debug');
  348. const currentRotation = transformParameters.rotation;
  349. if (Quaternion.equals(currentRotation, value)) {
  350. return;
  351. }
  352. transformParameters.rotation = Quaternion.clone(
  353. value,
  354. transformParameters.rotation
  355. );
  356. updateTransformFromParameters(this, transformParameters);
  357. },
  358. },
  359. /**
  360. * The node's local space scale. This is used internally to allow
  361. * animations in the model's asset to affect the node's properties.
  362. *
  363. * If the node's transformation was originally described using a matrix
  364. * in the model, then this will return undefined.
  365. *
  366. * @memberof ModelRuntimeNode.prototype
  367. * @type {Cartesian3}
  368. *
  369. * @exception {DeveloperError} The scale of a node cannot be set if it was defined using a matrix in the model's asset.
  370. * @private
  371. */
  372. scale: {
  373. get: function () {
  374. return defined(this._transformParameters)
  375. ? this._transformParameters.scale
  376. : undefined;
  377. },
  378. set: function (value) {
  379. const transformParameters = this._transformParameters;
  380. //>>includeStart('debug', pragmas.debug);
  381. if (!defined(transformParameters)) {
  382. throw new DeveloperError(
  383. "The scale of a node cannot be set if it was defined using a matrix in the model."
  384. );
  385. }
  386. //>>includeEnd('debug');
  387. const currentScale = transformParameters.scale;
  388. if (Cartesian3.equals(currentScale, value)) {
  389. return;
  390. }
  391. transformParameters.scale = Cartesian3.clone(
  392. value,
  393. transformParameters.scale
  394. );
  395. updateTransformFromParameters(this, transformParameters);
  396. },
  397. },
  398. /**
  399. * The node's morph weights. This is used internally to allow animations
  400. * in the model's asset to affect the node's properties.
  401. *
  402. * @memberof ModelRuntimeNode.prototype
  403. * @type {number[]}
  404. *
  405. * @private
  406. */
  407. morphWeights: {
  408. get: function () {
  409. return this._morphWeights;
  410. },
  411. set: function (value) {
  412. const valueLength = value.length;
  413. //>>includeStart('debug', pragmas.debug);
  414. if (this._morphWeights.length !== valueLength) {
  415. throw new DeveloperError(
  416. "value must have the same length as the original weights array."
  417. );
  418. }
  419. //>>includeEnd('debug');
  420. for (let i = 0; i < valueLength; i++) {
  421. this._morphWeights[i] = value[i];
  422. }
  423. },
  424. },
  425. /**
  426. * The skin applied to this node, if it exists.
  427. *
  428. * @memberof ModelRuntimeNode.prototype
  429. * @type {ModelSkin}
  430. * @readonly
  431. *
  432. * @private
  433. */
  434. runtimeSkin: {
  435. get: function () {
  436. return this._runtimeSkin;
  437. },
  438. },
  439. /**
  440. * The computed joint matrices of this node, derived from its skin.
  441. *
  442. * @memberof ModelRuntimeNode.prototype
  443. * @type {Matrix4[]}
  444. * @readonly
  445. *
  446. * @private
  447. */
  448. computedJointMatrices: {
  449. get: function () {
  450. return this._computedJointMatrices;
  451. },
  452. },
  453. });
  454. function initialize(runtimeNode) {
  455. const transform = runtimeNode.transform;
  456. const transformToRoot = runtimeNode.transformToRoot;
  457. const computedTransform = runtimeNode._computedTransform;
  458. runtimeNode._computedTransform = Matrix4.multiply(
  459. transformToRoot,
  460. transform,
  461. computedTransform
  462. );
  463. const node = runtimeNode.node;
  464. if (!defined(node.matrix)) {
  465. runtimeNode._transformParameters = new TranslationRotationScale(
  466. node.translation,
  467. node.rotation,
  468. node.scale
  469. );
  470. }
  471. if (defined(node.morphWeights)) {
  472. runtimeNode._morphWeights = node.morphWeights.slice();
  473. }
  474. // If this node is affected by an articulation from the AGI_articulations
  475. // extension, add this node to its list of affected nodes.
  476. const articulationName = node.articulationName;
  477. if (defined(articulationName)) {
  478. const sceneGraph = runtimeNode.sceneGraph;
  479. const runtimeArticulations = sceneGraph._runtimeArticulations;
  480. const runtimeArticulation = runtimeArticulations[articulationName];
  481. if (defined(runtimeArticulation)) {
  482. runtimeArticulation.runtimeNodes.push(runtimeNode);
  483. }
  484. }
  485. }
  486. function updateTransformFromParameters(runtimeNode, transformParameters) {
  487. runtimeNode._transformDirty = true;
  488. runtimeNode._transform = Matrix4.fromTranslationRotationScale(
  489. transformParameters,
  490. runtimeNode._transform
  491. );
  492. }
  493. /**
  494. * Returns the child with the given index.
  495. *
  496. * @param {number} index The index of the child.
  497. *
  498. * @returns {ModelRuntimeNode}
  499. *
  500. * @example
  501. * // Iterate through all children of a runtime node.
  502. * for (let i = 0; i < runtimeNode.children.length; i++)
  503. * {
  504. * const childNode = runtimeNode.getChild(i);
  505. * }
  506. *
  507. * @private
  508. */
  509. ModelRuntimeNode.prototype.getChild = function (index) {
  510. //>>includeStart('debug', pragmas.debug);
  511. Check.typeOf.number("index", index);
  512. if (index < 0 || index >= this.children.length) {
  513. throw new DeveloperError(
  514. "index must be greater than or equal to 0 and less than the number of children."
  515. );
  516. }
  517. //>>includeEnd('debug');
  518. return this.sceneGraph._runtimeNodes[this.children[index]];
  519. };
  520. /**
  521. * Configure the node pipeline stages. If the pipeline needs to be re-run, call
  522. * this method again to ensure the correct sequence of pipeline stages are
  523. * used.
  524. *
  525. * @private
  526. */
  527. ModelRuntimeNode.prototype.configurePipeline = function () {
  528. const node = this.node;
  529. const pipelineStages = this.pipelineStages;
  530. pipelineStages.length = 0;
  531. const updateStages = this.updateStages;
  532. updateStages.length = 0;
  533. if (defined(node.instances)) {
  534. pipelineStages.push(InstancingPipelineStage);
  535. }
  536. pipelineStages.push(NodeStatisticsPipelineStage);
  537. updateStages.push(ModelMatrixUpdateStage);
  538. };
  539. /**
  540. * Updates the computed transform used for rendering and instancing.
  541. *
  542. * @private
  543. */
  544. ModelRuntimeNode.prototype.updateComputedTransform = function () {
  545. this._computedTransform = Matrix4.multiply(
  546. this._transformToRoot,
  547. this._transform,
  548. this._computedTransform
  549. );
  550. };
  551. /**
  552. * Updates the joint matrices for this node, where each matrix is computed as
  553. * computedJointMatrix = nodeWorldTransform^(-1) * skinJointMatrix.
  554. *
  555. * @private
  556. */
  557. ModelRuntimeNode.prototype.updateJointMatrices = function () {
  558. const runtimeSkin = this._runtimeSkin;
  559. if (!defined(runtimeSkin)) {
  560. return;
  561. }
  562. runtimeSkin.updateJointMatrices();
  563. const computedJointMatrices = this._computedJointMatrices;
  564. const skinJointMatrices = runtimeSkin.jointMatrices;
  565. const length = skinJointMatrices.length;
  566. for (let i = 0; i < length; i++) {
  567. if (!defined(computedJointMatrices[i])) {
  568. computedJointMatrices[i] = new Matrix4();
  569. }
  570. const nodeWorldTransform = Matrix4.multiplyTransformation(
  571. this.transformToRoot,
  572. this.transform,
  573. computedJointMatrices[i]
  574. );
  575. const inverseNodeWorldTransform = Matrix4.inverseTransformation(
  576. nodeWorldTransform,
  577. computedJointMatrices[i]
  578. );
  579. computedJointMatrices[i] = Matrix4.multiplyTransformation(
  580. inverseNodeWorldTransform,
  581. skinJointMatrices[i],
  582. computedJointMatrices[i]
  583. );
  584. }
  585. };
  586. export default ModelRuntimeNode;