浏览代码

setPreTransformMatrix

David Catuhe 7 年之前
父节点
当前提交
086046f87d

文件差异内容过多而无法显示
+ 9860 - 9785
Playground/babylon.d.txt


文件差异内容过多而无法显示
+ 7 - 7
Viewer/dist/viewer.js


文件差异内容过多而无法显示
+ 7 - 7
Viewer/dist/viewer.min.js


文件差异内容过多而无法显示
+ 10437 - 10363
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 11 - 11
dist/preview release/babylon.js


+ 104 - 37
dist/preview release/babylon.max.js

@@ -6564,12 +6564,24 @@ var BABYLON;
      */
      */
     var EventState = /** @class */ (function () {
     var EventState = /** @class */ (function () {
         /**
         /**
-        * If the callback of a given Observer set skipNextObservers to true the following observers will be ignored
-        */
+         * Create a new EventState
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         */
         function EventState(mask, skipNextObservers, target, currentTarget) {
         function EventState(mask, skipNextObservers, target, currentTarget) {
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             this.initalize(mask, skipNextObservers, target, currentTarget);
             this.initalize(mask, skipNextObservers, target, currentTarget);
         }
         }
+        /**
+         * Initialize the current event state
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns the current event state
+         */
         EventState.prototype.initalize = function (mask, skipNextObservers, target, currentTarget) {
         EventState.prototype.initalize = function (mask, skipNextObservers, target, currentTarget) {
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             this.mask = mask;
             this.mask = mask;
@@ -6585,11 +6597,33 @@ var BABYLON;
      * Represent an Observer registered to a given Observable object.
      * Represent an Observer registered to a given Observable object.
      */
      */
     var Observer = /** @class */ (function () {
     var Observer = /** @class */ (function () {
-        function Observer(callback, mask, scope) {
+        /**
+         * Creates a new observer
+         * @param callback defines the callback to call when the observer is notified
+         * @param mask defines the mask of the observer (used to filter notifications)
+         * @param scope defines the current scope used to restore the JS context
+         */
+        function Observer(
+            /**
+             * Defines the callback to call when the observer is notified
+             */
+            callback, 
+            /**
+             * Defines the mask of the observer (used to filter notifications)
+             */
+            mask, 
+            /**
+             * Defines the current scope used to restore the JS context
+             */
+            scope) {
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
             this.callback = callback;
             this.callback = callback;
             this.mask = mask;
             this.mask = mask;
             this.scope = scope;
             this.scope = scope;
+            /**
+             * Gets or sets a property defining that the observer as to be unregistered after the next notification
+             */
+            this.unregisterOnNextCall = false;
         }
         }
         return Observer;
         return Observer;
     }());
     }());
@@ -6600,6 +6634,9 @@ var BABYLON;
     var MultiObserver = /** @class */ (function () {
     var MultiObserver = /** @class */ (function () {
         function MultiObserver() {
         function MultiObserver() {
         }
         }
+        /**
+         * Release associated resources
+         */
         MultiObserver.prototype.dispose = function () {
         MultiObserver.prototype.dispose = function () {
             if (this._observers && this._observables) {
             if (this._observers && this._observables) {
                 for (var index = 0; index < this._observers.length; index++) {
                 for (var index = 0; index < this._observers.length; index++) {
@@ -6609,6 +6646,14 @@ var BABYLON;
             this._observers = null;
             this._observers = null;
             this._observables = null;
             this._observables = null;
         };
         };
+        /**
+         * Raise a callback when one of the observable will notify
+         * @param observables defines a list of observables to watch
+         * @param callback defines the callback to call on notification
+         * @param mask defines the mask used to filter notifications
+         * @param scope defines the current scope used to restore the JS context
+         * @returns the new MultiObserver
+         */
         MultiObserver.Watch = function (observables, callback, mask, scope) {
         MultiObserver.Watch = function (observables, callback, mask, scope) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
@@ -6635,6 +6680,10 @@ var BABYLON;
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      */
      */
     var Observable = /** @class */ (function () {
     var Observable = /** @class */ (function () {
+        /**
+         * Creates a new observable
+         * @param onObserverAdded defines a callback to call when a new observer is added
+         */
         function Observable(onObserverAdded) {
         function Observable(onObserverAdded) {
             this._observers = new Array();
             this._observers = new Array();
             this._eventState = new EventState(0);
             this._eventState = new EventState(0);
@@ -6648,15 +6697,19 @@ var BABYLON;
          * @param mask the mask used to filter observers
          * @param mask the mask used to filter observers
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param scope optional scope for the callback to be called from
          * @param scope optional scope for the callback to be called from
+         * @param unregisterOnFirstCall defines if the observer as to be unregistered after the next notification
+         * @returns the new observer created for the callback
          */
          */
-        Observable.prototype.add = function (callback, mask, insertFirst, scope) {
+        Observable.prototype.add = function (callback, mask, insertFirst, scope, unregisterOnFirstCall) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             if (insertFirst === void 0) { insertFirst = false; }
             if (insertFirst === void 0) { insertFirst = false; }
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
+            if (unregisterOnFirstCall === void 0) { unregisterOnFirstCall = false; }
             if (!callback) {
             if (!callback) {
                 return null;
                 return null;
             }
             }
             var observer = new Observer(callback, mask, scope);
             var observer = new Observer(callback, mask, scope);
+            observer.unregisterOnNextCall = unregisterOnFirstCall;
             if (insertFirst) {
             if (insertFirst) {
                 this._observers.unshift(observer);
                 this._observers.unshift(observer);
             }
             }
@@ -6670,7 +6723,8 @@ var BABYLON;
         };
         };
         /**
         /**
          * Remove an Observer from the Observable object
          * Remove an Observer from the Observable object
-         * @param observer the instance of the Observer to remove. If it doesn't belong to this Observable, false will be returned.
+         * @param observer the instance of the Observer to remove
+         * @returns false if it doesn't belong to this Observable
          */
          */
         Observable.prototype.remove = function (observer) {
         Observable.prototype.remove = function (observer) {
             if (!observer) {
             if (!observer) {
@@ -6685,8 +6739,9 @@ var BABYLON;
         };
         };
         /**
         /**
          * Remove a callback from the Observable object
          * Remove a callback from the Observable object
-         * @param callback the callback to remove. If it doesn't belong to this Observable, false will be returned.
-         * @param scope optional scope. If used only the callbacks with this scope will be removed.
+         * @param callback the callback to remove
+         * @param scope optional scope. If used only the callbacks with this scope will be removed
+         * @returns false if it doesn't belong to this Observable
         */
         */
         Observable.prototype.removeCallback = function (callback, scope) {
         Observable.prototype.removeCallback = function (callback, scope) {
             for (var index = 0; index < this._observers.length; index++) {
             for (var index = 0; index < this._observers.length; index++) {
@@ -6697,11 +6752,20 @@ var BABYLON;
             }
             }
             return false;
             return false;
         };
         };
+        Observable.prototype._deferUnregister = function (observer) {
+            var _this = this;
+            BABYLON.Tools.SetImmediate(function () {
+                _this.remove(observer);
+            });
+        };
         /**
         /**
          * Notify all Observers by calling their respective callback with the given data
          * Notify all Observers by calling their respective callback with the given data
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
-         * @param eventData
-         * @param mask
+         * @param eventData defines the data to send to all observers
+         * @param mask defines the mask of the current notification (observers with incompatible mask (ie mask & observer.mask === 0) will not be notified)
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns false if the complete observer chain was not processed (because one observer set the skipNextObservers to true)
          */
          */
         Observable.prototype.notifyObservers = function (eventData, mask, target, currentTarget) {
         Observable.prototype.notifyObservers = function (eventData, mask, target, currentTarget) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -6723,6 +6787,9 @@ var BABYLON;
                     else {
                     else {
                         state.lastReturnValue = obs.callback(eventData, state);
                         state.lastReturnValue = obs.callback(eventData, state);
                     }
                     }
+                    if (obs.unregisterOnNextCall) {
+                        this._deferUnregister(obs);
+                    }
                 }
                 }
                 if (state.skipNextObservers) {
                 if (state.skipNextObservers) {
                     return false;
                     return false;
@@ -6739,11 +6806,12 @@ var BABYLON;
          *
          *
          * @param eventData The data to be sent to each callback
          * @param eventData The data to be sent to each callback
          * @param mask is used to filter observers defaults to -1
          * @param mask is used to filter observers defaults to -1
-         * @param target the callback target (see EventState)
-         * @param currentTarget The current object in the bubbling phase
+         * @param target defines the callback target (see EventState)
+         * @param currentTarget defines he current object in the bubbling phase
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          */
          */
         Observable.prototype.notifyObserversWithPromise = function (eventData, mask, target, currentTarget) {
         Observable.prototype.notifyObserversWithPromise = function (eventData, mask, target, currentTarget) {
+            var _this = this;
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             // create an empty promise
             // create an empty promise
             var p = Promise.resolve(eventData);
             var p = Promise.resolve(eventData);
@@ -6774,6 +6842,9 @@ var BABYLON;
                             return obs.callback(eventData, state);
                             return obs.callback(eventData, state);
                         });
                         });
                     }
                     }
+                    if (obs.unregisterOnNextCall) {
+                        _this._deferUnregister(obs);
+                    }
                 }
                 }
             });
             });
             // return the eventData
             // return the eventData
@@ -6781,8 +6852,9 @@ var BABYLON;
         };
         };
         /**
         /**
          * Notify a specific observer
          * Notify a specific observer
-         * @param eventData
-         * @param mask
+         * @param observer defines the observer to notify
+         * @param eventData defines the data to be sent to each callback
+         * @param mask is used to filter observers defaults to -1
          */
          */
         Observable.prototype.notifyObserver = function (observer, eventData, mask) {
         Observable.prototype.notifyObserver = function (observer, eventData, mask) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -6792,7 +6864,8 @@ var BABYLON;
             observer.callback(eventData, state);
             observer.callback(eventData, state);
         };
         };
         /**
         /**
-         * return true is the Observable has at least one Observer registered
+         * Gets a boolean indicating if the observable has at least one observer
+         * @returns true is the Observable has at least one Observer registered
          */
          */
         Observable.prototype.hasObservers = function () {
         Observable.prototype.hasObservers = function () {
             return this._observers.length > 0;
             return this._observers.length > 0;
@@ -6805,8 +6878,9 @@ var BABYLON;
             this._onObserverAdded = null;
             this._onObserverAdded = null;
         };
         };
         /**
         /**
-        * Clone the current observable
-        */
+         * Clone the current observable
+         * @returns a new observable
+         */
         Observable.prototype.clone = function () {
         Observable.prototype.clone = function () {
             var result = new Observable();
             var result = new Observable();
             result._observers = this._observers.slice(0);
             result._observers = this._observers.slice(0);
@@ -6814,8 +6888,8 @@ var BABYLON;
         };
         };
         /**
         /**
          * Does this observable handles observer registered with a given mask
          * Does this observable handles observer registered with a given mask
-         * @param {number} trigger - the mask to be tested
-         * @return {boolean} whether or not one observer registered with the given mask is handeled
+         * @param mask defines the mask to be tested
+         * @return whether or not one observer registered with the given mask is handeled
         **/
         **/
         Observable.prototype.hasSpecificMask = function (mask) {
         Observable.prototype.hasSpecificMask = function (mask) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -15195,13 +15269,21 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         /**
         /**
+         * Sets a new matrix to apply before all other transformation
+         * @param matrix defines the transform matrix
+         * @returns the current TransformNode
+         */
+        TransformNode.prototype.setPreTransformMatrix = function (matrix) {
+            return this.setPivotMatrix(matrix, false);
+        };
+        /**
          * Sets a new pivot matrix to the current node
          * Sets a new pivot matrix to the current node
          * @param matrix defines the new pivot matrix to use
          * @param matrix defines the new pivot matrix to use
-         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
-            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
+            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = true; }
             this._pivotMatrix = matrix.clone();
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
@@ -15379,12 +15461,10 @@ var BABYLON;
          * Sets a new pivot point to the current node
          * Sets a new pivot point to the current node
          * @param point defines the new pivot point to use
          * @param point defines the new pivot point to use
          * @param space defines if the point is in world or local space (local by default)
          * @param space defines if the point is in world or local space (local by default)
-         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
-        TransformNode.prototype.setPivotPoint = function (point, space, postMultiplyPivotMatrix) {
+        TransformNode.prototype.setPivotPoint = function (point, space) {
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
-            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             if (this.getScene().getRenderId() == 0) {
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
                 this.computeWorldMatrix(true);
             }
             }
@@ -15394,20 +15474,7 @@ var BABYLON;
                 wm.invertToRef(tmat);
                 wm.invertToRef(tmat);
                 point = BABYLON.Vector3.TransformCoordinates(point, tmat);
                 point = BABYLON.Vector3.TransformCoordinates(point, tmat);
             }
             }
-            BABYLON.Vector3.TransformCoordinatesToRef(point, wm, this.position);
-            this._pivotMatrix.m[12] = -point.x;
-            this._pivotMatrix.m[13] = -point.y;
-            this._pivotMatrix.m[14] = -point.z;
-            if (this._postMultiplyPivotMatrix) {
-                if (!this._pivotMatrixInverse) {
-                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-                }
-                else {
-                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
-                }
-            }
-            this._cache.pivotMatrixUpdated = true;
-            return this;
+            return this.setPivotMatrix(BABYLON.Matrix.Translation(point.x, point.y, point.z), true);
         };
         };
         /**
         /**
          * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.
          * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.

文件差异内容过多而无法显示
+ 11 - 11
dist/preview release/babylon.worker.js


文件差异内容过多而无法显示
+ 5517 - 5443
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


文件差异内容过多而无法显示
+ 11 - 11
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 104 - 37
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -6564,12 +6564,24 @@ var BABYLON;
      */
      */
     var EventState = /** @class */ (function () {
     var EventState = /** @class */ (function () {
         /**
         /**
-        * If the callback of a given Observer set skipNextObservers to true the following observers will be ignored
-        */
+         * Create a new EventState
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         */
         function EventState(mask, skipNextObservers, target, currentTarget) {
         function EventState(mask, skipNextObservers, target, currentTarget) {
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             this.initalize(mask, skipNextObservers, target, currentTarget);
             this.initalize(mask, skipNextObservers, target, currentTarget);
         }
         }
+        /**
+         * Initialize the current event state
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns the current event state
+         */
         EventState.prototype.initalize = function (mask, skipNextObservers, target, currentTarget) {
         EventState.prototype.initalize = function (mask, skipNextObservers, target, currentTarget) {
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             this.mask = mask;
             this.mask = mask;
@@ -6585,11 +6597,33 @@ var BABYLON;
      * Represent an Observer registered to a given Observable object.
      * Represent an Observer registered to a given Observable object.
      */
      */
     var Observer = /** @class */ (function () {
     var Observer = /** @class */ (function () {
-        function Observer(callback, mask, scope) {
+        /**
+         * Creates a new observer
+         * @param callback defines the callback to call when the observer is notified
+         * @param mask defines the mask of the observer (used to filter notifications)
+         * @param scope defines the current scope used to restore the JS context
+         */
+        function Observer(
+            /**
+             * Defines the callback to call when the observer is notified
+             */
+            callback, 
+            /**
+             * Defines the mask of the observer (used to filter notifications)
+             */
+            mask, 
+            /**
+             * Defines the current scope used to restore the JS context
+             */
+            scope) {
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
             this.callback = callback;
             this.callback = callback;
             this.mask = mask;
             this.mask = mask;
             this.scope = scope;
             this.scope = scope;
+            /**
+             * Gets or sets a property defining that the observer as to be unregistered after the next notification
+             */
+            this.unregisterOnNextCall = false;
         }
         }
         return Observer;
         return Observer;
     }());
     }());
@@ -6600,6 +6634,9 @@ var BABYLON;
     var MultiObserver = /** @class */ (function () {
     var MultiObserver = /** @class */ (function () {
         function MultiObserver() {
         function MultiObserver() {
         }
         }
+        /**
+         * Release associated resources
+         */
         MultiObserver.prototype.dispose = function () {
         MultiObserver.prototype.dispose = function () {
             if (this._observers && this._observables) {
             if (this._observers && this._observables) {
                 for (var index = 0; index < this._observers.length; index++) {
                 for (var index = 0; index < this._observers.length; index++) {
@@ -6609,6 +6646,14 @@ var BABYLON;
             this._observers = null;
             this._observers = null;
             this._observables = null;
             this._observables = null;
         };
         };
+        /**
+         * Raise a callback when one of the observable will notify
+         * @param observables defines a list of observables to watch
+         * @param callback defines the callback to call on notification
+         * @param mask defines the mask used to filter notifications
+         * @param scope defines the current scope used to restore the JS context
+         * @returns the new MultiObserver
+         */
         MultiObserver.Watch = function (observables, callback, mask, scope) {
         MultiObserver.Watch = function (observables, callback, mask, scope) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
@@ -6635,6 +6680,10 @@ var BABYLON;
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      */
      */
     var Observable = /** @class */ (function () {
     var Observable = /** @class */ (function () {
+        /**
+         * Creates a new observable
+         * @param onObserverAdded defines a callback to call when a new observer is added
+         */
         function Observable(onObserverAdded) {
         function Observable(onObserverAdded) {
             this._observers = new Array();
             this._observers = new Array();
             this._eventState = new EventState(0);
             this._eventState = new EventState(0);
@@ -6648,15 +6697,19 @@ var BABYLON;
          * @param mask the mask used to filter observers
          * @param mask the mask used to filter observers
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param scope optional scope for the callback to be called from
          * @param scope optional scope for the callback to be called from
+         * @param unregisterOnFirstCall defines if the observer as to be unregistered after the next notification
+         * @returns the new observer created for the callback
          */
          */
-        Observable.prototype.add = function (callback, mask, insertFirst, scope) {
+        Observable.prototype.add = function (callback, mask, insertFirst, scope, unregisterOnFirstCall) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             if (insertFirst === void 0) { insertFirst = false; }
             if (insertFirst === void 0) { insertFirst = false; }
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
+            if (unregisterOnFirstCall === void 0) { unregisterOnFirstCall = false; }
             if (!callback) {
             if (!callback) {
                 return null;
                 return null;
             }
             }
             var observer = new Observer(callback, mask, scope);
             var observer = new Observer(callback, mask, scope);
+            observer.unregisterOnNextCall = unregisterOnFirstCall;
             if (insertFirst) {
             if (insertFirst) {
                 this._observers.unshift(observer);
                 this._observers.unshift(observer);
             }
             }
@@ -6670,7 +6723,8 @@ var BABYLON;
         };
         };
         /**
         /**
          * Remove an Observer from the Observable object
          * Remove an Observer from the Observable object
-         * @param observer the instance of the Observer to remove. If it doesn't belong to this Observable, false will be returned.
+         * @param observer the instance of the Observer to remove
+         * @returns false if it doesn't belong to this Observable
          */
          */
         Observable.prototype.remove = function (observer) {
         Observable.prototype.remove = function (observer) {
             if (!observer) {
             if (!observer) {
@@ -6685,8 +6739,9 @@ var BABYLON;
         };
         };
         /**
         /**
          * Remove a callback from the Observable object
          * Remove a callback from the Observable object
-         * @param callback the callback to remove. If it doesn't belong to this Observable, false will be returned.
-         * @param scope optional scope. If used only the callbacks with this scope will be removed.
+         * @param callback the callback to remove
+         * @param scope optional scope. If used only the callbacks with this scope will be removed
+         * @returns false if it doesn't belong to this Observable
         */
         */
         Observable.prototype.removeCallback = function (callback, scope) {
         Observable.prototype.removeCallback = function (callback, scope) {
             for (var index = 0; index < this._observers.length; index++) {
             for (var index = 0; index < this._observers.length; index++) {
@@ -6697,11 +6752,20 @@ var BABYLON;
             }
             }
             return false;
             return false;
         };
         };
+        Observable.prototype._deferUnregister = function (observer) {
+            var _this = this;
+            BABYLON.Tools.SetImmediate(function () {
+                _this.remove(observer);
+            });
+        };
         /**
         /**
          * Notify all Observers by calling their respective callback with the given data
          * Notify all Observers by calling their respective callback with the given data
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
-         * @param eventData
-         * @param mask
+         * @param eventData defines the data to send to all observers
+         * @param mask defines the mask of the current notification (observers with incompatible mask (ie mask & observer.mask === 0) will not be notified)
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns false if the complete observer chain was not processed (because one observer set the skipNextObservers to true)
          */
          */
         Observable.prototype.notifyObservers = function (eventData, mask, target, currentTarget) {
         Observable.prototype.notifyObservers = function (eventData, mask, target, currentTarget) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -6723,6 +6787,9 @@ var BABYLON;
                     else {
                     else {
                         state.lastReturnValue = obs.callback(eventData, state);
                         state.lastReturnValue = obs.callback(eventData, state);
                     }
                     }
+                    if (obs.unregisterOnNextCall) {
+                        this._deferUnregister(obs);
+                    }
                 }
                 }
                 if (state.skipNextObservers) {
                 if (state.skipNextObservers) {
                     return false;
                     return false;
@@ -6739,11 +6806,12 @@ var BABYLON;
          *
          *
          * @param eventData The data to be sent to each callback
          * @param eventData The data to be sent to each callback
          * @param mask is used to filter observers defaults to -1
          * @param mask is used to filter observers defaults to -1
-         * @param target the callback target (see EventState)
-         * @param currentTarget The current object in the bubbling phase
+         * @param target defines the callback target (see EventState)
+         * @param currentTarget defines he current object in the bubbling phase
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          */
          */
         Observable.prototype.notifyObserversWithPromise = function (eventData, mask, target, currentTarget) {
         Observable.prototype.notifyObserversWithPromise = function (eventData, mask, target, currentTarget) {
+            var _this = this;
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             // create an empty promise
             // create an empty promise
             var p = Promise.resolve(eventData);
             var p = Promise.resolve(eventData);
@@ -6774,6 +6842,9 @@ var BABYLON;
                             return obs.callback(eventData, state);
                             return obs.callback(eventData, state);
                         });
                         });
                     }
                     }
+                    if (obs.unregisterOnNextCall) {
+                        _this._deferUnregister(obs);
+                    }
                 }
                 }
             });
             });
             // return the eventData
             // return the eventData
@@ -6781,8 +6852,9 @@ var BABYLON;
         };
         };
         /**
         /**
          * Notify a specific observer
          * Notify a specific observer
-         * @param eventData
-         * @param mask
+         * @param observer defines the observer to notify
+         * @param eventData defines the data to be sent to each callback
+         * @param mask is used to filter observers defaults to -1
          */
          */
         Observable.prototype.notifyObserver = function (observer, eventData, mask) {
         Observable.prototype.notifyObserver = function (observer, eventData, mask) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -6792,7 +6864,8 @@ var BABYLON;
             observer.callback(eventData, state);
             observer.callback(eventData, state);
         };
         };
         /**
         /**
-         * return true is the Observable has at least one Observer registered
+         * Gets a boolean indicating if the observable has at least one observer
+         * @returns true is the Observable has at least one Observer registered
          */
          */
         Observable.prototype.hasObservers = function () {
         Observable.prototype.hasObservers = function () {
             return this._observers.length > 0;
             return this._observers.length > 0;
@@ -6805,8 +6878,9 @@ var BABYLON;
             this._onObserverAdded = null;
             this._onObserverAdded = null;
         };
         };
         /**
         /**
-        * Clone the current observable
-        */
+         * Clone the current observable
+         * @returns a new observable
+         */
         Observable.prototype.clone = function () {
         Observable.prototype.clone = function () {
             var result = new Observable();
             var result = new Observable();
             result._observers = this._observers.slice(0);
             result._observers = this._observers.slice(0);
@@ -6814,8 +6888,8 @@ var BABYLON;
         };
         };
         /**
         /**
          * Does this observable handles observer registered with a given mask
          * Does this observable handles observer registered with a given mask
-         * @param {number} trigger - the mask to be tested
-         * @return {boolean} whether or not one observer registered with the given mask is handeled
+         * @param mask defines the mask to be tested
+         * @return whether or not one observer registered with the given mask is handeled
         **/
         **/
         Observable.prototype.hasSpecificMask = function (mask) {
         Observable.prototype.hasSpecificMask = function (mask) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -15195,13 +15269,21 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         /**
         /**
+         * Sets a new matrix to apply before all other transformation
+         * @param matrix defines the transform matrix
+         * @returns the current TransformNode
+         */
+        TransformNode.prototype.setPreTransformMatrix = function (matrix) {
+            return this.setPivotMatrix(matrix, false);
+        };
+        /**
          * Sets a new pivot matrix to the current node
          * Sets a new pivot matrix to the current node
          * @param matrix defines the new pivot matrix to use
          * @param matrix defines the new pivot matrix to use
-         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
-            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
+            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = true; }
             this._pivotMatrix = matrix.clone();
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
@@ -15379,12 +15461,10 @@ var BABYLON;
          * Sets a new pivot point to the current node
          * Sets a new pivot point to the current node
          * @param point defines the new pivot point to use
          * @param point defines the new pivot point to use
          * @param space defines if the point is in world or local space (local by default)
          * @param space defines if the point is in world or local space (local by default)
-         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
-        TransformNode.prototype.setPivotPoint = function (point, space, postMultiplyPivotMatrix) {
+        TransformNode.prototype.setPivotPoint = function (point, space) {
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
-            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             if (this.getScene().getRenderId() == 0) {
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
                 this.computeWorldMatrix(true);
             }
             }
@@ -15394,20 +15474,7 @@ var BABYLON;
                 wm.invertToRef(tmat);
                 wm.invertToRef(tmat);
                 point = BABYLON.Vector3.TransformCoordinates(point, tmat);
                 point = BABYLON.Vector3.TransformCoordinates(point, tmat);
             }
             }
-            BABYLON.Vector3.TransformCoordinatesToRef(point, wm, this.position);
-            this._pivotMatrix.m[12] = -point.x;
-            this._pivotMatrix.m[13] = -point.y;
-            this._pivotMatrix.m[14] = -point.z;
-            if (this._postMultiplyPivotMatrix) {
-                if (!this._pivotMatrixInverse) {
-                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-                }
-                else {
-                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
-                }
-            }
-            this._cache.pivotMatrixUpdated = true;
-            return this;
+            return this.setPivotMatrix(BABYLON.Matrix.Translation(point.x, point.y, point.z), true);
         };
         };
         /**
         /**
          * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.
          * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.

+ 104 - 37
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js

@@ -6550,12 +6550,24 @@ var BABYLON;
      */
      */
     var EventState = /** @class */ (function () {
     var EventState = /** @class */ (function () {
         /**
         /**
-        * If the callback of a given Observer set skipNextObservers to true the following observers will be ignored
-        */
+         * Create a new EventState
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         */
         function EventState(mask, skipNextObservers, target, currentTarget) {
         function EventState(mask, skipNextObservers, target, currentTarget) {
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             this.initalize(mask, skipNextObservers, target, currentTarget);
             this.initalize(mask, skipNextObservers, target, currentTarget);
         }
         }
+        /**
+         * Initialize the current event state
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns the current event state
+         */
         EventState.prototype.initalize = function (mask, skipNextObservers, target, currentTarget) {
         EventState.prototype.initalize = function (mask, skipNextObservers, target, currentTarget) {
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             this.mask = mask;
             this.mask = mask;
@@ -6571,11 +6583,33 @@ var BABYLON;
      * Represent an Observer registered to a given Observable object.
      * Represent an Observer registered to a given Observable object.
      */
      */
     var Observer = /** @class */ (function () {
     var Observer = /** @class */ (function () {
-        function Observer(callback, mask, scope) {
+        /**
+         * Creates a new observer
+         * @param callback defines the callback to call when the observer is notified
+         * @param mask defines the mask of the observer (used to filter notifications)
+         * @param scope defines the current scope used to restore the JS context
+         */
+        function Observer(
+            /**
+             * Defines the callback to call when the observer is notified
+             */
+            callback, 
+            /**
+             * Defines the mask of the observer (used to filter notifications)
+             */
+            mask, 
+            /**
+             * Defines the current scope used to restore the JS context
+             */
+            scope) {
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
             this.callback = callback;
             this.callback = callback;
             this.mask = mask;
             this.mask = mask;
             this.scope = scope;
             this.scope = scope;
+            /**
+             * Gets or sets a property defining that the observer as to be unregistered after the next notification
+             */
+            this.unregisterOnNextCall = false;
         }
         }
         return Observer;
         return Observer;
     }());
     }());
@@ -6586,6 +6620,9 @@ var BABYLON;
     var MultiObserver = /** @class */ (function () {
     var MultiObserver = /** @class */ (function () {
         function MultiObserver() {
         function MultiObserver() {
         }
         }
+        /**
+         * Release associated resources
+         */
         MultiObserver.prototype.dispose = function () {
         MultiObserver.prototype.dispose = function () {
             if (this._observers && this._observables) {
             if (this._observers && this._observables) {
                 for (var index = 0; index < this._observers.length; index++) {
                 for (var index = 0; index < this._observers.length; index++) {
@@ -6595,6 +6632,14 @@ var BABYLON;
             this._observers = null;
             this._observers = null;
             this._observables = null;
             this._observables = null;
         };
         };
+        /**
+         * Raise a callback when one of the observable will notify
+         * @param observables defines a list of observables to watch
+         * @param callback defines the callback to call on notification
+         * @param mask defines the mask used to filter notifications
+         * @param scope defines the current scope used to restore the JS context
+         * @returns the new MultiObserver
+         */
         MultiObserver.Watch = function (observables, callback, mask, scope) {
         MultiObserver.Watch = function (observables, callback, mask, scope) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
@@ -6621,6 +6666,10 @@ var BABYLON;
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      */
      */
     var Observable = /** @class */ (function () {
     var Observable = /** @class */ (function () {
+        /**
+         * Creates a new observable
+         * @param onObserverAdded defines a callback to call when a new observer is added
+         */
         function Observable(onObserverAdded) {
         function Observable(onObserverAdded) {
             this._observers = new Array();
             this._observers = new Array();
             this._eventState = new EventState(0);
             this._eventState = new EventState(0);
@@ -6634,15 +6683,19 @@ var BABYLON;
          * @param mask the mask used to filter observers
          * @param mask the mask used to filter observers
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param scope optional scope for the callback to be called from
          * @param scope optional scope for the callback to be called from
+         * @param unregisterOnFirstCall defines if the observer as to be unregistered after the next notification
+         * @returns the new observer created for the callback
          */
          */
-        Observable.prototype.add = function (callback, mask, insertFirst, scope) {
+        Observable.prototype.add = function (callback, mask, insertFirst, scope, unregisterOnFirstCall) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             if (insertFirst === void 0) { insertFirst = false; }
             if (insertFirst === void 0) { insertFirst = false; }
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
+            if (unregisterOnFirstCall === void 0) { unregisterOnFirstCall = false; }
             if (!callback) {
             if (!callback) {
                 return null;
                 return null;
             }
             }
             var observer = new Observer(callback, mask, scope);
             var observer = new Observer(callback, mask, scope);
+            observer.unregisterOnNextCall = unregisterOnFirstCall;
             if (insertFirst) {
             if (insertFirst) {
                 this._observers.unshift(observer);
                 this._observers.unshift(observer);
             }
             }
@@ -6656,7 +6709,8 @@ var BABYLON;
         };
         };
         /**
         /**
          * Remove an Observer from the Observable object
          * Remove an Observer from the Observable object
-         * @param observer the instance of the Observer to remove. If it doesn't belong to this Observable, false will be returned.
+         * @param observer the instance of the Observer to remove
+         * @returns false if it doesn't belong to this Observable
          */
          */
         Observable.prototype.remove = function (observer) {
         Observable.prototype.remove = function (observer) {
             if (!observer) {
             if (!observer) {
@@ -6671,8 +6725,9 @@ var BABYLON;
         };
         };
         /**
         /**
          * Remove a callback from the Observable object
          * Remove a callback from the Observable object
-         * @param callback the callback to remove. If it doesn't belong to this Observable, false will be returned.
-         * @param scope optional scope. If used only the callbacks with this scope will be removed.
+         * @param callback the callback to remove
+         * @param scope optional scope. If used only the callbacks with this scope will be removed
+         * @returns false if it doesn't belong to this Observable
         */
         */
         Observable.prototype.removeCallback = function (callback, scope) {
         Observable.prototype.removeCallback = function (callback, scope) {
             for (var index = 0; index < this._observers.length; index++) {
             for (var index = 0; index < this._observers.length; index++) {
@@ -6683,11 +6738,20 @@ var BABYLON;
             }
             }
             return false;
             return false;
         };
         };
+        Observable.prototype._deferUnregister = function (observer) {
+            var _this = this;
+            BABYLON.Tools.SetImmediate(function () {
+                _this.remove(observer);
+            });
+        };
         /**
         /**
          * Notify all Observers by calling their respective callback with the given data
          * Notify all Observers by calling their respective callback with the given data
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
-         * @param eventData
-         * @param mask
+         * @param eventData defines the data to send to all observers
+         * @param mask defines the mask of the current notification (observers with incompatible mask (ie mask & observer.mask === 0) will not be notified)
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns false if the complete observer chain was not processed (because one observer set the skipNextObservers to true)
          */
          */
         Observable.prototype.notifyObservers = function (eventData, mask, target, currentTarget) {
         Observable.prototype.notifyObservers = function (eventData, mask, target, currentTarget) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -6709,6 +6773,9 @@ var BABYLON;
                     else {
                     else {
                         state.lastReturnValue = obs.callback(eventData, state);
                         state.lastReturnValue = obs.callback(eventData, state);
                     }
                     }
+                    if (obs.unregisterOnNextCall) {
+                        this._deferUnregister(obs);
+                    }
                 }
                 }
                 if (state.skipNextObservers) {
                 if (state.skipNextObservers) {
                     return false;
                     return false;
@@ -6725,11 +6792,12 @@ var BABYLON;
          *
          *
          * @param eventData The data to be sent to each callback
          * @param eventData The data to be sent to each callback
          * @param mask is used to filter observers defaults to -1
          * @param mask is used to filter observers defaults to -1
-         * @param target the callback target (see EventState)
-         * @param currentTarget The current object in the bubbling phase
+         * @param target defines the callback target (see EventState)
+         * @param currentTarget defines he current object in the bubbling phase
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          */
          */
         Observable.prototype.notifyObserversWithPromise = function (eventData, mask, target, currentTarget) {
         Observable.prototype.notifyObserversWithPromise = function (eventData, mask, target, currentTarget) {
+            var _this = this;
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             // create an empty promise
             // create an empty promise
             var p = Promise.resolve(eventData);
             var p = Promise.resolve(eventData);
@@ -6760,6 +6828,9 @@ var BABYLON;
                             return obs.callback(eventData, state);
                             return obs.callback(eventData, state);
                         });
                         });
                     }
                     }
+                    if (obs.unregisterOnNextCall) {
+                        _this._deferUnregister(obs);
+                    }
                 }
                 }
             });
             });
             // return the eventData
             // return the eventData
@@ -6767,8 +6838,9 @@ var BABYLON;
         };
         };
         /**
         /**
          * Notify a specific observer
          * Notify a specific observer
-         * @param eventData
-         * @param mask
+         * @param observer defines the observer to notify
+         * @param eventData defines the data to be sent to each callback
+         * @param mask is used to filter observers defaults to -1
          */
          */
         Observable.prototype.notifyObserver = function (observer, eventData, mask) {
         Observable.prototype.notifyObserver = function (observer, eventData, mask) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -6778,7 +6850,8 @@ var BABYLON;
             observer.callback(eventData, state);
             observer.callback(eventData, state);
         };
         };
         /**
         /**
-         * return true is the Observable has at least one Observer registered
+         * Gets a boolean indicating if the observable has at least one observer
+         * @returns true is the Observable has at least one Observer registered
          */
          */
         Observable.prototype.hasObservers = function () {
         Observable.prototype.hasObservers = function () {
             return this._observers.length > 0;
             return this._observers.length > 0;
@@ -6791,8 +6864,9 @@ var BABYLON;
             this._onObserverAdded = null;
             this._onObserverAdded = null;
         };
         };
         /**
         /**
-        * Clone the current observable
-        */
+         * Clone the current observable
+         * @returns a new observable
+         */
         Observable.prototype.clone = function () {
         Observable.prototype.clone = function () {
             var result = new Observable();
             var result = new Observable();
             result._observers = this._observers.slice(0);
             result._observers = this._observers.slice(0);
@@ -6800,8 +6874,8 @@ var BABYLON;
         };
         };
         /**
         /**
          * Does this observable handles observer registered with a given mask
          * Does this observable handles observer registered with a given mask
-         * @param {number} trigger - the mask to be tested
-         * @return {boolean} whether or not one observer registered with the given mask is handeled
+         * @param mask defines the mask to be tested
+         * @return whether or not one observer registered with the given mask is handeled
         **/
         **/
         Observable.prototype.hasSpecificMask = function (mask) {
         Observable.prototype.hasSpecificMask = function (mask) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -15181,13 +15255,21 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         /**
         /**
+         * Sets a new matrix to apply before all other transformation
+         * @param matrix defines the transform matrix
+         * @returns the current TransformNode
+         */
+        TransformNode.prototype.setPreTransformMatrix = function (matrix) {
+            return this.setPivotMatrix(matrix, false);
+        };
+        /**
          * Sets a new pivot matrix to the current node
          * Sets a new pivot matrix to the current node
          * @param matrix defines the new pivot matrix to use
          * @param matrix defines the new pivot matrix to use
-         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
-            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
+            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = true; }
             this._pivotMatrix = matrix.clone();
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
@@ -15365,12 +15447,10 @@ var BABYLON;
          * Sets a new pivot point to the current node
          * Sets a new pivot point to the current node
          * @param point defines the new pivot point to use
          * @param point defines the new pivot point to use
          * @param space defines if the point is in world or local space (local by default)
          * @param space defines if the point is in world or local space (local by default)
-         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
-        TransformNode.prototype.setPivotPoint = function (point, space, postMultiplyPivotMatrix) {
+        TransformNode.prototype.setPivotPoint = function (point, space) {
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
-            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             if (this.getScene().getRenderId() == 0) {
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
                 this.computeWorldMatrix(true);
             }
             }
@@ -15380,20 +15460,7 @@ var BABYLON;
                 wm.invertToRef(tmat);
                 wm.invertToRef(tmat);
                 point = BABYLON.Vector3.TransformCoordinates(point, tmat);
                 point = BABYLON.Vector3.TransformCoordinates(point, tmat);
             }
             }
-            BABYLON.Vector3.TransformCoordinatesToRef(point, wm, this.position);
-            this._pivotMatrix.m[12] = -point.x;
-            this._pivotMatrix.m[13] = -point.y;
-            this._pivotMatrix.m[14] = -point.z;
-            if (this._postMultiplyPivotMatrix) {
-                if (!this._pivotMatrixInverse) {
-                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-                }
-                else {
-                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
-                }
-            }
-            this._cache.pivotMatrixUpdated = true;
-            return this;
+            return this.setPivotMatrix(BABYLON.Matrix.Translation(point.x, point.y, point.z), true);
         };
         };
         /**
         /**
          * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.
          * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.

+ 104 - 37
dist/preview release/es6.js

@@ -6550,12 +6550,24 @@ var BABYLON;
      */
      */
     var EventState = /** @class */ (function () {
     var EventState = /** @class */ (function () {
         /**
         /**
-        * If the callback of a given Observer set skipNextObservers to true the following observers will be ignored
-        */
+         * Create a new EventState
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         */
         function EventState(mask, skipNextObservers, target, currentTarget) {
         function EventState(mask, skipNextObservers, target, currentTarget) {
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             this.initalize(mask, skipNextObservers, target, currentTarget);
             this.initalize(mask, skipNextObservers, target, currentTarget);
         }
         }
+        /**
+         * Initialize the current event state
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns the current event state
+         */
         EventState.prototype.initalize = function (mask, skipNextObservers, target, currentTarget) {
         EventState.prototype.initalize = function (mask, skipNextObservers, target, currentTarget) {
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             if (skipNextObservers === void 0) { skipNextObservers = false; }
             this.mask = mask;
             this.mask = mask;
@@ -6571,11 +6583,33 @@ var BABYLON;
      * Represent an Observer registered to a given Observable object.
      * Represent an Observer registered to a given Observable object.
      */
      */
     var Observer = /** @class */ (function () {
     var Observer = /** @class */ (function () {
-        function Observer(callback, mask, scope) {
+        /**
+         * Creates a new observer
+         * @param callback defines the callback to call when the observer is notified
+         * @param mask defines the mask of the observer (used to filter notifications)
+         * @param scope defines the current scope used to restore the JS context
+         */
+        function Observer(
+            /**
+             * Defines the callback to call when the observer is notified
+             */
+            callback, 
+            /**
+             * Defines the mask of the observer (used to filter notifications)
+             */
+            mask, 
+            /**
+             * Defines the current scope used to restore the JS context
+             */
+            scope) {
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
             this.callback = callback;
             this.callback = callback;
             this.mask = mask;
             this.mask = mask;
             this.scope = scope;
             this.scope = scope;
+            /**
+             * Gets or sets a property defining that the observer as to be unregistered after the next notification
+             */
+            this.unregisterOnNextCall = false;
         }
         }
         return Observer;
         return Observer;
     }());
     }());
@@ -6586,6 +6620,9 @@ var BABYLON;
     var MultiObserver = /** @class */ (function () {
     var MultiObserver = /** @class */ (function () {
         function MultiObserver() {
         function MultiObserver() {
         }
         }
+        /**
+         * Release associated resources
+         */
         MultiObserver.prototype.dispose = function () {
         MultiObserver.prototype.dispose = function () {
             if (this._observers && this._observables) {
             if (this._observers && this._observables) {
                 for (var index = 0; index < this._observers.length; index++) {
                 for (var index = 0; index < this._observers.length; index++) {
@@ -6595,6 +6632,14 @@ var BABYLON;
             this._observers = null;
             this._observers = null;
             this._observables = null;
             this._observables = null;
         };
         };
+        /**
+         * Raise a callback when one of the observable will notify
+         * @param observables defines a list of observables to watch
+         * @param callback defines the callback to call on notification
+         * @param mask defines the mask used to filter notifications
+         * @param scope defines the current scope used to restore the JS context
+         * @returns the new MultiObserver
+         */
         MultiObserver.Watch = function (observables, callback, mask, scope) {
         MultiObserver.Watch = function (observables, callback, mask, scope) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
@@ -6621,6 +6666,10 @@ var BABYLON;
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      */
      */
     var Observable = /** @class */ (function () {
     var Observable = /** @class */ (function () {
+        /**
+         * Creates a new observable
+         * @param onObserverAdded defines a callback to call when a new observer is added
+         */
         function Observable(onObserverAdded) {
         function Observable(onObserverAdded) {
             this._observers = new Array();
             this._observers = new Array();
             this._eventState = new EventState(0);
             this._eventState = new EventState(0);
@@ -6634,15 +6683,19 @@ var BABYLON;
          * @param mask the mask used to filter observers
          * @param mask the mask used to filter observers
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param scope optional scope for the callback to be called from
          * @param scope optional scope for the callback to be called from
+         * @param unregisterOnFirstCall defines if the observer as to be unregistered after the next notification
+         * @returns the new observer created for the callback
          */
          */
-        Observable.prototype.add = function (callback, mask, insertFirst, scope) {
+        Observable.prototype.add = function (callback, mask, insertFirst, scope, unregisterOnFirstCall) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             if (insertFirst === void 0) { insertFirst = false; }
             if (insertFirst === void 0) { insertFirst = false; }
             if (scope === void 0) { scope = null; }
             if (scope === void 0) { scope = null; }
+            if (unregisterOnFirstCall === void 0) { unregisterOnFirstCall = false; }
             if (!callback) {
             if (!callback) {
                 return null;
                 return null;
             }
             }
             var observer = new Observer(callback, mask, scope);
             var observer = new Observer(callback, mask, scope);
+            observer.unregisterOnNextCall = unregisterOnFirstCall;
             if (insertFirst) {
             if (insertFirst) {
                 this._observers.unshift(observer);
                 this._observers.unshift(observer);
             }
             }
@@ -6656,7 +6709,8 @@ var BABYLON;
         };
         };
         /**
         /**
          * Remove an Observer from the Observable object
          * Remove an Observer from the Observable object
-         * @param observer the instance of the Observer to remove. If it doesn't belong to this Observable, false will be returned.
+         * @param observer the instance of the Observer to remove
+         * @returns false if it doesn't belong to this Observable
          */
          */
         Observable.prototype.remove = function (observer) {
         Observable.prototype.remove = function (observer) {
             if (!observer) {
             if (!observer) {
@@ -6671,8 +6725,9 @@ var BABYLON;
         };
         };
         /**
         /**
          * Remove a callback from the Observable object
          * Remove a callback from the Observable object
-         * @param callback the callback to remove. If it doesn't belong to this Observable, false will be returned.
-         * @param scope optional scope. If used only the callbacks with this scope will be removed.
+         * @param callback the callback to remove
+         * @param scope optional scope. If used only the callbacks with this scope will be removed
+         * @returns false if it doesn't belong to this Observable
         */
         */
         Observable.prototype.removeCallback = function (callback, scope) {
         Observable.prototype.removeCallback = function (callback, scope) {
             for (var index = 0; index < this._observers.length; index++) {
             for (var index = 0; index < this._observers.length; index++) {
@@ -6683,11 +6738,20 @@ var BABYLON;
             }
             }
             return false;
             return false;
         };
         };
+        Observable.prototype._deferUnregister = function (observer) {
+            var _this = this;
+            BABYLON.Tools.SetImmediate(function () {
+                _this.remove(observer);
+            });
+        };
         /**
         /**
          * Notify all Observers by calling their respective callback with the given data
          * Notify all Observers by calling their respective callback with the given data
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
-         * @param eventData
-         * @param mask
+         * @param eventData defines the data to send to all observers
+         * @param mask defines the mask of the current notification (observers with incompatible mask (ie mask & observer.mask === 0) will not be notified)
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns false if the complete observer chain was not processed (because one observer set the skipNextObservers to true)
          */
          */
         Observable.prototype.notifyObservers = function (eventData, mask, target, currentTarget) {
         Observable.prototype.notifyObservers = function (eventData, mask, target, currentTarget) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -6709,6 +6773,9 @@ var BABYLON;
                     else {
                     else {
                         state.lastReturnValue = obs.callback(eventData, state);
                         state.lastReturnValue = obs.callback(eventData, state);
                     }
                     }
+                    if (obs.unregisterOnNextCall) {
+                        this._deferUnregister(obs);
+                    }
                 }
                 }
                 if (state.skipNextObservers) {
                 if (state.skipNextObservers) {
                     return false;
                     return false;
@@ -6725,11 +6792,12 @@ var BABYLON;
          *
          *
          * @param eventData The data to be sent to each callback
          * @param eventData The data to be sent to each callback
          * @param mask is used to filter observers defaults to -1
          * @param mask is used to filter observers defaults to -1
-         * @param target the callback target (see EventState)
-         * @param currentTarget The current object in the bubbling phase
+         * @param target defines the callback target (see EventState)
+         * @param currentTarget defines he current object in the bubbling phase
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          */
          */
         Observable.prototype.notifyObserversWithPromise = function (eventData, mask, target, currentTarget) {
         Observable.prototype.notifyObserversWithPromise = function (eventData, mask, target, currentTarget) {
+            var _this = this;
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
             // create an empty promise
             // create an empty promise
             var p = Promise.resolve(eventData);
             var p = Promise.resolve(eventData);
@@ -6760,6 +6828,9 @@ var BABYLON;
                             return obs.callback(eventData, state);
                             return obs.callback(eventData, state);
                         });
                         });
                     }
                     }
+                    if (obs.unregisterOnNextCall) {
+                        _this._deferUnregister(obs);
+                    }
                 }
                 }
             });
             });
             // return the eventData
             // return the eventData
@@ -6767,8 +6838,9 @@ var BABYLON;
         };
         };
         /**
         /**
          * Notify a specific observer
          * Notify a specific observer
-         * @param eventData
-         * @param mask
+         * @param observer defines the observer to notify
+         * @param eventData defines the data to be sent to each callback
+         * @param mask is used to filter observers defaults to -1
          */
          */
         Observable.prototype.notifyObserver = function (observer, eventData, mask) {
         Observable.prototype.notifyObserver = function (observer, eventData, mask) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -6778,7 +6850,8 @@ var BABYLON;
             observer.callback(eventData, state);
             observer.callback(eventData, state);
         };
         };
         /**
         /**
-         * return true is the Observable has at least one Observer registered
+         * Gets a boolean indicating if the observable has at least one observer
+         * @returns true is the Observable has at least one Observer registered
          */
          */
         Observable.prototype.hasObservers = function () {
         Observable.prototype.hasObservers = function () {
             return this._observers.length > 0;
             return this._observers.length > 0;
@@ -6791,8 +6864,9 @@ var BABYLON;
             this._onObserverAdded = null;
             this._onObserverAdded = null;
         };
         };
         /**
         /**
-        * Clone the current observable
-        */
+         * Clone the current observable
+         * @returns a new observable
+         */
         Observable.prototype.clone = function () {
         Observable.prototype.clone = function () {
             var result = new Observable();
             var result = new Observable();
             result._observers = this._observers.slice(0);
             result._observers = this._observers.slice(0);
@@ -6800,8 +6874,8 @@ var BABYLON;
         };
         };
         /**
         /**
          * Does this observable handles observer registered with a given mask
          * Does this observable handles observer registered with a given mask
-         * @param {number} trigger - the mask to be tested
-         * @return {boolean} whether or not one observer registered with the given mask is handeled
+         * @param mask defines the mask to be tested
+         * @return whether or not one observer registered with the given mask is handeled
         **/
         **/
         Observable.prototype.hasSpecificMask = function (mask) {
         Observable.prototype.hasSpecificMask = function (mask) {
             if (mask === void 0) { mask = -1; }
             if (mask === void 0) { mask = -1; }
@@ -15181,13 +15255,21 @@ var BABYLON;
             configurable: true
             configurable: true
         });
         });
         /**
         /**
+         * Sets a new matrix to apply before all other transformation
+         * @param matrix defines the transform matrix
+         * @returns the current TransformNode
+         */
+        TransformNode.prototype.setPreTransformMatrix = function (matrix) {
+            return this.setPivotMatrix(matrix, false);
+        };
+        /**
          * Sets a new pivot matrix to the current node
          * Sets a new pivot matrix to the current node
          * @param matrix defines the new pivot matrix to use
          * @param matrix defines the new pivot matrix to use
-         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
         TransformNode.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
-            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
+            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = true; }
             this._pivotMatrix = matrix.clone();
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
@@ -15365,12 +15447,10 @@ var BABYLON;
          * Sets a new pivot point to the current node
          * Sets a new pivot point to the current node
          * @param point defines the new pivot point to use
          * @param point defines the new pivot point to use
          * @param space defines if the point is in world or local space (local by default)
          * @param space defines if the point is in world or local space (local by default)
-         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
-        TransformNode.prototype.setPivotPoint = function (point, space, postMultiplyPivotMatrix) {
+        TransformNode.prototype.setPivotPoint = function (point, space) {
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
             if (space === void 0) { space = BABYLON.Space.LOCAL; }
-            if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
             if (this.getScene().getRenderId() == 0) {
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
                 this.computeWorldMatrix(true);
             }
             }
@@ -15380,20 +15460,7 @@ var BABYLON;
                 wm.invertToRef(tmat);
                 wm.invertToRef(tmat);
                 point = BABYLON.Vector3.TransformCoordinates(point, tmat);
                 point = BABYLON.Vector3.TransformCoordinates(point, tmat);
             }
             }
-            BABYLON.Vector3.TransformCoordinatesToRef(point, wm, this.position);
-            this._pivotMatrix.m[12] = -point.x;
-            this._pivotMatrix.m[13] = -point.y;
-            this._pivotMatrix.m[14] = -point.z;
-            if (this._postMultiplyPivotMatrix) {
-                if (!this._pivotMatrixInverse) {
-                    this._pivotMatrixInverse = BABYLON.Matrix.Invert(this._pivotMatrix);
-                }
-                else {
-                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
-                }
-            }
-            this._cache.pivotMatrixUpdated = true;
-            return this;
+            return this.setPivotMatrix(BABYLON.Matrix.Translation(point.x, point.y, point.z), true);
         };
         };
         /**
         /**
          * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.
          * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.

+ 2 - 207
dist/preview release/typedocValidationBaseline.json

@@ -1,7 +1,7 @@
 {
 {
-  "errors": 8166,
+  "errors": 8133,
   "babylon.typedoc.json": {
   "babylon.typedoc.json": {
-    "errors": 8166,
+    "errors": 8133,
     "AnimationKeyInterpolation": {
     "AnimationKeyInterpolation": {
       "Enumeration": {
       "Enumeration": {
         "Comments": {
         "Comments": {
@@ -14203,37 +14203,6 @@
         }
         }
       }
       }
     },
     },
-    "EventState": {
-      "Method": {
-        "initalize": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "mask": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "skipNextObservers": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "target": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "currentTarget": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      }
-    },
     "ExecuteCodeAction": {
     "ExecuteCodeAction": {
       "Class": {
       "Class": {
         "Comments": {
         "Comments": {
@@ -21963,42 +21932,6 @@
         }
         }
       }
       }
     },
     },
-    "MultiObserver": {
-      "Method": {
-        "dispose": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "Watch": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "observables": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "callback": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "mask": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "scope": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      }
-    },
     "MultiRenderTarget": {
     "MultiRenderTarget": {
       "Class": {
       "Class": {
         "Comments": {
         "Comments": {
@@ -22481,144 +22414,6 @@
         }
         }
       }
       }
     },
     },
-    "Observable": {
-      "Constructor": {
-        "new Observable": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "onObserverAdded": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      },
-      "Property": {
-        "_observers": {
-          "Comments": {
-            "MissingText": true
-          }
-        }
-      },
-      "Method": {
-        "add": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "clone": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "hasObservers": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "hasSpecificMask": {
-          "Parameter": {
-            "mask": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "notifyObserver": {
-          "Parameter": {
-            "observer": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "eventData": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "notifyObservers": {
-          "Comments": {
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "eventData": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "target": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "currentTarget": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "remove": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeCallback": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        }
-      }
-    },
-    "Observer": {
-      "Constructor": {
-        "new Observer": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "callback": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "mask": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "scope": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      },
-      "Property": {
-        "callback": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "mask": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "scope": {
-          "Comments": {
-            "MissingText": true
-          }
-        }
-      }
-    },
     "Octree": {
     "Octree": {
       "Class": {
       "Class": {
         "Comments": {
         "Comments": {

文件差异内容过多而无法显示
+ 7 - 7
dist/preview release/viewer/babylon.viewer.js


+ 2 - 1
dist/preview release/what's new.md

@@ -25,7 +25,7 @@
    ([carloslanderas](https://github.com/carloslanderas))
    ([carloslanderas](https://github.com/carloslanderas))
 - VRHelper now exposes onNewMeshPicked observable that will notify a PickingInfo object after meshSelectionPredicate evaluation
 - VRHelper now exposes onNewMeshPicked observable that will notify a PickingInfo object after meshSelectionPredicate evaluation
    ([carloslanderas](https://github.com/carloslanderas))
    ([carloslanderas](https://github.com/carloslanderas))
-- `AssetsManager` will now clear its `tasks` lsit from all successfully loaded tasks ([deltakosh](https://github.com/deltakosh))tasks ([deltakosh](https://github.com/deltakosh))
+- `AssetsManager` will now clear its `tasks` lsit from all successfully loaded tasks ([deltakosh](https://github.com/deltakosh))
 - Added documentation to WebVRCamera and VRExperienceHelper ([trevordev](https://github.com/trevordev))
 - Added documentation to WebVRCamera and VRExperienceHelper ([trevordev](https://github.com/trevordev))
 - Introduced `isStroke` on `HighlightLayerOptions` which makes the highlight solid ([PixelsCommander](https://github.com/pixelscommander))
 - Introduced `isStroke` on `HighlightLayerOptions` which makes the highlight solid ([PixelsCommander](https://github.com/pixelscommander))
 - (Viewer) There is now an option to paste payload instead of a URL for configuration ([RaananW](https://github.com/RaananW))
 - (Viewer) There is now an option to paste payload instead of a URL for configuration ([RaananW](https://github.com/RaananW))
@@ -46,6 +46,7 @@
 - (Viewer) Introducing the viewer labs - testing new features. ([RaananW](https://github.com/RaananW))
 - (Viewer) Introducing the viewer labs - testing new features. ([RaananW](https://github.com/RaananW))
 
 
 ## Bug fixes
 ## Bug fixes
+- `setPivotMatrix` ws not setting pivot correctly. This is now fixed. We also introduced a new `setPreTransformMatrix` to reproduce the sometimes needed behavior of the previous `setPivotMatrix` function ([deltakosh](https://github.com/deltakosh))
 - Texture extension detection in `Engine.CreateTexture` ([sebavan](https://github.com/sebavan))
 - Texture extension detection in `Engine.CreateTexture` ([sebavan](https://github.com/sebavan))
 - Fixed a bug with merging vertex data ([bghgary](https://github.com/bghgary))
 - Fixed a bug with merging vertex data ([bghgary](https://github.com/bghgary))
 
 

+ 1 - 1
src/Mesh/babylon.mesh.ts

@@ -2382,7 +2382,7 @@
             mesh.scaling = Vector3.FromArray(parsedMesh.scaling);
             mesh.scaling = Vector3.FromArray(parsedMesh.scaling);
 
 
             if (parsedMesh.localMatrix) {
             if (parsedMesh.localMatrix) {
-                mesh.setPivotMatrix(Matrix.FromArray(parsedMesh.localMatrix));
+                mesh.setPreTransformMatrix(Matrix.FromArray(parsedMesh.localMatrix));
             } else if (parsedMesh.pivotMatrix) {
             } else if (parsedMesh.pivotMatrix) {
                 mesh.setPivotMatrix(Matrix.FromArray(parsedMesh.pivotMatrix));
                 mesh.setPivotMatrix(Matrix.FromArray(parsedMesh.pivotMatrix));
             }
             }

+ 14 - 20
src/Mesh/babylon.transformNode.ts

@@ -208,12 +208,21 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
+         * Sets a new matrix to apply before all other transformation
+         * @param matrix defines the transform matrix
+         * @returns the current TransformNode
+         */
+        public setPreTransformMatrix(matrix: Matrix): TransformNode {
+            return this.setPivotMatrix(matrix, false);
+        }
+
+        /**
          * Sets a new pivot matrix to the current node
          * Sets a new pivot matrix to the current node
          * @param matrix defines the new pivot matrix to use
          * @param matrix defines the new pivot matrix to use
-         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
+         * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
-        public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = false): TransformNode {
+        public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = true): TransformNode {
             this._pivotMatrix = matrix.clone();
             this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
@@ -401,10 +410,9 @@ module BABYLON {
          * Sets a new pivot point to the current node
          * Sets a new pivot point to the current node
          * @param point defines the new pivot point to use
          * @param point defines the new pivot point to use
          * @param space defines if the point is in world or local space (local by default)
          * @param space defines if the point is in world or local space (local by default)
-         * @param postMultiplyPivotMatrix defines if the pivot transformation must be cancelled in the world matrix. By default the pivot matrix is just applied at the beginning of the world matrix. When this parameter is set to true, the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */        
         */        
-        public setPivotPoint(point: Vector3, space: Space = Space.LOCAL, postMultiplyPivotMatrix = false): TransformNode {
+        public setPivotPoint(point: Vector3, space: Space = Space.LOCAL): TransformNode {
             if (this.getScene().getRenderId() == 0) {
             if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
                 this.computeWorldMatrix(true);
             }
             }
@@ -417,21 +425,7 @@ module BABYLON {
                 point = Vector3.TransformCoordinates(point, tmat);
                 point = Vector3.TransformCoordinates(point, tmat);
             }
             }
 
 
-            Vector3.TransformCoordinatesToRef(point, wm, this.position);
-            this._pivotMatrix.m[12] = -point.x;
-            this._pivotMatrix.m[13] = -point.y;
-            this._pivotMatrix.m[14] = -point.z;
-
-            if (this._postMultiplyPivotMatrix) {
-                if (!this._pivotMatrixInverse) {
-                    this._pivotMatrixInverse = Matrix.Invert(this._pivotMatrix);
-                } else {
-                    this._pivotMatrix.invertToRef(this._pivotMatrixInverse);
-                }
-            }
-
-            this._cache.pivotMatrixUpdated = true;
-            return this;
+            return this.setPivotMatrix(Matrix.Translation(point.x, point.y, point.z), true);
         }
         }
 
 
         /**
         /**
@@ -955,7 +949,7 @@ module BABYLON {
             }
             }
 
 
             if (parsedTransformNode.localMatrix) {
             if (parsedTransformNode.localMatrix) {
-                transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.localMatrix));
+                transformNode.setPreTransformMatrix(Matrix.FromArray(parsedTransformNode.localMatrix));
             } else if (parsedTransformNode.pivotMatrix) {
             } else if (parsedTransformNode.pivotMatrix) {
                 transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.pivotMatrix));
                 transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.pivotMatrix));
             }
             }

+ 2 - 2
src/Physics/Plugins/babylon.cannonJSPlugin.ts

@@ -435,7 +435,7 @@
 
 
                 //calculate the new center using a pivot (since this.BJSCANNON.js doesn't center height maps)
                 //calculate the new center using a pivot (since this.BJSCANNON.js doesn't center height maps)
                 var p = Matrix.Translation(boundingInfo.boundingBox.extendSizeWorld.x, 0, -boundingInfo.boundingBox.extendSizeWorld.z);
                 var p = Matrix.Translation(boundingInfo.boundingBox.extendSizeWorld.x, 0, -boundingInfo.boundingBox.extendSizeWorld.z);
-                mesh.setPivotMatrix(p);
+                mesh.setPreTransformMatrix(p);
                 mesh.computeWorldMatrix(true);
                 mesh.computeWorldMatrix(true);
 
 
                 //calculate the translation
                 //calculate the translation
@@ -448,7 +448,7 @@
                 //rotation is back
                 //rotation is back
                 mesh.rotationQuaternion = rotationQuaternion;
                 mesh.rotationQuaternion = rotationQuaternion;
 
 
-                mesh.setPivotMatrix(oldPivot);
+                mesh.setPreTransformMatrix(oldPivot);
                 mesh.computeWorldMatrix(true);
                 mesh.computeWorldMatrix(true);
             } else if (impostor.type === PhysicsImpostor.MeshImpostor) {
             } else if (impostor.type === PhysicsImpostor.MeshImpostor) {
                 this._tmpDeltaPosition.copyFromFloats(0, 0, 0);
                 this._tmpDeltaPosition.copyFromFloats(0, 0, 0);

+ 93 - 19
src/Tools/babylon.observable.ts

@@ -6,12 +6,24 @@
     export class EventState {
     export class EventState {
 
 
         /**
         /**
-        * If the callback of a given Observer set skipNextObservers to true the following observers will be ignored
-        */
+         * Create a new EventState
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         */
         constructor(mask: number, skipNextObservers = false, target?: any, currentTarget?: any) {
         constructor(mask: number, skipNextObservers = false, target?: any, currentTarget?: any) {
             this.initalize(mask, skipNextObservers, target, currentTarget);
             this.initalize(mask, skipNextObservers, target, currentTarget);
         }
         }
 
 
+        /**
+         * Initialize the current event state
+         * @param mask defines the mask associated with this state
+         * @param skipNextObservers defines a flag which will instruct the observable to skip following observers when set to true
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns the current event state
+         */
         public initalize(mask: number, skipNextObservers = false, target?: any, currentTarget?: any): EventState {
         public initalize(mask: number, skipNextObservers = false, target?: any, currentTarget?: any): EventState {
             this.mask = mask;
             this.mask = mask;
             this.skipNextObservers = skipNextObservers;
             this.skipNextObservers = skipNextObservers;
@@ -51,7 +63,30 @@
      * Represent an Observer registered to a given Observable object.
      * Represent an Observer registered to a given Observable object.
      */
      */
     export class Observer<T> {
     export class Observer<T> {
-        constructor(public callback: (eventData: T, eventState: EventState) => void, public mask: number, public scope: any = null) {
+        /**
+         * Gets or sets a property defining that the observer as to be unregistered after the next notification
+         */
+        public unregisterOnNextCall = false;
+
+        /**
+         * Creates a new observer
+         * @param callback defines the callback to call when the observer is notified
+         * @param mask defines the mask of the observer (used to filter notifications)
+         * @param scope defines the current scope used to restore the JS context
+         */
+        constructor(
+            /**
+             * Defines the callback to call when the observer is notified
+             */
+            public callback: (eventData: T, eventState: EventState) => void, 
+            /**
+             * Defines the mask of the observer (used to filter notifications)
+             */
+            public mask: number, 
+            /**
+             * Defines the current scope used to restore the JS context
+             */
+            public scope: any = null) {
         }
         }
     }
     }
 
 
@@ -62,6 +97,9 @@
         private _observers: Nullable<Observer<T>[]>;
         private _observers: Nullable<Observer<T>[]>;
         private _observables: Nullable<Observable<T>[]>;
         private _observables: Nullable<Observable<T>[]>;
 
 
+        /**
+         * Release associated resources
+         */
         public dispose(): void {
         public dispose(): void {
             if (this._observers && this._observables) {
             if (this._observers && this._observables) {
                 for (var index = 0; index < this._observers.length; index++) {
                 for (var index = 0; index < this._observers.length; index++) {
@@ -73,6 +111,14 @@
             this._observables = null;
             this._observables = null;
         }
         }
 
 
+        /**
+         * Raise a callback when one of the observable will notify
+         * @param observables defines a list of observables to watch
+         * @param callback defines the callback to call on notification
+         * @param mask defines the mask used to filter notifications
+         * @param scope defines the current scope used to restore the JS context
+         * @returns the new MultiObserver
+         */
         public static Watch<T>(observables: Observable<T>[], callback: (eventData: T, eventState: EventState) => void, mask: number = -1, scope: any = null): MultiObserver<T> {
         public static Watch<T>(observables: Observable<T>[], callback: (eventData: T, eventState: EventState) => void, mask: number = -1, scope: any = null): MultiObserver<T> {
             let result = new MultiObserver<T>();
             let result = new MultiObserver<T>();
 
 
@@ -98,12 +144,16 @@
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      * A given observer can register itself with only Move and Stop (mask = 0x03), then it will only be notified when one of these two occurs and will never be for Turn Left/Right.
      */
      */
     export class Observable<T> {
     export class Observable<T> {
-        _observers = new Array<Observer<T>>();
+        private _observers = new Array<Observer<T>>();
 
 
         private _eventState: EventState;
         private _eventState: EventState;
 
 
         private _onObserverAdded: Nullable<(observer: Observer<T>) => void>;
         private _onObserverAdded: Nullable<(observer: Observer<T>) => void>;
 
 
+        /**
+         * Creates a new observable
+         * @param onObserverAdded defines a callback to call when a new observer is added
+         */
         constructor(onObserverAdded?: (observer: Observer<T>) => void) {
         constructor(onObserverAdded?: (observer: Observer<T>) => void) {
             this._eventState = new EventState(0);
             this._eventState = new EventState(0);
 
 
@@ -118,13 +168,16 @@
          * @param mask the mask used to filter observers
          * @param mask the mask used to filter observers
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param insertFirst if true the callback will be inserted at the first position, hence executed before the others ones. If false (default behavior) the callback will be inserted at the last position, executed after all the others already present.
          * @param scope optional scope for the callback to be called from
          * @param scope optional scope for the callback to be called from
+         * @param unregisterOnFirstCall defines if the observer as to be unregistered after the next notification
+         * @returns the new observer created for the callback
          */
          */
-        public add(callback: (eventData: T, eventState: EventState) => void, mask: number = -1, insertFirst = false, scope: any = null): Nullable<Observer<T>> {
+        public add(callback: (eventData: T, eventState: EventState) => void, mask: number = -1, insertFirst = false, scope: any = null, unregisterOnFirstCall = false): Nullable<Observer<T>> {
             if (!callback) {
             if (!callback) {
                 return null;
                 return null;
             }
             }
 
 
             var observer = new Observer(callback, mask, scope);
             var observer = new Observer(callback, mask, scope);
+            observer.unregisterOnNextCall = unregisterOnFirstCall;
 
 
             if (insertFirst) {
             if (insertFirst) {
                 this._observers.unshift(observer);
                 this._observers.unshift(observer);
@@ -141,7 +194,8 @@
 
 
         /**
         /**
          * Remove an Observer from the Observable object
          * Remove an Observer from the Observable object
-         * @param observer the instance of the Observer to remove. If it doesn't belong to this Observable, false will be returned.
+         * @param observer the instance of the Observer to remove
+         * @returns false if it doesn't belong to this Observable
          */
          */
         public remove(observer: Nullable<Observer<T>>): boolean {
         public remove(observer: Nullable<Observer<T>>): boolean {
             if (!observer) {
             if (!observer) {
@@ -162,8 +216,9 @@
 
 
         /**
         /**
          * Remove a callback from the Observable object
          * Remove a callback from the Observable object
-         * @param callback the callback to remove. If it doesn't belong to this Observable, false will be returned.
-         * @param scope optional scope. If used only the callbacks with this scope will be removed.
+         * @param callback the callback to remove
+         * @param scope optional scope. If used only the callbacks with this scope will be removed
+         * @returns false if it doesn't belong to this Observable
         */
         */
         public removeCallback(callback: (eventData: T, eventState: EventState) => void, scope?: any): boolean {
         public removeCallback(callback: (eventData: T, eventState: EventState) => void, scope?: any): boolean {
 
 
@@ -177,11 +232,20 @@
             return false;
             return false;
         }
         }
 
 
+        private _deferUnregister(observer: Observer<T>): void {
+            Tools.SetImmediate(() => {
+                this.remove(observer);
+            })
+        }
+
         /**
         /**
          * Notify all Observers by calling their respective callback with the given data
          * Notify all Observers by calling their respective callback with the given data
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
          * Will return true if all observers were executed, false if an observer set skipNextObservers to true, then prevent the subsequent ones to execute
-         * @param eventData
-         * @param mask
+         * @param eventData defines the data to send to all observers
+         * @param mask defines the mask of the current notification (observers with incompatible mask (ie mask & observer.mask === 0) will not be notified)
+         * @param target defines the original target of the state
+         * @param currentTarget defines the current target of the state
+         * @returns false if the complete observer chain was not processed (because one observer set the skipNextObservers to true)
          */
          */
         public notifyObservers(eventData: T, mask: number = -1, target?: any, currentTarget?: any): boolean {
         public notifyObservers(eventData: T, mask: number = -1, target?: any, currentTarget?: any): boolean {
             if (!this._observers.length) {
             if (!this._observers.length) {
@@ -202,6 +266,10 @@
                     } else {
                     } else {
                         state.lastReturnValue = obs.callback(eventData, state);
                         state.lastReturnValue = obs.callback(eventData, state);
                     }
                     }
+
+                    if (obs.unregisterOnNextCall) {
+                        this._deferUnregister(obs);
+                    }
                 }
                 }
                 if (state.skipNextObservers) {
                 if (state.skipNextObservers) {
                     return false;
                     return false;
@@ -219,8 +287,8 @@
          * 
          * 
          * @param eventData The data to be sent to each callback
          * @param eventData The data to be sent to each callback
          * @param mask is used to filter observers defaults to -1
          * @param mask is used to filter observers defaults to -1
-         * @param target the callback target (see EventState)
-         * @param currentTarget The current object in the bubbling phase
+         * @param target defines the callback target (see EventState)
+         * @param currentTarget defines he current object in the bubbling phase
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          * @returns {Promise<T>} will return a Promise than resolves when all callbacks executed successfully.
          */
          */
         public notifyObserversWithPromise(eventData: T, mask: number = -1, target?: any, currentTarget?: any): Promise<T> {
         public notifyObserversWithPromise(eventData: T, mask: number = -1, target?: any, currentTarget?: any): Promise<T> {
@@ -256,6 +324,9 @@
                             return obs.callback(eventData, state);
                             return obs.callback(eventData, state);
                         });
                         });
                     }
                     }
+                    if (obs.unregisterOnNextCall) {
+                        this._deferUnregister(obs);
+                    }                    
                 }
                 }
             });
             });
 
 
@@ -265,8 +336,9 @@
 
 
         /**
         /**
          * Notify a specific observer
          * Notify a specific observer
-         * @param eventData
-         * @param mask
+         * @param observer defines the observer to notify
+         * @param eventData defines the data to be sent to each callback
+         * @param mask is used to filter observers defaults to -1
          */
          */
         public notifyObserver(observer: Observer<T>, eventData: T, mask: number = -1): void {
         public notifyObserver(observer: Observer<T>, eventData: T, mask: number = -1): void {
             let state = this._eventState;
             let state = this._eventState;
@@ -277,7 +349,8 @@
         }
         }
 
 
         /**
         /**
-         * return true is the Observable has at least one Observer registered
+         * Gets a boolean indicating if the observable has at least one observer
+         * @returns true is the Observable has at least one Observer registered
          */
          */
         public hasObservers(): boolean {
         public hasObservers(): boolean {
             return this._observers.length > 0;
             return this._observers.length > 0;
@@ -292,8 +365,9 @@
         }
         }
 
 
         /**
         /**
-        * Clone the current observable
-        */
+         * Clone the current observable
+         * @returns a new observable
+         */
         public clone(): Observable<T> {
         public clone(): Observable<T> {
             var result = new Observable<T>();
             var result = new Observable<T>();
 
 
@@ -304,8 +378,8 @@
 
 
         /**
         /**
          * Does this observable handles observer registered with a given mask
          * Does this observable handles observer registered with a given mask
-         * @param {number} trigger - the mask to be tested
-         * @return {boolean} whether or not one observer registered with the given mask is handeled 
+         * @param mask defines the mask to be tested
+         * @return whether or not one observer registered with the given mask is handeled 
         **/
         **/
         public hasSpecificMask(mask: number = -1): boolean {
         public hasSpecificMask(mask: number = -1): boolean {
             for (var obs of this._observers) {
             for (var obs of this._observers) {