Forráskód Böngészése

Merge pull request #3485 from BabylonJS/master

Merge
David Catuhe 7 éve
szülő
commit
8a0c6078d1

+ 16 - 13
.travis.yml

@@ -1,13 +1,16 @@
-language: node_js
-node_js:
-  - "6"
-addons:
-  sauce_connect:
-    username: "vandenberghe.sebastien@gmail.com"
-  jwt:
-    secure: kbaJ3o6cVqpB8Gzfd0yDSgGwH+SQXukevtpjMQ8Gip+N6FOumQxihPDqsqYKxc8svvR024nDmkIhbjPjz/YguvC3WUgYW4xk3Za7P7C9cpj2fYdrFuO7pD4sd/fNdUCqvKQ8jcxlIq4eEdBuoBTOHsP9J5KH7Z1M7e58atkx0o8=
-before_script:
-  - npm install -g gulp
-  - cd ./Tools/Gulp
-  - npm install
-script: gulp
+language: node_js
+node_js:
+- '6'
+addons:
+  sauce_connect:
+    username: vandenberghe.sebastien@gmail.com
+  jwt:
+    secure: kbaJ3o6cVqpB8Gzfd0yDSgGwH+SQXukevtpjMQ8Gip+N6FOumQxihPDqsqYKxc8svvR024nDmkIhbjPjz/YguvC3WUgYW4xk3Za7P7C9cpj2fYdrFuO7pD4sd/fNdUCqvKQ8jcxlIq4eEdBuoBTOHsP9J5KH7Z1M7e58atkx0o8=
+before_script:
+- npm install -g gulp
+- cd ./Tools/Gulp
+- npm install
+script: gulp
+notifications:
+  slack:
+    secure: TBYDAN8Dlkx3dM+Q5ClAZem7agAhQ1oB/fGT665qn7D+j2YfWChvlfXegvXL4LPDmQgbI0UfazcjWId5a0EwmmPkRb+kMJItPiMt5jiIp2WKoZQ+qob6H9tBCRJbbpWM430wiPeKfBfbcZP/XSlpVMWhgU5ogAFDSUKjvHT7IuE=

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 897 - 835
Playground/babylon.d.txt


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 867 - 804
dist/preview release/babylon.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 36 - 36
dist/preview release/babylon.js


+ 89 - 1
dist/preview release/babylon.max.js

@@ -20040,6 +20040,17 @@ var BABYLON;
             animatable.reset();
             return animatable;
         };
+        /**
+         * Begin a new animation on a given node
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {BABYLON.Animation[]} defines the list of animations to start
+         * @param {number} from defines the initial value
+         * @param {number} to defines the final value
+         * @param {boolean} loop defines if you want animation to loop (off by default)
+         * @param {number} speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of created animatables
+         */
         Scene.prototype.beginDirectAnimation = function (target, animations, from, to, loop, speedRatio, onAnimationEnd) {
             if (speedRatio === undefined) {
                 speedRatio = 1.0;
@@ -20047,6 +20058,27 @@ var BABYLON;
             var animatable = new BABYLON.Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd, animations);
             return animatable;
         };
