|
@@ -1,375 +0,0 @@
|
|
|
-module BABYLON {
|
|
|
- export class ShapeKeyGroup {
|
|
|
- // position elements converted to typed array
|
|
|
- private _affectedPositionElements : Uint16Array;
|
|
|
- private _nPosElements : number;
|
|
|
-
|
|
|
- // arrays for the storage of each state
|
|
|
- private _states = new Array<Float32Array>();
|
|
|
- private _normals = new Array<Float32Array>();
|
|
|
- private _stateNames = new Array<string>();
|
|
|
-
|
|
|
- // event series queue & reference vars for current seris & step within
|
|
|
- private _queue = new Array<AutomatonEventSeries>();
|
|
|
- private _currentSeries : AutomatonEventSeries = null;
|
|
|
- private _currentStepInSeries : ReferenceDeformation = null;
|
|
|
- private _endOfLastFrameTs = -1;
|
|
|
-
|
|
|
- // affected vertices are used for normals, since all the entire vertex is involved, even if only the x of a position is affected
|
|
|
- private _affectedVertices : Uint16Array;
|
|
|
- private _nVertices;
|
|
|
-
|
|
|
- // reference vars for the current & prior deformation; assigned either an item of (_states / _normals) or one of the reusables
|
|
|
- private _currFinalPositionVals : Float32Array;
|
|
|
- private _priorFinalPositionVals : Float32Array;
|
|
|
- private _currFinalNormalVals : Float32Array;
|
|
|
- private _priorFinalNormalVals : Float32Array;
|
|
|
-
|
|
|
- // typed arrays are more expense to create, pre-allocate pairs for reuse
|
|
|
- private _reusablePositionFinals = new Array<Float32Array>();
|
|
|
- private _reusableNormalFinals = new Array<Float32Array>();
|
|
|
- private _lastReusablePosUsed = 0;
|
|
|
- private _lastReusableNormUsed = 0;
|
|
|
-
|
|
|
- // rotation control members
|
|
|
- private _doingRotation = false;
|
|
|
- private _rotationStartVec : Vector3;
|
|
|
- private _rotationEndVec : Vector3;
|
|
|
-
|
|
|
- // position control members
|
|
|
- private _doingMovePOV = false;
|
|
|
- private _positionStartVec : Vector3; // for lerp(ing) when NOT also rotating too
|
|
|
- private _positionEndVec : Vector3; // for lerp(ing) when NOT also rotating too
|
|
|
- private _fullAmtRight : number; // for when also rotating
|
|
|
- private _fullAmtUp : number; // for when also rotating
|
|
|
- private _fullAmtForward : number; // for when also rotating
|
|
|
- private _amtRightSoFar : number; // for when also rotating
|
|
|
- private _amtUpSoFar : number; // for when also rotating
|
|
|
- private _amtForwardSoFar : number; // for when also rotating
|
|
|
-
|
|
|
- // misc
|
|
|
- private _activeLockedCamera : any = null; // any, or would require casting to FreeCamera & no point in JavaScript
|
|
|
- private _mirrorAxis = -1; // when in use x = 1, y = 2, z = 3
|
|
|
-
|
|
|
- /**
|
|
|
- * @param {Automaton} _automaton - reference of Automaton this ShapeKeyGroup is a part of
|
|
|
- * @param {String} _name - Name of the Key Group, upper case only
|
|
|
- * @param {Array} affectedPositionElements - index of either an x, y, or z of positions. Not all 3 of a vertex need be present. Ascending order.
|
|
|
- * @param {Array} basisState - original state of the affectedPositionElements of positions
|
|
|
- */
|
|
|
- constructor(private _automaton : Automaton, private _name : string, affectedPositionElements : Array<number>, basisState : Array<number>){
|
|
|
- if (!(affectedPositionElements instanceof Array) || affectedPositionElements.length === 0 ) throw "ShapeKeyGroup: invalid affectedPositionElements arg";
|
|
|
- if (!(basisState instanceof Array) || basisState.length !== affectedPositionElements.length) throw "ShapeKeyGroup: invalid basisState arg";
|
|
|
-
|
|
|
- // validation that position elements are in ascending order; normals relies on this being true
|
|
|
- this._affectedPositionElements = new Uint16Array(affectedPositionElements);
|
|
|
- this._nPosElements = affectedPositionElements.length;
|
|
|
- for (var i = 0; i + 1 < this._nPosElements; i++)
|
|
|
- if (!(this._affectedPositionElements[i] < this._affectedPositionElements[i + 1])) throw "ShapeKeyGroup: affectedPositionElements must be in ascending order";
|
|
|
-
|
|
|
- // initialize 2 position reusables, the size needed
|
|
|
- this._reusablePositionFinals.push(new Float32Array(this._nPosElements));
|
|
|
- this._reusablePositionFinals.push(new Float32Array(this._nPosElements));
|
|
|
-
|
|
|
- // determine affectedVertices for updating cooresponding normals
|
|
|
- var affectedVert = new Array<number>(); // final size unknown, so use a push-able array & convert to Uint16Array at end
|
|
|
- var vertIdx = -1;
|
|
|
- var nextVertIdx : number;
|
|
|
-
|
|
|
- // go through each position element
|
|
|
- for (var i = 0; i < this._nPosElements; i++){
|
|
|
- // the vertex index is 1/3 the position element index
|
|
|
- nextVertIdx = Math.floor(this._affectedPositionElements[i] / 3);
|
|
|
-
|
|
|
- // since position element indexes in ascending order, check if vertex not already added by the x, or y elements
|
|
|
- if (vertIdx !== nextVertIdx){
|
|
|
- vertIdx = nextVertIdx;
|
|
|
- affectedVert.push(vertIdx);
|
|
|
- }
|
|
|
- }
|
|
|
- this._affectedVertices = new Uint16Array(affectedVert);
|
|
|
- this._nVertices = this._affectedVertices.length;
|
|
|
-
|
|
|
- // initialize 2 normal reusables, the size needed
|
|
|
- this._reusableNormalFinals.push(new Float32Array(this._nVertices * 3));
|
|
|
- this._reusableNormalFinals.push(new Float32Array(this._nVertices * 3));
|
|
|
-
|
|
|
- // push 'BASIS' to _states & _stateNames, then initialize _currFinalVals to 'BASIS' state
|
|
|
- this.addShapeKey("BASIS", basisState);
|
|
|
- this._currFinalPositionVals = this._states [0];
|
|
|
- this._currFinalNormalVals = this._normals[0];
|
|
|
- }
|
|
|
- // =============================== Shape-Key adding & deriving ===============================
|
|
|
- private getDerivedName(referenceIdx : number, endStateIdx : number, endStateRatio : number) : string{
|
|
|
- return referenceIdx + "-" + endStateIdx + "@" + endStateRatio;
|
|
|
- }
|
|
|
- /**
|
|
|
- * add a derived key from the data contained in a deformation; wrapper for addDerivedKey()
|
|
|
- * @param {ReferenceDeformation} deformation - mined for its reference & end state names, and end state ratio
|
|
|
- */
|
|
|
- public addDerivedKeyFromDeformation(deformation : ReferenceDeformation) : void{
|
|
|
- this.addDerivedKey(deformation.getReferenceStateName(), deformation.getEndStateName(), deformation.getEndStateRatio());
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * add a derived key from the arguments
|
|
|
- * @param {string} referenceStateName - Name of the reference state to be based on
|
|
|
- * @param {string} endStateName - Name of the end state to be based on
|
|
|
- * @param {number} endStateRatio - Unvalidated, but if -1 < or > 1, then can never be called, since Deformation validates
|
|
|
- */
|
|
|
- public addDerivedKey(referenceStateName : string, endStateName : string, endStateRatio : number) : void{
|
|
|
- var referenceIdx = this.getIdxForState(referenceStateName.toUpperCase());
|
|
|
- var endStateIdx = this.getIdxForState(endStateName .toUpperCase());
|
|
|
- if (referenceIdx === -1 || endStateIdx === -1) throw "ShapeKeyGroup: invalid source state name(s)";
|
|
|
- if (endStateRatio === 1) throw "ShapeKeyGroup: deriving a shape key where the endStateRatio is 1 is pointless";
|
|
|
-
|
|
|
- var stateName = this.getDerivedName(referenceIdx, endStateIdx, endStateRatio);
|
|
|
- var stateKey = new Float32Array(this._nPosElements);
|
|
|
- this.buildPosEndPoint(stateKey, referenceIdx, endStateIdx, endStateRatio);
|
|
|
- this.addShapeKeyInternal(stateName, stateKey);
|
|
|
- }
|
|
|
-
|
|
|
- /** called in construction code from TOB, but outside the constructor, except for 'BASIS'. Unlikely to be called by application code. */
|
|
|
- public addShapeKey(stateName : string, stateKey : Array<number>) : void {
|
|
|
- if (!(stateKey instanceof Array) || stateKey.length !== this._nPosElements) throw "ShapeKeyGroup: invalid stateKey arg";
|
|
|
- this.addShapeKeyInternal(stateName, new Float32Array(stateKey) );
|
|
|
- }
|
|
|
-
|
|
|
- /** worker method for both the addShapeKey() & addDerivedKey() methods */
|
|
|
- private addShapeKeyInternal(stateName : string, stateKey : Float32Array) : void {
|
|
|
- if (typeof stateName !== 'string' || stateName.length === 0) throw "ShapeKeyGroup: invalid stateName arg";
|
|
|
- if (this.getIdxForState(stateName) !== -1) throw "ShapeKeyGroup: stateName " + stateName + " is a duplicate";
|
|
|
-
|
|
|
- this._states.push(stateKey);
|
|
|
- this._stateNames.push(stateName);
|
|
|
-
|
|
|
- var coorespondingNormals = new Float32Array(this._nVertices * 3);
|
|
|
- this.buildNormEndPoint(coorespondingNormals, stateKey);
|
|
|
- this._normals.push(coorespondingNormals);
|
|
|
-
|
|
|
- if (this._automaton.debug) console.log("Shape key: " + stateName + " added to group: " + this._name + " on Automaton: " + this._automaton.name);
|
|
|
- }
|
|
|
- // =================================== inside before render ==================================
|
|
|
- /**
|
|
|
- * Called by the beforeRender() registered by this._automaton
|
|
|
- * @param {Float32Array} positions - Array of the positions for the entire mesh, portion updated based on _affectedIndices
|
|
|
- * @param {Float32Array } normals - Array of the normals for the entire mesh, if not null, portion updated based on _affectedVertices
|
|
|
- */
|
|
|
- public incrementallyDeform(positions : Float32Array, normals :Float32Array) : boolean {
|
|
|
- // series level of processing; get another series from the queue when none or last is done
|
|
|
- if (this._currentSeries === null || !this._currentSeries.hasMoreEvents() ){
|
|
|
- if (! this._nextEventSeries()) return false;
|
|
|
- }
|
|
|
-
|
|
|
- // ok, have an active event series, now get the next deformation in series if required
|
|
|
- while (this._currentStepInSeries === null || this._currentStepInSeries.isComplete() ){
|
|
|
- var next : any = this._currentSeries.nextEvent(this._name);
|
|
|
-
|
|
|
- if (next === null) return false; // being blocked, this must be a multi-group series, not ready for us
|
|
|
- if (next instanceof Action){
|
|
|
- (<Action> next).execute(ActionEvent.CreateNew(this._automaton));
|
|
|
- }
|
|
|
- else if (typeof next === "function"){
|
|
|
- next.call();
|
|
|
- }
|
|
|
- else{
|
|
|
- this._nextDeformation(<ReferenceDeformation> next); // must be a new deformation. _currentStepInSeries assigned if valid
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // have a deformation to process
|
|
|
- var ratioComplete = this._currentStepInSeries.getCompletionMilestone();
|
|
|
- if (ratioComplete < 0) return false; // Deformation.BLOCKED or Deformation.WAITING
|
|
|
-
|
|
|
- // update the positions
|
|
|
- for (var i = 0; i < this._nPosElements; i++){
|
|
|
- positions[this._affectedPositionElements[i]] = this._priorFinalPositionVals[i] + ((this._currFinalPositionVals[i] - this._priorFinalPositionVals[i]) * ratioComplete);
|
|
|
- }
|
|
|
-
|
|
|
- // update the normals
|
|
|
- var mIdx : number, kIdx : number;
|
|
|
- for (var i = 0; i < this._nVertices; i++){
|
|
|
- mIdx = 3 * this._affectedVertices[i] // offset for this vertex in the entire mesh
|
|
|
- kIdx = 3 * i; // offset for this vertex in the shape key group
|
|
|
- normals[mIdx ] = this._priorFinalNormalVals[kIdx ] + ((this._currFinalNormalVals[kIdx ] - this._priorFinalNormalVals[kIdx ]) * ratioComplete);
|
|
|
- normals[mIdx + 1] = this._priorFinalNormalVals[kIdx + 1] + ((this._currFinalNormalVals[kIdx + 1] - this._priorFinalNormalVals[kIdx + 1]) * ratioComplete);
|
|
|
- normals[mIdx + 2] = this._priorFinalNormalVals[kIdx + 2] + ((this._currFinalNormalVals[kIdx + 2] - this._priorFinalNormalVals[kIdx + 2]) * ratioComplete);
|
|
|
- }
|
|
|
-
|
|
|
- if (this._doingRotation){
|
|
|
- this._automaton.rotation = BABYLON.Vector3.Lerp(this._rotationStartVec, this._rotationEndVec, ratioComplete);
|
|
|
- }
|
|
|
-
|
|
|
- if (this._doingMovePOV === true){
|
|
|
- if (this._doingRotation){
|
|
|
- // some of these amounts, could be negative, if has a Pace with a hiccup
|
|
|
- var amtRight = (this._fullAmtRight * ratioComplete) - this._amtRightSoFar;
|
|
|
- var amtUp = (this._fullAmtUp * ratioComplete) - this._amtUpSoFar;
|
|
|
- var amtForward = (this._fullAmtForward * ratioComplete) - this._amtForwardSoFar;
|
|
|
-
|
|
|
- this._automaton.movePOV(amtRight, amtUp, amtForward);
|
|
|
-
|
|
|
- this._amtRightSoFar += amtRight;
|
|
|
- this._amtUpSoFar += amtUp;
|
|
|
- this._amtForwardSoFar += amtForward;
|
|
|
- }else{
|
|
|
- this._automaton.position = BABYLON.Vector3.Lerp(this._positionStartVec, this._positionEndVec, ratioComplete);
|
|
|
- }
|
|
|
-
|
|
|
- if (this._activeLockedCamera !== null) this._activeLockedCamera._getViewMatrix();
|
|
|
- }
|
|
|
- this._endOfLastFrameTs = Automaton.now();
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- public resumePlay() : void {
|
|
|
- if (this._currentStepInSeries !== null) this._currentStepInSeries.resumePlay();
|
|
|
- }
|
|
|
- // ============================ Event Series Queueing & retrieval ============================
|
|
|
- public queueEventSeries(eSeries : AutomatonEventSeries) :void {
|
|
|
- this._queue.push(eSeries);
|
|
|
- }
|
|
|
-
|
|
|
- private _nextEventSeries() : boolean {
|
|
|
- var ret = this._queue.length > 0;
|
|
|
- if (ret){
|
|
|
- this._currentSeries = this._queue.shift();
|
|
|
- this._currentSeries.activate(this._name);
|
|
|
- }
|
|
|
- return ret;
|
|
|
- }
|
|
|
- // ===================================== deformation prep ====================================
|
|
|
- private _nextDeformation(deformation : ReferenceDeformation) : void {
|
|
|
- // do this as soon as possible to get the clock started, retroactively, when sole group in the series, and within 50 millis of last deform
|
|
|
- var lateStart = Automaton.now() - this._endOfLastFrameTs;
|
|
|
- deformation.activate((this._currentSeries.nGroups === 1 && lateStart - this._endOfLastFrameTs < 50) ? lateStart : 0);
|
|
|
-
|
|
|
- this._currentStepInSeries = deformation;
|
|
|
- this._priorFinalPositionVals = this._currFinalPositionVals;
|
|
|
- this._priorFinalNormalVals = this._currFinalNormalVals ;
|
|
|
-
|
|
|
- var referenceIdx = this.getIdxForState(deformation.getReferenceStateName() );
|
|
|
- var endStateIdx = this.getIdxForState(deformation.getEndStateName () );
|
|
|
- if (referenceIdx === -1 || endStateIdx === -1) throw "ShapeKeyGroup " + this._name + ": invalid deformation, source state name(s) not found";
|
|
|
-
|
|
|
- var endStateRatio = deformation.getEndStateRatio();
|
|
|
- if (endStateRatio < 0 && this._mirrorAxis === -1) throw "ShapeKeyGroup " + this._name + ": invalid deformation, negative end state ratios when not mirroring";
|
|
|
-
|
|
|
- // when endStateRatio is 1 or 0, just assign _currFinalVals directly from _states
|
|
|
- if (endStateRatio === 1 || endStateRatio === 0){
|
|
|
- if (endStateRatio === 0) endStateIdx = referenceIdx; // really just the reference when 0
|
|
|
- this._currFinalPositionVals = this._states [endStateIdx];
|
|
|
- this._currFinalNormalVals = this._normals[endStateIdx];
|
|
|
- }else{
|
|
|
- // check there was not a pre-built derived key to assign
|
|
|
- var derivedIdx = this.getIdxForState(this.getDerivedName(referenceIdx, endStateIdx, endStateRatio) );
|
|
|
- if (derivedIdx !== -1){
|
|
|
- this._currFinalPositionVals = this._states [derivedIdx];
|
|
|
- this._currFinalNormalVals = this._normals[derivedIdx];
|
|
|
- } else{
|
|
|
- // need to build _currFinalVals, toggling the _lastReusableUsed
|
|
|
- this._lastReusablePosUsed = (this._lastReusablePosUsed === 1) ? 0 : 1;
|
|
|
- this.buildPosEndPoint(this._reusablePositionFinals[this._lastReusablePosUsed], referenceIdx, endStateIdx, endStateRatio, this._automaton.debug);
|
|
|
- this._currFinalPositionVals = this._reusablePositionFinals[this._lastReusablePosUsed];
|
|
|
-
|
|
|
- // need to build _currFinalNormalVals, toggling the _lastReusableUsed
|
|
|
- this._lastReusableNormUsed = (this._lastReusableNormUsed === 1) ? 0 : 1;
|
|
|
- this.buildNormEndPoint(this._reusableNormalFinals[this._lastReusableNormUsed], this._currFinalPositionVals);
|
|
|
- this._currFinalNormalVals = this._reusableNormalFinals[this._lastReusableNormUsed];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // prepare for rotation, if deformation calls for
|
|
|
- this._doingRotation = deformation.rotatePOV !== null;
|
|
|
- if (this._doingRotation){
|
|
|
- this._rotationStartVec = this._automaton.rotation; // no clone required, since Lerp() returns a new Vec3 written over .rotation
|
|
|
- this._rotationEndVec = this._rotationStartVec.add(this._automaton.calcRotatePOV(deformation.rotatePOV.x, deformation.rotatePOV.y, deformation.rotatePOV.z));
|
|
|
- }
|
|
|
-
|
|
|
- // prepare for POV move, if deformation calls for
|
|
|
- this._doingMovePOV = deformation.movePOV !== null;
|
|
|
- if (this._doingMovePOV){
|
|
|
- this._fullAmtRight = deformation.movePOV.x; this._amtRightSoFar = 0;
|
|
|
- this._fullAmtUp = deformation.movePOV.y; this._amtUpSoFar = 0;
|
|
|
- this._fullAmtForward = deformation.movePOV.z; this._amtForwardSoFar = 0;
|
|
|
-
|
|
|
- // less resources to calcMovePOV() once then Lerp(), but calcMovePOV() uses rotation, so can only go fast when not rotating too
|
|
|
- if (!this._doingRotation){
|
|
|
- this._positionStartVec = this._automaton.position; // no clone required, since Lerp() returns a new Vec3 written over .position
|
|
|
- this._positionEndVec = this._positionStartVec.add(this._automaton.calcMovePOV(this._fullAmtRight, this._fullAmtUp, this._fullAmtForward));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // determine if camera needs to be woke up for tracking
|
|
|
- this._activeLockedCamera = null; // assigned for failure
|
|
|
-
|
|
|
- if (this._doingRotation || this._doingMovePOV){
|
|
|
- var activeCamera = <any> this._automaton.getScene().activeCamera;
|
|
|
- if(activeCamera.lockedTarget && activeCamera.lockedTarget === this._automaton)
|
|
|
- this._activeLockedCamera = activeCamera;
|
|
|
- }
|
|
|
- }
|
|
|
- /**
|
|
|
- * Called by addShapeKeyInternal() & _nextDeformation() to build the positions for an end point
|
|
|
- * @param {Float32Array} targetArray - location of output. One of the _reusablePositionFinals for _nextDeformation(). Bound for: _states[], if addShapeKeyInternal().
|
|
|
- * @param {number} referenceIdx - the index into _states[] to use as a reference
|
|
|
- * @param {number} endStateIdx - the index into _states[] to use as a target
|
|
|
- * @param {number} endStateRatio - the ratio of the target state to achive, relative to the reference state
|
|
|
- * @param {boolean} log - write console message of action, when true (Default false)
|
|
|
- *
|
|
|
- */
|
|
|
- private buildPosEndPoint(targetArray : Float32Array, referenceIdx: number, endStateIdx : number, endStateRatio : number, log = false) : void {
|
|
|
- var refEndState = this._states[referenceIdx];
|
|
|
- var newEndState = this._states[endStateIdx];
|
|
|
-
|
|
|
- // compute each of the new final values of positions
|
|
|
- var deltaToRefState : number;
|
|
|
- for (var i = 0; i < this._nPosElements; i++){
|
|
|
- deltaToRefState = (newEndState[i] - refEndState[i]) * endStateRatio;
|
|
|
-
|
|
|
- // reverse sign on appropriate elements of referenceDelta when ratio neg & mirroring
|
|
|
- if (endStateRatio < 0 && this._mirrorAxis !== (i + 1) % 3){
|
|
|
- deltaToRefState *= -1;
|
|
|
- }
|
|
|
- targetArray[i] = refEndState[i] + deltaToRefState;
|
|
|
- }
|
|
|
- if (log) console.log(this._name + " end Point built for referenceIdx: " + referenceIdx + ", endStateIdx: " + endStateIdx + ", endStateRatio: " + endStateRatio);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Called by addShapeKeyInternal() & _nextDeformation() to build the normals for an end point
|
|
|
- * @param {Float32Array} targetArray - location of output. One of the _reusableNormalFinals for _nextDeformation(). Bound for: _normals[], if addShapeKeyInternal().
|
|
|
- * @param {Float32Array} endStatePos - postion data to build the normals for. Output from buildPosEndPoint, or data passed in from addShapeKey()
|
|
|
- */
|
|
|
- private buildNormEndPoint(targetArray : Float32Array, endStatePos : Float32Array) : void {
|
|
|
- // build a full, mesh sized, set of positions & populate with the left-over initial data
|
|
|
- var futurePos = new Float32Array(this._automaton.getVerticesData(VertexBuffer.PositionKind));
|
|
|
-
|
|
|
- // populate the changes that this state has
|
|
|
- for (var i = 0; i < this._nPosElements; i++){
|
|
|
- futurePos[this._affectedPositionElements[i]] = endStatePos[i];
|
|
|
- }
|
|
|
-
|
|
|
- // compute using method in _automaton
|
|
|
- this._automaton.normalsforVerticesInPlace(this._affectedVertices, targetArray, futurePos);
|
|
|
- }
|
|
|
- // ==================================== Getters & setters ====================================
|
|
|
- private getIdxForState(stateName : string) : number{
|
|
|
- for (var i = this._stateNames.length - 1; i >= 0; i--){
|
|
|
- if (this._stateNames[i] === stateName){
|
|
|
- return i;
|
|
|
- }
|
|
|
- }
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- public getName() : string { return this._name; }
|
|
|
- public getNPosElements() : number { return this._nPosElements; }
|
|
|
- public getNStates() : number { return this._stateNames.length; }
|
|
|
- public toString() : string { return 'ShapeKeyGroup: ' + this._name + ', n position elements: ' + this._nPosElements + ',\nStates: ' + this._stateNames; }
|
|
|
-
|
|
|
- public mirrorAxisOnX() : void {this._mirrorAxis = 1;}
|
|
|
- public mirrorAxisOnY() : void {this._mirrorAxis = 2;}
|
|
|
- public mirrorAxisOnZ() : void {this._mirrorAxis = 3;}
|
|
|
- }
|
|
|
-}
|