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;