+        /**
+         * Begin a new animation on a given node and its hierarchy
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {boolean} directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used.
+         * @param {BABYLON.Animation[]} defines the list of animations to start
+         * @param {number} from defines the initial value
+         * @param {number} to defines the final value
+         * @param {boolean} loop defines if you want animation to loop (off by default)
+         * @param {number} speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of animatables created for all nodes
+         */
+        Scene.prototype.beginDirectHierarchyAnimation = function (target, directDescendantsOnly, animations, from, to, loop, speedRatio, onAnimationEnd) {
+            var children = target.getDescendants(directDescendantsOnly);
+            var result = [];
+            for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
+                var child = children_1[_i];
+                result.push(this.beginDirectAnimation(child, animations, from, to, loop, speedRatio, onAnimationEnd));
+            }
+            return result;
+        };
         Scene.prototype.getAnimatableByTarget = function (target) {
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 if (this._activeAnimatables[index].target === target) {
@@ -41776,6 +41808,20 @@ var BABYLON;
             animation.setEasingFunction(easingFunction);
             return animation;
         };
+        /**
+         * Create and start an animation on a node
+         * @param {string} name defines the name of the global animation that will be run on all nodes
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {string} targetProperty defines property to animate
+         * @param {number} framePerSecond defines the number of frame per second yo use
+         * @param {number} totalFrame defines the number of frames in total
+         * @param {any} from defines the initial value
+         * @param {any} to defines the final value
+         * @param {number} loopMode defines which loop mode you want to use (off by default)
+         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param onAnimationEnd defines the callback to call when animation end
+         * @returns the animatable created for this animation
+         */
         Animation.CreateAndStartAnimation = function (name, node, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction, onAnimationEnd) {
             var animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
             if (!animation) {
@@ -41783,6 +41829,29 @@ var BABYLON;
             }
             return node.getScene().beginDirectAnimation(node, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
         };
+        /**
+         * Create and start an animation on a node and its descendants
+         * @param {string} name defines the name of the global animation that will be run on all nodes
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {boolean} directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used.
+         * @param {string} targetProperty defines property to animate
+         * @param {number} framePerSecond defines the number of frame per second yo use
+         * @param {number} totalFrame defines the number of frames in total
+         * @param {any} from defines the initial value
+         * @param {any} to defines the final value
+         * @param {number} loopMode defines which loop mode you want to use (off by default)
+         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of animatables created for all nodes
+         */
+        Animation.CreateAndStartHierarchyAnimation = function (name, node, directDescendantsOnly, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction, onAnimationEnd) {
+            var animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
+            if (!animation) {
+                return null;
+            }
+            var scene = node.getScene();
+            return scene.beginDirectHierarchyAnimation(node, directDescendantsOnly, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
+        };
         Animation.CreateMergeAndStartAnimation = function (name, node, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction, onAnimationEnd) {
             var animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
             if (!animation) {
@@ -73585,6 +73654,14 @@ var BABYLON;
             this._padSensibilityDown = 0.35;
             this.onNewMeshSelected = new BABYLON.Observable();
             /**
+             * Observable raised before camera teleportation
+            */
+            this.onBeforeCameraTeleport = new BABYLON.Observable();
+            /**
+             *  Observable raised after camera teleportation
+            */
+            this.onAfterCameraTeleport = new BABYLON.Observable();
+            /**
             * Observable raised when current selected mesh gets unselected
             */
             this.onSelectedMeshUnselected = new BABYLON.Observable();
@@ -74635,6 +74712,7 @@ var BABYLON;
             else {
                 this._workingVector.y += this._defaultHeight;
             }
+            this.onBeforeCameraTeleport.notifyObservers(this._workingVector);
             // Create animation from the camera's position to the new location
             this.currentVRCamera.animations = [];
             var animationCameraTeleportation = new BABYLON.Animation("animationCameraTeleportation", "position", 90, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
@@ -74689,7 +74767,9 @@ var BABYLON;
             this._scene.beginAnimation(this._postProcessMove, 0, 11, false, 1, function () {
                 _this._webVRCamera.detachPostProcess(_this._postProcessMove);
             });
-            this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1);
+            this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1, function () {
+                _this.onAfterCameraTeleport.notifyObservers(_this._workingVector);
+            });
         };
         VRExperienceHelper.prototype._castRayAndSelectObject = function () {
             if (!(this.currentVRCamera instanceof BABYLON.FreeCamera)) {
@@ -77577,6 +77657,7 @@ var BABYLON;
     BABYLON.HDRCubeTextureAssetTask = HDRCubeTextureAssetTask;
     var AssetsManager = /** @class */ (function () {
         function AssetsManager(scene) {
+            this._isLoading = false;
             this.tasks = new Array();
             this.waitingTasksCount = 0;
             //Observables
@@ -77650,6 +77731,7 @@ var BABYLON;
                     BABYLON.Tools.Error("Error running tasks-done callbacks.");
                     console.log(e);
                 }
+                this._isLoading = false;
                 this._scene.getEngine().hideLoadingUI();
             }
         };
@@ -77681,16 +77763,22 @@ var BABYLON;
             task.run(this._scene, done, error);
         };
         AssetsManager.prototype.reset = function () {
+            this._isLoading = false;
             this.tasks = new Array();
             return this;
         };
         AssetsManager.prototype.load = function () {
+            if (this._isLoading) {
+                return this;
+            }
+            this._isLoading = true;
             this.waitingTasksCount = this.tasks.length;
             if (this.waitingTasksCount === 0) {
                 if (this.onFinish) {
                     this.onFinish(this.tasks);
                 }
                 this.onTasksDoneObservable.notifyObservers(this.tasks);
+                this._isLoading = false;
                 return this;
             }
             if (this.useDefaultLoadingScreen) {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 37 - 37
dist/preview release/babylon.worker.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 5766 - 5702
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 36 - 36
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 90 - 1
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -20040,6 +20040,17 @@ var BABYLON;
             animatable.reset();
             return animatable;
         };
+        /**
+         * Begin a new animation on a given node
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {BABYLON.Animation[]} defines the list of animations to start
+         * @param {number} from defines the initial value
+         * @param {number} to defines the final value
+         * @param {boolean} loop defines if you want animation to loop (off by default)
+         * @param {number} speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of created animatables
+         */
         Scene.prototype.beginDirectAnimation = function (target, animations, from, to, loop, speedRatio, onAnimationEnd) {
             if (speedRatio === undefined) {
                 speedRatio = 1.0;
@@ -20047,6 +20058,27 @@ var BABYLON;
             var animatable = new BABYLON.Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd, animations);
             return animatable;
         };
+        /**
+         * Begin a new animation on a given node and its hierarchy
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {boolean} directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used.
+         * @param {BABYLON.Animation[]} defines the list of animations to start
+         * @param {number} from defines the initial value
+         * @param {number} to defines the final value
+         * @param {boolean} loop defines if you want animation to loop (off by default)
+         * @param {number} speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of animatables created for all nodes
+         */
+        Scene.prototype.beginDirectHierarchyAnimation = function (target, directDescendantsOnly, animations, from, to, loop, speedRatio, onAnimationEnd) {
+            var children = target.getDescendants(directDescendantsOnly);
+            var result = [];
+            for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
+                var child = children_1[_i];
+                result.push(this.beginDirectAnimation(child, animations, from, to, loop, speedRatio, onAnimationEnd));
+            }
+            return result;
+        };
         Scene.prototype.getAnimatableByTarget = function (target) {
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 if (this._activeAnimatables[index].target === target) {
@@ -41776,6 +41808,20 @@ var BABYLON;
             animation.setEasingFunction(easingFunction);
             return animation;
         };
+        /**
+         * Create and start an animation on a node
+         * @param {string} name defines the name of the global animation that will be run on all nodes
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {string} targetProperty defines property to animate
+         * @param {number} framePerSecond defines the number of frame per second yo use
+         * @param {number} totalFrame defines the number of frames in total
+         * @param {any} from defines the initial value
+         * @param {any} to defines the final value
+         * @param {number} loopMode defines which loop mode you want to use (off by default)
+         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param onAnimationEnd defines the callback to call when animation end
+         * @returns the animatable created for this animation
+         */
         Animation.CreateAndStartAnimation = function (name, node, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction, onAnimationEnd) {
             var animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
             if (!animation) {
@@ -41783,6 +41829,30 @@ var BABYLON;
             }
             return node.getScene().beginDirectAnimation(node, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
         };
+        /**
+         * Create and start an animation on a node and its descendants
+         * @param {string} name defines the name of the global animation that will be run on all nodes
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {boolean} directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used.
+         * @param {string} targetProperty defines property to animate
+         * @param {number} framePerSecond defines the number of frame per second yo use
+         * @param {number} totalFrame defines the number of frames in total
+         * @param {any} from defines the initial value
+         * @param {any} to defines the final value
+         * @param {number} loopMode defines which loop mode you want to use (off by default)
+         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of animatables created for all nodes
+         * @example https://www.babylonjs-playground.com/#MH0VLI
+         */
+        Animation.CreateAndStartHierarchyAnimation = function (name, node, directDescendantsOnly, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction, onAnimationEnd) {
+            var animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
+            if (!animation) {
+                return null;
+            }
+            var scene = node.getScene();
+            return scene.beginDirectHierarchyAnimation(node, directDescendantsOnly, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
+        };
         Animation.CreateMergeAndStartAnimation = function (name, node, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction, onAnimationEnd) {
             var animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
             if (!animation) {
@@ -73431,6 +73501,14 @@ var BABYLON;
             this._padSensibilityDown = 0.35;
             this.onNewMeshSelected = new BABYLON.Observable();
             /**
+             * Observable raised before camera teleportation
+            */
+            this.onBeforeCameraTeleport = new BABYLON.Observable();
+            /**
+             *  Observable raised after camera teleportation
+            */
+            this.onAfterCameraTeleport = new BABYLON.Observable();
+            /**
             * Observable raised when current selected mesh gets unselected
             */
             this.onSelectedMeshUnselected = new BABYLON.Observable();
@@ -74481,6 +74559,7 @@ var BABYLON;
             else {
                 this._workingVector.y += this._defaultHeight;
             }
+            this.onBeforeCameraTeleport.notifyObservers(this._workingVector);
             // Create animation from the camera's position to the new location
             this.currentVRCamera.animations = [];
             var animationCameraTeleportation = new BABYLON.Animation("animationCameraTeleportation", "position", 90, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
@@ -74535,7 +74614,9 @@ var BABYLON;
             this._scene.beginAnimation(this._postProcessMove, 0, 11, false, 1, function () {
                 _this._webVRCamera.detachPostProcess(_this._postProcessMove);
             });
-            this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1);
+            this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1, function () {
+                _this.onAfterCameraTeleport.notifyObservers(_this._workingVector);
+            });
         };
         VRExperienceHelper.prototype._castRayAndSelectObject = function () {
             if (!(this.currentVRCamera instanceof BABYLON.FreeCamera)) {
@@ -77423,6 +77504,7 @@ var BABYLON;
     BABYLON.HDRCubeTextureAssetTask = HDRCubeTextureAssetTask;
     var AssetsManager = /** @class */ (function () {
         function AssetsManager(scene) {
+            this._isLoading = false;
             this.tasks = new Array();
             this.waitingTasksCount = 0;
             //Observables
@@ -77496,6 +77578,7 @@ var BABYLON;
                     BABYLON.Tools.Error("Error running tasks-done callbacks.");
                     console.log(e);
                 }
+                this._isLoading = false;
                 this._scene.getEngine().hideLoadingUI();
             }
         };
@@ -77527,16 +77610,22 @@ var BABYLON;
             task.run(this._scene, done, error);
         };
         AssetsManager.prototype.reset = function () {
+            this._isLoading = false;
             this.tasks = new Array();
             return this;
         };
         AssetsManager.prototype.load = function () {
+            if (this._isLoading) {
+                return this;
+            }
+            this._isLoading = true;
             this.waitingTasksCount = this.tasks.length;
             if (this.waitingTasksCount === 0) {
                 if (this.onFinish) {
                     this.onFinish(this.tasks);
                 }
                 this.onTasksDoneObservable.notifyObservers(this.tasks);
+                this._isLoading = false;
                 return this;
             }
             if (this.useDefaultLoadingScreen) {

+ 3 - 0
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -485,6 +485,9 @@ var BABYLON;
                     }
                     gltf.meshes.push(mesh);
                     node.mesh = gltf.meshes.length - 1;
+                    if (babylonMesh.name) {
+                        node.name = babylonMesh.name;
+                    }
                     gltf.nodes.push(node);
                     scene.nodes.push(gltf.nodes.length - 1);
                 }

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 3 - 0
dist/preview release/serializers/babylonjs.serializers.js

@@ -629,6 +629,9 @@ var BABYLON;
                     }
                     gltf.meshes.push(mesh);
                     node.mesh = gltf.meshes.length - 1;
+                    if (babylonMesh.name) {
+                        node.name = babylonMesh.name;
+                    }
                     gltf.nodes.push(node);
                     scene.nodes.push(gltf.nodes.length - 1);
                 }

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 35 - 35
dist/preview release/viewer/babylon.viewer.js


+ 5 - 0
dist/preview release/what's new.md

@@ -8,6 +8,11 @@
 - New watcher configuration for VSCode. Now the task only compiles changed files ([sebavan](https://github.com/sebavan))
 - Added new draw modes to engine (points, lines, linesloop, linestrip, trianglestrip, trianglefan) ([benaadams](https://github.com/benaadams))
 - Added GUI Textblock.lineSpacing setter and getter to configure vertical space between lines in pixels or percentage values when working with text wrapping ([carloslanderas](github.com/carloslanderas))
+- VRHelper now has onSelectedMeshUnselected observable that will notify observers when the current selected mesh gets unselected
+  ([carloslanderas](github.com/carloslanderas))
+- VRHelper now has onBeforeCameraTeleport and onAfterCameraTeleport observables that will be notified before and after camera teleportation is triggered.
+  ([carloslanderas](github.com/carloslanderas))
+  
 
 ## Bug fixes
 

+ 4 - 0
serializers/src/glTF/2.0/babylon.glTFSerializer.ts

@@ -10,6 +10,7 @@ module BABYLON {
     }
     interface OGLTFNode {
         mesh: number;
+        name?: string;
         translation?: number[];
         scale?: number[];
         rotation?: number[];
@@ -623,6 +624,9 @@ module BABYLON {
                     }
                     gltf.meshes.push(mesh);
                     node.mesh = gltf.meshes.length - 1;
+                    if (babylonMesh.name) {
+                      node.name = babylonMesh.name;
+                    }
                     gltf.nodes.push(node);
 
                     scene.nodes.push(gltf.nodes.length - 1);

+ 44 - 0
src/Animations/babylon.animation.ts

@@ -151,6 +151,20 @@
             return animation;
         }
 
+        /**
+         * Create and start an animation on a node
+         * @param {string} name defines the name of the global animation that will be run on all nodes
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {string} targetProperty defines property to animate
+         * @param {number} framePerSecond defines the number of frame per second yo use
+         * @param {number} totalFrame defines the number of frames in total
+         * @param {any} from defines the initial value
+         * @param {any} to defines the final value
+         * @param {number} loopMode defines which loop mode you want to use (off by default)
+         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param onAnimationEnd defines the callback to call when animation end
+         * @returns the animatable created for this animation
+         */
         public static CreateAndStartAnimation(name: string, node: Node, targetProperty: string,
             framePerSecond: number, totalFrame: number,
             from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void): Nullable<Animatable> {
@@ -164,6 +178,36 @@
             return node.getScene().beginDirectAnimation(node, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
         }
 
+        /**
+         * Create and start an animation on a node and its descendants
+         * @param {string} name defines the name of the global animation that will be run on all nodes
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {boolean} directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used.
+         * @param {string} targetProperty defines property to animate
+         * @param {number} framePerSecond defines the number of frame per second yo use
+         * @param {number} totalFrame defines the number of frames in total
+         * @param {any} from defines the initial value
+         * @param {any} to defines the final value
+         * @param {number} loopMode defines which loop mode you want to use (off by default)
+         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of animatables created for all nodes
+         * @example https://www.babylonjs-playground.com/#MH0VLI
+         */
+        public static CreateAndStartHierarchyAnimation(name: string, node: Node, directDescendantsOnly: boolean, targetProperty: string,
+            framePerSecond: number, totalFrame: number,
+            from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void): Nullable<Animatable[]> {
+
+            var animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
+
+            if (!animation) {
+                return null;
+            }
+
+            let scene = node.getScene();
+            return scene.beginDirectHierarchyAnimation(node, directDescendantsOnly, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
+        }
+
         public static CreateMergeAndStartAnimation(name: string, node: Node, targetProperty: string,
             framePerSecond: number, totalFrame: number,
             from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void): Nullable<Animatable> {

+ 15 - 1
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -113,6 +113,16 @@ module BABYLON {
         private _circleEase: CircleEase;
 
         /**
+         * Observable raised before camera teleportation        
+        */
+        public onBeforeCameraTeleport = new Observable<Vector3>();
+
+        /**
+         *  Observable raised after camera teleportation
+        */
+        public onAfterCameraTeleport = new Observable<Vector3>();
+
+        /**
         * Observable raised when current selected mesh gets unselected
         */
         public onSelectedMeshUnselected = new Observable<AbstractMesh>();
@@ -1223,6 +1233,8 @@ module BABYLON {
                 this._workingVector.y += this._defaultHeight;
             }
 
+            this.onBeforeCameraTeleport.notifyObservers(this._workingVector);
+
             // Create animation from the camera's position to the new location
             this.currentVRCamera.animations = [];
             var animationCameraTeleportation = new Animation("animationCameraTeleportation", "position", 90, Animation.ANIMATIONTYPE_VECTOR3, Animation.ANIMATIONLOOPMODE_CONSTANT);
@@ -1289,7 +1301,9 @@ module BABYLON {
             this._scene.beginAnimation(this._postProcessMove, 0, 11, false, 1, () => {
                 this._webVRCamera.detachPostProcess(this._postProcessMove)
             });
-            this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1);
+            this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1, () => {
+                this.onAfterCameraTeleport.notifyObservers(this._workingVector);
+            });
         }
 
         private _castRayAndSelectObject() {

+ 16 - 9
src/Tools/babylon.assetsManager.ts

@@ -7,7 +7,7 @@ module BABYLON {
         ERROR
     }
 
-    export abstract class AbstractAssetTask  {
+    export abstract class AbstractAssetTask {
         public onSuccess: (task: any) => void;
         public onError: (task: any, message?: string, exception?: any) => void;
 
@@ -112,7 +112,7 @@ module BABYLON {
         public text: string;
 
         public onSuccess: (task: TextFileAssetTask) => void;
-        public onError: (task: TextFileAssetTask, message?: string, exception?: any) => void;        
+        public onError: (task: TextFileAssetTask, message?: string, exception?: any) => void;
 
         constructor(public name: string, public url: string) {
             super(name);
@@ -134,7 +134,7 @@ module BABYLON {
         public data: ArrayBuffer;
 
         public onSuccess: (task: BinaryFileAssetTask) => void;
-        public onError: (task: BinaryFileAssetTask, message?: string, exception?: any) => void;               
+        public onError: (task: BinaryFileAssetTask, message?: string, exception?: any) => void;
 
         constructor(public name: string, public url: string) {
             super(name);
@@ -156,7 +156,7 @@ module BABYLON {
         public image: HTMLImageElement;
 
         public onSuccess: (task: ImageAssetTask) => void;
-        public onError: (task: ImageAssetTask, message?: string, exception?: any) => void;            
+        public onError: (task: ImageAssetTask, message?: string, exception?: any) => void;
 
         constructor(public name: string, public url: string) {
             super(name);
@@ -186,9 +186,9 @@ module BABYLON {
 
     export class TextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<Texture> {
         public texture: Texture;
-        
+
         public onSuccess: (task: TextureAssetTask) => void;
-        public onError: (task: TextureAssetTask, message?: string, exception?: any) => void;    
+        public onError: (task: TextureAssetTask, message?: string, exception?: any) => void;
 
         constructor(public name: string, public url: string, public noMipmap?: boolean, public invertY?: boolean, public samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
             super(name);
@@ -212,7 +212,7 @@ module BABYLON {
         public texture: CubeTexture;
 
         public onSuccess: (task: CubeTextureAssetTask) => void;
-        public onError: (task: CubeTextureAssetTask, message?: string, exception?: any) => void;            
+        public onError: (task: CubeTextureAssetTask, message?: string, exception?: any) => void;
 
         constructor(public name: string, public url: string, public extensions?: string[], public noMipmap?: boolean, public files?: string[]) {
             super(name);
@@ -236,7 +236,7 @@ module BABYLON {
         public texture: HDRCubeTexture;
 
         public onSuccess: (task: HDRCubeTextureAssetTask) => void;
-        public onError: (task: HDRCubeTextureAssetTask, message?: string, exception?: any) => void;               
+        public onError: (task: HDRCubeTextureAssetTask, message?: string, exception?: any) => void;
 
         constructor(public name: string, public url: string, public size?: number, public noMipmap = false, public generateHarmonics = true, public useInGammaSpace = false, public usePMREMGenerator = false) {
             super(name);
@@ -258,6 +258,7 @@ module BABYLON {
 
     export class AssetsManager {
         private _scene: Scene;
+        private _isLoading = false;
 
         protected tasks = new Array<AbstractAssetTask>();
         protected waitingTasksCount = 0;
@@ -365,7 +366,7 @@ module BABYLON {
                     Tools.Error("Error running tasks-done callbacks.");
                     console.log(e);
                 }
-
+                this._isLoading = false;
                 this._scene.getEngine().hideLoadingUI();
             }
         }
@@ -401,11 +402,16 @@ module BABYLON {
         }
 
         public reset(): AssetsManager {
+            this._isLoading = false;
             this.tasks = new Array<AbstractAssetTask>();
             return this;
         }
 
         public load(): AssetsManager {
+            if (this._isLoading) {
+                return this;
+            }
+            this._isLoading = true;
             this.waitingTasksCount = this.tasks.length;
 
             if (this.waitingTasksCount === 0) {
@@ -413,6 +419,7 @@ module BABYLON {
                     this.onFinish(this.tasks);
                 }
                 this.onTasksDoneObservable.notifyObservers(this.tasks);
+                this._isLoading = false;
                 return this;
             }
 

+ 33 - 0
src/babylon.scene.ts

@@ -2022,6 +2022,17 @@
             return animatable;
         }
 
+        /**
+         * Begin a new animation on a given node
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {BABYLON.Animation[]} defines the list of animations to start
+         * @param {number} from defines the initial value
+         * @param {number} to defines the final value
+         * @param {boolean} loop defines if you want animation to loop (off by default)
+         * @param {number} speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of created animatables
+         */
         public beginDirectAnimation(target: any, animations: Animation[], from: number, to: number, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Animatable {
             if (speedRatio === undefined) {
                 speedRatio = 1.0;
@@ -2032,6 +2043,28 @@
             return animatable;
         }
 
+        /**
+         * Begin a new animation on a given node and its hierarchy
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {boolean} directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used.
+         * @param {BABYLON.Animation[]} defines the list of animations to start
+         * @param {number} from defines the initial value
+         * @param {number} to defines the final value
+         * @param {boolean} loop defines if you want animation to loop (off by default)
+         * @param {number} speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of animatables created for all nodes
+         */
+        public beginDirectHierarchyAnimation(target: Node, directDescendantsOnly: boolean, animations: Animation[], from: number, to: number, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Animatable[] {
+            let children = target.getDescendants(directDescendantsOnly);
+            let result = [];
+            for (var child of children) {
+                result.push(this.beginDirectAnimation(child, animations, from, to, loop, speedRatio, onAnimationEnd));
+            }
+
+            return result;
+        }
+
         public getAnimatableByTarget(target: any): Nullable<Animatable> {
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 if (this._activeAnimatables[index].target === target) {

BIN
tests/validation/ReferenceImages/GLTF LOD.png


BIN
tests/validation/ReferenceImages/instancedBones.png


BIN
tests/validation/ReferenceImages/instances.png


BIN
tests/validation/ReferenceImages/waterMaterial.png


+ 5 - 23
tests/validation/config.json

@@ -241,8 +241,7 @@
       "renderCount": 20,
       "scriptToRun": "/Demos/GLTF1CesiumMan/index.js",
       "functionToCall": "createScene",
-      "referenceImage": "gltf1CesiumMan.png",
-      "onlyVisual": true
+      "referenceImage": "gltf1CesiumMan.png"
     },
     {
       "title": "GLTF Mesh Primitive Attribute Test",
@@ -267,13 +266,6 @@
       "referenceImage": "pbrrough.png"
     },
     {
-      "title": "GLTF LOD",
-      "renderCount": 500,
-      "playgroundId": "#TC80RU#41",
-      "referenceImage": "GLTF LOD.png",
-      "onlyVisual": true
-    },
-    {
       "title": "Reflection probes",
       "renderCount": 10,
       "scriptToRun": "/Demos/RefProbe/reflectionProbe.js",
@@ -341,30 +333,20 @@
       "replace": "./land, https://cdn.rawgit.com/BabylonJS/Website/06ecbea7/Demos/Procedural/land"
     },
     {
-      "title": "Water material (only visual check)",
-      "renderCount": 10,
-      "scriptToRun": "/Demos/WaterMaterial/water.js",
-      "functionToCall": "CreateWaterTestScene",
-      "referenceImage": "waterMaterial.png",
-      "onlyVisual": true
-    },
-    {
-      "title": "Instances (only visual check)",
+      "title": "Instances",
       "renderCount": 10,
       "scriptToRun": "/Demos/Instances/instances.js",
       "functionToCall": "CreateInstancesTestScene",
       "referenceImage": "instances.png",
-      "replace": "ground., Ground.",
-      "onlyVisual": true
+      "replace": "ground., Ground."
     },
     {
-      "title": "Instanced Bones (only visual check)",
+      "title": "Instanced Bones",
       "renderCount": 10,
       "scriptToRun": "/Demos/InstancedBones/bones2.js",
       "functionToCall": "CreateBones2TestScene",
       "referenceImage": "instancedBones.png",
-      "replace": "Dude.babylon, dude.babylon",
-      "onlyVisual": true
+      "replace": "Dude.babylon, dude.babylon"
     }
   ]
 }

+ 15 - 0
tests/validation/validation.js

@@ -9,6 +9,20 @@ var justOnce;
 var threshold = 25;
 var errorRatio = 5;
 
+// Overload the random to make it deterministic
+var seed = 100000,
+    constant = Math.pow(2, 13) + 1,
+    prime = 37,
+    maximum = Math.pow(2, 50);
+
+Math.random = function () {
+    seed *= constant;
+    seed += prime;
+    seed %= maximum;
+
+    return seed / maximum;
+}
+
 function compare(renderData, referenceCanvas) {
     var width = referenceCanvas.width;
     var height = referenceCanvas.height;
@@ -77,6 +91,7 @@ function saveRenderImage(data, canvas) {
 }
 
 function evaluate(test, resultCanvas, result, renderImage, index, waitRing, done) {
+    seed = 100000;
     var renderData = getRenderData(canvas, engine);
     var testRes = true;