123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- import Cartesian3 from "../../Core/Cartesian3.js";
- import Check from "../../Core/Check.js";
- import defaultValue from "../../Core/defaultValue.js";
- import defined from "../../Core/defined.js";
- import DeveloperError from "../../Core/DeveloperError.js";
- import Matrix4 from "../../Core/Matrix4.js";
- import InstancingPipelineStage from "./InstancingPipelineStage.js";
- import ModelMatrixUpdateStage from "./ModelMatrixUpdateStage.js";
- import TranslationRotationScale from "../../Core/TranslationRotationScale.js";
- import Quaternion from "../../Core/Quaternion.js";
- /**
- * An in-memory representation of a node as part of the {@link ModelExperimentalSceneGraph}.
- *
- *
- * @param {Object} options An object containing the following options:
- * @param {ModelComponents.Node} options.node The corresponding node components from the 3D model
- * @param {Matrix4} options.transform The transform of this node, excluding transforms from the node's ancestors or children.
- * @param {Matrix4} options.transformToRoot The product of the transforms of all the node's ancestors, excluding the node's own transform.
- * @param {ModelExperimentalSceneGraph} options.sceneGraph The scene graph this node belongs to.
- * @param {Number[]} options.children The indices of the children of this node in the runtime nodes array of the scene graph.
- *
- * @alias ModelExperimentalNode
- * @constructor
- *
- * @private
- */
- export default function ModelExperimentalNode(options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.object("options.node", options.node);
- Check.typeOf.object("options.transform", options.transform);
- Check.typeOf.object("options.transformToRoot", options.transformToRoot);
- Check.typeOf.object("options.sceneGraph", options.sceneGraph);
- Check.typeOf.object("options.children", options.children);
- //>>includeEnd('debug');
- const sceneGraph = options.sceneGraph;
- const transform = options.transform;
- const transformToRoot = options.transformToRoot;
- const node = options.node;
- this._sceneGraph = sceneGraph;
- this._children = options.children;
- this._node = node;
- this._name = node.name; // Helps with debugging
- this._originalTransform = Matrix4.clone(transform, this._originalTransform);
- this._transform = Matrix4.clone(transform, this._transform);
- this._transformToRoot = Matrix4.clone(transformToRoot, this._transformToRoot);
- this._originalTransform = Matrix4.clone(transform, this._originalTransform);
- const computedTransform = Matrix4.multiply(
- transformToRoot,
- transform,
- new Matrix4()
- );
- this._computedTransform = computedTransform;
- this._transformDirty = false;
- // Used for animation
- this._transformParameters = defined(node.matrix)
- ? undefined
- : new TranslationRotationScale(node.translation, node.rotation, node.scale);
- this._morphWeights = defined(node.morphWeights)
- ? node.morphWeights.slice()
- : [];
- // Will be set by the scene graph after the skins have been created
- this._runtimeSkin = undefined;
- this._computedJointMatrices = [];
- /**
- * Pipeline stages to apply across all the mesh primitives of this node. This
- * is an array of classes, each with a static method called
- * <code>process()</code>
- *
- * @type {Object[]}
- * @readonly
- *
- * @private
- */
- this.pipelineStages = [];
- /**
- * The mesh primitives that belong to this node
- *
- * @type {ModelExperimentalPrimitive[]}
- * @readonly
- *
- * @private
- */
- this.runtimePrimitives = [];
- /**
- * Update stages to apply to this primitive.
- *
- * @private
- */
- this.updateStages = [];
- this.configurePipeline();
- }
- Object.defineProperties(ModelExperimentalNode.prototype, {
- /**
- * The internal node this runtime node represents.
- *
- * @type {ModelComponents.Node}
- * @readonly
- *
- * @private
- */
- node: {
- get: function () {
- return this._node;
- },
- },
- /**
- * The scene graph this node belongs to.
- *
- * @type {ModelExperimentalSceneGraph}
- * @readonly
- *
- * @private
- */
- sceneGraph: {
- get: function () {
- return this._sceneGraph;
- },
- },
- /**
- * The indices of the children of this node in the scene graph.
- *
- * @type {Number[]}
- * @readonly
- */
- children: {
- get: function () {
- return this._children;
- },
- },
- /**
- * The node's local space transform. This can be changed externally so animation
- * can be driven by another source, not just an animation in the model's asset.
- * <p>
- * For changes to take effect, this property must be assigned to;
- * setting individual elements of the matrix will not work.
- * </p>
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {Matrix4}
- */
- transform: {
- get: function () {
- return this._transform;
- },
- set: function (value) {
- if (Matrix4.equals(this._transform, value)) {
- return;
- }
- this._transformDirty = true;
- this._transform = Matrix4.clone(value, this._transform);
- },
- },
- /**
- * The transforms of all the node's ancestors, not including this node's
- * transform.
- *
- * @see ModelExperimentalNode#computedTransform
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {Matrix4}
- * @readonly
- */
- transformToRoot: {
- get: function () {
- return this._transformToRoot;
- },
- },
- /**
- * A transform from the node's local space to the model's scene graph space.
- * This is the product of transformToRoot * transform.
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {Matrix4}
- * @readonly
- */
- computedTransform: {
- get: function () {
- return this._computedTransform;
- },
- },
- /**
- * The node's original transform, as specified in the model. Does not include transformations from the node's ancestors.
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {Matrix4}
- * @readonly
- */
- originalTransform: {
- get: function () {
- return this._originalTransform;
- },
- },
- /**
- * The node's local space translation. This is used internally to allow
- * animations in the model's asset to affect the node's properties.
- *
- * If the node's transformation was originally described using a matrix
- * in the model, then this will return undefined.
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {Cartesian3}
- *
- * @exception {DeveloperError} The translation of a node cannot be set if it was defined using a matrix in the model.
- *
- * @private
- */
- translation: {
- get: function () {
- return defined(this._transformParameters)
- ? this._transformParameters.translation
- : undefined;
- },
- set: function (value) {
- const transformParameters = this._transformParameters;
- //>>includeStart('debug', pragmas.debug);
- if (!defined(transformParameters)) {
- throw new DeveloperError(
- "The translation of a node cannot be set if it was defined using a matrix in the model."
- );
- }
- //>>includeEnd('debug');
- const currentTranslation = transformParameters.translation;
- if (Cartesian3.equals(currentTranslation, value)) {
- return;
- }
- transformParameters.translation = Cartesian3.clone(
- value,
- transformParameters.translation
- );
- updateTransformFromParameters(this, transformParameters);
- },
- },
- /**
- * The node's local space rotation. This is used internally to allow
- * animations in the model's asset to affect the node's properties.
- *
- * If the node's transformation was originally described using a matrix
- * in the model, then this will return undefined.
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {Quaternion}
- *
- * @exception {DeveloperError} The rotation of a node cannot be set if it was defined using a matrix in the model.
- *
- * @private
- */
- rotation: {
- get: function () {
- return defined(this._transformParameters)
- ? this._transformParameters.rotation
- : undefined;
- },
- set: function (value) {
- const transformParameters = this._transformParameters;
- //>>includeStart('debug', pragmas.debug);
- if (!defined(transformParameters)) {
- throw new DeveloperError(
- "The rotation of a node cannot be set if it was defined using a matrix in the model."
- );
- }
- //>>includeEnd('debug');
- const currentRotation = transformParameters.rotation;
- if (Quaternion.equals(currentRotation, value)) {
- return;
- }
- transformParameters.rotation = Quaternion.clone(
- value,
- transformParameters.rotation
- );
- updateTransformFromParameters(this, transformParameters);
- },
- },
- /**
- * The node's local space scale. This is used internally to allow
- * animations in the model's asset to affect the node's properties.
- *
- * If the node's transformation was originally described using a matrix
- * in the model, then this will return undefined.
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {Cartesian3}
- *
- * @exception {DeveloperError} The scale of a node cannot be set if it was defined using a matrix in the model.
- * @private
- */
- scale: {
- get: function () {
- return defined(this._transformParameters)
- ? this._transformParameters.scale
- : undefined;
- },
- set: function (value) {
- const transformParameters = this._transformParameters;
- //>>includeStart('debug', pragmas.debug);
- if (!defined(transformParameters)) {
- throw new DeveloperError(
- "The scale of a node cannot be set if it was defined using a matrix in the model."
- );
- }
- //>>includeEnd('debug');
- const currentScale = transformParameters.scale;
- if (Cartesian3.equals(currentScale, value)) {
- return;
- }
- transformParameters.scale = Cartesian3.clone(
- value,
- transformParameters.scale
- );
- updateTransformFromParameters(this, transformParameters);
- },
- },
- /**
- * The node's morph weights. This is used internally to allow animations
- * in the model's asset to affect the node's properties.
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {Number[]}
- *
- * @private
- */
- morphWeights: {
- get: function () {
- return this._morphWeights;
- },
- set: function (value) {
- const valueLength = value.length;
- //>>includeStart('debug', pragmas.debug);
- if (this._morphWeights.length !== valueLength) {
- throw new DeveloperError(
- "value must have the same length as the original weights array."
- );
- }
- //>>includeEnd('debug');
- for (let i = 0; i < valueLength; i++) {
- this._morphWeights[i] = value[i];
- }
- },
- },
- /**
- * The skin applied to this node, if it exists.
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {ModelExperimentalSkin}
- * @readonly
- */
- runtimeSkin: {
- get: function () {
- return this._runtimeSkin;
- },
- },
- /**
- * The computed joint matrices of this node, derived from its skin.
- *
- * @memberof ModelExperimentalNode.prototype
- * @type {Matrix4[]}
- * @readonly
- */
- computedJointMatrices: {
- get: function () {
- return this._computedJointMatrices;
- },
- },
- });
- function updateTransformFromParameters(runtimeNode, transformParameters) {
- runtimeNode._transformDirty = true;
- runtimeNode._transform = Matrix4.fromTranslationRotationScale(
- transformParameters,
- runtimeNode._transform
- );
- }
- /**
- * Returns the child with the given index.
- *
- * @param {Number} index The index of the child.
- *
- * @returns {ModelExperimentalNode}
- *
- * @example
- * // Iterate through all children of a runtime node.
- * for (let i = 0; i < runtimeNode.children.length; i++)
- * {
- * const childNode = runtimeNode.getChild(i);
- * }
- */
- ModelExperimentalNode.prototype.getChild = function (index) {
- //>>includeStart('debug', pragmas.debug);
- Check.typeOf.number("index", index);
- if (index < 0 || index >= this.children.length) {
- throw new DeveloperError(
- "index must be greater than or equal to 0 and less than the number of children."
- );
- }
- //>>includeEnd('debug');
- return this.sceneGraph.runtimeNodes[this.children[index]];
- };
- /**
- * Configure the node pipeline stages. If the pipeline needs to be re-run, call
- * this method again to ensure the correct sequence of pipeline stages are
- * used.
- *
- * @private
- */
- ModelExperimentalNode.prototype.configurePipeline = function () {
- const node = this.node;
- const pipelineStages = this.pipelineStages;
- pipelineStages.length = 0;
- const updateStages = this.updateStages;
- updateStages.length = 0;
- if (defined(node.instances)) {
- pipelineStages.push(InstancingPipelineStage);
- }
- updateStages.push(ModelMatrixUpdateStage);
- };
- /**
- * Updates the computed transform used for rendering and instancing.
- *
- * @private
- */
- ModelExperimentalNode.prototype.updateComputedTransform = function () {
- this._computedTransform = Matrix4.multiply(
- this._transformToRoot,
- this._transform,
- this._computedTransform
- );
- };
- /**
- * Updates the joint matrices for this node, where each matrix is computed as
- * computedJointMatrix = nodeWorldTransform^(-1) * skinJointMatrix.
- *
- * @private
- */
- ModelExperimentalNode.prototype.updateJointMatrices = function () {
- const runtimeSkin = this._runtimeSkin;
- if (!defined(runtimeSkin)) {
- return;
- }
- runtimeSkin.updateJointMatrices();
- const computedJointMatrices = this._computedJointMatrices;
- const skinJointMatrices = runtimeSkin.jointMatrices;
- const length = skinJointMatrices.length;
- for (let i = 0; i < length; i++) {
- if (!defined(computedJointMatrices[i])) {
- computedJointMatrices[i] = new Matrix4();
- }
- const nodeWorldTransform = Matrix4.multiplyTransformation(
- this.transformToRoot,
- this.transform,
- computedJointMatrices[i]
- );
- const inverseNodeWorldTransform = Matrix4.inverseTransformation(
- nodeWorldTransform,
- computedJointMatrices[i]
- );
- computedJointMatrices[i] = Matrix4.multiplyTransformation(
- inverseNodeWorldTransform,
- skinJointMatrices[i],
- computedJointMatrices[i]
- );
- }
- };
|