import Check from "../../Core/Check.js"; import defaultValue from "../../Core/defaultValue.js"; import defined from "../../Core/defined.js"; import Matrix4 from "../../Core/Matrix4.js"; import ModelArticulationStage from "./ModelArticulationStage.js"; /** * An in-memory representation of an articulation that affects nodes in the * {@link ModelSceneGraph}. This is defined in a model by the * AGI_articulations extension. * * @param {object} options An object containing the following options: * @param {ModelComponents.Articulation} options.articulation The articulation components from the 3D model. * @param {ModelSceneGraph} options.sceneGraph The scene graph this articulation belongs to. * * @alias ModelArticulation * @constructor * * @private */ function ModelArticulation(options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); const articulation = options.articulation; const sceneGraph = options.sceneGraph; //>>includeStart('debug', pragmas.debug); Check.typeOf.object("options.articulation", articulation); Check.typeOf.object("options.sceneGraph", sceneGraph); //>>includeEnd('debug'); this._articulation = articulation; this._sceneGraph = sceneGraph; this._name = articulation.name; this._runtimeStages = []; this._runtimeStagesByName = {}; // Will be populated as the runtime nodes are created this._runtimeNodes = []; // Set to true so that the first call to // ModelSceneGraph.applyArticulations will work. this._dirty = true; initialize(this); } Object.defineProperties(ModelArticulation.prototype, { /** * The internal articulation that this runtime articulation represents. * * @memberof ModelArticulation.prototype * @type {ModelComponents.Articulation} * @readonly * * @private */ articulation: { get: function () { return this._articulation; }, }, /** * The scene graph that this articulation belongs to. * * @memberof ModelArticulation.prototype * @type {ModelSceneGraph} * @readonly * * @private */ sceneGraph: { get: function () { return this._sceneGraph; }, }, /** * The name of this articulation. * * @memberof ModelArticulation.prototype * @type {string} * @readonly * * @private */ name: { get: function () { return this._name; }, }, /** * The runtime stages that belong to this articulation. * * @memberof ModelArticulation.prototype * @type {ModelArticulationStage[]} * @readonly * * @private */ runtimeStages: { get: function () { return this._runtimeStages; }, }, /** * The runtime nodes that are affected by this articulation. * * @memberof ModelArticulation.prototype * @type {ModelRuntimeNode[]} * @readonly * * @private */ runtimeNodes: { get: function () { return this._runtimeNodes; }, }, }); function initialize(runtimeArticulation) { const articulation = runtimeArticulation.articulation; const stages = articulation.stages; const length = stages.length; const runtimeStages = runtimeArticulation._runtimeStages; const runtimeStagesByName = runtimeArticulation._runtimeStagesByName; for (let i = 0; i < length; i++) { const stage = stages[i]; const runtimeStage = new ModelArticulationStage({ stage: stage, runtimeArticulation: runtimeArticulation, }); // Store the stages in an array to preserve the order in which // they appeared in the 3D model. runtimeStages.push(runtimeStage); // Store the stages in a dictionary for retrieval by name. const stageName = stage.name; runtimeStagesByName[stageName] = runtimeStage; } } /** * Sets the current value of an articulation stage. * * @param {string} stageName The name of the articulation stage. * @param {number} value The numeric value of this stage of the articulation. * * @private */ ModelArticulation.prototype.setArticulationStage = function (stageName, value) { const stage = this._runtimeStagesByName[stageName]; if (defined(stage)) { stage.currentValue = value; } }; const scratchArticulationMatrix = new Matrix4(); const scratchNodeMatrix = new Matrix4(); /** * Applies the chain of articulation stages to the transform of each node that * participates in the articulation. This only recomputes the node transforms * if any stage in the articulation has been modified. *

* Note that this will overwrite any existing transformations on participating * nodes. *

* * @private */ ModelArticulation.prototype.apply = function () { if (!this._dirty) { return; } this._dirty = false; let articulationMatrix = Matrix4.clone( Matrix4.IDENTITY, scratchArticulationMatrix ); let i; const stages = this._runtimeStages; const stagesLength = stages.length; // Compute the result of the articulation stages... for (i = 0; i < stagesLength; i++) { const stage = stages[i]; articulationMatrix = stage.applyStageToMatrix(articulationMatrix); } // ...then apply it to the transforms of the affected nodes. const nodes = this._runtimeNodes; const nodesLength = nodes.length; for (i = 0; i < nodesLength; i++) { const node = nodes[i]; const transform = Matrix4.multiplyTransformation( node.originalTransform, articulationMatrix, scratchNodeMatrix ); node.transform = transform; } }; export default ModelArticulation;