All files / engine/Source/Scene/Model ModelArticulation.js

100% Statements 53/53
83.33% Branches 5/6
100% Functions 9/9
100% Lines 52/52

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215                                          20x   20x 20x   20x 19x     18x 18x   18x 18x 18x     18x       18x   18x     1x                       19x                             1x                             15x                             4x                             17x           18x   18x 18x   18x 18x 18x 145x 145x             145x     145x 145x                       1x 28x 28x 27x       1x 1x                         1x 21x 1x   20x   20x           20x 20x     20x 193x 193x       20x 20x 20x 20x 20x         20x          
import Check from "../../Core/Check.js";
import Frozen from "../../Core/Frozen.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 = options ?? Frozen.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;