浏览代码

Merge pull request #3208 from BabylonJS/master

Nightly
David Catuhe 7 年之前
父节点
当前提交
4864255cae
共有 45 个文件被更改,包括 4211 次插入2339 次删除
  1. 1 0
      .gitignore
  2. 785 649
      Playground/babylon.d.txt
  3. 1 1
      Playground/package.json
  4. 2 1
      Tools/Gulp/config.json
  5. 1 1
      Tools/Gulp/gulpfile.js
  6. 811 675
      dist/preview release/babylon.d.ts
  7. 47 47
      dist/preview release/babylon.js
  8. 555 104
      dist/preview release/babylon.max.js
  9. 46 46
      dist/preview release/babylon.worker.js
  10. 496 360
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  11. 51 51
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  12. 555 104
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  13. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js
  14. 4 2
      dist/preview release/inspector/babylon.inspector.css
  15. 3 1
      dist/preview release/what's new.md
  16. 3 1
      inspector/sass/_detailPanel.scss
  17. 1 1
      package.json
  18. 8 4
      src/Audio/babylon.sound.ts
  19. 1 1
      src/Behaviors/Cameras/babylon.bouncingBehavior.ts
  20. 1 1
      src/Behaviors/Cameras/babylon.framingBehavior.ts
  21. 112 8
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  22. 1 1
      src/Cameras/babylon.arcRotateCamera.ts
  23. 2 2
      src/Collisions/babylon.collisionCoordinator.ts
  24. 28 18
      src/Engine/babylon.engine.ts
  25. 25 25
      src/Engine/babylon.nullEngine.ts
  26. 1 1
      src/LensFlare/babylon.lensFlareSystem.ts
  27. 6 1
      src/Materials/Textures/babylon.colorGradingTexture.ts
  28. 1 1
      src/Materials/Textures/babylon.texture.ts
  29. 44 20
      src/Materials/babylon.shaderMaterial.ts
  30. 1 18
      src/Mesh/babylon.abstractMesh.ts
  31. 17 17
      src/Mesh/babylon.mesh.ts
  32. 1 1
      src/Mesh/babylon.meshBuilder.ts
  33. 154 99
      src/Mesh/babylon.transformNode.ts
  34. 7 1
      src/Physics/babylon.physicsEngine.ts
  35. 332 0
      src/Physics/babylon.physicsHelper.ts
  36. 43 38
      src/Tools/babylon.assetsManager.ts
  37. 14 9
      src/Tools/babylon.decorators.ts
  38. 2 2
      src/Tools/babylon.observable.ts
  39. 7 10
      src/Tools/babylon.tools.ts
  40. 13 2
      src/babylon.node.ts
  41. 12 12
      src/babylon.scene.ts
  42. 2 1
      src/tsconfig.json
  43. 二进制
      tests/validation/ReferenceImages/Billboard.png
  44. 二进制
      tests/validation/ReferenceImages/setParent.png
  45. 13 1
      tests/validation/config.json

+ 1 - 0
.gitignore

@@ -166,3 +166,4 @@ localDev/src/*
 /dist/preview release/babylon.custom.js
 /dist/preview release/babylon.custom.max.js
 /localDev/src/index.js
+package-lock.json

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


+ 1 - 1
Playground/package.json

@@ -9,7 +9,7 @@
   "readme": "https://github.com/BabylonJS/Babylon.js/blob/master/readme.md",
   "license": "(Apache-2.0)",
   "devDependencies": {
-    "monaco-editor": "~0.10.0"
+    "monaco-editor": "~0.10.1"
   },
   "scripts": {
     "test": "browser-sync start --server --files **/* --no-inject-changes --startPath index.html"

+ 2 - 1
Tools/Gulp/config.json

@@ -926,6 +926,7 @@
                 "../../src/Physics/babylon.physicsJoint.js",
                 "../../src/Physics/babylon.physicsImpostor.js",
                 "../../src/Physics/babylon.physicsEngine.js",
+                "../../src/Physics/babylon.physicsHelper.js",
                 "../../src/Physics/Plugins/babylon.cannonJSPlugin.js",
                 "../../src/Physics/Plugins/babylon.oimoJSPlugin.js"
             ],
@@ -1661,4 +1662,4 @@
             "distOutputDirectory": "/inspector/"
         }
     }
-}
+}

+ 1 - 1
Tools/Gulp/gulpfile.js

@@ -49,7 +49,7 @@ var tsConfig = {
     noImplicitThis: true,
     noUnusedLocals: true,
     strictNullChecks: true,
-    strictFunctionTypes: false
+    strictFunctionTypes: true
 };
 var tsProject = typescript.createProject(tsConfig);
 

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


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


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


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


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


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


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


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


+ 4 - 2
dist/preview release/inspector/babylon.inspector.css

