215 lines
5.4 KiB
JavaScript
215 lines
5.4 KiB
JavaScript
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
|
|
* <code>AGI_articulations</code> 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.
|
|
* <p>
|
|
* Note that this will overwrite any existing transformations on participating
|
|
* nodes.
|
|
* </p>
|
|
*
|
|
* @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;
|