@@ -295,8 +295,10 @@
       display: flex;
       width: 100%; }
       .insp-wrapper .insp-details .base-row .base-property, .insp-wrapper .insp-details .row .base-property, .insp-wrapper .insp-details .header-row .base-property, .insp-wrapper .insp-details .base-row .prop-name, .insp-wrapper .insp-details .row .prop-name, .insp-wrapper .insp-details .header-row .prop-name, .insp-wrapper .insp-details .base-row .prop-value, .insp-wrapper .insp-details .row .prop-value, .insp-wrapper .insp-details .header-row .prop-value {
-        word-wrap: break-word;
-        padding: 2px 0 2px 0; }
+        padding: 2px 0 2px 0;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        overflow: hidden; }
       .insp-wrapper .insp-details .base-row .prop-name, .insp-wrapper .insp-details .row .prop-name, .insp-wrapper .insp-details .header-row .prop-name {
         width: 35%; }
       .insp-wrapper .insp-details .base-row .prop-value, .insp-wrapper .insp-details .row .prop-value, .insp-wrapper .insp-details .header-row .prop-value {

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

@@ -2,7 +2,8 @@
 
 ## Major updates
 - Viewer (TODO)
-- EnvironmentHelper + BackgroundMaterial (TODO)
+- Added BackgroundMaterial [Doc here](https://doc.babylonjs.com/how_to/backgroundmaterial)
+- Added EnvironmentHelper [Doc here](https://doc.babylonjs.com/babylon101/environment#skybox-and-ground)
 - Added support for webgl context lost and restored events. [Doc here](http://doc.babylonjs.com/tutorials/optimizing_your_scene#handling-webgl-context-lost) ([deltakosh](https://github.com/deltakosh))
 - Added support for non-pow2 textures when in WebGL2 mode ([deltakosh](https://github.com/deltakosh))
 - Engine can now be initialized with an existing webgl context ([deltakosh](https://github.com/deltakosh))
@@ -57,3 +58,4 @@
 ## Breaking changes
 - `Gamepads` was removed in favor of `scene.gamepadManager`
 - `DynamicFloatArray`, `MapTexture` and `RectPakingMap` were removed because there were not used anymore
+- `IAssetTask` was removed in favor of `AbstractAssetTask` class

+ 3 - 1
inspector/sass/_detailPanel.scss

@@ -16,8 +16,10 @@
     width:100%;
 
     .base-property {
-        word-wrap: break-word;
         padding: 2px 0 2px 0;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        overflow: hidden;
     }
     // Property name
     .prop-name {

+ 1 - 1
package.json

@@ -67,4 +67,4 @@
     "readmeFilename": "README.md",
     "_id": "babylonjs@3.1.0-alpha2",
     "_from": "babylonjs@"
-}
+}

+ 8 - 4
src/Audio/babylon.sound.ts

@@ -39,7 +39,7 @@ module BABYLON {
         private _scene: Scene;
         private _connectedMesh: Nullable<AbstractMesh>;
         private _customAttenuationFunction: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number;
-        private _registerFunc: Nullable<(connectedMesh: AbstractMesh) => any>;
+        private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
         private _isOutputConnected = false;
         private _htmlAudioElement: HTMLAudioElement;
         private _urlType: string = "Unknown";
@@ -547,7 +547,7 @@ module BABYLON {
                 }
             }
             this._onRegisterAfterWorldMatrixUpdate(this._connectedMesh);
-            this._registerFunc = (connectedMesh: AbstractMesh) => this._onRegisterAfterWorldMatrixUpdate(connectedMesh);
+            this._registerFunc = (connectedMesh: TransformNode) => this._onRegisterAfterWorldMatrixUpdate(connectedMesh);
             meshToConnectTo.registerAfterWorldMatrixUpdate(this._registerFunc);
         }
 
@@ -559,8 +559,12 @@ module BABYLON {
             }
         }
 
-        private _onRegisterAfterWorldMatrixUpdate(connectedMesh: AbstractMesh): void {
-            let boundingInfo = connectedMesh.getBoundingInfo();
+        private _onRegisterAfterWorldMatrixUpdate(node: TransformNode): void {
+            if (!(<any>node).getBoundingInfo) {
+                return;
+            }
+            let mesh = node as AbstractMesh;
+            let boundingInfo = mesh.getBoundingInfo();
             this.setPosition(boundingInfo.boundingSphere.centerWorld);
             if (Engine.audioEngine.canUseWebAudio && this._isDirectional && this.isPlaying) {
                 this._updateDirection();

+ 1 - 1
src/Behaviors/Cameras/babylon.bouncingBehavior.ts

@@ -77,7 +77,7 @@ module BABYLON {
         // Connection
         private _attachedCamera: Nullable<ArcRotateCamera>;
 		private _onAfterCheckInputsObserver: Nullable<Observer<Camera>>;	
-		private _onMeshTargetChangedObserver: Nullable<Observer<AbstractMesh>>;
+		private _onMeshTargetChangedObserver: Nullable<Observer<Nullable<AbstractMesh>>>;
 
 		public init(): void {
 			// Do notihng

+ 1 - 1
src/Behaviors/Cameras/babylon.framingBehavior.ts

@@ -142,7 +142,7 @@ module BABYLON {
         // Default behavior functions
         private _onPrePointerObservableObserver: Nullable<Observer<PointerInfoPre>>;
 		private _onAfterCheckInputsObserver: Nullable<Observer<Camera>>;
-		private _onMeshTargetChangedObserver: Nullable<Observer<AbstractMesh>>;
+		private _onMeshTargetChangedObserver: Nullable<Observer<Nullable<AbstractMesh>>>;
         private _attachedCamera: Nullable<ArcRotateCamera>;
         private _isPointerDown = false;
 		private _lastInteractionTime = -Infinity;

+ 112 - 8
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -45,19 +45,20 @@ module BABYLON {
         private _teleportationAllowed: boolean = false;
         private _rotationAllowed: boolean = true;
         private _teleportationRequestInitiated = false;
-        private _xboxGamepadTeleportationRequestInitiated = false;
+        //private _classicGamepadTeleportationRequestInitiated = false;
         private _rotationRightAsked = false;
         private _rotationLeftAsked = false;
-        private _teleportationCircle: BABYLON.Mesh;
+        private _teleportationCircle: Mesh;
         private _postProcessMove: ImageProcessingPostProcess;
         private _passProcessMove: PassPostProcess;
         private _teleportationFillColor: string = "#444444";
         private _teleportationBorderColor: string = "#FFFFFF";
         private _rotationAngle: number = 0;
-        private _haloCenter = new BABYLON.Vector3(0, 0, 0);
+        private _haloCenter = new Vector3(0, 0, 0);
         private _rayHelper: RayHelper;
+        private _gazeTracker: BABYLON.Mesh;
 
-        public meshSelectionPredicate: (mesh: BABYLON.Mesh) => boolean;
+        public meshSelectionPredicate: (mesh: AbstractMesh) => boolean;
 
         public get deviceOrientationCamera(): DeviceOrientationCamera {
             return this._deviceOrientationCamera;
@@ -370,16 +371,21 @@ module BABYLON {
                 this._enableTeleportationOnController(this._webVRCamera.rightController)
             }
 
+            this._scene.gamepadManager.onGamepadConnectedObservable.add((pad) => this._onNewGamepadConnected(pad));
+
             this._postProcessMove = new BABYLON.ImageProcessingPostProcess("postProcessMove", 1.0, this._webVRCamera);
             this._passProcessMove = new BABYLON.PassPostProcess("pass", 1.0, this._webVRCamera);
             this._scene.imageProcessingConfiguration.vignetteColor = new BABYLON.Color4(0, 0, 0, 0);
             this._scene.imageProcessingConfiguration.vignetteEnabled = true;      
             this._scene.imageProcessingConfiguration.isEnabled = false;      
 
+            this._createGazeTracker();
             this._createTeleportationCircles();
 
             this.meshSelectionPredicate = (mesh) => {
-                if (mesh.name.indexOf(this._floorMeshName) !== -1) {
+                if (mesh.isVisible && mesh.name.indexOf("gazeTracker") === -1 
+                        && mesh.name.indexOf("teleportationCircle") === -1
+                        && mesh.name.indexOf("torusTeleportation") === -1) {
                     return true;
                 }
                 return false;
@@ -390,6 +396,57 @@ module BABYLON {
             });
         }
 
+        private _onNewGamepadConnected(gamepad: Gamepad) {
+            if (gamepad.leftStick) {
+                gamepad.onleftstickchanged((stickValues) => {
+                    if (!this._teleportationRequestInitiated) {
+                        if (stickValues.y < -0.6) {
+                            this._teleportationRequestInitiated = true;
+                        }
+                    }
+                    else {
+                        if (stickValues.y > -0.4) {
+                            if (this._teleportationAllowed) {
+                                this._teleportCamera();
+                            }
+                            this._teleportationRequestInitiated = false;
+                        }
+                    }
+                });
+            }
+            if (gamepad.rightStick) {
+                gamepad.onrightstickchanged((stickValues) => {
+                    if (!this._rotationLeftAsked) {
+                        if (stickValues.x < -0.6) {
+                            this._rotationLeftAsked = true;
+                            if (this._rotationAllowed) {
+                                this._rotateCamera(false);
+                            }
+                        }
+                    }
+                    else {
+                        if (stickValues.x > -0.4) {
+                            this._rotationLeftAsked = false;
+                        }
+                    }
+        
+                    if (!this._rotationRightAsked) {
+                        if (stickValues.x > 0.6) {
+                            this._rotationRightAsked = true;
+                            if (this._rotationAllowed) {
+                                this._rotateCamera(true);
+                            }
+                        }
+                    }
+                    else {
+                        if (stickValues.x < 0.4) {
+                            this._rotationRightAsked = false;
+                        }
+                    }
+                });
+            }
+        }
+
         private _enableTeleportationOnController(webVRController: WebVRController) {
             var controllerMesh = webVRController.mesh;
             if (controllerMesh) {
@@ -465,6 +522,19 @@ module BABYLON {
             }
         }
 
+        // Gaze support used to point to teleport or to interact with an object
+        private _createGazeTracker() {
+            this._gazeTracker = BABYLON.Mesh.CreateTorus("gazeTracker", 0.0035, 0.0025, 20, this._scene, false);
+            this._gazeTracker.bakeCurrentTransformIntoVertices();
+            this._gazeTracker.isPickable = false;
+
+            var targetMat = new BABYLON.StandardMaterial("targetMat", this._scene);
+            targetMat.specularColor = BABYLON.Color3.Black();
+            targetMat.emissiveColor = BABYLON.Color3.White();
+            targetMat.backFaceCulling = false;
+            this._gazeTracker.material = targetMat;
+        }
+
         private _createTeleportationCircles() {
             this._teleportationCircle = BABYLON.Mesh.CreateGround("teleportationCircle", 2, 2, 2, this._scene);
             
@@ -491,7 +561,7 @@ module BABYLON {
             teleportationCircleMaterial.diffuseTexture = dynamicTexture;
             this._teleportationCircle.material = teleportationCircleMaterial;
             
-            var torus = BABYLON.Mesh.CreateTorus("torus", 0.75, 0.1, 25, this._scene, false);
+            var torus = BABYLON.Mesh.CreateTorus("torusTeleportation", 0.75, 0.1, 25, this._scene, false);
             torus.parent = this._teleportationCircle;
             
             var animationInnerCircle = new BABYLON.Animation("animationInnerCircle", "position.y", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
@@ -623,7 +693,7 @@ module BABYLON {
 
         private _moveTeleportationSelectorTo(coordinates: Vector3) {
             this._teleportationAllowed = true;
-            if (this._teleportationRequestInitiated || this._xboxGamepadTeleportationRequestInitiated) {
+            if (this._teleportationRequestInitiated) {
                 this._displayTeleportationCircle();
             }
             else {
@@ -738,13 +808,47 @@ module BABYLON {
             }
         
             var hit = this._scene.pickWithRay(ray, this.meshSelectionPredicate);
+
+            if (hit && hit.pickedPoint) {
+                this._gazeTracker.scaling.x = hit.distance;
+                this._gazeTracker.scaling.y = hit.distance;
+                this._gazeTracker.scaling.z = hit.distance;
+        
+                var pickNormal = hit.getNormal();
+                var deltaFighting = 0.002;
+                
+                if (pickNormal) {
+                    var axis1 = BABYLON.Vector3.Cross(BABYLON.Axis.Y, pickNormal);
+                    var axis2 = BABYLON.Vector3.Cross( pickNormal, axis1);
+                    BABYLON.Vector3.RotationFromAxisToRef(axis2, pickNormal, axis1, this._gazeTracker.rotation);
+                }
+                this._gazeTracker.position.copyFrom(hit.pickedPoint);
+        
+                if (this._gazeTracker.position.x < 0) {
+                    this._gazeTracker.position.x += deltaFighting;
+                }
+                else {
+                    this._gazeTracker.position.x -= deltaFighting;
+                }
+                if (this._gazeTracker.position.y < 0) {
+                    this._gazeTracker.position.y += deltaFighting;
+                }
+                else {
+                    this._gazeTracker.position.y -= deltaFighting;
+                }
+                if (this._gazeTracker.position.z < 0) {
+                    this._gazeTracker.position.z += deltaFighting;
+                }
+                else {
+                    this._gazeTracker.position.z -= deltaFighting;
+                }
+            }
         
             if (this._rayHelper) {
                 this._rayHelper.dispose();
             }
         
             if ((<WebVRFreeCamera>this.currentVRCamera).rightController) {
-                //if (target) target.isVisible = false;
                 this._rayHelper = BABYLON.RayHelper.CreateAndShow(ray, this._scene, new BABYLON.Color3(0.7, 0.7, 0.7));
             }
         

+ 1 - 1
src/Cameras/babylon.arcRotateCamera.ts

@@ -320,7 +320,7 @@
             }
         }
 
-        public onMeshTargetChangedObservable = new Observable<AbstractMesh>();
+        public onMeshTargetChangedObservable = new Observable<Nullable<AbstractMesh>>();
 
         // Collisions
         public onCollide: (collidedMesh: AbstractMesh) => void;

+ 2 - 2
src/Collisions/babylon.collisionCoordinator.ts

@@ -240,8 +240,8 @@ module BABYLON {
             this.onMeshUpdated(mesh);
         }
 
-        public onMeshUpdated = (mesh: AbstractMesh) => {
-            this._addUpdateMeshesList[mesh.uniqueId] = CollisionCoordinatorWorker.SerializeMesh(mesh);
+        public onMeshUpdated = (transformNode: TransformNode) => {
+            this._addUpdateMeshesList[transformNode.uniqueId] = CollisionCoordinatorWorker.SerializeMesh(transformNode as AbstractMesh);
         }
 
         public onMeshRemoved(mesh: AbstractMesh) {

+ 28 - 18
src/Engine/babylon.engine.ts

@@ -173,8 +173,8 @@
             }
         };
 
-        const onerror = (request: XMLHttpRequest, exception: any) => {
-            if (onErrorCallBack) {
+        const onerror = (request?: XMLHttpRequest, exception?: any) => {
+            if (onErrorCallBack && request) {
                 onErrorCallBack(request.status + " " + request.statusText, exception);
             }
         };
@@ -563,7 +563,7 @@
         }
 
         public static get Version(): string {
-            return "3.1-beta-5";
+            return "3.1-beta-6";
         }
 
         // Updatable statics so stick with vars here
@@ -2986,7 +2986,7 @@
          * @returns {WebGLTexture} for assignment back into BABYLON.Texture
          */
         public createTexture(urlArg: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<Scene>, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE,
-            onLoad: Nullable<() => void> = null, onError: Nullable<() => void> = null,
+            onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null,
             buffer: Nullable<ArrayBuffer | HTMLImageElement> = null, fallBack: Nullable<InternalTexture> = null, format: Nullable<number> = null): InternalTexture {
             var url = String(urlArg); // assign a new string, so that the original is still available in case of fallback
             var fromData = url.substr(0, 5) === "data:";
@@ -3021,23 +3021,31 @@
                 texture._buffer = buffer;
             }
 
-            if (onLoad) {
-                texture.onLoadedObservable.add(onLoad);
+            let onLoadObserver: Nullable<Observer<InternalTexture>> = null;
+            if (onLoad && !fallBack) {
+                onLoadObserver = texture.onLoadedObservable.add(onLoad);
             }
+
             if (!fallBack) this._internalTexturesCache.push(texture);
 
-            var onerror = () => {
+            var onerror = (message?: string, exception?: any) => {
                 if (scene) {
                     scene._removePendingData(texture);
                 }
 
+                if (onLoadObserver) {
+                    texture.onLoadedObservable.remove(onLoadObserver);
+                }
+
                 // fallback for when compressed file not found to try again.  For instance, etc1 does not have an alpha capable type
                 if (isKTX) {
                     this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
-                } else if ((isTGA || isDDS) && BABYLON.Tools.UseFallbackTexture) {
+                } else if (BABYLON.Tools.UseFallbackTexture) {
                     this.createTexture(BABYLON.Tools.fallbackTexture, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
-                } else if (onError) {
-                    onError();
+                }
+
+                if (onError) {
+                    onError(message || "Unknown error", exception);
                 }
             };
 
@@ -3083,7 +3091,9 @@
                         if (callback) {
                             callback(data);
                         }
-                    }, undefined, scene ? scene.database : undefined, true, onerror);
+                    }, undefined, scene ? scene.database : undefined, true, (request?: XMLHttpRequest, exception?: any) => {
+                        onerror("Unable to load " + (request ? request.responseURL : url, exception));
+                    });
                 } else {
                     if (callback) {
                         callback(buffer);
@@ -3930,8 +3940,8 @@
                 isDDS = (extension === ".dds");
             }
 
-            let onerror = (request: XMLHttpRequest, exception: any) => {
-                if (onError) {
+            let onerror = (request?: XMLHttpRequest, exception?: any) => {
+                if (onError && request) {
                     onError(request.status + " " + request.statusText, exception);
                 }
             }
@@ -4057,7 +4067,7 @@
                     if (!noMipmap) {
                         gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                     }
-                    
+
                     this.setCubeMapTextureParams(gl, !noMipmap);
 
                     texture.width = width;
@@ -4218,9 +4228,9 @@
             texture.url = url;
             this._internalTexturesCache.push(texture);
 
-            var onerror = (request: XMLHttpRequest, exception: any) => {
+            var onerror = (request?: XMLHttpRequest, exception?: any) => {
                 scene._removePendingData(texture);
-                if (onError) {
+                if (onError && request) {
                     onError(request.status + " " + request.statusText, exception);
                 }
             };
@@ -4993,13 +5003,13 @@
 
         public attachContextLostEvent(callback: ((event: WebGLContextEvent) => void)): void {
             if (this._renderingCanvas) {
-                this._renderingCanvas.addEventListener("webglcontextlost", callback, false);
+                this._renderingCanvas.addEventListener("webglcontextlost", <any>callback, false);
             }
         }
 
         public attachContextRestoredEvent(callback: ((event: WebGLContextEvent) => void)): void {
             if (this._renderingCanvas) {
-                this._renderingCanvas.addEventListener("webglcontextrestored", callback, false);
+                this._renderingCanvas.addEventListener("webglcontextrestored", <any>callback, false);
             }
         }
 

+ 25 - 25
src/Engine/babylon.nullEngine.ts

@@ -6,12 +6,12 @@
 
         public textureSize = 512;
     }
-   
+
     /**
      * The null engine class provides support for headless version of babylon.js.
      * This can be used in server side scenario or for testing purposes
      */
-    export class NullEngine extends Engine {  
+    export class NullEngine extends Engine {
         private _options: NullEngineOptions;
 
         public constructor(options: NullEngineOptions = new NullEngineOptions()) {
@@ -69,14 +69,14 @@
             // Wrappers
             if (typeof URL === "undefined") {
                 (<any>URL) = {
-                    createObjectURL: function() {},
-                    revokeObjectURL: function() {}
+                    createObjectURL: function () { },
+                    revokeObjectURL: function () { }
                 }
             }
 
             if (typeof Blob === "undefined") {
-                (<any>Blob) = function() {};         
-            }   
+                (<any>Blob) = function () { };
+            }
         }
 
         public createVertexBuffer(vertices: FloatArray): WebGLBuffer {
@@ -144,11 +144,11 @@
                 effect.onBind(effect);
             }
             effect.onBindObservable.notifyObservers(effect);
-        }   
-        
+        }
+
         public setState(culling: boolean, zOffset: number = 0, force?: boolean, reverseSide = false): void {
-        }        
-        
+        }
+
         public setIntArray(uniform: WebGLUniformLocation, array: Int32Array): void {
         }
 
@@ -229,7 +229,7 @@
                 this.setDepthWrite(mode === Engine.ALPHA_DISABLE);
             }
             this._alphaMode = mode;
-        }        
+        }
 
         public bindBuffers(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: WebGLBuffer, effect: Effect): void {
         }
@@ -262,9 +262,9 @@
             return {};
         }
 
-        public createTexture(urlArg: string, noMipmap: boolean, invertY: boolean, scene: Scene, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<() => void> = null, buffer: Nullable<ArrayBuffer | HTMLImageElement> = null, fallBack?: InternalTexture, format?: number): InternalTexture {
+        public createTexture(urlArg: string, noMipmap: boolean, invertY: boolean, scene: Scene, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null, buffer: Nullable<ArrayBuffer | HTMLImageElement> = null, fallBack?: InternalTexture, format?: number): InternalTexture {
             var texture = new InternalTexture(this, InternalTexture.DATASOURCE_URL);
-            var url = String(urlArg); 
+            var url = String(urlArg);
 
             texture.url = url;
             texture.generateMipMaps = !noMipmap;
@@ -273,12 +273,12 @@
             texture.baseWidth = this._options.textureSize;
             texture.baseHeight = this._options.textureSize;
             texture.width = this._options.textureSize;
-            texture.height = this._options.textureSize;  
-            if (format) {          
-                texture.format = format;    
+            texture.height = this._options.textureSize;
+            if (format) {
+                texture.format = format;
             }
 
-            texture.isReady = true;            
+            texture.isReady = true;
 
             if (onLoad) {
                 onLoad();
@@ -322,12 +322,12 @@
             texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
             texture._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
             return texture;
-        }     
-        
+        }
+
         public updateTextureSamplingMode(samplingMode: number, texture: InternalTexture): void {
             texture.samplingMode = samplingMode;
-        }      
-        
+        }
+
         public bindFramebuffer(texture: InternalTexture, faceIndex?: number, requiredWidth?: number, requiredHeight?: number, forceFullscreenViewport?: boolean): void {
             if (this._currentRenderTarget) {
                 this.unBindFramebuffer(this._currentRenderTarget);
@@ -336,7 +336,7 @@
             this._currentFramebuffer = texture._MSAAFramebuffer ? texture._MSAAFramebuffer : texture._framebuffer;
             if (this._cachedViewport && !forceFullscreenViewport) {
                 this.setViewport(this._cachedViewport, requiredWidth, requiredHeight);
-            } 
+            }
         }
 
         public unBindFramebuffer(texture: InternalTexture, disableGenerateMipMaps = false, onBeforeUnbind?: () => void): void {
@@ -354,7 +354,7 @@
         public createDynamicVertexBuffer(vertices: FloatArray): WebGLBuffer {
             var vbo = {
                 capacity: 1,
-                references: 1,                
+                references: 1,
                 is32Bits: false
             }
 
@@ -365,7 +365,7 @@
         }
 
         public updateDynamicVertexBuffer(vertexBuffer: WebGLBuffer, vertices: FloatArray, offset?: number, count?: number): void {
-        }        
+        }
 
         public _bindTextureDirectly(target: number, texture: InternalTexture): void {
             if (this._activeTexturesCache[this._activeTextureChannel] !== texture) {
@@ -389,6 +389,6 @@
             }
 
             return false;
-        }        
+        }
     }
 }

+ 1 - 1
src/LensFlare/babylon.lensFlareSystem.ts

@@ -3,7 +3,7 @@
         public lensFlares = new Array<LensFlare>();
         public borderLimit = 300;
         public viewportBorder = 0;
-        public meshesSelectionPredicate: (mesh: Mesh) => boolean;
+        public meshesSelectionPredicate: (mesh: AbstractMesh) => boolean;
         public layerMask: number = 0x0FFFFFFF;
         public id: string;
 

+ 6 - 1
src/Materials/Textures/babylon.colorGradingTexture.ts

@@ -86,7 +86,12 @@ module BABYLON {
 
             this._texture = texture;
 
-            var callback = (text: string) => {
+            var callback = (text: string | ArrayBuffer) => {
+
+                if (typeof text !== "string") {
+                    return;
+                }
+
                 var data: Nullable<Uint8Array> = null;
                 var tempData: Nullable<Float32Array> = null;
 

+ 1 - 1
src/Materials/Textures/babylon.texture.ts

@@ -103,7 +103,7 @@
             return this._samplingMode;
         }
 
-        constructor(url: Nullable<string>, scene: Nullable<Scene>, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, esception?: any) => void> = null, buffer: any = null, deleteBuffer: boolean = false, format?: number) {
+        constructor(url: Nullable<string>, scene: Nullable<Scene>, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, buffer: any = null, deleteBuffer: boolean = false, format?: number) {
             super(scene);
 
             this.name = url || "";

+ 44 - 20
src/Materials/babylon.shaderMaterial.ts

@@ -15,6 +15,7 @@
         private _matrices: { [name: string]: Matrix } = {};
         private _matrices3x3: { [name: string]: Float32Array } = {};
         private _matrices2x2: { [name: string]: Float32Array } = {};
+        private _vectors2Arrays: { [name: string]: number[] } = {};
         private _vectors3Arrays: { [name: string]: number[] } = {};
         private _cachedWorldViewMatrix = new Matrix();
         private _renderId: number;
@@ -151,6 +152,13 @@
             return this;
         }
 
+        public setArray2(name: string, value: number[]): ShaderMaterial {
+            this._checkUniform(name);
+            this._vectors2Arrays[name] = value;
+
+            return this;
+        }
+
         public setArray3(name: string, value: number[]): ShaderMaterial {
             this._checkUniform(name);
             this._vectors3Arrays[name] = value;
@@ -169,7 +177,7 @@
 
             return false;
         }
-        
+
         public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
             var scene = this.getScene();
             var engine = scene.getEngine();
@@ -202,7 +210,7 @@
                 attribs.push(VertexBuffer.ColorKind);
                 defines.push("#define VERTEXCOLOR");
             }
-            
+
             // Bones
             if (mesh && mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
                 attribs.push(VertexBuffer.MatricesIndicesKind);
@@ -221,7 +229,7 @@
 
             } else {
                 defines.push("#define NUM_BONE_INFLUENCERS 0");
-            }  
+            }
 
             // Textures
             for (var name in this._textures) {
@@ -239,15 +247,15 @@
             var join = defines.join("\n");
 
             this._effect = engine.createEffect(this._shaderPath, <EffectCreationOptions>{
-                    attributes: attribs,
-                    uniformsNames: this._options.uniforms,
-                    uniformBuffersNames: this._options.uniformBuffers,
-                    samplers: this._options.samplers,
-                    defines: join,
-                    fallbacks: fallbacks,
-                    onCompiled: this.onCompiled,
-                    onError: this.onError
-                }, engine);
+                attributes: attribs,
+                uniformsNames: this._options.uniforms,
+                uniformBuffersNames: this._options.uniformBuffers,
+                samplers: this._options.samplers,
+                defines: join,
+                fallbacks: fallbacks,
+                onCompiled: this.onCompiled,
+                onError: this.onError
+            }, engine);
 
             if (!this._effect.isReady()) {
                 return false;
@@ -368,7 +376,12 @@
                 for (name in this._matrices2x2) {
                     this._effect.setMatrix2x2(name, this._matrices2x2[name]);
                 }
-                
+
+                // Vector2Array   
+                for (name in this._vectors2Arrays) {
+                    this._effect.setArray2(name, this._vectors2Arrays[name]);
+                }
+
                 // Vector3Array   
                 for (name in this._vectors3Arrays) {
                     this._effect.setArray3(name, this._vectors3Arrays[name]);
@@ -404,7 +417,7 @@
                 if (this._textures[name] === texture) {
                     return true;
                 }
-            }       
+            }
 
             for (var name in this._textureArrays) {
                 var array = this._textureArrays[name];
@@ -413,9 +426,9 @@
                         return true;
                     }
                 }
-            }             
+            }
 
-            return false;    
+            return false;
         }
 
         public clone(name: string): ShaderMaterial {
@@ -448,7 +461,7 @@
         public serialize(): any {
             var serializationObject = SerializationHelper.Serialize(this);
             serializationObject.customType = "BABYLON.ShaderMaterial";
-			
+
             serializationObject.options = this._options;
             serializationObject.shaderPath = this._shaderPath;
 
@@ -536,12 +549,18 @@
                 serializationObject.matrices2x2[name] = this._matrices2x2[name];
             }
 
+            // Vector2Array
+            serializationObject.vectors2Arrays = {};
+            for (name in this._vectors2Arrays) {
+                serializationObject.vectors2Arrays[name] = this._vectors2Arrays[name];
+            }
+
             // Vector3Array
             serializationObject.vectors3Arrays = {};
             for (name in this._vectors3Arrays) {
                 serializationObject.vectors3Arrays[name] = this._vectors3Arrays[name];
             }
-            
+
             return serializationObject;
         }
 
@@ -629,12 +648,17 @@
                 material.setMatrix2x2(name, source.matrices2x2[name]);
             }
 
+            // Vector2Array
+            for (name in source.vectors2Arrays) {
+                material.setArray2(name, source.vectors2Arrays[name]);
+            }
+
             // Vector3Array
             for (name in source.vectors3Arrays) {
                 material.setArray3(name, source.vectors3Arrays[name]);
             }
-            
+
             return material;
         }
     }
-} 
+}

+ 1 - 18
src/Mesh/babylon.abstractMesh.ts

@@ -1311,9 +1311,6 @@
             // Skeleton
             this.skeleton = null;
 
-            // Animations
-            this.getScene().stopAnimation(this);
-
             // Physics
             if (this.physicsImpostor) {
                 this.physicsImpostor.dispose(/*!doNotRecurse*/);
@@ -1395,7 +1392,6 @@
             // Remove from scene
             this.getScene().removeMesh(this);
 
-            this._cache = null;
             if (disposeMaterialAndTextures) {
                 if (this.material) {
                     this.material.dispose(false, true);
@@ -1410,19 +1406,6 @@
                         index--;
                     }
                 }
-
-                // Children
-                var objects = this.getDescendants(true);
-                for (index = 0; index < objects.length; index++) {
-                    objects[index].dispose();
-                }
-            } else {
-                var childMeshes = this.getChildMeshes(true);
-                for (index = 0; index < childMeshes.length; index++) {
-                    var child = childMeshes[index];
-                    child.parent = null;
-                    child.computeWorldMatrix(true);
-                }
             }
 
             // facet data
@@ -1436,7 +1419,7 @@
 
             this._isDisposed = true;
 
-            super.dispose();
+            super.dispose(doNotRecurse);
         }
 
         /**

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

@@ -197,12 +197,12 @@
                 var index: number;
                 if (!doNotCloneChildren) {
                     // Children
-                    for (index = 0; index < scene.meshes.length; index++) {
-                        var mesh = scene.meshes[index];
+                    let directDescendants = source.getDescendants(true);
+                    for (let index = 0; index < directDescendants.length; index++) {
+                        var child = directDescendants[index];
 
-                        if (mesh.parent === source) {
-                            // doNotCloneChildren is always going to be False
-                            mesh.clone(name + "." + mesh.name, this, doNotCloneChildren);
+                        if ((<any>child).clone) {
+                            (<any>child).clone(name + "." + child.name, this);
                         }
                     }
                 }
@@ -372,7 +372,7 @@
             } else {
                 let boundingInfo = this.getBoundingInfo();
 
-                bSphere =  boundingInfo.boundingSphere;
+                bSphere = boundingInfo.boundingSphere;
             }
 
             var distanceToCamera = bSphere.centerWorld.subtract(camera.globalPosition).length();
@@ -523,7 +523,7 @@
                 return false;
             }
             return this._geometry.isVertexBufferUpdatable(kind);
-        }        
+        }
         /**
          * Returns a string : the list of existing `kinds` of Vertex Data for this mesh.  
          * Possible `kind` values :
@@ -1195,7 +1195,7 @@
 
                 let visibleInstancesForSubMesh = batch.visibleInstances[subMesh._id];
 
-                if (visibleInstancesForSubMesh) {                
+                if (visibleInstancesForSubMesh) {
                     for (var instanceIndex = 0; instanceIndex < visibleInstancesForSubMesh.length; instanceIndex++) {
                         var instance = visibleInstancesForSubMesh[instanceIndex];
 
@@ -1338,11 +1338,10 @@
             return this;
         }
 
-        private _onBeforeDraw(isInstance: boolean, world: Matrix, effectiveMaterial: Material): Mesh {
-            if (isInstance) {
+        private _onBeforeDraw(isInstance: boolean, world: Matrix, effectiveMaterial?: Material): void {
+            if (isInstance && effectiveMaterial) {
                 effectiveMaterial.bindOnlyWorldMatrix(world);
             }
-            return this;
         }
 
         /**
@@ -1595,7 +1594,8 @@
 
             // Sources
             var meshes = this.getScene().meshes;
-            meshes.forEach((mesh: Mesh) => {
+            meshes.forEach((abstractMesh: AbstractMesh) => {
+                let mesh = abstractMesh as Mesh;
                 if (mesh._source && mesh._source === this) {
                     mesh._source = null;
                 }
@@ -2101,7 +2101,7 @@
 
             // Physics
             //TODO implement correct serialization for physics impostors.
-            
+
             let impostor = this.getPhysicsImpostor();
             if (impostor) {
                 serializationObject.physicsMass = impostor.getParam("mass");
@@ -2181,7 +2181,7 @@
                 for (var index = 0; index < morphTargetManager.numInfluencers; index++) {
                     var morphTarget = morphTargetManager.getActiveTarget(index);
 
-                    const positions = morphTarget.getPositions(); 
+                    const positions = morphTarget.getPositions();
                     if (!positions) {
                         Tools.Error("Invalid morph target. Target must have positions.");
                         return;
@@ -3106,7 +3106,7 @@
 
             var matricesIndicesData = this.getVerticesData(VertexBuffer.MatricesIndicesKind);
             var matricesWeightsData = this.getVerticesData(VertexBuffer.MatricesWeightsKind);
-            
+
             if (!matricesWeightsData || !matricesIndicesData) {
                 return this;
             }
@@ -3117,7 +3117,7 @@
 
             if (!matricesWeightsExtraData || !matricesIndicesExtraData) {
                 return this;
-            }            
+            }
 
             var skeletonMatrices = skeleton.getTransformMatrices(this);
 
@@ -3171,7 +3171,7 @@
         public static MinMax(meshes: AbstractMesh[]): { min: Vector3; max: Vector3 } {
             var minVector: Nullable<Vector3> = null;
             var maxVector: Nullable<Vector3> = null;
-            
+
             meshes.forEach(function (mesh, index, array) {
                 let boundingInfo = mesh.getBoundingInfo();
 

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

@@ -448,7 +448,7 @@
             var dashSize = options.dashSize || 3;
 
             if (instance) {  //  dashed lines update
-                var positionFunction = (positions: number[]): void => {
+                var positionFunction = (positions: FloatArray): void => {
                     var curvect = Vector3.Zero();
                     var nbSeg = positions.length / 6;
                     var lg = 0;

+ 154 - 99
src/Mesh/babylon.transformNode.ts

@@ -6,19 +6,31 @@ module BABYLON {
         public static BILLBOARDMODE_Y = 2;
         public static BILLBOARDMODE_Z = 4;
         public static BILLBOARDMODE_ALL = 7;
-        
+
         // Properties
+        @serializeAsVector3()
         private _rotation = Vector3.Zero();
+
+        @serializeAsQuaternion()
         private _rotationQuaternion: Nullable<Quaternion>;
+
+        @serializeAsVector3()
         protected _scaling = Vector3.One();
         protected _isDirty = false;
         private _transformToBoneReferal: Nullable<TransformNode>;
-        
+
+        @serialize()
         public billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
+
+        @serialize()
         public scalingDeterminant = 1;
+
+        @serialize()
         public infiniteDistance = false;
+
+        @serializeAsVector3()
         public position = Vector3.Zero();
-                        
+
         // Cache        
         public _poseMatrix: Matrix;
         private _localWorld = Matrix.Zero();
@@ -26,15 +38,15 @@ module BABYLON {
         private _absolutePosition = Vector3.Zero();
         private _pivotMatrix = Matrix.Identity();
         private _pivotMatrixInverse: Matrix;
-        private _postMultiplyPivotMatrix = false;        
-        
+        private _postMultiplyPivotMatrix = false;
+
         protected _isWorldMatrixFrozen = false;
 
         /**
         * An event triggered after the world matrix is updated
         * @type {BABYLON.Observable}
         */
-        public onAfterWorldMatrixUpdateObservable = new Observable<TransformNode>();        
+        public onAfterWorldMatrixUpdateObservable = new Observable<TransformNode>();
 
         constructor(name: string, scene: Nullable<Scene> = null, isPure = true) {
             super(name, scene);
@@ -42,13 +54,13 @@ module BABYLON {
             if (isPure) {
                 this.getScene().addTransformNode(this);
             }
-        }        
-                
-       /**
-         * Rotation property : a Vector3 depicting the rotation value in radians around each local axis X, Y, Z. 
-         * If rotation quaternion is set, this Vector3 will (almost always) be the Zero vector!
-         * Default : (0.0, 0.0, 0.0)
-         */
+        }
+
+        /**
+          * Rotation property : a Vector3 depicting the rotation value in radians around each local axis X, Y, Z. 
+          * If rotation quaternion is set, this Vector3 will (almost always) be the Zero vector!
+          * Default : (0.0, 0.0, 0.0)
+          */
         public get rotation(): Vector3 {
             return this._rotation;
         }
@@ -125,7 +137,7 @@ module BABYLON {
         public getPoseMatrix(): Matrix {
             return this._poseMatrix;
         }
-        
+
         public _isSynchronized(): boolean {
             if (this._isDirty) {
                 return false;
@@ -177,7 +189,7 @@ module BABYLON {
             this._currentRenderId = Number.MAX_VALUE;
             this._isDirty = true;
             return this;
-        }        
+        }
 
         /**
          * Returns the current mesh absolute position.
@@ -240,10 +252,10 @@ module BABYLON {
             return this._isWorldMatrixFrozen;
         }
 
-     /**
-         * Retuns the mesh absolute position in the World.  
-         * Returns a Vector3.
-         */
+        /**
+            * Retuns the mesh absolute position in the World.  
+            * Returns a Vector3.
+            */
         public getAbsolutePosition(): Vector3 {
             this.computeWorldMatrix();
             return this._absolutePosition;
@@ -284,12 +296,12 @@ module BABYLON {
                 this.position.z = absolutePositionZ;
             }
             return this;
-        }   
+        }
 
-      /**
-         * Sets the mesh position in its local space.  
-         * Returns the AbstractMesh.  
-         */
+        /**
+           * Sets the mesh position in its local space.  
+           * Returns the AbstractMesh.  
+           */
         public setPositionWithLocalVector(vector3: Vector3): TransformNode {
             this.computeWorldMatrix();
             this.position = Vector3.TransformNormal(vector3, this._localWorld);
@@ -336,12 +348,12 @@ module BABYLON {
             this.rotationQuaternion = this.rotationQuaternion || new Quaternion();
             Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
             return this;
-        }        
+        }
 
-       /**
-         * Returns a new Vector3 what is the localAxis, expressed in the mesh local space, rotated like the mesh.  
-         * This Vector3 is expressed in the World space.  
-         */
+        /**
+          * Returns a new Vector3 what is the localAxis, expressed in the mesh local space, rotated like the mesh.  
+          * This Vector3 is expressed in the World space.  
+          */
         public getDirection(localAxis: Vector3): Vector3 {
             var result = Vector3.Zero();
 
@@ -422,21 +434,24 @@ module BABYLON {
             this.getPivotPointToRef(result);
             Vector3.TransformCoordinatesToRef(result, this.getWorldMatrix(), result);
             return this;
-        }        
+        }
 
         /**
-         * Defines the passed mesh as the parent of the current mesh.  
-         * Returns the AbstractMesh.  
+         * Defines the passed node as the parent of the current node.  
+         * The node will remain exactly where it is and its position / rotation will be updated accordingly
+         * Returns the TransformNode.
          */
-        public setParent(mesh: Nullable<AbstractMesh>): TransformNode {
-            var parent = (<AbstractMesh>mesh);
-
-            if (mesh == null) {
+        public setParent(node: Nullable<TransformNode>): TransformNode {
 
+            if (node == null) {
                 var rotation = Tmp.Quaternion[0];
                 var position = Tmp.Vector3[0];
                 var scale = Tmp.Vector3[1];
 
+                if (this.parent && (<TransformNode>this.parent).computeWorldMatrix) {
+                    (<TransformNode>this.parent).computeWorldMatrix(true);
+                }
+                this.computeWorldMatrix(true);
                 this.getWorldMatrix().decompose(scale, rotation, position);
 
                 if (this.rotationQuaternion) {
@@ -445,24 +460,46 @@ module BABYLON {
                     rotation.toEulerAnglesToRef(this.rotation);
                 }
 
+                this.scaling.x = scale.x;
+                this.scaling.y = scale.y;
+                this.scaling.z = scale.z;
+
                 this.position.x = position.x;
                 this.position.y = position.y;
                 this.position.z = position.z;
-
             } else {
-
+                var rotation = Tmp.Quaternion[0];
                 var position = Tmp.Vector3[0];
-                var m1 = Tmp.Matrix[0];
+                var scale = Tmp.Vector3[1];
+                var diffMatrix = Tmp.Matrix[0];
+                var invParentMatrix = Tmp.Matrix[1];
+
+                this.computeWorldMatrix(true);
+                node.computeWorldMatrix(true);
 
-                parent.getWorldMatrix().invertToRef(m1);
-                Vector3.TransformCoordinatesToRef(this.position, m1, position);
+                node.getWorldMatrix().invertToRef(invParentMatrix);
+                this.getWorldMatrix().multiplyToRef(invParentMatrix, diffMatrix);
+                diffMatrix.decompose(scale, rotation, position);
 
-                this.position.copyFrom(position);
+                if (this.rotationQuaternion) {
+                    this.rotationQuaternion.copyFrom(rotation);
+                } else {
+                    rotation.toEulerAnglesToRef(this.rotation);
+                }
+
+                this.position.x = position.x;
+                this.position.y = position.y;
+                this.position.z = position.z;
+
+                this.scaling.x = scale.x;
+                this.scaling.y = scale.y;
+                this.scaling.z = scale.z;
             }
-            this.parent = parent;
+
+            this.parent = node;
             return this;
-        }       
-        
+        }
+
         private _nonUniformScaling = false;
         public get nonUniformScaling(): boolean {
             return this._nonUniformScaling;
@@ -475,7 +512,7 @@ module BABYLON {
 
             this._nonUniformScaling = true;
             return true;
-        }        
+        }
 
         /**
          * Attach the current TransformNode to another TransformNode associated with a bone
@@ -503,7 +540,7 @@ module BABYLON {
             this._transformToBoneReferal = null;
             this.parent = null;
             return this;
-        }        
+        }
 
         private static _rotationAxisCache = new Quaternion();
         /**
@@ -611,8 +648,8 @@ module BABYLON {
                 rotationQuaternion.toEulerAnglesToRef(this.rotation);
             }
             return this;
-        }        
-        
+        }
+
         /**
          * Computes the mesh World matrix and returns it.  
          * If the mesh world matrix is frozen, this computation does nothing more than returning the last frozen values.  
@@ -660,7 +697,7 @@ module BABYLON {
 
             // Translation
             let camera = (<Camera>this.getScene().activeCamera);
-            
+
             if (this.infiniteDistance && !this.parent && camera) {
 
                 var cameraWorldMatrix = camera.getWorldMatrix();
@@ -779,7 +816,7 @@ module BABYLON {
             }
 
             return this._worldMatrix;
-        }   
+        }
 
         protected _afterComputeWorldMatrix(): void {
         }
@@ -802,9 +839,16 @@ module BABYLON {
         public unregisterAfterWorldMatrixUpdate(func: (mesh: TransformNode) => void): TransformNode {
             this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
             return this;
-        }        
+        }
 
-        public clone(name: string, newParent: Node): Nullable<TransformNode> {
+        /**
+         * Clone the current transform node
+         * Returns the new transform node
+         * @param name Name of the new clone
+         * @param newParent New parent for the clone
+         * @param doNotCloneChildren Do not clone children hierarchy
+         */
+        public clone(name: string, newParent: Node, doNotCloneChildren?: boolean): Nullable<TransformNode> {
             var result = SerializationHelper.Clone(() => new TransformNode(name, this.getScene()), this);
 
             result.name = name;
@@ -814,48 +858,43 @@ module BABYLON {
                 result.parent = newParent;
             }
 
-            return result;
-        }        
+            if (!doNotCloneChildren) {
+                // Children
+                let directDescendants = this.getDescendants(true);
+                for (let index = 0; index < directDescendants.length; index++) {
+                    var child = directDescendants[index];
 
-        public serialize(serializationObject: any = null): any {
-            if (!serializationObject) {
-                serializationObject = {};
+                    if ((<any>child).clone) {
+                        (<any>child).clone(name + "." + child.name, result);
+                    }
+                }
             }
 
-            serializationObject.name = this.name;
-            serializationObject.id = this.id;
+            return result;
+        }
+
+        public serialize(currentSerializationObject?: any): any {
+            let serializationObject = SerializationHelper.Serialize(this, currentSerializationObject);
             serializationObject.type = this.getClassName();
 
-            if (Tags && Tags.HasTags(this)) {
-                serializationObject.tags = Tags.GetTags(this);
+            // Parent
+            if (this.parent) {
+                serializationObject.parentId = this.parent.id;
             }
 
-            serializationObject.position = this.position.asArray();
-
-            if (this.rotationQuaternion) {
-                serializationObject.rotationQuaternion = this.rotationQuaternion.asArray();
-            } else if (this.rotation) {
-                serializationObject.rotation = this.rotation.asArray();
+            if (Tags && Tags.HasTags(this)) {
+                serializationObject.tags = Tags.GetTags(this);
             }
 
-            serializationObject.scaling = this.scaling.asArray();
             serializationObject.localMatrix = this.getPivotMatrix().asArray();
 
             serializationObject.isEnabled = this.isEnabled();
-            serializationObject.infiniteDistance = this.infiniteDistance;
-
-            serializationObject.billboardMode = this.billboardMode;
 
             // Parent
             if (this.parent) {
                 serializationObject.parentId = this.parent.id;
             }
 
-            // Metadata
-            if (this.metadata) {
-                serializationObject.metadata = this.metadata;
-            }
-
             return serializationObject;
         }
 
@@ -866,28 +905,12 @@ module BABYLON {
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          */
         public static Parse(parsedTransformNode: any, scene: Scene, rootUrl: string): TransformNode {
-            var transformNode = new TransformNode(parsedTransformNode.name, scene);
-
-            transformNode.id = parsedTransformNode.id;
+            var transformNode = SerializationHelper.Parse(() => new TransformNode(parsedTransformNode.name, scene), parsedTransformNode, scene, rootUrl);
 
             if (Tags) {
                 Tags.AddTagsTo(transformNode, parsedTransformNode.tags);
             }
 
-            transformNode.position = Vector3.FromArray(parsedTransformNode.position);
-
-            if (parsedTransformNode.metadata !== undefined) {
-                transformNode.metadata = parsedTransformNode.metadata;
-            }
-
-            if (parsedTransformNode.rotationQuaternion) {
-                transformNode.rotationQuaternion = Quaternion.FromArray(parsedTransformNode.rotationQuaternion);
-            } else if (parsedTransformNode.rotation) {
-                transformNode.rotation = Vector3.FromArray(parsedTransformNode.rotation);
-            }
-
-            transformNode.scaling = Vector3.FromArray(parsedTransformNode.scaling);
-
             if (parsedTransformNode.localMatrix) {
                 transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.localMatrix));
             } else if (parsedTransformNode.pivotMatrix) {
@@ -895,16 +918,48 @@ module BABYLON {
             }
 
             transformNode.setEnabled(parsedTransformNode.isEnabled);
-            transformNode.infiniteDistance = parsedTransformNode.infiniteDistance;
-
-            transformNode.billboardMode = parsedTransformNode.billboardMode;
 
             // Parent
             if (parsedTransformNode.parentId) {
                 transformNode._waitingParentId = parsedTransformNode.parentId;
             }
-         
+
             return transformNode;
-        }        
+        }
+
+        /**
+         * Disposes the TransformNode.  
+         * By default, all the children are also disposed unless the parameter `doNotRecurse` is set to `true`.  
+         * Returns nothing.  
+         */
+        public dispose(doNotRecurse?: boolean): void {
+            // Animations
+            this.getScene().stopAnimation(this);
+
+            // Remove from scene
+            this.getScene().removeTransformNode(this);
+
+            this._cache = null;
+
+            if (!doNotRecurse) {
+                // Children
+                var objects = this.getDescendants(true);
+                for (var index = 0; index < objects.length; index++) {
+                    objects[index].dispose();
+                }
+            } else {
+                var childMeshes = this.getChildMeshes(true);
+                for (index = 0; index < childMeshes.length; index++) {
+                    var child = childMeshes[index];
+                    child.parent = null;
+                    child.computeWorldMatrix(true);
+                }
+            }
+
+            this.onAfterWorldMatrixUpdateObservable.clear();
+
+            super.dispose();
+        }
+
     }
 }

+ 7 - 1
src/Physics/babylon.physicsEngine.ts

@@ -61,7 +61,7 @@
 
         private _impostors: Array<PhysicsImpostor> = [];
         private _joints: Array<PhysicsImpostorJoint> = [];
-
+        
         /**
          * Adding a new impostor for the impostor tracking.
          * This will be done by the impostor itself.
@@ -146,6 +146,10 @@
         public getPhysicsPlugin(): IPhysicsEnginePlugin {
             return this._physicsPlugin;
         }
+        
+        public getImpostors(): Array<PhysicsImpostor> {
+            return this._impostors;
+        }
 
         public getImpostorForPhysicsObject(object: IPhysicsEnabledObject): Nullable<PhysicsImpostor> {
             for (var i = 0; i < this._impostors.length; ++i) {
@@ -166,6 +170,7 @@
 
             return null;
         }
+
     }
 
     export interface IPhysicsEnginePlugin {
@@ -205,4 +210,5 @@
         syncMeshWithImpostor(mesh:AbstractMesh, impostor:PhysicsImpostor): void;
         dispose(): void;
     }
+    
 }

+ 332 - 0
src/Physics/babylon.physicsHelper.ts

@@ -0,0 +1,332 @@
+module BABYLON {
+
+    /**
+     * The strenght of the force in correspondence to the distance of the affected object
+     */
+    export enum PhysicsRadialImpulseFallof {
+        Constant, // impulse is constant in strength across it's whole radius
+        Linear // impulse gets weaker if it's further from the origin
+    }
+
+    export class PhysicsHelper {
+
+        private _scene: Scene;
+        private _physicsEngine: Nullable<PhysicsEngine>;
+
+        constructor(scene: Scene) {
+            this._scene = scene;
+            this._physicsEngine = this._scene.getPhysicsEngine();
+
+            if (!this._physicsEngine) {
+                Tools.Warn('Physics engine not enabled. Please enable the physics before you can use the methods.');
+            }
+        }
+
+        /**
+         * @param {Vector3} origin the origin of the explosion
+         * @param {number} radius the explosion radius
+         * @param {number} strength the explosion strength
+         * @param {PhysicsRadialImpulseFallof} falloff possible options: Constant & Linear. Defaults to Constant
+         */
+        public applyRadialExplosionImpulse(origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFallof = PhysicsRadialImpulseFallof.Constant): Nullable<PhysicsRadialExplosionEvent> {
+            if (!this._physicsEngine) {
+                Tools.Warn('Physics engine not enabled. Please enable the physics before you call this method.');
+                return null;
+            }
+
+            var impostors = this._physicsEngine.getImpostors();
+            if (impostors.length === 0) {
+                return null;
+            }
+
+            var event = new PhysicsRadialExplosionEvent(this._scene);
+
+            impostors.forEach(impostor => {
+
+                var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(impostor, origin, radius, strength, falloff);
+                if (!impostorForceAndContactPoint) {
+                    return;
+                }
+
+                impostor.applyImpulse(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
+            });
+
+            event.cleanup(false);
+
+            return event;
+        }
+
+        /**
+         * @param {Vector3} origin the origin of the explosion
+         * @param {number} radius the explosion radius
+         * @param {number} strength the explosion strength
+         * @param {PhysicsRadialImpulseFallof} falloff possible options: Constant & Linear. Defaults to Constant
+         */
+        public applyRadialExplosionForce(origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFallof = PhysicsRadialImpulseFallof.Constant): Nullable<PhysicsRadialExplosionEvent> {
+            if (!this._physicsEngine) {
+                Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
+                return null;
+            }
+
+            var impostors = this._physicsEngine.getImpostors();
+            if (impostors.length === 0) {
+                return null;
+            }
+
+            var event = new PhysicsRadialExplosionEvent(this._scene);
+
+            impostors.forEach(impostor => {
+                var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(impostor, origin, radius, strength, falloff);
+
+                if (!impostorForceAndContactPoint) {
+                    return;
+                }
+
+                impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
+            })
+
+            event.cleanup(false);
+
+            return event;
+        }
+
+        /**
+         * @param {Vector3} origin the origin of the explosion
+         * @param {number} radius the explosion radius
+         * @param {number} strength the explosion strength
+         * @param {PhysicsRadialImpulseFallof} falloff possible options: Constant & Linear. Defaults to Constant
+         */
+        public gravitationalField(origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFallof = PhysicsRadialImpulseFallof.Constant): Nullable<PhysicsGravitationalFieldEvent> {
+            if (!this._physicsEngine) {
+                Tools.Warn('Physics engine not enabled. Please enable the physics before you call the PhysicsHelper.');
+                return null;
+            }
+
+            var impostors = this._physicsEngine.getImpostors();
+            if (impostors.length === 0) {
+                return null;
+            }
+
+            var event = new PhysicsGravitationalFieldEvent(this, this._scene, origin, radius, strength, falloff);
+
+            event.cleanup(false);
+
+            return event;
+        }
+    }
+
+    /***** Radial explosion *****/
+
+    export class PhysicsRadialExplosionEvent {
+
+        private _scene: Scene;
+        private _radialSphere: Mesh; // create a sphere, so we can get the intersecting meshes inside
+        private _rays: Array<Ray> = [];
+        private _dataFetched: boolean = false; // check if the data has been fetched. If not, do cleanup
+
+        constructor(scene: Scene) {
+            this._scene = scene;
+        }
+
+        /**
+         * Returns the data related to the radial explosion event (radialSphere & rays).
+         * @returns {PhysicsRadialExplosionEventData}
+         */
+        public getData(): PhysicsRadialExplosionEventData {
+            this._dataFetched = true;
+
+            return {
+                radialSphere: this._radialSphere,
+                rays: this._rays,
+            };
+        }
+
+        /**
+         * Returns the force and contact point of the impostor or false, if the impostor is not affected by the force/impulse.
+         * @param impostor 
+         * @param {Vector3} origin the origin of the explosion
+         * @param {number} radius the explosion radius
+         * @param {number} strength the explosion strength
+         * @param {PhysicsRadialImpulseFallof} falloff possible options: Constant & Linear
+         * @returns {Nullable<PhysicsForceAndContactPoint>}
+         */
+        public getImpostorForceAndContactPoint(impostor: PhysicsImpostor, origin: Vector3, radius: number, strength: number, falloff: PhysicsRadialImpulseFallof): Nullable<PhysicsForceAndContactPoint> {
+            if (impostor.mass === 0) {
+                return null;
+            }
+
+            if (!this._intersectsWithRadialSphere(impostor, origin, radius)) {
+                return null;
+            }
+
+            var impostorObject = (<Mesh>impostor.object);
+            var impostorObjectCenter = impostor.getObjectCenter();
+            var direction = impostorObjectCenter.subtract(origin);
+
+            var ray = new Ray(origin, direction, radius);
+            this._rays.push(ray);
+            var hit = ray.intersectsMesh(impostorObject);
+
+            var contactPoint = hit.pickedPoint;
+            if (!contactPoint) {
+                return null;
+            }
+
+            var distanceFromOrigin = Vector3.Distance(origin, contactPoint);
+            if (distanceFromOrigin > radius) {
+                return null;
+            }
+
+            var multiplier = falloff === PhysicsRadialImpulseFallof.Constant
+                ? strength
+                : strength * (1 - (distanceFromOrigin / radius));
+
+            var force = direction.multiplyByFloats(multiplier, multiplier, multiplier);
+
+            return { force: force, contactPoint: contactPoint };
+        }
+
+        /**
+         * Disposes the radialSphere.
+         * @param {bolean} force
+         */
+        public cleanup(force: boolean = true) {
+            if (force) {
+                this._radialSphere.dispose();
+            } else {
+                setTimeout(() => {
+                    if (!this._dataFetched) {
+                        this._radialSphere.dispose();
+                    }
+                }, 0);
+            }
+        }
+
+        /*** Helpers ***/
+
+        private _prepareRadialSphere(): void {
+            if (!this._radialSphere) {
+                this._radialSphere = MeshBuilder.CreateSphere("radialSphere", { segments: 32, diameter: 1 }, this._scene);
+                this._radialSphere.isVisible = false;
+            }
+        }
+
+        private _intersectsWithRadialSphere(impostor: PhysicsImpostor, origin: Vector3, radius: number): boolean {
+            var impostorObject = <Mesh>impostor.object;
+
+            this._prepareRadialSphere();
+
+            this._radialSphere.position = origin;
+            this._radialSphere.scaling = new Vector3(radius * 2, radius * 2, radius * 2);
+            this._radialSphere._updateBoundingInfo();
+            this._radialSphere.computeWorldMatrix(true);
+
+            return this._radialSphere.intersectsMesh(impostorObject, true);
+        }
+
+    }
+
+    export interface PhysicsRadialExplosionEventData {
+        radialSphere: Mesh;
+        rays: Array<Ray>;
+    }
+
+    export interface PhysicsForceAndContactPoint {
+        force: Vector3;
+        contactPoint: Vector3;
+    }
+
+
+    /***** Gravitational Field *****/
+
+    export class PhysicsGravitationalFieldEvent {
+
+        private _physicsHelper: PhysicsHelper;
+        private _scene: Scene;
+        private _origin: Vector3;
+        private _radius: number;
+        private _strength: number;
+        private _falloff: PhysicsRadialImpulseFallof;
+        private _tickCallback: any;
+        private _radialSphere: Mesh;
+        private _dataFetched: boolean = false; // check if the has been fetched the data. If not, do cleanup
+
+        constructor(
+            physicsHelper: PhysicsHelper,
+            scene: Scene,
+            origin: Vector3,
+            radius: number,
+            strength: number,
+            falloff: PhysicsRadialImpulseFallof = PhysicsRadialImpulseFallof.Constant
+        ) {
+            this._physicsHelper = physicsHelper;
+            this._scene = scene;
+            this._origin = origin;
+            this._radius = radius;
+            this._strength = strength;
+            this._falloff = falloff;
+            this._tickCallback = this._tick.bind(this);
+        }
+
+        /**
+         * Returns the data related to the gravitational field event (radialSphere).
+         * @returns {PhysicsGravitationalFieldEventData}
+         */
+        public getData(): PhysicsGravitationalFieldEventData {
+            this._dataFetched = true;
+
+            return {
+                radialSphere: this._radialSphere,
+            };
+        }
+
+        /**
+         * Enables the gravitational field.
+         */
+        public enable() {
+            this._tickCallback.call(this);
+            this._scene.registerBeforeRender(this._tickCallback);
+        }
+
+        /**
+         * Disables the gravitational field.
+         */
+        public disable() {
+            this._scene.unregisterBeforeRender(this._tickCallback);
+        }
+
+        /**
+         * Disposes the radialSphere.
+         * @param {bolean} force
+         */
+        public cleanup(force: boolean = true) {
+            if (force) {
+                this._radialSphere.dispose();
+            } else {
+                setTimeout(() => {
+                    if (!this._dataFetched) {
+                        this._radialSphere.dispose();
+                    }
+                }, 0);
+            }
+        }
+
+        private _tick() {
+            // Since the params won't change, we fetch the event only once
+            if (this._radialSphere) {
+                this._physicsHelper.applyRadialExplosionForce(this._origin, this._radius, this._strength * -1, this._falloff);
+            } else {
+                var radialExplosionEvent = this._physicsHelper.applyRadialExplosionForce(this._origin, this._radius, this._strength * -1, this._falloff);
+                if (radialExplosionEvent) {
+                    this._radialSphere = <Mesh>radialExplosionEvent.getData().radialSphere.clone('radialSphereClone');
+                }
+            }
+        }
+
+    }
+
+    export interface PhysicsGravitationalFieldEventData {
+        radialSphere: Mesh;
+    }
+
+}

+ 43 - 38
src/Tools/babylon.assetsManager.ts

@@ -7,30 +7,14 @@ module BABYLON {
         ERROR
     }
 
-    export interface IAssetTask<T extends AbstractAssetTask> {
-        onSuccess: (task: T) => void;
-        onError: (task: T, message?: string, exception?: any) => void;
-        isCompleted: boolean;
-        name: string;
-
-        taskState: AssetTaskState;
-        errorObject: {
-            message?: string;
-            exception?: any;
-        }
-
-        runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void): void;
-    }
-
-    export abstract class AbstractAssetTask implements IAssetTask<AbstractAssetTask> {
+    export abstract class AbstractAssetTask  {
+        public onSuccess: (task: any) => void;
+        public onError: (task: any, message?: string, exception?: any) => void;
 
         constructor(public name: string) {
             this.taskState = AssetTaskState.INIT;
         }
 
-        onSuccess: (task: this) => void;
-        onError: (task: this, message?: string, exception?: any) => void;
-
         isCompleted: boolean = false;
         taskState: AssetTaskState;
         errorObject: { message?: string; exception?: any; };
@@ -98,11 +82,14 @@ module BABYLON {
         }
     }
 
-    export class MeshAssetTask extends AbstractAssetTask implements IAssetTask<MeshAssetTask> {
+    export class MeshAssetTask extends AbstractAssetTask {
         public loadedMeshes: Array<AbstractMesh>;
         public loadedParticleSystems: Array<ParticleSystem>;
         public loadedSkeletons: Array<Skeleton>;
 
+        public onSuccess: (task: MeshAssetTask) => void;
+        public onError: (task: MeshAssetTask, message?: string, exception?: any) => void;
+
         constructor(public name: string, public meshesNames: any, public rootUrl: string, public sceneFilename: string) {
             super(name);
         }
@@ -121,9 +108,12 @@ module BABYLON {
         }
     }
 
-    export class TextFileAssetTask extends AbstractAssetTask implements IAssetTask<TextFileAssetTask> {
+    export class TextFileAssetTask extends AbstractAssetTask {
         public text: string;
 
+        public onSuccess: (task: TextFileAssetTask) => void;
+        public onError: (task: TextFileAssetTask, message?: string, exception?: any) => void;        
+
         constructor(public name: string, public url: string) {
             super(name);
         }
@@ -140,9 +130,12 @@ module BABYLON {
         }
     }
 
-    export class BinaryFileAssetTask extends AbstractAssetTask implements IAssetTask<BinaryFileAssetTask> {
+    export class BinaryFileAssetTask extends AbstractAssetTask {
         public data: ArrayBuffer;
 
+        public onSuccess: (task: BinaryFileAssetTask) => void;
+        public onError: (task: BinaryFileAssetTask, message?: string, exception?: any) => void;               
+
         constructor(public name: string, public url: string) {
             super(name);
         }
@@ -160,9 +153,12 @@ module BABYLON {
         }
     }
 
-    export class ImageAssetTask extends AbstractAssetTask implements IAssetTask<ImageAssetTask> {
+    export class ImageAssetTask extends AbstractAssetTask {
         public image: HTMLImageElement;
 
+        public onSuccess: (task: ImageAssetTask) => void;
+        public onError: (task: ImageAssetTask, message?: string, exception?: any) => void;            
+
         constructor(public name: string, public url: string) {
             super(name);
         }
@@ -185,12 +181,15 @@ module BABYLON {
         }
     }
 
-    export interface ITextureAssetTask<TEX extends BaseTexture, T extends AbstractAssetTask> extends IAssetTask<T> {
+    export interface ITextureAssetTask<TEX extends BaseTexture> {
         texture: TEX;
     }
 
-    export class TextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<Texture, TextureAssetTask> {
+    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;    
 
         constructor(public name: string, public url: string, public noMipmap?: boolean, public invertY?: boolean, public samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
             super(name);
@@ -202,17 +201,20 @@ module BABYLON {
                 onSuccess();
             };
 
-            var onerror = (msg: string, exception: any) => {
-                onError(msg, exception);
+            var onerror = (message?: string, exception?: any) => {
+                onError(message, exception);
             };
 
             this.texture = new Texture(this.url, scene, this.noMipmap, this.invertY, this.samplingMode, onload, onerror);
         }
     }
 
-    export class CubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<CubeTexture, CubeTextureAssetTask> {
+    export class CubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<CubeTexture> {
         public texture: CubeTexture;
 
+        public onSuccess: (task: CubeTextureAssetTask) => 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);
         }
@@ -223,17 +225,20 @@ module BABYLON {
                 onSuccess();
             };
 
-            var onerror = (msg: string, exception: any) => {
-                onError(msg, exception);
+            var onerror = (message?: string, exception?: any) => {
+                onError(message, exception);
             };
 
             this.texture = new CubeTexture(this.url, scene, this.extensions, this.noMipmap, this.files, onload, onerror);
         }
     }
 
-    export class HDRCubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<HDRCubeTexture, HDRCubeTextureAssetTask> {
+    export class HDRCubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<HDRCubeTexture> {
         public texture: HDRCubeTexture;
 
+        public onSuccess: (task: HDRCubeTextureAssetTask) => 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,16 +263,16 @@ module BABYLON {
         protected tasks = new Array<AbstractAssetTask>();
         protected waitingTasksCount = 0;
 
-        public onFinish: (tasks: IAssetTask<AbstractAssetTask>[]) => void;
-        public onTaskSuccess: (task: IAssetTask<AbstractAssetTask>) => void;
-        public onTaskError: (task: IAssetTask<AbstractAssetTask>) => void;
-        public onProgress: (remainingCount: number, totalCount: number, task: IAssetTask<AbstractAssetTask>) => void;
+        public onFinish: (tasks: AbstractAssetTask[]) => void;
+        public onTaskSuccess: (task: AbstractAssetTask) => void;
+        public onTaskError: (task: AbstractAssetTask) => void;
+        public onProgress: (remainingCount: number, totalCount: number, task: AbstractAssetTask) => void;
 
         //Observables
 
-        public onTaskSuccessObservable = new Observable<IAssetTask<AbstractAssetTask>>();
-        public onTaskErrorObservable = new Observable<IAssetTask<AbstractAssetTask>>();
-        public onTasksDoneObservable = new Observable<IAssetTask<AbstractAssetTask>[]>();
+        public onTaskSuccessObservable = new Observable<AbstractAssetTask>();
+        public onTaskErrorObservable = new Observable<AbstractAssetTask>();
+        public onTasksDoneObservable = new Observable<AbstractAssetTask[]>();
         public onProgressObservable = new Observable<IAssetsProgressEvent>();
 
         public useDefaultLoadingScreen = true;

+ 14 - 9
src/Tools/babylon.decorators.ts

@@ -2,7 +2,7 @@
     var __decoratorInitialStore = {};
     var __mergedStore = {};
 
-    var _copySource = function<T>(creationFunction: () => T, source: T, instanciate: boolean): T {
+    var _copySource = function <T>(creationFunction: () => T, source: T, instanciate: boolean): T {
         var destination = creationFunction();
 
         // Tags
@@ -25,14 +25,15 @@
                         (<any>destination)[property] = sourceProperty;
                         break;
                     case 1:     // Texture
-                        (<any>destination)[property] = (instanciate||sourceProperty.isRenderTarget)?sourceProperty:sourceProperty.clone();
+                        (<any>destination)[property] = (instanciate || sourceProperty.isRenderTarget) ? sourceProperty : sourceProperty.clone();
                         break;
                     case 2:     // Color3
                     case 3:     // FresnelParameters
                     case 4:     // Vector2
                     case 5:     // Vector3
                     case 7:     // Color Curves
-                        (<any>destination)[property] = instanciate?sourceProperty:sourceProperty.clone();
+                    case 10:    // Quaternion
+                        (<any>destination)[property] = instanciate ? sourceProperty : sourceProperty.clone();
                         break;
                 }
             }
@@ -70,7 +71,7 @@
         while (currentKey) {
             let initialStore = (<any>__decoratorInitialStore)[currentKey];
             for (var property in initialStore) {
-                store[property] = initialStore[property];                
+                store[property] = initialStore[property];
             }
 
             let parent: any;
@@ -94,7 +95,7 @@
             if (done) {
                 break;
             }
-            
+
             currentKey = parent.getClassName();
             currentTarget = parent;
         }
@@ -116,15 +117,15 @@
         return (target: any, propertyKey: string) => {
             var key = targetKey || ("_" + propertyKey);
             Object.defineProperty(target, propertyKey, {
-                get: function () {
+                get: function (this: any) {
                     return this[key];
                 },
-                set: function (value) {
+                set: function (this: any, value) {
                     if (this[key] === value) {
                         return;
                     }
                     this[key] = value;
-                    
+
                     target[setCallback].apply(this);
                 },
                 enumerable: true,
@@ -164,7 +165,7 @@
     export function serializeAsMeshReference(sourceName?: string) {
         return generateSerializableMember(6, sourceName); // mesh reference member
     }
-    
+
     export function serializeAsColorCurves(sourceName?: string) {
         return generateSerializableMember(7, sourceName); // color curves
     }
@@ -177,6 +178,10 @@
         return generateSerializableMember(9, sourceName); // image processing
     }
 
+    export function serializeAsQuaternion(sourceName?: string) {
+        return generateSerializableMember(10, sourceName); // quaternion member
+    }
+
 
     export class SerializationHelper {
 

+ 2 - 2
src/Tools/babylon.observable.ts

@@ -45,7 +45,7 @@
      * Represent an Observer registered to a given Observable object.
      */
     export class Observer<T> {
-        constructor(public callback: (eventData: Nullable<T>, eventState: EventState) => void, public mask: number, public scope: any = null) {
+        constructor(public callback: (eventData: T, eventState: EventState) => void, public mask: number, public scope: any = null) {
         }
     }
 
@@ -176,7 +176,7 @@
          * @param eventData
          * @param mask
          */
-        public notifyObservers(eventData: Nullable<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) {
                 return true;
             }

+ 7 - 10
src/Tools/babylon.tools.ts

@@ -433,10 +433,7 @@
             img.onerror = err => {
                 Tools.Error("Error while trying to load image: " + url);
 
-                if (Tools.UseFallbackTexture) {
-                    img.src = Tools.fallbackTexture;
-                    onLoad(img);
-                } else {
+                if (onError) {
                     onError("Error while trying to load image: " + url, err);
                 }
             };
@@ -691,14 +688,14 @@
             return true;
         }
 
-        public static RegisterTopRootEvents(events: { name: string; handler: EventListener }[]): void {
+        public static RegisterTopRootEvents(events: { name: string; handler: Nullable<(e: FocusEvent) => any> }[]): void {
             for (var index = 0; index < events.length; index++) {
                 var event = events[index];
-                window.addEventListener(event.name, event.handler, false);
+                window.addEventListener(event.name, <any>event.handler, false);
 
                 try {
                     if (window.parent) {
-                        window.parent.addEventListener(event.name, event.handler, false);
+                        window.parent.addEventListener(event.name, <any>event.handler, false);
                     }
                 } catch (e) {
                     // Silently fails...
@@ -706,14 +703,14 @@
             }
         }
 
-        public static UnregisterTopRootEvents(events: { name: string; handler: EventListener }[]): void {
+        public static UnregisterTopRootEvents(events: { name: string; handler: Nullable<(e: FocusEvent) => any> }[]): void {
             for (var index = 0; index < events.length; index++) {
                 var event = events[index];
-                window.removeEventListener(event.name, event.handler);
+                window.removeEventListener(event.name, <any>event.handler);
 
                 try {
                     if (window.parent) {
-                        window.parent.removeEventListener(event.name, event.handler);
+                        window.parent.removeEventListener(event.name, <any>event.handler);
                     }
                 } catch (e) {
                     // Silently fails...

+ 13 - 2
src/babylon.node.ts

@@ -321,15 +321,26 @@
         /**
          * Get all child-meshes of this node.
          */
-        public getChildMeshes(directDecendantsOnly?: boolean, predicate?: (node: Node) => boolean): AbstractMesh[] {
+        public getChildMeshes(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): AbstractMesh[] {
             var results: Array<AbstractMesh> = [];
-            this._getDescendants(results, directDecendantsOnly, (node: Node) => {
+            this._getDescendants(results, directDescendantsOnly, (node: Node) => {
                 return ((!predicate || predicate(node)) && (node instanceof AbstractMesh));
             });
             return results;
         }
 
         /**
+         * Get all child-transformNodes of this node.
+         */
+        public getChildTransformNodes(directDescendantsOnly?: boolean, predicate?: (node: Node) => boolean): TransformNode[] {
+            var results: Array<TransformNode> = [];
+            this._getDescendants(results, directDescendantsOnly, (node: Node) => {
+                return ((!predicate || predicate(node)) && (node instanceof TransformNode));
+            });
+            return results;
+        }
+
+        /**
          * Get all direct children of this node.
         */
         public getChildren(predicate?: (node: Node) => boolean): Node[] {

+ 12 - 12
src/babylon.scene.ts

@@ -558,8 +558,8 @@
          * Observable event triggered each time an keyboard event is received from the hosting window
          */
         public onKeyboardObservable = new Observable<KeyboardInfo>();
-        private _onKeyDown: (evt: Event) => void;
-        private _onKeyUp: (evt: Event) => void;
+        private _onKeyDown: (evt: KeyboardEvent) => void;
+        private _onKeyUp: (evt: KeyboardEvent) => void;
         private _onCanvasFocusObserver: Nullable<Observer<Engine>>;
         private _onCanvasBlurObserver: Nullable<Observer<Engine>>;
 
@@ -1786,18 +1786,18 @@
             }
 
             if (attachMove) {
-                canvas.addEventListener(eventPrefix + "move", this._onPointerMove, false);
+                canvas.addEventListener(eventPrefix + "move", <any>this._onPointerMove, false);
                 // Wheel
-                canvas.addEventListener('mousewheel', this._onPointerMove, false);
-                canvas.addEventListener('DOMMouseScroll', this._onPointerMove, false);
+                canvas.addEventListener('mousewheel', <any>this._onPointerMove, false);
+                canvas.addEventListener('DOMMouseScroll', <any>this._onPointerMove, false);
             }
 
             if (attachDown) {
-                canvas.addEventListener(eventPrefix + "down", this._onPointerDown, false);
+                canvas.addEventListener(eventPrefix + "down", <any>this._onPointerDown, false);
             }
 
             if (attachUp) {
-                window.addEventListener(eventPrefix + "up", this._onPointerUp, false);
+                window.addEventListener(eventPrefix + "up", <any>this._onPointerUp, false);
             }
 
             canvas.tabIndex = 1;
@@ -1812,9 +1812,9 @@
                 return;
             }
 
-            canvas.removeEventListener(eventPrefix + "move", this._onPointerMove);
-            canvas.removeEventListener(eventPrefix + "down", this._onPointerDown);
-            window.removeEventListener(eventPrefix + "up", this._onPointerUp);
+            canvas.removeEventListener(eventPrefix + "move", <any>this._onPointerMove);
+            canvas.removeEventListener(eventPrefix + "down", <any>this._onPointerDown);
+            window.removeEventListener(eventPrefix + "up", <any>this._onPointerUp);
 
             if (this._onCanvasBlurObserver) {
                 engine.onCanvasBlurObservable.remove(this._onCanvasBlurObserver);
@@ -1825,8 +1825,8 @@
             }
 
             // Wheel
-            canvas.removeEventListener('mousewheel', this._onPointerMove);
-            canvas.removeEventListener('DOMMouseScroll', this._onPointerMove);
+            canvas.removeEventListener('mousewheel', <any>this._onPointerMove);
+            canvas.removeEventListener('DOMMouseScroll', <any>this._onPointerMove);
 
             // Keyboard
             canvas.removeEventListener("keydown", this._onKeyDown);

+ 2 - 1
src/tsconfig.json

@@ -10,6 +10,7 @@
     "noImplicitReturns": true,
     "noImplicitThis": true,
     "noUnusedLocals": true,    
-    "strictNullChecks": true
+    "strictNullChecks": true,
+    "strictFunctionTypes": true
   }
 }

二进制
tests/validation/ReferenceImages/Billboard.png


二进制
tests/validation/ReferenceImages/setParent.png


+ 13 - 1
tests/validation/config.json

@@ -285,12 +285,24 @@
       "referenceImage": "DefaultRenderingPipeline.png"
     },
     {
+      "title": "Billboard",
+      "renderCount": 20,
+      "playgroundId": "#UJEIL#13",
+      "referenceImage": "Billboard.png"
+    },
+    {
+      "title": "setParent",
+      "renderCount": 20,
+      "playgroundId": "#JD49CT#2",
+      "referenceImage": "setParent.png"
+    },
+    {
       "title": "GUI",
       "renderCount": 20,
       "scriptToRun": "/Demos/GUI/gui.js",
       "functionToCall": "createScene",
       "referenceImage": "GUI.png"
-    },    
+    },
     {
       "title": "Water material (only visual check)",
       "renderCount": 10,