소스 검색

Merge pull request #3138 from BabylonJS/master

Nightly
David Catuhe 7 년 전
부모
커밋
4cced16bcc
63개의 변경된 파일12677개의 추가작업 그리고 9570개의 파일을 삭제
  1. 1 0
      Tools/Gulp/config.json
  2. 4 1
      Viewer/dist/basicExample.html
  3. 67 0
      Viewer/dist/eventsExample.html
  4. 3452 1657
      Viewer/dist/viewer.js
  5. 8 8
      Viewer/package.json
  6. 12 3
      Viewer/src/configuration/configuration.ts
  7. 2 2
      Viewer/src/configuration/types/default.ts
  8. 39 0
      Viewer/src/util/promiseObservable.ts
  9. 7 1
      Viewer/src/viewer/defaultViewer.ts
  10. 36 11
      Viewer/src/viewer/viewer.ts
  11. 23 0
      Viewer/src/viewer/viewerManager.ts
  12. 839 760
      dist/preview release/babylon.d.ts
  13. 43 43
      dist/preview release/babylon.js
  14. 982 756
      dist/preview release/babylon.max.js
  15. 839 760
      dist/preview release/babylon.module.d.ts
  16. 47 47
      dist/preview release/babylon.worker.js
  17. 1981 1902
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  18. 46 46
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  19. 987 762
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  20. 1981 1902
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  21. 1 0
      dist/preview release/gui/babylon.gui.d.ts
  22. 19 0
      dist/preview release/gui/babylon.gui.js
  23. 3 3
      dist/preview release/gui/babylon.gui.min.js
  24. 1 0
      dist/preview release/gui/babylon.gui.module.d.ts
  25. 4 4
      dist/preview release/inspector/babylon.inspector.bundle.js
  26. 3 2
      dist/preview release/inspector/babylon.inspector.d.ts
  27. 16 8
      dist/preview release/inspector/babylon.inspector.js
  28. 4 4
      dist/preview release/inspector/babylon.inspector.min.js
  29. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  30. 9 6
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  31. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  32. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  33. 9 6
      dist/preview release/loaders/babylon.glTFFileLoader.js
  34. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  35. 9 6
      dist/preview release/loaders/babylonjs.loaders.js
  36. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  37. 2 2
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  38. 1 0
      dist/preview release/materialsLibrary/babylon.gridMaterial.d.ts
  39. 3 0
      dist/preview release/materialsLibrary/babylon.gridMaterial.js
  40. 1 1
      dist/preview release/materialsLibrary/babylon.gridMaterial.min.js
  41. 3 0
      dist/preview release/materialsLibrary/babylonjs.materials.js
  42. 3 3
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  43. 1 0
      dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts
  44. 1 0
      dist/preview release/what's new.md
  45. 18 1
      gui/src/advancedDynamicTexture.ts
  46. 18 9
      inspector/src/adapters/MeshAdapter.ts
  47. 11 6
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  48. 2 2
      loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts
  49. 24 7
      localDev/index.html
  50. 4 0
      materialsLibrary/src/grid/babylon.gridmaterial.ts
  51. 32 6
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  52. 32 30
      src/Engine/babylon.engine.ts
  53. 10 0
      src/Engine/babylon.nullEngine.ts
  54. 82 29
      src/Helpers/babylon.environmentHelper.ts
  55. 17 2
      src/Materials/Background/babylon.backgroundMaterial.ts
  56. 20 746
      src/Mesh/babylon.abstractMesh.ts
  57. 1 0
      src/Mesh/babylon.mesh.ts
  58. 788 0
      src/Mesh/babylon.transformNode.ts
  59. 3 3
      src/Physics/babylon.physicsImpostor.ts
  60. 11 0
      src/Shaders/ShadersInclude/helperFunctions.fx
  61. 4 0
      src/Shaders/background.fragment.fx
  62. 95 8
      src/babylon.scene.ts
  63. 5 4
      tests/nullEngine/app.js

+ 1 - 0
Tools/Gulp/config.json

@@ -181,6 +181,7 @@
                 "../../src/Culling/babylon.boundingSphere.js",
                 "../../src/Culling/babylon.boundingBox.js",
                 "../../src/Culling/babylon.boundingInfo.js",
+                "../../src/Mesh/babylon.transformNode.js",
                 "../../src/Mesh/babylon.abstractMesh.js",
                 "../../src/Lights/babylon.light.js",
                 "../../src/Cameras/babylon.camera.js",

+ 4 - 1
Viewer/dist/basicExample.html

@@ -11,7 +11,7 @@
                 max-width: 800px;
                 max-height: 500px;
                 width: 100%;
-                height: 100%;
+                height: 600px;
             }
         </style>
     </head>
@@ -21,6 +21,9 @@
             model.url="https://playground.babylonjs.com/scenes/Rabbit.babylon" camera.behaviors.auto-rotate="0" templates.nav-bar.params.disable-on-fullscreen="true"></babylon>
         <script src="viewer.js"></script>
         <script>
+            // The following lines are redundant. 
+            // They are only here to show how you could achive the tag initialization on your own.
+
             // a simple way of disabling auto init 
             BabylonViewer.disableInit = true;
             // Initializing the viewer on specific HTML tags.

+ 67 - 0
Viewer/dist/eventsExample.html

@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="en">
+
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta http-equiv="X-UA-Compatible" content="ie=edge">
+        <title>BabylonJS Viewer - Basic usage</title>
+        <style>
+            babylon {
+                max-width: 800px;
+                max-height: 500px;
+                width: 100%;
+                height: 600px;
+            }
+        </style>
+    </head>
+
+    <body>
+        <babylon id="babylon-viewer" model.title="Amazing Rabbit" model.subtitle="BabylonJS" model.thumbnail="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png"
+            model.url="https://playground.babylonjs.com/scenes/Rabbit.babylon" observers.on-scene-init="globalSceneInitCallback"></babylon>
+        <script src="viewer.js"></script>
+        <script>
+            //get by id ONLY after viewer init
+            var willNotWork = BabylonViewer.viewerManager.getViewerById('babylon-viewer');
+            console.log('viewer not yet initialized');
+
+            // Pomise-based API:
+            BabylonViewer.viewerManager.getViewerPromiseById('babylon-viewer').then(function (viewer) {
+                // this will resolve only after the viewer with this specific ID is initialized
+                console.log('Using promises: ', 'viewer - ' + viewer.getBaseId());
+
+                viewerObservables(viewer);
+            });
+
+            // call back variant:
+            BabylonViewer.viewerManager.onViewerAdded = function (viewer) {
+                console.log('Using viewerManager.onViewerAdded: ', 'viewer - ' + viewer.getBaseId());
+            }
+
+            // using observers:
+            BabylonViewer.viewerManager.onViewerAddedObservable.add(function (viewer) {
+                console.log('Using viewerManager.onViewerAddedObservable: ', 'viewer - ' + viewer.getBaseId());
+            });
+
+            function viewerObservables(viewer) {
+                viewer.onEngineInitObservable.add(function (engine) {
+                    console.log('Engine initialized');
+                });
+
+                viewer.onSceneInitObservable.add(function (scene) {
+                    console.log('Scene initialized');
+                });
+
+                viewer.onModelLoadedObservable.add(function (meshes) {
+                    console.log('Model loaded');
+                });
+            }
+
+            function globalSceneInitCallback(scene) {
+                console.log('scene-init function defined in the configuration');
+            }
+
+        </script>
+    </body>
+
+</html>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 3452 - 1657
Viewer/dist/viewer.js


+ 8 - 8
Viewer/package.json

@@ -1,6 +1,6 @@
 {
     "name": "babylonjs-viewer",
-    "version": "0.1.0",
+    "version": "0.2.0",
     "description": "A viewer using BabylonJS to display 3D elements natively",
     "scripts": {
         "start:server": "webpack-dev-server",
@@ -23,7 +23,7 @@
     },
     "homepage": "https://github.com/BabylonJS/Babylon.js#readme",
     "devDependencies": {
-        "@types/node": "^8.0.47",
+        "@types/node": "^8.0.51",
         "base64-image-loader": "^1.2.0",
         "html-loader": "^0.5.1",
         "json-loader": "^0.5.7",
@@ -34,15 +34,15 @@
         "webpack-dev-server": "^2.9.4"
     },
     "dependencies": {
-        "babylonjs": "^3.1.0-alpha3.7",
-        "babylonjs-loaders": "^3.1.0-alpha3.7",
-        "babylonjs-materials": "^3.1.0-alpha3.7",
-        "babylonjs-post-process": "^3.1.0-alpha3.7",
-        "babylonjs-procedural-textures": "^3.1.0-alpha3.7",
+        "babylonjs": "^3.1.0-beta1.1.1",
+        "babylonjs-loaders": "^3.1.0-beta1.1",
+        "babylonjs-materials": "^3.1.0-beta1.1",
+        "babylonjs-post-process": "^3.1.0-beta1.1",
+        "babylonjs-procedural-textures": "^3.1.0-beta1.1",
         "es6-promise": "^4.1.1",
         "handlebars": "^4.0.11",
         "lodash": "^4.17.4",
         "lodash.merge": "^4.6.0",
         "promise-polyfill": "^6.0.2"
     }
-}
+}

+ 12 - 3
Viewer/src/configuration/configuration.ts

@@ -13,7 +13,8 @@ export interface ViewerConfiguration {
         mapper?: string; // json (default), html, yaml, xml, etc'. if not provided, file extension will be used.
     };
 
-    // native (!!!) javascript events. Mainly used in the JSON-format.
+    // Deprecated
+    /*// native (!!!) javascript events. Mainly used in the JSON-format.
     // those events will be triggered by the container element (the <babylon> tag);
     events?: {
         load: boolean | string;
@@ -23,8 +24,15 @@ export interface ViewerConfiguration {
         pointerup: boolean | string;
         pointermove: boolean | string;
         // load: 'onViewerLoaded' // will trigger the event prefix-onViewerLoaded instead of prefix-onLoad (and ONLY this event).
-    } | boolean; //events: true - fire all events
-    eventPrefix?: string;
+    } | boolean; //events: true - fire all events*/
+    //eventPrefix?: string;
+
+    // names of functions in the window context.
+    observers?: {
+        onEngineInit?: string;
+        onSceneInit?: string;
+        onModelLoaded?: string;
+    }
 
     canvasElement?: string; // if there is a need to override the standard implementation - ID of HTMLCanvasElement
 
@@ -46,6 +54,7 @@ export interface ViewerConfiguration {
     };
 
     scene?: {
+        debug?: boolean;
         autoRotate?: boolean;
         rotationSpeed?: number;
         defaultCamera?: boolean;

+ 2 - 2
Viewer/src/configuration/types/default.ts

@@ -2,8 +2,8 @@ import { ViewerConfiguration } from './../configuration';
 
 export let defaultConfiguration: ViewerConfiguration = {
     version: "0.1",
-    eventPrefix: 'babylonviewer-',
-    events: true,
+    //eventPrefix: 'babylonviewer-',
+    //events: true,
     templates: {
         main: {
             html: require("../../../assets/templates/default/defaultTemplate.html")

+ 39 - 0
Viewer/src/util/promiseObservable.ts

@@ -0,0 +1,39 @@
+import { Observable, Nullable, Observer } from "babylonjs";
+
+export class PromiseObservable<T> extends Observable<T> {
+
+    public notifyWithPromise(eventData: T, mask: number = -1, target?: any, currentTarget?: any): Promise<any> {
+
+        let p = Promise.resolve();
+
+        if (!this._observers.length) {
+            return p;
+        }
+
+        let state = this['_eventState'];
+        state.mask = mask;
+        state.target = target;
+        state.currentTarget = currentTarget;
+        state.skipNextObservers = false;
+
+        this._observers.forEach(obs => {
+            if (state.skipNextObservers) {
+                return;
+            }
+            if (obs.mask & mask) {
+                if (obs.scope) {
+                    // TODO - I can add the variable from the last function here. Requires changing callback sig
+                    p = p.then(() => {
+                        return obs.callback.apply(obs.scope, [eventData, state]);
+                    });
+                } else {
+                    p = p.then(() => {
+                        return obs.callback(eventData, state);
+                    });
+                }
+            }
+        });
+
+        return p;
+    }
+}

+ 7 - 1
Viewer/src/viewer/defaultViewer.ts

@@ -1,3 +1,4 @@
+import { ViewerConfiguration } from './../configuration/configuration';
 import { Template } from './../templateManager';
 import { AbstractViewer } from './viewer';
 import { Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ShadowOnlyMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight } from 'babylonjs';
@@ -11,6 +12,11 @@ export class DefaultViewer extends AbstractViewer {
 
     public camera: ArcRotateCamera;
 
+    constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = { extends: 'default' }) {
+        super(containerElement, initialConfiguration);
+        this.onModelLoadedObservable.add(this.onModelLoaded);
+    }
+
     public initScene(): Promise<Scene> {
         return super.initScene().then(() => {
             this.extendClassWithConfig(this.scene, this.configuration.scene);
@@ -132,7 +138,7 @@ export class DefaultViewer extends AbstractViewer {
         });
     }
 
-    public onModelLoaded(meshes: Array<AbstractMesh>) {
+    private onModelLoaded = (meshes: Array<AbstractMesh>) => {
 
         // here we could set the navbar's model information:
         this.setModelMetaData();

+ 36 - 11
Viewer/src/viewer/viewer.ts

@@ -3,6 +3,7 @@ import { TemplateManager } from './../templateManager';
 import configurationLoader from './../configuration/loader';
 import { Observable, Engine, Scene, ArcRotateCamera, Vector3, SceneLoader, AbstractMesh, Mesh, HemisphericLight } from 'babylonjs';
 import { ViewerConfiguration } from '../configuration/configuration';
+import { PromiseObservable } from '../util/promiseObservable';
 
 export abstract class AbstractViewer {
 
@@ -14,6 +15,11 @@ export abstract class AbstractViewer {
 
     protected configuration: ViewerConfiguration;
 
+    // observables
+    public onSceneInitObservable: PromiseObservable<Scene>;
+    public onEngineInitObservable: PromiseObservable<Engine>;
+    public onModelLoadedObservable: PromiseObservable<AbstractMesh[]>;
+
     constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = {}) {
         // if exists, use the container id. otherwise, generate a random string.
         if (containerElement.id) {
@@ -22,6 +28,10 @@ export abstract class AbstractViewer {
             this.baseId = containerElement.id = 'bjs' + Math.random().toString(32).substr(2, 8);
         }
 
+        this.onSceneInitObservable = new PromiseObservable();
+        this.onEngineInitObservable = new PromiseObservable();
+        this.onModelLoadedObservable = new PromiseObservable();
+
         // add this viewer to the viewer manager
         viewerManager.addViewer(this);
 
@@ -33,6 +43,20 @@ export abstract class AbstractViewer {
         // extend the configuration
         configurationLoader.loadConfiguration(initialConfiguration).then((configuration) => {
             this.configuration = configuration;
+
+            // adding preconfigured functions
+            if (this.configuration.observers) {
+                if (this.configuration.observers.onEngineInit) {
+                    this.onEngineInitObservable.add(window[this.configuration.observers.onEngineInit]);
+                }
+                if (this.configuration.observers.onSceneInit) {
+                    this.onSceneInitObservable.add(window[this.configuration.observers.onSceneInit]);
+                }
+                if (this.configuration.observers.onModelLoaded) {
+                    this.onModelLoadedObservable.add(window[this.configuration.observers.onModelLoaded]);
+                }
+            }
+
             // initialize the templates
             let templateConfiguration = this.configuration.templates || {};
             this.templateManager.initTemplate(templateConfiguration);
@@ -93,7 +117,9 @@ export abstract class AbstractViewer {
         var scale = Math.max(0.5, 1 / (window.devicePixelRatio || 2));
         this.engine.setHardwareScalingLevel(scale);
 
-        return Promise.resolve(this.engine);
+        return this.onEngineInitObservable.notifyWithPromise(this.engine).then(() => {
+            return this.engine;
+        });
     }
 
     protected initScene(): Promise<Scene> {
@@ -107,7 +133,12 @@ export abstract class AbstractViewer {
         this.scene = new Scene(this.engine);
         // make sure there is a default camera and light.
         this.scene.createDefaultCameraOrLight(true, true, true);
-        return Promise.resolve(this.scene);
+        if (this.configuration.scene && this.configuration.scene.debug) {
+            this.scene.debugLayer.show();
+        }
+        return this.onSceneInitObservable.notifyWithPromise(this.scene).then(() => {
+            return this.scene;
+        });
     }
 
     public loadModel(model: any = this.configuration.model, clearScene: boolean = true): Promise<Scene> {
@@ -130,15 +161,9 @@ export abstract class AbstractViewer {
                 }, plugin);
             });
         }).then((meshes: Array<AbstractMesh>) => {
-            return this.onModelLoaded(meshes);
+            return this.onModelLoadedObservable.notifyWithPromise(meshes).then(() => {
+                return this.scene;
+            });
         });
     }
-
-    protected onModelLoaded(meshes: Array<AbstractMesh>): Promise<Scene> {
-        console.log("model loaded");
-        return Promise.resolve(this.scene);
-    }
-
-    public abstract initEnvironment(): Promise<Scene>;
-
 }

+ 23 - 0
Viewer/src/viewer/viewerManager.ts

@@ -1,15 +1,21 @@
+import { Observable } from 'babylonjs';
 import { AbstractViewer } from './viewer';
 
 class ViewerManager {
 
     private viewers: { [key: string]: AbstractViewer };
 
+    public onViewerAdded: (viewer: AbstractViewer) => void;
+    public onViewerAddedObservable: Observable<AbstractViewer>;
+
     constructor() {
         this.viewers = {};
+        this.onViewerAddedObservable = new Observable();
     }
 
     public addViewer(viewer: AbstractViewer) {
         this.viewers[viewer.getBaseId()] = viewer;
+        this._onViewerAdded(viewer);
     }
 
     public getViewerById(id: string): AbstractViewer {
@@ -23,6 +29,23 @@ class ViewerManager {
             }
         }
     }
+
+    public getViewerPromiseById(id: string): Promise<AbstractViewer> {
+        return new Promise((resolve, reject) => {
+            let viewerFunction = (viewer: AbstractViewer) => {
+                if (viewer.getBaseId() === id) {
+                    resolve(viewer);
+                    this.onViewerAddedObservable.removeCallback(viewerFunction);
+                }
+            }
+            this.onViewerAddedObservable.add(viewerFunction);
+        });
+    }
+
+    private _onViewerAdded(viewer: AbstractViewer) {
+        this.onViewerAdded && this.onViewerAdded(viewer);
+        this.onViewerAddedObservable.notifyObservers(viewer);
+    }
 }
 
 export let viewerManager = new ViewerManager();

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 839 - 760
dist/preview release/babylon.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 43 - 43
dist/preview release/babylon.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 982 - 756
dist/preview release/babylon.max.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 839 - 760
dist/preview release/babylon.module.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 47 - 47
dist/preview release/babylon.worker.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1981 - 1902
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 46 - 46
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 987 - 762
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1981 - 1902
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


+ 1 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -36,6 +36,7 @@ declare module BABYLON.GUI {
         readonly layer: Nullable<Layer>;
         readonly rootContainer: Container;
         focusedControl: Nullable<IFocusableControl>;
+        isForeground: boolean;
         constructor(name: string, width: number | undefined, height: number | undefined, scene: Nullable<Scene>, generateMipMaps?: boolean, samplingMode?: number);
         executeOnAllControls(func: (control: Control) => void, container?: Container): void;
         markAsDirty(): void;

+ 19 - 0
dist/preview release/gui/babylon.gui.js

@@ -154,6 +154,25 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
+            Object.defineProperty(AdvancedDynamicTexture.prototype, "isForeground", {
+                get: function () {
+                    if (!this.layer) {
+                        return true;
+                    }
+                    return (!this.layer.isBackground);
+                },
+                set: function (value) {
+                    if (!this.layer) {
+                        return;
+                    }
+                    if (this.layer.isBackground === !value) {
+                        return;
+                    }
+                    this.layer.isBackground = !value;
+                },
+                enumerable: true,
+                configurable: true
+            });
             AdvancedDynamicTexture.prototype.executeOnAllControls = function (func, container) {
                 if (!container) {
                     container = this._rootContainer;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


+ 1 - 0
dist/preview release/gui/babylon.gui.module.d.ts

@@ -41,6 +41,7 @@ declare module BABYLON.GUI {
         readonly layer: Nullable<Layer>;
         readonly rootContainer: Container;
         focusedControl: Nullable<IFocusableControl>;
+        isForeground: boolean;
         constructor(name: string, width: number | undefined, height: number | undefined, scene: Nullable<Scene>, generateMipMaps?: boolean, samplingMode?: number);
         executeOnAllControls(func: (control: Control) => void, container?: Container): void;
         markAsDirty(): void;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 4 - 4
dist/preview release/inspector/babylon.inspector.bundle.js


+ 3 - 2
dist/preview release/inspector/babylon.inspector.d.ts

@@ -398,7 +398,8 @@ declare module INSPECTOR {
     class MeshAdapter extends Adapter implements IToolVisible, IToolDebug, IToolBoundingBox, IToolInfo {
         /** Keep track of the axis of the actual object */
         private _axesViewer;
-        constructor(obj: BABYLON.AbstractMesh);
+        private onBeforeRenderObserver;
+        constructor(mesh: BABYLON.AbstractMesh);
         /** Returns the name displayed in the tree */
         id(): string;
         /** Returns the type of this object - displayed in the tree */
@@ -410,7 +411,7 @@ declare module INSPECTOR {
         isVisible(): boolean;
         isBoxVisible(): boolean;
         setBoxVisible(b: boolean): boolean;
-        debug(b: boolean): void;
+        debug(enable: boolean): void;
         /** Returns some information about this mesh */
         getInfo(): string;
         /** Draw X, Y and Z axis for the actual object if this adapter.

+ 16 - 8
dist/preview release/inspector/babylon.inspector.js

@@ -1244,8 +1244,8 @@ var INSPECTOR;
 (function (INSPECTOR) {
     var MeshAdapter = /** @class */ (function (_super) {
         __extends(MeshAdapter, _super);
-        function MeshAdapter(obj) {
-            return _super.call(this, obj) || this;
+        function MeshAdapter(mesh) {
+            return _super.call(this, mesh) || this;
         }
         /** Returns the name displayed in the tree */
         MeshAdapter.prototype.id = function () {
@@ -1286,13 +1286,15 @@ var INSPECTOR;
         MeshAdapter.prototype.setBoxVisible = function (b) {
             return this._obj.showBoundingBox = b;
         };
-        MeshAdapter.prototype.debug = function (b) {
+        MeshAdapter.prototype.debug = function (enable) {
             // Draw axis the first time
             if (!this._axesViewer) {
                 this._drawAxis();
             }
             // Display or hide axis
-            if (!b && this._axesViewer) {
+            if (!enable && this._axesViewer) {
+                var mesh = this._obj;
+                mesh.getScene().onBeforeRenderObservable.remove(this.onBeforeRenderObserver);
                 this._axesViewer.dispose();
                 this._axesViewer = null;
             }
@@ -1305,14 +1307,20 @@ var INSPECTOR;
          * Should be called only one time as it will fill this._axis
          */
         MeshAdapter.prototype._drawAxis = function () {
+            var _this = this;
             this._obj.computeWorldMatrix();
             var mesh = this._obj;
             // Axis
-            var x = new BABYLON.Vector3(8 / Math.abs(mesh.scaling.x), 0, 0);
-            var y = new BABYLON.Vector3(0, 8 / Math.abs(mesh.scaling.y), 0);
-            var z = new BABYLON.Vector3(0, 0, 8 / Math.abs(mesh.scaling.z));
+            var x = new BABYLON.Vector3(1, 0, 0);
+            var y = new BABYLON.Vector3(0, 1, 0);
+            var z = new BABYLON.Vector3(0, 0, 1);
             this._axesViewer = new BABYLON.Debug.AxesViewer(this._obj.getScene());
-            this._axesViewer.update(this._obj.position, x, y, z);
+            this.onBeforeRenderObserver = mesh.getScene().onBeforeRenderObservable.add(function () {
+                var matrix = mesh.getWorldMatrix();
+                var extend = mesh.getBoundingInfo().boundingBox.extendSizeWorld;
+                _this._axesViewer.scaleLines = Math.max(extend.x, extend.y, extend.z) * 2;
+                _this._axesViewer.update(_this._obj.position, BABYLON.Vector3.TransformNormal(x, matrix), BABYLON.Vector3.TransformNormal(y, matrix), BABYLON.Vector3.TransformNormal(z, matrix));
+            });
         };
         return MeshAdapter;
     }(INSPECTOR.Adapter));

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 4 - 4
dist/preview release/inspector/babylon.inspector.min.js


+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -247,10 +247,10 @@ declare module BABYLON.GLTF2 {
         index: number;
         parent?: IGLTFNode;
         babylonMesh: Mesh;
-        babylonBones: {
+        babylonBones?: {
             [skin: number]: Bone;
         };
-        babylonAnimationTargets: Node[];
+        babylonAnimationTargets?: Node[];
     }
     interface IGLTFSampler extends IGLTFChildRootProperty {
         magFilter?: ETextureMagFilter;

+ 9 - 6
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -545,8 +545,6 @@ var BABYLON;
                 this._rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (context, node) {
-                node.babylonBones = {};
-                node.babylonAnimationTargets = [];
                 if (GLTF2.GLTFLoaderExtension.LoadNode(this, context, node)) {
                     return;
                 }
@@ -561,6 +559,7 @@ var BABYLON;
                     this._loadMesh("#/meshes/" + node.mesh, node, mesh);
                 }
                 node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(node.babylonMesh);
                 if (node.skin != null) {
                     var skin = GLTFLoader._GetProperty(this._gltf.skins, node.skin);
@@ -989,7 +988,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._createBone = function (node, skin, parent, localMatrix, baseMatrix, index) {
                 var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+                node.babylonBones = node.babylonBones || {};
                 node.babylonBones[skin.index] = babylonBone;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(babylonBone);
                 return babylonBone;
             };
@@ -1203,10 +1204,12 @@ var BABYLON;
                         var animationName = animation.name || "anim" + animation.index;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
-                        for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
-                            var target = _a[_i];
-                            target.animations.push(babylonAnimation.clone());
-                            animation.targets.push(target);
+                        if (targetNode.babylonAnimationTargets) {
+                            for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
+                                var target = _a[_i];
+                                target.animations.push(babylonAnimation.clone());
+                                animation.targets.push(target);
+                            }
                         }
                     }
                 };

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -794,10 +794,10 @@ declare module BABYLON.GLTF2 {
         index: number;
         parent?: IGLTFNode;
         babylonMesh: Mesh;
-        babylonBones: {
+        babylonBones?: {
             [skin: number]: Bone;
         };
-        babylonAnimationTargets: Node[];
+        babylonAnimationTargets?: Node[];
     }
     interface IGLTFSampler extends IGLTFChildRootProperty {
         magFilter?: ETextureMagFilter;

+ 9 - 6
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -2690,8 +2690,6 @@ var BABYLON;
                 this._rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (context, node) {
-                node.babylonBones = {};
-                node.babylonAnimationTargets = [];
                 if (GLTF2.GLTFLoaderExtension.LoadNode(this, context, node)) {
                     return;
                 }
@@ -2706,6 +2704,7 @@ var BABYLON;
                     this._loadMesh("#/meshes/" + node.mesh, node, mesh);
                 }
                 node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(node.babylonMesh);
                 if (node.skin != null) {
                     var skin = GLTFLoader._GetProperty(this._gltf.skins, node.skin);
@@ -3134,7 +3133,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._createBone = function (node, skin, parent, localMatrix, baseMatrix, index) {
                 var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+                node.babylonBones = node.babylonBones || {};
                 node.babylonBones[skin.index] = babylonBone;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(babylonBone);
                 return babylonBone;
             };
@@ -3348,10 +3349,12 @@ var BABYLON;
                         var animationName = animation.name || "anim" + animation.index;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
-                        for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
-                            var target = _a[_i];
-                            target.animations.push(babylonAnimation.clone());
-                            animation.targets.push(target);
+                        if (targetNode.babylonAnimationTargets) {
+                            for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
+                                var target = _a[_i];
+                                target.animations.push(babylonAnimation.clone());
+                                animation.targets.push(target);
+                            }
                         }
                     }
                 };

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 9 - 6
dist/preview release/loaders/babylonjs.loaders.js

@@ -3646,8 +3646,6 @@ var BABYLON;
                 this._rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (context, node) {
-                node.babylonBones = {};
-                node.babylonAnimationTargets = [];
                 if (GLTF2.GLTFLoaderExtension.LoadNode(this, context, node)) {
                     return;
                 }
@@ -3662,6 +3660,7 @@ var BABYLON;
                     this._loadMesh("#/meshes/" + node.mesh, node, mesh);
                 }
                 node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(node.babylonMesh);
                 if (node.skin != null) {
                     var skin = GLTFLoader._GetProperty(this._gltf.skins, node.skin);
@@ -4090,7 +4089,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._createBone = function (node, skin, parent, localMatrix, baseMatrix, index) {
                 var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+                node.babylonBones = node.babylonBones || {};
                 node.babylonBones[skin.index] = babylonBone;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(babylonBone);
                 return babylonBone;
             };
@@ -4304,10 +4305,12 @@ var BABYLON;
                         var animationName = animation.name || "anim" + animation.index;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
-                        for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
-                            var target = _a[_i];
-                            target.animations.push(babylonAnimation.clone());
-                            animation.targets.push(target);
+                        if (targetNode.babylonAnimationTargets) {
+                            for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
+                                var target = _a[_i];
+                                target.animations.push(babylonAnimation.clone());
+                                animation.targets.push(target);
+                            }
                         }
                     }
                 };

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 2 - 2
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -892,10 +892,10 @@ declare module BABYLON.GLTF2 {
         index: number;
         parent?: IGLTFNode;
         babylonMesh: Mesh;
-        babylonBones: {
+        babylonBones?: {
             [skin: number]: Bone;
         };
-        babylonAnimationTargets: Node[];
+        babylonAnimationTargets?: Node[];
     }
     interface IGLTFSampler extends IGLTFChildRootProperty {
         magFilter?: ETextureMagFilter;

+ 1 - 0
dist/preview release/materialsLibrary/babylon.gridMaterial.d.ts

@@ -49,6 +49,7 @@ declare module BABYLON {
          * Returns wehter or not the grid requires alpha blending.
          */
         needAlphaBlending(): boolean;
+        needAlphaBlendingForMesh(mesh: AbstractMesh): boolean;
         isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
         dispose(forceDisposeEffect?: boolean): void;

+ 3 - 0
dist/preview release/materialsLibrary/babylon.gridMaterial.js

@@ -83,6 +83,9 @@ var BABYLON;
         GridMaterial.prototype.needAlphaBlending = function () {
             return this.opacity < 1.0;
         };
+        GridMaterial.prototype.needAlphaBlendingForMesh = function (mesh) {
+            return this.needAlphaBlending();
+        };
         GridMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
             if (this.isFrozen) {
                 if (this._wasPreviouslyReady && subMesh.effect) {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 1
dist/preview release/materialsLibrary/babylon.gridMaterial.min.js


+ 3 - 0
dist/preview release/materialsLibrary/babylonjs.materials.js

@@ -3863,6 +3863,9 @@ var BABYLON;
         GridMaterial.prototype.needAlphaBlending = function () {
             return this.opacity < 1.0;
         };
+        GridMaterial.prototype.needAlphaBlendingForMesh = function (mesh) {
+            return this.needAlphaBlending();
+        };
         GridMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
             if (this.isFrozen) {
                 if (this._wasPreviouslyReady && subMesh.effect) {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 3 - 3
dist/preview release/materialsLibrary/babylonjs.materials.min.js


+ 1 - 0
dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts

@@ -491,6 +491,7 @@ declare module BABYLON {
          * Returns wehter or not the grid requires alpha blending.
          */
         needAlphaBlending(): boolean;
+        needAlphaBlendingForMesh(mesh: AbstractMesh): boolean;
         isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
         dispose(forceDisposeEffect?: boolean): void;

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

@@ -22,6 +22,7 @@
 - Complete rework of Unity3D exporter. [Doc here](http://doc.babylonjs.com/resources/intro) ([MackeyK24](https://github.com/MackeyK24))
 
 ## Updates
+- Introduced `TransformNode` class as a parent of `AbstractMesh`. This class was extensively asked by the community to hold only tranformation for a node ([deltakosh](https://github.com/deltakosh))
 - Added `boundingInfo.centerOn` to recreate the bounding info to be centered around a specific point given a specific extend ([deltakosh](https://github.com/deltakosh))
 - Added `mesh.normalizeToUnitCube` to uniformly scales the mesh to fit inside of a unit cube (1 X 1 X 1 units) ([deltakosh](https://github.com/deltakosh))
 - Added `scene.onDataLoadedObservable` which is raised when SceneLoader.Append or SceneLoader.Load or SceneLoader.ImportMesh were successfully executed ([deltakosh](https://github.com/deltakosh))

+ 18 - 1
gui/src/advancedDynamicTexture.ts

@@ -113,6 +113,23 @@ module BABYLON.GUI {
 
             this._focusedControl = control;
         }
+        
+        public get isForeground(): boolean {
+            if (!this.layer) {
+                return true;
+            }
+            return (!this.layer.isBackground);
+        }
+
+        public set isForeground(value: boolean) {
+            if (!this.layer) {
+                return;
+            }            
+            if (this.layer.isBackground === !value) {
+                return;
+            }
+            this.layer.isBackground = !value;
+        }   
        
         constructor(name: string, width = 0, height = 0, scene: Nullable<Scene>, generateMipMaps = false, samplingMode = Texture.NEAREST_SAMPLINGMODE) {
             super(name, {width: width, height: height}, scene, generateMipMaps, samplingMode, Engine.TEXTUREFORMAT_RGBA);
@@ -507,4 +524,4 @@ module BABYLON.GUI {
             return result;
         }
     }    
-}
+}

+ 18 - 9
inspector/src/adapters/MeshAdapter.ts

@@ -6,9 +6,10 @@ module INSPECTOR {
 
         /** Keep track of the axis of the actual object */
         private _axesViewer: BABYLON.Nullable<BABYLON.Debug.AxesViewer>;
+        private onBeforeRenderObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Scene>>;
 
-        constructor(obj: BABYLON.AbstractMesh) {
-            super(obj);
+        constructor(mesh: BABYLON.AbstractMesh) {
+            super(mesh);
         }
 
         /** Returns the name displayed in the tree */
@@ -56,13 +57,15 @@ module INSPECTOR {
             return (this._obj as BABYLON.AbstractMesh).showBoundingBox = b;
         }
 
-        public debug(b: boolean) {
+        public debug(enable: boolean) {
             // Draw axis the first time
             if (!this._axesViewer) {
                  this._drawAxis();
             }
             // Display or hide axis
-            if (!b && this._axesViewer) {
+            if (!enable && this._axesViewer) {
+                let mesh = this._obj as BABYLON.AbstractMesh;
+                mesh.getScene().onBeforeRenderObservable.remove(this.onBeforeRenderObserver);
                 this._axesViewer.dispose();
                 this._axesViewer = null;
             }
@@ -79,15 +82,21 @@ module INSPECTOR {
         private _drawAxis() {
             this._obj.computeWorldMatrix();
 
-            let mesh = this._obj as BABYLON.AbstractMesh;
+            let mesh = this._obj as BABYLON.Mesh;
 
             // Axis
-            var x = new BABYLON.Vector3(8 / Math.abs(mesh.scaling.x), 0, 0);
-            var y = new BABYLON.Vector3(0, 8 / Math.abs(mesh.scaling.y), 0);
-            var z = new BABYLON.Vector3(0, 0, 8 / Math.abs(mesh.scaling.z));
+            var x = new BABYLON.Vector3(1, 0, 0);
+            var y = new BABYLON.Vector3(0, 1, 0);
+            var z = new BABYLON.Vector3(0, 0, 1);
 
             this._axesViewer = new BABYLON.Debug.AxesViewer(this._obj.getScene());
-            this._axesViewer.update(this._obj.position, x, y, z);
+
+            this.onBeforeRenderObserver = mesh.getScene().onBeforeRenderObservable.add(() => {
+                let matrix = mesh.getWorldMatrix();
+                let extend = mesh.getBoundingInfo()!.boundingBox.extendSizeWorld;
+                this._axesViewer!.scaleLines = Math.max(extend.x, extend.y, extend.z) * 2;
+                this._axesViewer!.update(this._obj.position, BABYLON.Vector3.TransformNormal(x, matrix), BABYLON.Vector3.TransformNormal(y, matrix), BABYLON.Vector3.TransformNormal(z, matrix));
+            });
         }
     }
 }

+ 11 - 6
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -315,9 +315,6 @@ module BABYLON.GLTF2 {
         }
 
         public _loadNode(context: string, node: IGLTFNode): void {
-            node.babylonBones = {};
-            node.babylonAnimationTargets = [];
-
             if (GLTFLoaderExtension.LoadNode(this, context, node)) {
                 return;
             }
@@ -338,6 +335,7 @@ module BABYLON.GLTF2 {
 
             node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
 
+            node.babylonAnimationTargets = node.babylonAnimationTargets || [];
             node.babylonAnimationTargets.push(node.babylonMesh);
 
             if (node.skin != null) {
@@ -785,8 +783,13 @@ module BABYLON.GLTF2 {
 
         private _createBone(node: IGLTFNode, skin: IGLTFSkin, parent: Nullable<Bone>, localMatrix: Matrix, baseMatrix: Matrix, index: number): Bone {
             const babylonBone = new Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+
+            node.babylonBones = node.babylonBones || {};
             node.babylonBones[skin.index] = babylonBone;
+
+            node.babylonAnimationTargets = node.babylonAnimationTargets || [];
             node.babylonAnimationTargets.push(babylonBone);
+
             return babylonBone;
         }
 
@@ -1027,9 +1030,11 @@ module BABYLON.GLTF2 {
                     const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                     babylonAnimation.setKeys(keys);
 
-                    for (const target of targetNode.babylonAnimationTargets) {
-                        target.animations.push(babylonAnimation.clone());
-                        animation.targets.push(target);
+                    if (targetNode.babylonAnimationTargets) {
+                        for (const target of targetNode.babylonAnimationTargets) {
+                            target.animations.push(babylonAnimation.clone());
+                            animation.targets.push(target);
+                        }
                     }
                 }
             };

+ 2 - 2
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -234,8 +234,8 @@ module BABYLON.GLTF2 {
         index: number;
         parent?: IGLTFNode;
         babylonMesh: Mesh;
-        babylonBones: { [skin: number]: Bone };
-        babylonAnimationTargets: Node[];
+        babylonBones?: { [skin: number]: Bone };
+        babylonAnimationTargets?: Node[];
     }
 
     export interface IGLTFSampler extends IGLTFChildRootProperty {

+ 24 - 7
localDev/index.html

@@ -69,14 +69,31 @@
 					BABYLONDEVTOOLS.Loader.debugShortcut(engine);
 
 					// call the scene creation from the js.
-					var scene = createScene();
+					if (typeof delayCreateScene !== "undefined") {
+						var scene = delayCreateScene();
 
-					if (scene) {
-						// Register a render loop to repeatedly render the scene
-						engine.runRenderLoop(function () {
-							scene.render();
-							divFps.innerHTML = engine.getFps().toFixed() + " fps";
-						});
+						if (scene) {
+							// Register a render loop to repeatedly render the scene
+
+							engine.runRenderLoop(function () {
+								if (scene.activeCamera) {
+									scene.render();
+								}
+								divFps.innerHTML = engine.getFps().toFixed() + " fps";
+							});
+						}
+					}
+					else {
+						var scene = createScene();
+
+						if (scene) {
+							// Register a render loop to repeatedly render the scene
+
+							engine.runRenderLoop(function () {
+								scene.render();
+								divFps.innerHTML = engine.getFps().toFixed() + " fps";
+							});
+						}
 					}
 
 					// Resize

+ 4 - 0
materialsLibrary/src/grid/babylon.gridmaterial.ts

@@ -86,6 +86,10 @@ module BABYLON {
             return this.opacity < 1.0;
         }
 
+        public needAlphaBlendingForMesh(mesh: AbstractMesh): boolean {
+            return this.needAlphaBlending();
+        }
+
         public isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean {   
             if (this.isFrozen) {
                 if (this._wasPreviouslyReady && subMesh.effect) {

+ 32 - 6
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -30,23 +30,43 @@ module BABYLON {
         public onEnteringVR: () => void;
         public onExitingVR: () => void;
         public onControllerMeshLoaded: (controller: WebVRController) => void;
+
+        public get deviceOrientationCamera(): DeviceOrientationCamera {
+            return this._deviceOrientationCamera;
+        }
+
+        // Based on the current WebVR support, returns the current VR camera used
+        public get currentVRCamera(): FreeCamera {
+            if (this._webVRready) {
+                return this._webVRCamera;
+            }
+            else {
+                return this._vrDeviceOrientationCamera;
+            }
+        }
+
+        public get webVRCamera(): WebVRFreeCamera {
+            return this._webVRCamera;
+        }
+
+        public get vrDeviceOrientationCamera(): VRDeviceOrientationFreeCamera {
+            return this._vrDeviceOrientationCamera;
+        }
                 
         constructor(scene: Scene, public webVROptions: WebVROptions = {}) {
             this._scene = scene;
 
             if (!this._scene.activeCamera || isNaN(this._scene.activeCamera.position.x)) {
+                this._position = new BABYLON.Vector3(0, 2, 0);
                 this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", new BABYLON.Vector3(0, 2, 0), scene);
             }
             else {
-                this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", this._scene.activeCamera.position, scene);
-                if ((<FreeCamera>scene.activeCamera).rotation) {
-                    this._deviceOrientationCamera.rotation = (<FreeCamera>scene.activeCamera).rotation.clone();
-                }
+                this._position = this._scene.activeCamera.position.clone();
+                this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", this._position, scene);
                 this._deviceOrientationCamera.minZ = this._scene.activeCamera.minZ;
                 this._deviceOrientationCamera.maxZ = this._scene.activeCamera.maxZ;
             }
             this._scene.activeCamera = this._deviceOrientationCamera;
-            this._position = this._scene.activeCamera.position;
             this._canvas = scene.getEngine().getRenderingCanvas();
             if (this._canvas) {
                 this._scene.activeCamera.attachControl(this._canvas);
@@ -136,6 +156,7 @@ module BABYLON {
             this.updateButtonVisibility();
         }
 
+        // Raised when one of the controller has loaded successfully its associated default mesh
         private _onDefaultMeshLoaded(webVRController: WebVRController) {
             if (this.onControllerMeshLoaded) {
                 this.onControllerMeshLoaded(webVRController);
@@ -207,6 +228,10 @@ module BABYLON {
          * Otherwise, will use the fullscreen API.
          */
         public enterVR() {
+            if (this._scene.activeCamera) {
+                this._position = this._scene.activeCamera.position.clone();
+            }
+
             if (this.onEnteringVR) {
                 this.onEnteringVR();
             }
@@ -243,7 +268,8 @@ module BABYLON {
                 this._scene.getEngine().disableVR();
             }
             if (this._scene.activeCamera) {
-                this._position = this._scene.activeCamera.position;
+                this._position = this._scene.activeCamera.position.clone();
+                
             }
             this._deviceOrientationCamera.position = this._position;
             this._scene.activeCamera = this._deviceOrientationCamera;

+ 32 - 30
src/Engine/babylon.engine.ts

@@ -4827,40 +4827,42 @@
             this.disableVR();
 
             // Events
-            window.removeEventListener("blur", this._onBlur);
-            window.removeEventListener("focus", this._onFocus);
-            window.removeEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted);
-            window.removeEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted);
-            if (this._renderingCanvas) {
-                this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
-                this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
-                this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
+            if (Tools.IsWindowObjectExist()) {
+                window.removeEventListener("blur", this._onBlur);
+                window.removeEventListener("focus", this._onFocus);
+                window.removeEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted);
+                window.removeEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted);
+                if (this._renderingCanvas) {
+                    this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
+                    this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
+                    this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
 
-                if (!this._doNotHandleContextLost) {
-                    this._renderingCanvas.removeEventListener("webglcontextlost", this._onContextLost);
-                    this._renderingCanvas.removeEventListener("webglcontextrestored", this._onContextRestored);
-                }
-            }
-            document.removeEventListener("fullscreenchange", this._onFullscreenChange);
-            document.removeEventListener("mozfullscreenchange", this._onFullscreenChange);
-            document.removeEventListener("webkitfullscreenchange", this._onFullscreenChange);
-            document.removeEventListener("msfullscreenchange", this._onFullscreenChange);
-            document.removeEventListener("pointerlockchange", this._onPointerLockChange);
-            document.removeEventListener("mspointerlockchange", this._onPointerLockChange);
-            document.removeEventListener("mozpointerlockchange", this._onPointerLockChange);
-            document.removeEventListener("webkitpointerlockchange", this._onPointerLockChange);
-
-            if (this._onVrDisplayConnect) {
-                window.removeEventListener('vrdisplayconnect', this._onVrDisplayConnect);
-                if (this._onVrDisplayDisconnect) {
-                    window.removeEventListener('vrdisplaydisconnect', this._onVrDisplayDisconnect);
+                    if (!this._doNotHandleContextLost) {
+                        this._renderingCanvas.removeEventListener("webglcontextlost", this._onContextLost);
+                        this._renderingCanvas.removeEventListener("webglcontextrestored", this._onContextRestored);
+                    }
                 }
+                document.removeEventListener("fullscreenchange", this._onFullscreenChange);
+                document.removeEventListener("mozfullscreenchange", this._onFullscreenChange);
+                document.removeEventListener("webkitfullscreenchange", this._onFullscreenChange);
+                document.removeEventListener("msfullscreenchange", this._onFullscreenChange);
+                document.removeEventListener("pointerlockchange", this._onPointerLockChange);
+                document.removeEventListener("mspointerlockchange", this._onPointerLockChange);
+                document.removeEventListener("mozpointerlockchange", this._onPointerLockChange);
+                document.removeEventListener("webkitpointerlockchange", this._onPointerLockChange);
+
+                if (this._onVrDisplayConnect) {
+                    window.removeEventListener('vrdisplayconnect', this._onVrDisplayConnect);
+                    if (this._onVrDisplayDisconnect) {
+                        window.removeEventListener('vrdisplaydisconnect', this._onVrDisplayDisconnect);
+                    }
 
-                if (this._onVrDisplayPresentChange) {
-                    window.removeEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
+                    if (this._onVrDisplayPresentChange) {
+                        window.removeEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
+                    }
+                    this._onVrDisplayConnect = null;
+                    this._onVrDisplayDisconnect = null;
                 }
-                this._onVrDisplayConnect = null;
-                this._onVrDisplayDisconnect = null;
             }
 
             // Remove from Instances

+ 10 - 0
src/Engine/babylon.nullEngine.ts

@@ -380,5 +380,15 @@
 
             this._bindTextureDirectly(0, texture);
         }
+
+        public _releaseBuffer(buffer: WebGLBuffer): boolean {
+            buffer.references--;
+
+            if (buffer.references === 0) {
+                return true;
+            }
+
+            return false;
+        }        
     }
 }

+ 82 - 29
src/Helpers/babylon.environmentHelper.ts

@@ -118,6 +118,11 @@ namespace BABYLON {
         sizeAuto: boolean;
 
         /**
+         * Default position of the rootMesh if autoSize is not true.
+         */
+        rootPosition: Vector3;
+
+        /**
          * Sets up the inmage processing in the scene.
          * true by default.
          */
@@ -150,6 +155,12 @@ namespace BABYLON {
         toneMappingEnabled: boolean;
     }
 
+    interface ISceneSize {
+        groundSize: number,
+        skyboxSize: number,
+        rootPosition: Vector3
+    }
+
     /**
      * The Environment helper class can be used to add a fully featuread none expensive background to your scene.
      * It includes by default a skybox and a ground relying on the BackgroundMaterial.
@@ -180,8 +191,8 @@ namespace BABYLON {
                 createGround: true,
                 groundSize: 15,
                 groundTexture: this._groundTextureCDNUrl,
-                groundColor: new BABYLON.Color3(0.2, 0.2, 0.3),
-                groundOpacity: 1,
+                groundColor: new BABYLON.Color3(0.2, 0.2, 0.3).toLinearSpace().scale(3),
+                groundOpacity: 0.9,
                 enableGroundShadow: true,
                 groundShadowLevel: 0.5,
 
@@ -196,15 +207,16 @@ namespace BABYLON {
                 createSkybox: true,
                 skyboxSize: 20,
                 skyboxTexture: this._skyboxTextureCDNUrl,
-                skyboxColor: new BABYLON.Color3(0.2, 0.2, 0.3),
+                skyboxColor: new BABYLON.Color3(0.2, 0.2, 0.3).toLinearSpace().scale(3),
 
                 backgroundYRotation: 0,
                 sizeAuto: true,
+                rootPosition: Vector3.Zero(),
 
                 setupImageProcessing: true,
                 environmentTexture: this._environmentTextureCDNUrl,
                 cameraExposure: 0.8,
-                cameraContrast: 1.6,
+                cameraContrast: 1.2,
                 toneMappingEnabled: true,
             };
         }
@@ -416,42 +428,76 @@ namespace BABYLON {
             }
             this._rootMesh.rotation.y = this._options.backgroundYRotation;
 
+            const sceneSize = this._getSceneSize();
             if (this._options.createGround) {
-                this._setupGround();
+                this._setupGround(sceneSize);
                 this._setupGroundMaterial();
                 this._setupGroundDiffuseTexture();
 
                 if (this._options.enableGroundMirror) {
-                    this._setupGroundMirrorTexture();
+                    this._setupGroundMirrorTexture(sceneSize);
                     this._setupMirrorInGroundMaterial();
                 }
             }
 
             if (this._options.createSkybox) {
-                this._setupSkybox();
+                this._setupSkybox(sceneSize);
                 this._setupSkyboxMaterial();
                 this._setupSkyboxReflectionTexture();
             }
+
+            this._rootMesh.position.x = sceneSize.rootPosition.x;
+            this._rootMesh.position.z = sceneSize.rootPosition.z;
+            this._rootMesh.position.y = sceneSize.rootPosition.y;
+        }
+
+        /**
+         * Get the scene sizes according to the setup.
+         */
+        private _getSceneSize(): ISceneSize {
+            let groundSize = this._options.groundSize;
+            let skyboxSize = this._options.skyboxSize;
+            let rootPosition = this._options.rootPosition;
+            const sceneExtends = this._scene.getWorldExtends();
+            const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
+            let bias = 0.0001;
+
+            if (this._options.sizeAuto) {
+                if (this._scene.activeCamera instanceof ArcRotateCamera &&
+                    this._scene.activeCamera.upperRadiusLimit) {
+                    groundSize = this._scene.activeCamera.upperRadiusLimit * 2;
+                }
+
+                if (this._scene.activeCamera) {
+                    bias = (this._scene.activeCamera.maxZ - this._scene.activeCamera.minZ) / 10000;
+                }
+
+                const sceneDiagonalLenght = sceneDiagonal.length();
+                if (sceneDiagonalLenght > groundSize) {
+                    groundSize = sceneDiagonalLenght * 2;
+                }
+
+                // 10 % bigger.
+                groundSize *= 1.1;
+                skyboxSize *= 1.5;
+                rootPosition = sceneExtends.min.add(sceneDiagonal.scale(0.5));
+                rootPosition.y = sceneExtends.min.y - bias;
+            }
+
+            return { groundSize, skyboxSize, rootPosition };
         }
 
         /**
          * Setup the ground according to the specified options.
          */
-        private _setupGround(): void {
+        private _setupGround(sceneSize: ISceneSize): void {
             if (!this._ground) {
-                let size = this._options.groundSize;
-                if (this._options.sizeAuto) {
-                    if (this._scene.activeCamera instanceof ArcRotateCamera &&
-                        this._scene.activeCamera.upperRadiusLimit) {
-                        size = this._scene.activeCamera.upperRadiusLimit * 0.9;
-                    }
-                }
-                this._ground = Mesh.CreatePlane("BackgroundPlane", size, this._scene);
+                this._ground = Mesh.CreatePlane("BackgroundPlane", sceneSize.groundSize, this._scene);
+                this._ground.rotation.x = Math.PI / 2; // Face up by default.
+                this._ground.parent = this._rootMesh;
             }
             
-            this._ground.rotation.x = Math.PI / 2; // Face up by default.
             this._ground.receiveShadows = this._options.enableGroundShadow;
-            this._ground.parent = this._rootMesh;
         }
 
         /**
@@ -469,6 +515,7 @@ namespace BABYLON {
             this._groundMaterial.secondaryLevel = 0;
             this._groundMaterial.tertiaryLevel = 0;
             this._groundMaterial.useRGBColor = false;
+            this._groundMaterial.enableNoise = true;
             
             if (this._ground) {
                 this._ground.material = this._groundMaterial;
@@ -501,7 +548,7 @@ namespace BABYLON {
         /**
          * Setup the ground mirror texture according to the specified options.
          */
-        private _setupGroundMirrorTexture(): void {
+        private _setupGroundMirrorTexture(sceneSize: ISceneSize): void {
             let wrapping = BABYLON.Texture.CLAMP_ADDRESSMODE;
             if (!this._groundMirror) {
                 this._groundMirror = new BABYLON.MirrorTexture("BackgroundPlaneMirrorTexture", 
@@ -511,10 +558,22 @@ namespace BABYLON {
                     this._options.groundMirrorTextureType,
                     BABYLON.Texture.BILINEAR_SAMPLINGMODE,
                     true);
-                this._groundMirror.mirrorPlane = new BABYLON.Plane(0, -1, 0, 0);
+                this._groundMirror.mirrorPlane = new BABYLON.Plane(0, -1, 0, sceneSize.rootPosition.y);
                 this._groundMirror.anisotropicFilteringLevel = 1;
                 this._groundMirror.wrapU = wrapping;
                 this._groundMirror.wrapV = wrapping;
+                this._groundMirror.gammaSpace = false;
+
+                if (this._groundMirror.renderList) {
+                    for (let i = 0; i < this._scene.meshes.length; i++) {
+                        const mesh = this._scene.meshes[i];
+                        if (mesh !== this._ground && 
+                            mesh !== this._skybox &&
+                            mesh !== this._rootMesh) {
+                            this._groundMirror.renderList.push(mesh);
+                        }
+                    }
+                }
             }
             
             this._groundMirror.clearColor = new BABYLON.Color4(
@@ -541,16 +600,9 @@ namespace BABYLON {
         /**
          * Setup the skybox according to the specified options.
          */
-        private _setupSkybox(): void {
+        private _setupSkybox(sceneSize: ISceneSize): void {
             if (!this._skybox) {
-                let size = this._options.skyboxSize;
-                if (this._options.sizeAuto) {
-                    if (this._scene.activeCamera instanceof ArcRotateCamera &&
-                        this._scene.activeCamera.upperRadiusLimit) {
-                        size = this._scene.activeCamera.upperRadiusLimit;
-                    }
-                }
-                this._skybox = Mesh.CreateBox("BackgroundSkybox", size, this._scene, undefined, BABYLON.Mesh.BACKSIDE);
+                this._skybox = Mesh.CreateBox("BackgroundSkybox", sceneSize.skyboxSize, this._scene, undefined, BABYLON.Mesh.BACKSIDE);
             }
             this._skybox.parent = this._rootMesh;
         }
@@ -571,6 +623,7 @@ namespace BABYLON {
             this._skyboxMaterial.primaryColor = this._options.skyboxColor;
             this._skyboxMaterial.secondaryLevel = 0;
             this._skyboxMaterial.tertiaryLevel = 0;
+            this._skyboxMaterial.enableNoise = true;
 
             this._skybox.material = this._skyboxMaterial;
         }

+ 17 - 2
src/Materials/Background/babylon.backgroundMaterial.ts

@@ -58,6 +58,11 @@
          */
         public USERGBCOLOR = false;
 
+        /**
+         * True to add noise in order to reduce the banding effect.
+         */
+        public NOISE = false;
+
         // Image Processing Configuration.
         public IMAGEPROCESSING = false;
         public VIGNETTE = false;
@@ -317,6 +322,15 @@
         public useRGBColor: boolean = true;
 
         /**
+         * This helps reducing the banding effect that could occur on the background.
+         */
+        @serialize()
+        protected _enableNoise: boolean;
+        @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+        public enableNoise: boolean = false;
+
+
+        /**
          * Number of Simultaneous lights allowed on the material.
          */
         @serialize()
@@ -519,7 +533,7 @@
          * @returns false
          */
         public needAlphaTesting(): boolean {
-            return false;
+            return true;
         }
 
         /**
@@ -641,7 +655,7 @@
                             this._reflectionControls.x = this.reflectionAmount;
                             this._reflectionControls.y = this.reflectionReflectance0;
                             this._reflectionControls.z = this.reflectionReflectance90;
-                            this._reflectionControls.w = this.reflectionFalloffDistance;
+                            this._reflectionControls.w = 1 / this.reflectionFalloffDistance;
                         }
                         else {
                             defines.REFLECTIONFRESNEL = false;
@@ -670,6 +684,7 @@
 
                 defines.PREMULTIPLYALPHA = (this.alphaMode === Engine.ALPHA_PREMULTIPLIED || this.alphaMode === Engine.ALPHA_PREMULTIPLIED_PORTERDUFF);
                 defines.USERGBCOLOR = this._useRGBColor;
+                defines.NOISE = this._enableNoise;
             }
 
             if (defines._areImageProcessingDirty) {

+ 20 - 746
src/Mesh/babylon.abstractMesh.ts

@@ -1,12 +1,5 @@
 module BABYLON {
-    export class AbstractMesh extends Node implements IDisposable, ICullable, IGetSetVerticesData {
-        // Statics
-        private static _BILLBOARDMODE_NONE = 0;
-        private static _BILLBOARDMODE_X = 1;
-        private static _BILLBOARDMODE_Y = 2;
-        private static _BILLBOARDMODE_Z = 4;
-        private static _BILLBOARDMODE_ALL = 7;
-
+    export class AbstractMesh extends TransformNode implements IDisposable, ICullable, IGetSetVerticesData {
         public static OCCLUSION_TYPE_NONE = 0;
         public static OCCLUSION_TYPE_OPTIMISTIC = 1;
         public static OCCLUSION_TYPE_STRICT = 2;
@@ -14,23 +7,23 @@
         public static OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE = 1;
 
         public static get BILLBOARDMODE_NONE(): number {
-            return AbstractMesh._BILLBOARDMODE_NONE;
+            return TransformNode.BILLBOARDMODE_NONE;
         }
 
         public static get BILLBOARDMODE_X(): number {
-            return AbstractMesh._BILLBOARDMODE_X;
+            return TransformNode.BILLBOARDMODE_X;
         }
 
         public static get BILLBOARDMODE_Y(): number {
-            return AbstractMesh._BILLBOARDMODE_Y;
+            return TransformNode.BILLBOARDMODE_Y;
         }
 
         public static get BILLBOARDMODE_Z(): number {
-            return AbstractMesh._BILLBOARDMODE_Z;
+            return TransformNode.BILLBOARDMODE_Z;
         }
 
         public static get BILLBOARDMODE_ALL(): number {
-            return AbstractMesh._BILLBOARDMODE_ALL;
+            return TransformNode.BILLBOARDMODE_ALL;
         }
 
         // facetData private properties
@@ -110,18 +103,12 @@
             return this._facetDataEnabled;
         }
 
-        // Normal matrix
-        private _nonUniformScaling = false;
-        public get nonUniformScaling(): boolean {
-            return this._nonUniformScaling;
-        }
-
-        public set nonUniformScaling(value: boolean) {
-            if (this._nonUniformScaling === value) {
-                return;
+        public _updateNonUniformScalingState(value: boolean): boolean {
+            if (!super._updateNonUniformScalingState(value)) {
+                return false;
             }
-            this._nonUniformScaling = true;
             this._markSubMeshesAsMiscDirty();
+            return true;
         }
 
         // Events
@@ -155,12 +142,6 @@
         }
 
         /**
-        * An event triggered after the world matrix is updated
-        * @type {BABYLON.Observable}
-        */
-        public onAfterWorldMatrixUpdateObservable = new Observable<AbstractMesh>();
-
-        /**
         * An event triggered when material is changed
         * @type {BABYLON.Observable}
         */
@@ -168,7 +149,6 @@
 
         // Properties
         public definedFacingForward = true; // orientation for POV movement & rotation
-        public position = Vector3.Zero();
 
         /**
         * This property determines the type of occlusion query algorithm to run in WebGl, you can use:
@@ -222,13 +202,8 @@
 
         private _occlusionQuery: Nullable<WebGLQuery>;
 
-        private _rotation = Vector3.Zero();
-        private _rotationQuaternion: Nullable<Quaternion>;
-        private _scaling = Vector3.One();
-        public billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
         public visibility = 1.0;
         public alphaIndex = Number.MAX_VALUE;
-        public infiniteDistance = false;
         public isVisible = true;
         public isPickable = true;
         public showBoundingBox = false;
@@ -345,8 +320,6 @@
             this._markSubMeshesAsMiscDirty();
         }
 
-        public scalingDeterminant = 1;
-
         public useOctreeForRenderingSelection = true;
         public useOctreeForPicking = true;
         public useOctreeForCollisions = true;
@@ -407,27 +380,17 @@
             this._collisionGroup = !isNaN(mask) ? mask : -1;
         }
 
-        // Attach to bone
-        private _meshToBoneReferal: Nullable<AbstractMesh>;
-
         // Edges
         public edgesWidth = 1;
         public edgesColor = new Color4(1, 0, 0, 1);
         public _edgesRenderer: Nullable<EdgesRenderer>;
 
         // Cache
-        private _localWorld = Matrix.Zero();
-        public _worldMatrix = Matrix.Zero();
-        private _absolutePosition = Vector3.Zero();
         private _collisionsTransformMatrix = Matrix.Zero();
         private _collisionsScalingMatrix = Matrix.Zero();
-        private _isDirty = false;
         public _masterMesh: Nullable<AbstractMesh>;
 
         public _boundingInfo: Nullable<BoundingInfo>;
-        private _pivotMatrix = Matrix.Identity();
-        private _pivotMatrixInverse: Matrix;
-        private _postMultiplyPivotMatrix = false;
         public _isDisposed = false;
         public _renderId = 0;
 
@@ -435,12 +398,8 @@
         public _submeshesOctree: Octree<SubMesh>;
         public _intersectionsInProgress = new Array<AbstractMesh>();
 
-        private _isWorldMatrixFrozen = false;
-
         public _unIndexed = false;
 
-        public _poseMatrix: Matrix;
-
         public _lightSources = new Array<Light>();
 
         public get _positions(): Nullable<Vector3[]> {
@@ -479,7 +438,7 @@
 
         // Constructor
         constructor(name: string, scene: Nullable<Scene> = null) {
-            super(name, scene);
+            super(name, scene, false);
 
             this.getScene().addMesh(this);
 
@@ -612,20 +571,7 @@
             }
         }
 
-        /**
-         * 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;
-        }
-
-        public set rotation(newRotation: Vector3) {
-            this._rotation = newRotation;
-        }
-
-        /**
+         /**
          * Scaling property : a Vector3 depicting the mesh scaling along each local axis X, Y, Z.  
          * Default : (1.0, 1.0, 1.0)
          */
@@ -633,6 +579,10 @@
             return this._scaling;
         }
 
+        /**
+         * Scaling property : a Vector3 depicting the mesh scaling along each local axis X, Y, Z.  
+         * Default : (1.0, 1.0, 1.0)
+         */
         public set scaling(newScaling: Vector3) {
             this._scaling = newScaling;
             if (this.physicsImpostor) {
@@ -640,40 +590,7 @@
             }
         }
 
-        /**
-         * Rotation Quaternion property : this a Quaternion object depicting the mesh rotation by using a unit quaternion. 
-         * It's null by default.  
-         * If set, only the rotationQuaternion is then used to compute the mesh rotation and its property `.rotation\ is then ignored and set to (0.0, 0.0, 0.0)
-         */
-        public get rotationQuaternion(): Nullable<Quaternion> {
-            return this._rotationQuaternion;
-        }
-
-        public set rotationQuaternion(quaternion: Nullable<Quaternion>) {
-            this._rotationQuaternion = quaternion;
-            //reset the rotation vector. 
-            if (quaternion && this.rotation.length()) {
-                this.rotation.copyFromFloats(0.0, 0.0, 0.0);
-            }
-        }
-
         // Methods
-        /**
-         * Copies the paramater passed Matrix into the mesh Pose matrix.  
-         * Returns the AbstractMesh.  
-         */
-        public updatePoseMatrix(matrix: Matrix): AbstractMesh {
-            this._poseMatrix.copyFrom(matrix);
-            return this;
-        }
-
-        /**
-         * Returns the mesh Pose matrix.  
-         * Returned object : Matrix
-         */
-        public getPoseMatrix(): Matrix {
-            return this._poseMatrix;
-        }
 
         /**
          * Disables the mesh edger rendering mode.  
@@ -868,7 +785,7 @@
         public _activate(renderId: number): void {
             this._renderId = renderId;
         }
-
+   
         /**
          * Returns the latest update of the World matrix
          * Returns a Matrix.  
@@ -878,208 +795,7 @@
                 return this._masterMesh.getWorldMatrix();
             }
 
-            if (this._currentRenderId !== this.getScene().getRenderId()) {
-                this.computeWorldMatrix();
-            }
-            return this._worldMatrix;
-        }
-
-        /**
-         * Returns directly the latest state of the mesh World matrix. 
-         * A Matrix is returned.    
-         */
-        public get worldMatrixFromCache(): Matrix {
-            return this._worldMatrix;
-        }
-
-        /**
-         * Returns the current mesh absolute position.
-         * Retuns a Vector3.
-         */
-        public get absolutePosition(): Vector3 {
-            return this._absolutePosition;
-        }
-        /**
-         * Prevents the World matrix to be computed any longer.
-         * Returns the AbstractMesh.  
-         */
-        public freezeWorldMatrix(): AbstractMesh {
-            this._isWorldMatrixFrozen = false;  // no guarantee world is not already frozen, switch off temporarily
-            this.computeWorldMatrix(true);
-            this._isWorldMatrixFrozen = true;
-            return this;
-        }
-
-        /**
-         * Allows back the World matrix computation. 
-         * Returns the AbstractMesh.  
-         */
-        public unfreezeWorldMatrix() {
-            this._isWorldMatrixFrozen = false;
-            this.computeWorldMatrix(true);
-            return this;
-        }
-
-        /**
-         * True if the World matrix has been frozen.  
-         * Returns a boolean.  
-         */
-        public get isWorldMatrixFrozen(): boolean {
-            return this._isWorldMatrixFrozen;
-        }
-
-        private static _rotationAxisCache = new Quaternion();
-        /**
-         * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in the given space.  
-         * space (default LOCAL) can be either BABYLON.Space.LOCAL, either BABYLON.Space.WORLD.
-         * Note that the property `rotationQuaternion` is then automatically updated and the property `rotation` is set to (0,0,0) and no longer used.  
-         * The passed axis is also normalized.  
-         * Returns the AbstractMesh.
-         */
-        public rotate(axis: Vector3, amount: number, space?: Space): AbstractMesh {
-            axis.normalize();
-            if (!this.rotationQuaternion) {
-                this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
-                this.rotation = Vector3.Zero();
-            }
-            var rotationQuaternion: Quaternion;
-            if (!space || (space as any) === Space.LOCAL) {
-                rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, AbstractMesh._rotationAxisCache);
-                this.rotationQuaternion.multiplyToRef(rotationQuaternion, this.rotationQuaternion);
-            }
-            else {
-                if (this.parent) {
-                    var invertParentWorldMatrix = this.parent.getWorldMatrix().clone();
-                    invertParentWorldMatrix.invert();
-                    axis = Vector3.TransformNormal(axis, invertParentWorldMatrix);
-                }
-                rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, AbstractMesh._rotationAxisCache);
-                rotationQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
-            }
-            return this;
-        }
-
-        /**
-         * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in world space.  
-         * Note that the property `rotationQuaternion` is then automatically updated and the property `rotation` is set to (0,0,0) and no longer used.  
-         * The passed axis is also normalized.  
-         * Returns the AbstractMesh.
-         * Method is based on http://www.euclideanspace.com/maths/geometry/affine/aroundPoint/index.htm
-         */
-        public rotateAround(point: Vector3, axis: Vector3, amount: number): AbstractMesh {
-            axis.normalize();
-            if (!this.rotationQuaternion) {
-                this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
-                this.rotation.copyFromFloats(0, 0, 0);
-            }
-            point.subtractToRef(this.position, Tmp.Vector3[0]);
-            Matrix.TranslationToRef(Tmp.Vector3[0].x, Tmp.Vector3[0].y, Tmp.Vector3[0].z, Tmp.Matrix[0]);
-            Tmp.Matrix[0].invertToRef(Tmp.Matrix[2]);
-            Matrix.RotationAxisToRef(axis, amount, Tmp.Matrix[1]);
-            Tmp.Matrix[2].multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[2]);
-            Tmp.Matrix[2].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[2]);
-
-            Tmp.Matrix[2].decompose(Tmp.Vector3[0], Tmp.Quaternion[0], Tmp.Vector3[1]);
-
-            this.position.addInPlace(Tmp.Vector3[1]);
-            Tmp.Quaternion[0].multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
-
-            return this;
-        }
-
-        /**
-         * Translates the mesh along the axis vector for the passed distance in the given space.  
-         * space (default LOCAL) can be either BABYLON.Space.LOCAL, either BABYLON.Space.WORLD.
-         * Returns the AbstractMesh.
-         */
-        public translate(axis: Vector3, distance: number, space?: Space): AbstractMesh {
-            var displacementVector = axis.scale(distance);
-            if (!space || (space as any) === Space.LOCAL) {
-                var tempV3 = this.getPositionExpressedInLocalSpace().add(displacementVector);
-                this.setPositionWithLocalVector(tempV3);
-            }
-            else {
-                this.setAbsolutePosition(this.getAbsolutePosition().add(displacementVector));
-            }
-            return this;
-        }
-
-        /**
-         * Adds a rotation step to the mesh current rotation.  
-         * x, y, z are Euler angles expressed in radians.  
-         * This methods updates the current mesh rotation, either mesh.rotation, either mesh.rotationQuaternion if it's set.  
-         * This means this rotation is made in the mesh local space only.   
-         * It's useful to set a custom rotation order different from the BJS standard one YXZ.  
-         * Example : this rotates the mesh first around its local X axis, then around its local Z axis, finally around its local Y axis.  
-         * ```javascript
-         * mesh.addRotation(x1, 0, 0).addRotation(0, 0, z2).addRotation(0, 0, y3);
-         * ```
-         * Note that `addRotation()` accumulates the passed rotation values to the current ones and computes the .rotation or .rotationQuaternion updated values.  
-         * Under the hood, only quaternions are used. So it's a little faster is you use .rotationQuaternion because it doesn't need to translate them back to Euler angles.   
-         * Returns the AbstractMesh.  
-         */
-        public addRotation(x: number, y: number, z: number): AbstractMesh {
-            var rotationQuaternion;
-            if (this.rotationQuaternion) {
-                rotationQuaternion = this.rotationQuaternion;
-            }
-            else {
-                rotationQuaternion = Tmp.Quaternion[1];
-                Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, rotationQuaternion);
-            }
-            var accumulation = BABYLON.Tmp.Quaternion[0];
-            Quaternion.RotationYawPitchRollToRef(y, x, z, accumulation);
-            rotationQuaternion.multiplyInPlace(accumulation);
-            if (!this.rotationQuaternion) {
-                rotationQuaternion.toEulerAnglesToRef(this.rotation);
-            }
-            return this;
-        }
-
-        /**
-         * Retuns the mesh absolute position in the World.  
-         * Returns a Vector3.
-         */
-        public getAbsolutePosition(): Vector3 {
-            this.computeWorldMatrix();
-            return this._absolutePosition;
-        }
-
-        /**
-         * Sets the mesh absolute position in the World from a Vector3 or an Array(3).
-         * Returns the AbstractMesh.  
-         */
-        public setAbsolutePosition(absolutePosition: Vector3): AbstractMesh {
-            if (!absolutePosition) {
-                return this;
-            }
-            var absolutePositionX;
-            var absolutePositionY;
-            var absolutePositionZ;
-            if (absolutePosition.x === undefined) {
-                if (arguments.length < 3) {
-                    return this;
-                }
-                absolutePositionX = arguments[0];
-                absolutePositionY = arguments[1];
-                absolutePositionZ = arguments[2];
-            }
-            else {
-                absolutePositionX = absolutePosition.x;
-                absolutePositionY = absolutePosition.y;
-                absolutePositionZ = absolutePosition.z;
-            }
-            if (this.parent) {
-                var invertParentWorldMatrix = this.parent.getWorldMatrix().clone();
-                invertParentWorldMatrix.invert();
-                var worldPosition = new Vector3(absolutePositionX, absolutePositionY, absolutePositionZ);
-                this.position = Vector3.TransformCoordinates(worldPosition, invertParentWorldMatrix);
-            } else {
-                this.position.x = absolutePositionX;
-                this.position.y = absolutePositionY;
-                this.position.z = absolutePositionZ;
-            }
-            return this;
+            return super.getWorldMatrix();
         }
 
         // ================================== Point of View Movement =================================
@@ -1148,83 +864,6 @@
         }
 
         /**
-         * Sets a new pivot matrix to the mesh.  
-         * Returns the AbstractMesh.
-         */
-        public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = false): AbstractMesh {
-            this._pivotMatrix = matrix.clone();
-            this._cache.pivotMatrixUpdated = true;
-            this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
-
-            if (this._postMultiplyPivotMatrix) {
-                this._pivotMatrixInverse = Matrix.Invert(matrix);
-            }
-            return this;
-        }
-
-        /**
-         * Returns the mesh pivot matrix.
-         * Default : Identity.  
-         * A Matrix is returned.  
-         */
-        public getPivotMatrix(): Matrix {
-            return this._pivotMatrix;
-        }
-
-        public _isSynchronized(): boolean {
-            if (this._isDirty) {
-                return false;
-            }
-
-            if (this.billboardMode !== this._cache.billboardMode || this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE)
-                return false;
-
-            if (this._cache.pivotMatrixUpdated) {
-                return false;
-            }
-
-            if (this.infiniteDistance) {
-                return false;
-            }
-
-            if (!this._cache.position.equals(this.position))
-                return false;
-
-            if (this.rotationQuaternion) {
-                if (!this._cache.rotationQuaternion.equals(this.rotationQuaternion))
-                    return false;
-            }
-
-            if (!this._cache.rotation.equals(this.rotation))
-                return false;
-
-            if (!this._cache.scaling.equals(this.scaling))
-                return false;
-
-            return true;
-        }
-
-        public _initCache() {
-            super._initCache();
-
-            this._cache.localMatrixUpdated = false;
-            this._cache.position = Vector3.Zero();
-            this._cache.scaling = Vector3.Zero();
-            this._cache.rotation = Vector3.Zero();
-            this._cache.rotationQuaternion = new Quaternion(0, 0, 0, 0);
-            this._cache.billboardMode = -1;
-        }
-
-        public markAsDirty(property: string): AbstractMesh {
-            if (property === "rotation") {
-                this.rotationQuaternion = null;
-            }
-            this._currentRenderId = Number.MAX_VALUE;
-            this._isDirty = true;
-            return this;
-        }
-
-        /**
          * Return the minimum and maximum world vectors of the entire hierarchy under current mesh
          * @param includeDescendants Include bounding info from descendants as well (true by default).
          */
@@ -1299,173 +938,9 @@
             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.  
-         * If the parameter `force` is let to `false` (default), the current cached World matrix is returned. 
-         * If the parameter `force`is set to `true`, the actual computation is done.  
-         * Returns the mesh World Matrix.
-         */
-        public computeWorldMatrix(force?: boolean): Matrix {
-            if (this._isWorldMatrixFrozen) {
-                return this._worldMatrix;
-            }
-
-            if (!force && this.isSynchronized(true)) {
-                return this._worldMatrix;
-            }
-
-            this._cache.position.copyFrom(this.position);
-            this._cache.scaling.copyFrom(this.scaling);
-            this._cache.pivotMatrixUpdated = false;
-            this._cache.billboardMode = this.billboardMode;
-            this._currentRenderId = this.getScene().getRenderId();
-            this._isDirty = false;
-
-            // Scaling
-            Matrix.ScalingToRef(this.scaling.x * this.scalingDeterminant, this.scaling.y * this.scalingDeterminant, this.scaling.z * this.scalingDeterminant, Tmp.Matrix[1]);
-
-            // Rotation
-
-            //rotate, if quaternion is set and rotation was used
-            if (this.rotationQuaternion) {
-                var len = this.rotation.length();
-                if (len) {
-                    this.rotationQuaternion.multiplyInPlace(BABYLON.Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z))
-                    this.rotation.copyFromFloats(0, 0, 0);
-                }
-            }
-
-            if (this.rotationQuaternion) {
-                this.rotationQuaternion.toRotationMatrix(Tmp.Matrix[0]);
-                this._cache.rotationQuaternion.copyFrom(this.rotationQuaternion);
-            } else {
-                Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, Tmp.Matrix[0]);
-                this._cache.rotation.copyFrom(this.rotation);
-            }
-
-            // Translation
-            let camera = (<Camera>this.getScene().activeCamera);
-            
-            if (this.infiniteDistance && !this.parent && camera) {
-
-                var cameraWorldMatrix = camera.getWorldMatrix();
-
-                var cameraGlobalPosition = new Vector3(cameraWorldMatrix.m[12], cameraWorldMatrix.m[13], cameraWorldMatrix.m[14]);
-
-                Matrix.TranslationToRef(this.position.x + cameraGlobalPosition.x, this.position.y + cameraGlobalPosition.y,
-                    this.position.z + cameraGlobalPosition.z, Tmp.Matrix[2]);
-            } else {
-                Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, Tmp.Matrix[2]);
-            }
-
-            // Composing transformations
-            this._pivotMatrix.multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[4]);
-            Tmp.Matrix[4].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
-
-            // Billboarding (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
-            if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE && camera) {
-                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL) {
-                    // Need to decompose each rotation here
-                    var currentPosition = Tmp.Vector3[3];
-
-                    if (this.parent && this.parent.getWorldMatrix) {
-                        if (this._meshToBoneReferal) {
-                            this.parent.getWorldMatrix().multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
-                            Vector3.TransformCoordinatesToRef(this.position, Tmp.Matrix[6], currentPosition);
-                        } else {
-                            Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), currentPosition);
-                        }
-                    } else {
-                        currentPosition.copyFrom(this.position);
-                    }
-
-                    currentPosition.subtractInPlace(camera.globalPosition);
-
-                    var finalEuler = Tmp.Vector3[4].copyFromFloats(0, 0, 0);
-                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_X) === AbstractMesh.BILLBOARDMODE_X) {
-                        finalEuler.x = Math.atan2(-currentPosition.y, currentPosition.z);
-                    }
-
-                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Y) === AbstractMesh.BILLBOARDMODE_Y) {
-                        finalEuler.y = Math.atan2(currentPosition.x, currentPosition.z);
-                    }
-
-                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Z) === AbstractMesh.BILLBOARDMODE_Z) {
-                        finalEuler.z = Math.atan2(currentPosition.y, currentPosition.x);
-                    }
-
-                    Matrix.RotationYawPitchRollToRef(finalEuler.y, finalEuler.x, finalEuler.z, Tmp.Matrix[0]);
-                } else {
-                    Tmp.Matrix[1].copyFrom(camera.getViewMatrix());
-
-                    Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
-                    Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
-                }
-
-                Tmp.Matrix[1].copyFrom(Tmp.Matrix[5]);
-                Tmp.Matrix[1].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
-            }
-
-            // Local world
-            Tmp.Matrix[5].multiplyToRef(Tmp.Matrix[2], this._localWorld);
-
-            // Parent
-            if (this.parent && this.parent.getWorldMatrix) {
-                if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE) {
-                    if (this._meshToBoneReferal) {
-                        this.parent.getWorldMatrix().multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
-                        Tmp.Matrix[5].copyFrom(Tmp.Matrix[6]);
-                    } else {
-                        Tmp.Matrix[5].copyFrom(this.parent.getWorldMatrix());
-                    }
-
-                    this._localWorld.getTranslationToRef(Tmp.Vector3[5]);
-                    Vector3.TransformCoordinatesToRef(Tmp.Vector3[5], Tmp.Matrix[5], Tmp.Vector3[5]);
-                    this._worldMatrix.copyFrom(this._localWorld);
-                    this._worldMatrix.setTranslation(Tmp.Vector3[5]);
-
-                } else {
-                    if (this._meshToBoneReferal) {
-                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
-                        Tmp.Matrix[6].multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), this._worldMatrix);
-                    } else {
-                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
-                    }
-                }
-                this._markSyncedWithParent();
-            } else {
-                this._worldMatrix.copyFrom(this._localWorld);
-            }
-
-            // Post multiply inverse of pivotMatrix
-            if (this._postMultiplyPivotMatrix) {
-                this._worldMatrix.multiplyToRef(this._pivotMatrixInverse, this._worldMatrix);
-            }
-
-            // Normal matrix
-            if (this.scaling.isNonUniform) {
-                this.nonUniformScaling = true;
-            } else if (this.parent && (<AbstractMesh>this.parent)._nonUniformScaling) {
-                this.nonUniformScaling = (<AbstractMesh>this.parent)._nonUniformScaling;
-            } else {
-                this.nonUniformScaling = false;
-            }
-
+        protected _afterComputeWorldMatrix(): void {
             // Bounding info
             this._updateBoundingInfo();
-
-            // Absolute position
-            this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
-
-            // Callbacks
-            this.onAfterWorldMatrixUpdateObservable.notifyObservers(this);
-
-            if (!this._poseMatrix) {
-                this._poseMatrix = Matrix.Invert(this._worldMatrix);
-            }
-
-            return this._worldMatrix;
         }
 
         /**
@@ -1489,81 +964,6 @@
         }
 
         /**
-         * Sets the mesh position in its local space.  
-         * Returns the AbstractMesh.  
-         */
-        public setPositionWithLocalVector(vector3: Vector3): AbstractMesh {
-            this.computeWorldMatrix();
-            this.position = Vector3.TransformNormal(vector3, this._localWorld);
-            return this;
-        }
-
-        /**
-         * Returns the mesh position in the local space from the current World matrix values.
-         * Returns a new Vector3.
-         */
-        public getPositionExpressedInLocalSpace(): Vector3 {
-            this.computeWorldMatrix();
-            var invLocalWorldMatrix = this._localWorld.clone();
-            invLocalWorldMatrix.invert();
-
-            return Vector3.TransformNormal(this.position, invLocalWorldMatrix);
-        }
-
-        /**
-         * Translates the mesh along the passed Vector3 in its local space.  
-         * Returns the AbstractMesh. 
-         */
-        public locallyTranslate(vector3: Vector3): AbstractMesh {
-            this.computeWorldMatrix(true);
-            this.position = Vector3.TransformCoordinates(vector3, this._localWorld);
-            return this;
-        }
-
-        private static _lookAtVectorCache = new Vector3(0, 0, 0);
-        public lookAt(targetPoint: Vector3, yawCor: number = 0, pitchCor: number = 0, rollCor: number = 0, space: Space = Space.LOCAL): AbstractMesh {
-            /// <summary>Orients a mesh towards a target point. Mesh must be drawn facing user.</summary>
-            /// <param name="targetPoint" type="Vector3">The position (must be in same space as current mesh) to look at</param>
-            /// <param name="yawCor" type="Number">optional yaw (y-axis) correction in radians</param>
-            /// <param name="pitchCor" type="Number">optional pitch (x-axis) correction in radians</param>
-            /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param>
-            /// <returns>Mesh oriented towards targetMesh</returns>
-
-            var dv = AbstractMesh._lookAtVectorCache;
-            var pos = space === Space.LOCAL ? this.position : this.getAbsolutePosition();
-            targetPoint.subtractToRef(pos, dv);
-            var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
-            var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
-            var pitch = Math.atan2(dv.y, len);
-            this.rotationQuaternion = this.rotationQuaternion || new Quaternion();
-            Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
-            return this;
-        }
-
-        public attachToBone(bone: Bone, affectedMesh: AbstractMesh): AbstractMesh {
-            this._meshToBoneReferal = affectedMesh;
-            this.parent = bone;
-
-            if (bone.getWorldMatrix().determinant() < 0) {
-                this.scalingDeterminant *= -1;
-            }
-            return this;
-        }
-
-        public detachFromBone(): AbstractMesh {
-            if (!this.parent) {
-                return this;
-            }
-
-            if (this.parent.getWorldMatrix().determinant() < 0) {
-                this.scalingDeterminant *= -1;
-            }
-            this._meshToBoneReferal = null;
-            this.parent = null;
-            return this;
-        }
-
-        /**
          * Returns `true` if the mesh is within the frustum defined by the passed array of planes.  
          * A mesh is in the frustum if its bounding box intersects the frustum.  
          * Boolean returned.  
@@ -2055,120 +1455,7 @@
             this._isDisposed = true;
 
             super.dispose();
-        }
-
-        /**
-         * 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();
-
-            this.getDirectionToRef(localAxis, result);
-
-            return result;
-        }
-
-        /**
-         * Sets the Vector3 "result" as the rotated Vector3 "localAxis" in the same rotation than the mesh.
-         * localAxis is expressed in the mesh local space.
-         * result is computed in the Wordl space from the mesh World matrix.  
-         * Returns the AbstractMesh.  
-         */
-        public getDirectionToRef(localAxis: Vector3, result: Vector3): AbstractMesh {
-            Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
-            return this;
-        }
-
-        public setPivotPoint(point: Vector3, space: Space = Space.LOCAL): AbstractMesh {
-
-            if (this.getScene().getRenderId() == 0) {
-                this.computeWorldMatrix(true);
-            }
-
-            var wm = this.getWorldMatrix();
-
-            if (space == Space.WORLD) {
-                var tmat = Tmp.Matrix[0];
-                wm.invertToRef(tmat);
-                point = Vector3.TransformCoordinates(point, tmat);
-            }
-
-            Vector3.TransformCoordinatesToRef(point, wm, this.position);
-            this._pivotMatrix.m[12] = -point.x;
-            this._pivotMatrix.m[13] = -point.y;
-            this._pivotMatrix.m[14] = -point.z;
-            this._cache.pivotMatrixUpdated = true;
-            return this;
-        }
-
-        /**
-         * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.  
-         */
-        public getPivotPoint(): Vector3 {
-            var point = Vector3.Zero();
-            this.getPivotPointToRef(point);
-            return point;
-        }
-
-        /**
-         * Sets the passed Vector3 "result" with the coordinates of the mesh pivot point in the local space.   
-         * Returns the AbstractMesh.   
-         */
-        public getPivotPointToRef(result: Vector3): AbstractMesh {
-            result.x = -this._pivotMatrix.m[12];
-            result.y = -this._pivotMatrix.m[13];
-            result.z = -this._pivotMatrix.m[14];
-            return this;
-        }
-
-        /**
-         * Returns a new Vector3 set with the mesh pivot point World coordinates.  
-         */
-        public getAbsolutePivotPoint(): Vector3 {
-            var point = Vector3.Zero();
-            this.getAbsolutePivotPointToRef(point);
-            return point;
-        }
-
-        /**
-         * Defines the passed mesh as the parent of the current mesh.  
-         * Returns the AbstractMesh.  
-         */
-        public setParent(mesh: Nullable<AbstractMesh>): AbstractMesh {
-            var parent = (<AbstractMesh>mesh);
-
-            if (mesh == null) {
-
-                var rotation = Tmp.Quaternion[0];
-                var position = Tmp.Vector3[0];
-                var scale = Tmp.Vector3[1];
-
-                this.getWorldMatrix().decompose(scale, rotation, 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;
-
-            } else {
-
-                var position = Tmp.Vector3[0];
-                var m1 = Tmp.Matrix[0];
-
-                parent.getWorldMatrix().invertToRef(m1);
-                Vector3.TransformCoordinatesToRef(this.position, m1, position);
-
-                this.position.copyFrom(position);
-            }
-            this.parent = parent;
-            return this;
-        }
+        } 
 
         /**
          * Adds the passed mesh as a child to the current mesh.  
@@ -2188,19 +1475,6 @@
             return this;
         }
 
-        /**
-         * Sets the Vector3 "result" coordinates with the mesh pivot point World coordinates.  
-         * Returns the AbstractMesh.  
-         */
-        public getAbsolutePivotPointToRef(result: Vector3): AbstractMesh {
-            result.x = this._pivotMatrix.m[12];
-            result.y = this._pivotMatrix.m[13];
-            result.z = this._pivotMatrix.m[14];
-            this.getPivotPointToRef(result);
-            Vector3.TransformCoordinatesToRef(result, this.getWorldMatrix(), result);
-            return this;
-        }
-
         // Facet data
         /** 
          *  Initialize the facet data arrays : facetNormals, facetPositions and facetPartitioning.   

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

@@ -912,6 +912,7 @@
         /**
          * Sets the mesh indices.  
          * Expects an array populated with integers or a typed array (Int32Array, Uint32Array, Uint16Array).
+         * Type is Uint16Array by default unless the mesh has more than 65536 vertices.
          * If the mesh has no geometry, a new Geometry object is created and set to the mesh. 
          * This method creates a new index buffer each call.  
          * Returns the Mesh.  

+ 788 - 0
src/Mesh/babylon.transformNode.ts

@@ -0,0 +1,788 @@
+module BABYLON {
+    export class TransformNode extends Node {
+        // Statics
+        public static BILLBOARDMODE_NONE = 0;
+        public static BILLBOARDMODE_X = 1;
+        public static BILLBOARDMODE_Y = 2;
+        public static BILLBOARDMODE_Z = 4;
+        public static BILLBOARDMODE_ALL = 7;
+        
+        // Properties
+        private _rotation = Vector3.Zero();
+        private _rotationQuaternion: Nullable<Quaternion>;
+        protected _scaling = Vector3.One();
+        protected _isDirty = false;
+        private _transformToBoneReferal: Nullable<TransformNode>;
+        
+        public billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
+        public scalingDeterminant = 1;
+        public infiniteDistance = false;
+        public position = Vector3.Zero();
+                        
+        // Cache        
+        public _poseMatrix: Matrix;
+        private _localWorld = Matrix.Zero();
+        public _worldMatrix = Matrix.Zero();
+        private _absolutePosition = Vector3.Zero();
+        private _pivotMatrix = Matrix.Identity();
+        private _pivotMatrixInverse: Matrix;
+        private _postMultiplyPivotMatrix = false;        
+        
+        protected _isWorldMatrixFrozen = false;
+
+        /**
+        * An event triggered after the world matrix is updated
+        * @type {BABYLON.Observable}
+        */
+        public onAfterWorldMatrixUpdateObservable = new Observable<TransformNode>();        
+
+        constructor(name: string, scene: Nullable<Scene> = null, isPure = true) {
+            super(name, scene);
+
+            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)
+         */
+        public get rotation(): Vector3 {
+            return this._rotation;
+        }
+
+        public set rotation(newRotation: Vector3) {
+            this._rotation = newRotation;
+        }
+
+        /**
+         * Scaling property : a Vector3 depicting the mesh scaling along each local axis X, Y, Z.  
+         * Default : (1.0, 1.0, 1.0)
+         */
+        public get scaling(): Vector3 {
+            return this._scaling;
+        }
+
+        /**
+         * Scaling property : a Vector3 depicting the mesh scaling along each local axis X, Y, Z.  
+         * Default : (1.0, 1.0, 1.0)
+        */
+        public set scaling(newScaling: Vector3) {
+            this._scaling = newScaling;
+        }
+
+        /**
+         * Rotation Quaternion property : this a Quaternion object depicting the mesh rotation by using a unit quaternion. 
+         * It's null by default.  
+         * If set, only the rotationQuaternion is then used to compute the mesh rotation and its property `.rotation\ is then ignored and set to (0.0, 0.0, 0.0)
+         */
+        public get rotationQuaternion(): Nullable<Quaternion> {
+            return this._rotationQuaternion;
+        }
+
+        public set rotationQuaternion(quaternion: Nullable<Quaternion>) {
+            this._rotationQuaternion = quaternion;
+            //reset the rotation vector. 
+            if (quaternion && this.rotation.length()) {
+                this.rotation.copyFromFloats(0.0, 0.0, 0.0);
+            }
+        }
+
+        /**
+         * Returns the latest update of the World matrix
+         * Returns a Matrix.  
+         */
+        public getWorldMatrix(): Matrix {
+            if (this._currentRenderId !== this.getScene().getRenderId()) {
+                this.computeWorldMatrix();
+            }
+            return this._worldMatrix;
+        }
+
+        /**
+         * Returns directly the latest state of the mesh World matrix. 
+         * A Matrix is returned.    
+         */
+        public get worldMatrixFromCache(): Matrix {
+            return this._worldMatrix;
+        }
+
+        /**
+         * Copies the paramater passed Matrix into the mesh Pose matrix.  
+         * Returns the AbstractMesh.  
+         */
+        public updatePoseMatrix(matrix: Matrix): TransformNode {
+            this._poseMatrix.copyFrom(matrix);
+            return this;
+        }
+
+        /**
+         * Returns the mesh Pose matrix.  
+         * Returned object : Matrix
+         */
+        public getPoseMatrix(): Matrix {
+            return this._poseMatrix;
+        }
+        
+        public _isSynchronized(): boolean {
+            if (this._isDirty) {
+                return false;
+            }
+
+            if (this.billboardMode !== this._cache.billboardMode || this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE)
+                return false;
+
+            if (this._cache.pivotMatrixUpdated) {
+                return false;
+            }
+
+            if (this.infiniteDistance) {
+                return false;
+            }
+
+            if (!this._cache.position.equals(this.position))
+                return false;
+
+            if (this.rotationQuaternion) {
+                if (!this._cache.rotationQuaternion.equals(this.rotationQuaternion))
+                    return false;
+            }
+
+            if (!this._cache.rotation.equals(this.rotation))
+                return false;
+
+            if (!this._cache.scaling.equals(this.scaling))
+                return false;
+
+            return true;
+        }
+
+        public _initCache() {
+            super._initCache();
+
+            this._cache.localMatrixUpdated = false;
+            this._cache.position = Vector3.Zero();
+            this._cache.scaling = Vector3.Zero();
+            this._cache.rotation = Vector3.Zero();
+            this._cache.rotationQuaternion = new Quaternion(0, 0, 0, 0);
+            this._cache.billboardMode = -1;
+        }
+
+        public markAsDirty(property: string): TransformNode {
+            if (property === "rotation") {
+                this.rotationQuaternion = null;
+            }
+            this._currentRenderId = Number.MAX_VALUE;
+            this._isDirty = true;
+            return this;
+        }        
+
+        /**
+         * Returns the current mesh absolute position.
+         * Retuns a Vector3.
+         */
+        public get absolutePosition(): Vector3 {
+            return this._absolutePosition;
+        }
+
+        /**
+         * Sets a new pivot matrix to the mesh.  
+         * Returns the AbstractMesh.
+        */
+        public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = false): TransformNode {
+            this._pivotMatrix = matrix.clone();
+            this._cache.pivotMatrixUpdated = true;
+            this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
+
+            if (this._postMultiplyPivotMatrix) {
+                this._pivotMatrixInverse = Matrix.Invert(matrix);
+            }
+            return this;
+        }
+
+        /**
+         * Returns the mesh pivot matrix.
+         * Default : Identity.  
+         * A Matrix is returned.  
+         */
+        public getPivotMatrix(): Matrix {
+            return this._pivotMatrix;
+        }
+
+        /**
+         * Prevents the World matrix to be computed any longer.
+         * Returns the AbstractMesh.  
+         */
+        public freezeWorldMatrix(): TransformNode {
+            this._isWorldMatrixFrozen = false;  // no guarantee world is not already frozen, switch off temporarily
+            this.computeWorldMatrix(true);
+            this._isWorldMatrixFrozen = true;
+            return this;
+        }
+
+        /**
+         * Allows back the World matrix computation. 
+         * Returns the AbstractMesh.  
+         */
+        public unfreezeWorldMatrix() {
+            this._isWorldMatrixFrozen = false;
+            this.computeWorldMatrix(true);
+            return this;
+        }
+
+        /**
+         * True if the World matrix has been frozen.  
+         * Returns a boolean.  
+         */
+        public get isWorldMatrixFrozen(): boolean {
+            return this._isWorldMatrixFrozen;
+        }
+
+     /**
+         * Retuns the mesh absolute position in the World.  
+         * Returns a Vector3.
+         */
+        public getAbsolutePosition(): Vector3 {
+            this.computeWorldMatrix();
+            return this._absolutePosition;
+        }
+
+        /**
+         * Sets the mesh absolute position in the World from a Vector3 or an Array(3).
+         * Returns the AbstractMesh.  
+         */
+        public setAbsolutePosition(absolutePosition: Vector3): TransformNode {
+            if (!absolutePosition) {
+                return this;
+            }
+            var absolutePositionX;
+            var absolutePositionY;
+            var absolutePositionZ;
+            if (absolutePosition.x === undefined) {
+                if (arguments.length < 3) {
+                    return this;
+                }
+                absolutePositionX = arguments[0];
+                absolutePositionY = arguments[1];
+                absolutePositionZ = arguments[2];
+            }
+            else {
+                absolutePositionX = absolutePosition.x;
+                absolutePositionY = absolutePosition.y;
+                absolutePositionZ = absolutePosition.z;
+            }
+            if (this.parent) {
+                var invertParentWorldMatrix = this.parent.getWorldMatrix().clone();
+                invertParentWorldMatrix.invert();
+                var worldPosition = new Vector3(absolutePositionX, absolutePositionY, absolutePositionZ);
+                this.position = Vector3.TransformCoordinates(worldPosition, invertParentWorldMatrix);
+            } else {
+                this.position.x = absolutePositionX;
+                this.position.y = absolutePositionY;
+                this.position.z = absolutePositionZ;
+            }
+            return this;
+        }   
+
+      /**
+         * 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);
+            return this;
+        }
+
+        /**
+         * Returns the mesh position in the local space from the current World matrix values.
+         * Returns a new Vector3.
+         */
+        public getPositionExpressedInLocalSpace(): Vector3 {
+            this.computeWorldMatrix();
+            var invLocalWorldMatrix = this._localWorld.clone();
+            invLocalWorldMatrix.invert();
+
+            return Vector3.TransformNormal(this.position, invLocalWorldMatrix);
+        }
+
+        /**
+         * Translates the mesh along the passed Vector3 in its local space.  
+         * Returns the AbstractMesh. 
+         */
+        public locallyTranslate(vector3: Vector3): TransformNode {
+            this.computeWorldMatrix(true);
+            this.position = Vector3.TransformCoordinates(vector3, this._localWorld);
+            return this;
+        }
+
+        private static _lookAtVectorCache = new Vector3(0, 0, 0);
+        public lookAt(targetPoint: Vector3, yawCor: number = 0, pitchCor: number = 0, rollCor: number = 0, space: Space = Space.LOCAL): TransformNode {
+            /// <summary>Orients a mesh towards a target point. Mesh must be drawn facing user.</summary>
+            /// <param name="targetPoint" type="Vector3">The position (must be in same space as current mesh) to look at</param>
+            /// <param name="yawCor" type="Number">optional yaw (y-axis) correction in radians</param>
+            /// <param name="pitchCor" type="Number">optional pitch (x-axis) correction in radians</param>
+            /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param>
+            /// <returns>Mesh oriented towards targetMesh</returns>
+
+            var dv = AbstractMesh._lookAtVectorCache;
+            var pos = space === Space.LOCAL ? this.position : this.getAbsolutePosition();
+            targetPoint.subtractToRef(pos, dv);
+            var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
+            var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
+            var pitch = Math.atan2(dv.y, len);
+            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.  
+         */
+        public getDirection(localAxis: Vector3): Vector3 {
+            var result = Vector3.Zero();
+
+            this.getDirectionToRef(localAxis, result);
+
+            return result;
+        }
+
+        /**
+         * Sets the Vector3 "result" as the rotated Vector3 "localAxis" in the same rotation than the mesh.
+         * localAxis is expressed in the mesh local space.
+         * result is computed in the Wordl space from the mesh World matrix.  
+         * Returns the AbstractMesh.  
+         */
+        public getDirectionToRef(localAxis: Vector3, result: Vector3): TransformNode {
+            Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
+            return this;
+        }
+
+        public setPivotPoint(point: Vector3, space: Space = Space.LOCAL): TransformNode {
+            if (this.getScene().getRenderId() == 0) {
+                this.computeWorldMatrix(true);
+            }
+
+            var wm = this.getWorldMatrix();
+
+            if (space == Space.WORLD) {
+                var tmat = Tmp.Matrix[0];
+                wm.invertToRef(tmat);
+                point = Vector3.TransformCoordinates(point, tmat);
+            }
+
+            Vector3.TransformCoordinatesToRef(point, wm, this.position);
+            this._pivotMatrix.m[12] = -point.x;
+            this._pivotMatrix.m[13] = -point.y;
+            this._pivotMatrix.m[14] = -point.z;
+            this._cache.pivotMatrixUpdated = true;
+            return this;
+        }
+
+        /**
+         * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.  
+         */
+        public getPivotPoint(): Vector3 {
+            var point = Vector3.Zero();
+            this.getPivotPointToRef(point);
+            return point;
+        }
+
+        /**
+         * Sets the passed Vector3 "result" with the coordinates of the mesh pivot point in the local space.   
+         * Returns the AbstractMesh.   
+         */
+        public getPivotPointToRef(result: Vector3): TransformNode {
+            result.x = -this._pivotMatrix.m[12];
+            result.y = -this._pivotMatrix.m[13];
+            result.z = -this._pivotMatrix.m[14];
+            return this;
+        }
+
+        /**
+         * Returns a new Vector3 set with the mesh pivot point World coordinates.  
+         */
+        public getAbsolutePivotPoint(): Vector3 {
+            var point = Vector3.Zero();
+            this.getAbsolutePivotPointToRef(point);
+            return point;
+        }
+
+        /**
+         * Sets the Vector3 "result" coordinates with the mesh pivot point World coordinates.  
+         * Returns the AbstractMesh.  
+         */
+        public getAbsolutePivotPointToRef(result: Vector3): TransformNode {
+            result.x = this._pivotMatrix.m[12];
+            result.y = this._pivotMatrix.m[13];
+            result.z = this._pivotMatrix.m[14];
+            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.  
+         */
+        public setParent(mesh: Nullable<AbstractMesh>): TransformNode {
+            var parent = (<AbstractMesh>mesh);
+
+            if (mesh == null) {
+
+                var rotation = Tmp.Quaternion[0];
+                var position = Tmp.Vector3[0];
+                var scale = Tmp.Vector3[1];
+
+                this.getWorldMatrix().decompose(scale, rotation, 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;
+
+            } else {
+
+                var position = Tmp.Vector3[0];
+                var m1 = Tmp.Matrix[0];
+
+                parent.getWorldMatrix().invertToRef(m1);
+                Vector3.TransformCoordinatesToRef(this.position, m1, position);
+
+                this.position.copyFrom(position);
+            }
+            this.parent = parent;
+            return this;
+        }       
+        
+        private _nonUniformScaling = false;
+        public get nonUniformScaling(): boolean {
+            return this._nonUniformScaling;
+        }
+
+        public _updateNonUniformScalingState(value: boolean): boolean {
+            if (this._nonUniformScaling === value) {
+                return false;
+            }
+
+            this._nonUniformScaling = true;
+            return true;
+        }        
+
+        /**
+         * Attach the current TransformNode to another TransformNode associated with a bone
+         * @param bone Bone affecting the TransformNode
+         * @param affectedTransformNode TransformNode associated with the bone 
+         */
+        public attachToBone(bone: Bone, affectedTransformNode: TransformNode): TransformNode {
+            this._transformToBoneReferal = affectedTransformNode;
+            this.parent = bone;
+
+            if (bone.getWorldMatrix().determinant() < 0) {
+                this.scalingDeterminant *= -1;
+            }
+            return this;
+        }
+
+        public detachFromBone(): TransformNode {
+            if (!this.parent) {
+                return this;
+            }
+
+            if (this.parent.getWorldMatrix().determinant() < 0) {
+                this.scalingDeterminant *= -1;
+            }
+            this._transformToBoneReferal = null;
+            this.parent = null;
+            return this;
+        }        
+
+        private static _rotationAxisCache = new Quaternion();
+        /**
+         * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in the given space.  
+         * space (default LOCAL) can be either BABYLON.Space.LOCAL, either BABYLON.Space.WORLD.
+         * Note that the property `rotationQuaternion` is then automatically updated and the property `rotation` is set to (0,0,0) and no longer used.  
+         * The passed axis is also normalized.  
+         * Returns the AbstractMesh.
+         */
+        public rotate(axis: Vector3, amount: number, space?: Space): TransformNode {
+            axis.normalize();
+            if (!this.rotationQuaternion) {
+                this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
+                this.rotation = Vector3.Zero();
+            }
+            var rotationQuaternion: Quaternion;
+            if (!space || (space as any) === Space.LOCAL) {
+                rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, AbstractMesh._rotationAxisCache);
+                this.rotationQuaternion.multiplyToRef(rotationQuaternion, this.rotationQuaternion);
+            }
+            else {
+                if (this.parent) {
+                    var invertParentWorldMatrix = this.parent.getWorldMatrix().clone();
+                    invertParentWorldMatrix.invert();
+                    axis = Vector3.TransformNormal(axis, invertParentWorldMatrix);
+                }
+                rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, AbstractMesh._rotationAxisCache);
+                rotationQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
+            }
+            return this;
+        }
+
+        /**
+         * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in world space.  
+         * Note that the property `rotationQuaternion` is then automatically updated and the property `rotation` is set to (0,0,0) and no longer used.  
+         * The passed axis is also normalized.  
+         * Returns the AbstractMesh.
+         * Method is based on http://www.euclideanspace.com/maths/geometry/affine/aroundPoint/index.htm
+         */
+        public rotateAround(point: Vector3, axis: Vector3, amount: number): TransformNode {
+            axis.normalize();
+            if (!this.rotationQuaternion) {
+                this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
+                this.rotation.copyFromFloats(0, 0, 0);
+            }
+            point.subtractToRef(this.position, Tmp.Vector3[0]);
+            Matrix.TranslationToRef(Tmp.Vector3[0].x, Tmp.Vector3[0].y, Tmp.Vector3[0].z, Tmp.Matrix[0]);
+            Tmp.Matrix[0].invertToRef(Tmp.Matrix[2]);
+            Matrix.RotationAxisToRef(axis, amount, Tmp.Matrix[1]);
+            Tmp.Matrix[2].multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[2]);
+            Tmp.Matrix[2].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[2]);
+
+            Tmp.Matrix[2].decompose(Tmp.Vector3[0], Tmp.Quaternion[0], Tmp.Vector3[1]);
+
+            this.position.addInPlace(Tmp.Vector3[1]);
+            Tmp.Quaternion[0].multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
+
+            return this;
+        }
+
+        /**
+         * Translates the mesh along the axis vector for the passed distance in the given space.  
+         * space (default LOCAL) can be either BABYLON.Space.LOCAL, either BABYLON.Space.WORLD.
+         * Returns the AbstractMesh.
+         */
+        public translate(axis: Vector3, distance: number, space?: Space): TransformNode {
+            var displacementVector = axis.scale(distance);
+            if (!space || (space as any) === Space.LOCAL) {
+                var tempV3 = this.getPositionExpressedInLocalSpace().add(displacementVector);
+                this.setPositionWithLocalVector(tempV3);
+            }
+            else {
+                this.setAbsolutePosition(this.getAbsolutePosition().add(displacementVector));
+            }
+            return this;
+        }
+
+        /**
+         * Adds a rotation step to the mesh current rotation.  
+         * x, y, z are Euler angles expressed in radians.  
+         * This methods updates the current mesh rotation, either mesh.rotation, either mesh.rotationQuaternion if it's set.  
+         * This means this rotation is made in the mesh local space only.   
+         * It's useful to set a custom rotation order different from the BJS standard one YXZ.  
+         * Example : this rotates the mesh first around its local X axis, then around its local Z axis, finally around its local Y axis.  
+         * ```javascript
+         * mesh.addRotation(x1, 0, 0).addRotation(0, 0, z2).addRotation(0, 0, y3);
+         * ```
+         * Note that `addRotation()` accumulates the passed rotation values to the current ones and computes the .rotation or .rotationQuaternion updated values.  
+         * Under the hood, only quaternions are used. So it's a little faster is you use .rotationQuaternion because it doesn't need to translate them back to Euler angles.   
+         * Returns the AbstractMesh.  
+         */
+        public addRotation(x: number, y: number, z: number): TransformNode {
+            var rotationQuaternion;
+            if (this.rotationQuaternion) {
+                rotationQuaternion = this.rotationQuaternion;
+            }
+            else {
+                rotationQuaternion = Tmp.Quaternion[1];
+                Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, rotationQuaternion);
+            }
+            var accumulation = BABYLON.Tmp.Quaternion[0];
+            Quaternion.RotationYawPitchRollToRef(y, x, z, accumulation);
+            rotationQuaternion.multiplyInPlace(accumulation);
+            if (!this.rotationQuaternion) {
+                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.  
+         * If the parameter `force` is let to `false` (default), the current cached World matrix is returned. 
+         * If the parameter `force`is set to `true`, the actual computation is done.  
+         * Returns the mesh World Matrix.
+         */
+        public computeWorldMatrix(force?: boolean): Matrix {
+            if (this._isWorldMatrixFrozen) {
+                return this._worldMatrix;
+            }
+
+            if (!force && this.isSynchronized(true)) {
+                return this._worldMatrix;
+            }
+
+            this._cache.position.copyFrom(this.position);
+            this._cache.scaling.copyFrom(this.scaling);
+            this._cache.pivotMatrixUpdated = false;
+            this._cache.billboardMode = this.billboardMode;
+            this._currentRenderId = this.getScene().getRenderId();
+            this._isDirty = false;
+
+            // Scaling
+            Matrix.ScalingToRef(this.scaling.x * this.scalingDeterminant, this.scaling.y * this.scalingDeterminant, this.scaling.z * this.scalingDeterminant, Tmp.Matrix[1]);
+
+            // Rotation
+
+            //rotate, if quaternion is set and rotation was used
+            if (this.rotationQuaternion) {
+                var len = this.rotation.length();
+                if (len) {
+                    this.rotationQuaternion.multiplyInPlace(BABYLON.Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z))
+                    this.rotation.copyFromFloats(0, 0, 0);
+                }
+            }
+
+            if (this.rotationQuaternion) {
+                this.rotationQuaternion.toRotationMatrix(Tmp.Matrix[0]);
+                this._cache.rotationQuaternion.copyFrom(this.rotationQuaternion);
+            } else {
+                Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, Tmp.Matrix[0]);
+                this._cache.rotation.copyFrom(this.rotation);
+            }
+
+            // Translation
+            let camera = (<Camera>this.getScene().activeCamera);
+            
+            if (this.infiniteDistance && !this.parent && camera) {
+
+                var cameraWorldMatrix = camera.getWorldMatrix();
+
+                var cameraGlobalPosition = new Vector3(cameraWorldMatrix.m[12], cameraWorldMatrix.m[13], cameraWorldMatrix.m[14]);
+
+                Matrix.TranslationToRef(this.position.x + cameraGlobalPosition.x, this.position.y + cameraGlobalPosition.y,
+                    this.position.z + cameraGlobalPosition.z, Tmp.Matrix[2]);
+            } else {
+                Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, Tmp.Matrix[2]);
+            }
+
+            // Composing transformations
+            this._pivotMatrix.multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[4]);
+            Tmp.Matrix[4].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
+
+            // Billboarding (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
+            if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE && camera) {
+                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL) {
+                    // Need to decompose each rotation here
+                    var currentPosition = Tmp.Vector3[3];
+
+                    if (this.parent && this.parent.getWorldMatrix) {
+                        if (this._transformToBoneReferal) {
+                            this.parent.getWorldMatrix().multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
+                            Vector3.TransformCoordinatesToRef(this.position, Tmp.Matrix[6], currentPosition);
+                        } else {
+                            Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), currentPosition);
+                        }
+                    } else {
+                        currentPosition.copyFrom(this.position);
+                    }
+
+                    currentPosition.subtractInPlace(camera.globalPosition);
+
+                    var finalEuler = Tmp.Vector3[4].copyFromFloats(0, 0, 0);
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_X) === AbstractMesh.BILLBOARDMODE_X) {
+                        finalEuler.x = Math.atan2(-currentPosition.y, currentPosition.z);
+                    }
+
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Y) === AbstractMesh.BILLBOARDMODE_Y) {
+                        finalEuler.y = Math.atan2(currentPosition.x, currentPosition.z);
+                    }
+
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Z) === AbstractMesh.BILLBOARDMODE_Z) {
+                        finalEuler.z = Math.atan2(currentPosition.y, currentPosition.x);
+                    }
+
+                    Matrix.RotationYawPitchRollToRef(finalEuler.y, finalEuler.x, finalEuler.z, Tmp.Matrix[0]);
+                } else {
+                    Tmp.Matrix[1].copyFrom(camera.getViewMatrix());
+
+                    Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
+                    Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
+                }
+
+                Tmp.Matrix[1].copyFrom(Tmp.Matrix[5]);
+                Tmp.Matrix[1].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
+            }
+
+            // Local world
+            Tmp.Matrix[5].multiplyToRef(Tmp.Matrix[2], this._localWorld);
+
+            // Parent
+            if (this.parent && this.parent.getWorldMatrix) {
+                if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE) {
+                    if (this._transformToBoneReferal) {
+                        this.parent.getWorldMatrix().multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
+                        Tmp.Matrix[5].copyFrom(Tmp.Matrix[6]);
+                    } else {
+                        Tmp.Matrix[5].copyFrom(this.parent.getWorldMatrix());
+                    }
+
+                    this._localWorld.getTranslationToRef(Tmp.Vector3[5]);
+                    Vector3.TransformCoordinatesToRef(Tmp.Vector3[5], Tmp.Matrix[5], Tmp.Vector3[5]);
+                    this._worldMatrix.copyFrom(this._localWorld);
+                    this._worldMatrix.setTranslation(Tmp.Vector3[5]);
+
+                } else {
+                    if (this._transformToBoneReferal) {
+                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
+                        Tmp.Matrix[6].multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), this._worldMatrix);
+                    } else {
+                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
+                    }
+                }
+                this._markSyncedWithParent();
+            } else {
+                this._worldMatrix.copyFrom(this._localWorld);
+            }
+
+            // Post multiply inverse of pivotMatrix
+            if (this._postMultiplyPivotMatrix) {
+                this._worldMatrix.multiplyToRef(this._pivotMatrixInverse, this._worldMatrix);
+            }
+
+            // Normal matrix
+            if (this.scaling.isNonUniform) {
+                this._updateNonUniformScalingState(true);
+            } else if (this.parent && (<TransformNode>this.parent)._nonUniformScaling) {
+                this._updateNonUniformScalingState((<TransformNode>this.parent)._nonUniformScaling);
+            } else {
+                this._updateNonUniformScalingState(false);
+            }
+
+            this._afterComputeWorldMatrix();
+
+            // Absolute position
+            this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
+
+            // Callbacks
+            this.onAfterWorldMatrixUpdateObservable.notifyObservers(this);
+
+            if (!this._poseMatrix) {
+                this._poseMatrix = Matrix.Invert(this._worldMatrix);
+            }
+
+            return this._worldMatrix;
+        }   
+
+        protected _afterComputeWorldMatrix(): void {
+
+        }
+    }
+}

+ 3 - 3
src/Physics/babylon.physicsImpostor.ts

@@ -24,9 +24,9 @@ module BABYLON {
         getScene?(): Scene;
         getAbsolutePosition(): Vector3;
         getAbsolutePivotPoint(): Vector3;
-        rotate(axis: Vector3, amount: number, space?: Space): IPhysicsEnabledObject;
-        translate(axis: Vector3, distance: number, space?: Space): IPhysicsEnabledObject;
-        setAbsolutePosition(absolutePosition: Vector3): IPhysicsEnabledObject;
+        rotate(axis: Vector3, amount: number, space?: Space): TransformNode;
+        translate(axis: Vector3, distance: number, space?: Space): TransformNode;
+        setAbsolutePosition(absolutePosition: Vector3): TransformNode;
     }
 
     export class PhysicsImpostor {

+ 11 - 0
src/Shaders/ShadersInclude/helperFunctions.fx

@@ -63,4 +63,15 @@ float square(float value)
 float getLuminance(vec3 color)
 {
     return clamp(dot(color, LuminanceEncodeApprox), 0., 1.);
+}
+
+// https://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
+float getRand(vec2 seed) {
+	return fract(sin(dot(seed.xy ,vec2(12.9898,78.233))) * 43758.5453);
+}
+
+vec3 dither(vec2 seed, vec3 color) {
+	float rand = getRand(seed);
+	color.rgb += mix(-0.5/255.0, 0.5/255.0, rand);
+	return color;
 }

+ 4 - 0
src/Shaders/background.fragment.fx

@@ -281,5 +281,9 @@ vec4 color = vec4(finalColor, finalAlpha);
     color.rgb *= color.a;
 #endif
 
+#ifdef NOISE
+    color.rgb = dither(vPositionW.xy, color.rgb);
+#endif
+
     gl_FragColor = color;
 }

+ 95 - 8
src/babylon.scene.ts

@@ -403,6 +403,18 @@
         public onGeometryRemovedObservable = new Observable<Geometry>();
 
         /**
+        * An event triggered when a transform node is created
+        * @type {BABYLON.Observable}
+        */
+        public onNewTransformNodeAddedObservable = new Observable<TransformNode>();
+        
+        /**
+        * An event triggered when a transform node is removed
+        * @type {BABYLON.Observable}
+        */
+        public onTransformNodeRemovedObservable = new Observable<TransformNode>();        
+
+        /**
         * An event triggered when a mesh is created
         * @type {BABYLON.Observable}
         */
@@ -666,6 +678,13 @@
 
         // Meshes
         /**
+        * All of the tranform nodes added to this scene.
+        * @see BABYLON.TransformNode
+        * @type {BABYLON.TransformNode[]}
+        */
+        public transformNodes = new Array<TransformNode>();
+
+        /**
         * All of the (abstract) meshes added to this scene.
         * @see BABYLON.AbstractMesh
         * @type {BABYLON.AbstractMesh[]}
@@ -2166,14 +2185,28 @@
                 // Remove from the scene if mesh found
                 this.meshes.splice(index, 1);
             }
-            //notify the collision coordinator
-            if (this.collisionCoordinator) {
-                this.collisionCoordinator.onMeshRemoved(toRemove);
-            }
 
             this.onMeshRemovedObservable.notifyObservers(toRemove);
 
             return index;
+        }        
+
+        public addTransformNode(newTransformNode: TransformNode) {
+            this.transformNodes.push(newTransformNode);
+
+            this.onNewTransformNodeAddedObservable.notifyObservers(newTransformNode);
+        }        
+
+        public removeTransformNode(toRemove: TransformNode): number {
+            var index = this.transformNodes.indexOf(toRemove);
+            if (index !== -1) {
+                // Remove from the scene if found
+                this.transformNodes.splice(index, 1);
+            }
+
+            this.onTransformNodeRemovedObservable.notifyObservers(toRemove);
+
+            return index;
         }
 
         public removeSkeleton(toRemove: Skeleton): number {
@@ -2572,6 +2605,27 @@
         }
 
         /**
+         * Get the first added transform node found of a given ID
+         * @param {string} id - the id to search for
+         * @return {BABYLON.TransformNode|null} the transform node found or null if not found at all.
+         */
+        public getTransformNodeByID(id: string): Nullable<TransformNode> {
+            for (var index = 0; index < this.transformNodes.length; index++) {
+                if (this.transformNodes[index].id === id) {
+                    return this.transformNodes[index];
+                }
+            }
+
+            return null;
+        }
+
+        public getTransformNodesByID(id: string): Array<TransformNode> {
+            return this.transformNodes.filter(function (m) {
+                return m.id === id;
+            })
+        }        
+
+        /**
          * Get a mesh with its auto-generated unique id
          * @param {number} uniqueId - the unique id to search for
          * @return {BABYLON.AbstractMesh|null} the mesh found or null if not found at all.
@@ -2614,6 +2668,12 @@
                 }
             }
 
+            for (index = this.transformNodes.length - 1; index >= 0; index--) {
+                if (this.transformNodes[index].id === id) {
+                    return this.transformNodes[index];
+                }
+            }            
+
             for (index = this.cameras.length - 1; index >= 0; index--) {
                 if (this.cameras[index].id === id) {
                     return this.cameras[index];
@@ -2687,6 +2747,16 @@
             return null;
         }
 
+        public getTransformNodeByName(name: string): Nullable<TransformNode> {
+            for (var index = 0; index < this.transformNodes.length; index++) {
+                if (this.transformNodes[index].name === name) {
+                    return this.transformNodes[index];
+                }
+            }
+
+            return null;
+        }        
+
         public getSoundByName(name: string): Nullable<Sound> {
             var index: number;
             if (AudioEngine) {
@@ -3778,6 +3848,9 @@
             while (this.meshes.length) {
                 this.meshes[0].dispose(true);
             }
+            while (this.transformNodes.length) {
+                this.removeTransformNode(this.transformNodes[0]);
+            }
 
             // Release cameras
             while (this.cameras.length) {
@@ -4094,6 +4167,8 @@
             return this._internalPickSprites(ray, predicate, fastCheck, camera);
         }
 
+        private _cachedRayForTransform: Ray;
+
         /** Use the given ray to pick a mesh in the scene
          * @param ray The ray to use to pick meshes
          * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
@@ -4105,7 +4180,13 @@
                     this._pickWithRayInverseMatrix = Matrix.Identity();
                 }
                 world.invertToRef(this._pickWithRayInverseMatrix);
-                return Ray.Transform(ray, this._pickWithRayInverseMatrix);
+
+                if (!this._cachedRayForTransform) {
+                    this._cachedRayForTransform = new Ray(Vector3.Zero(), Vector3.Zero());
+                }
+                
+                Ray.TransformToRef(ray, this._pickWithRayInverseMatrix, this._cachedRayForTransform);
+                return this._cachedRayForTransform;
             }, predicate, fastCheck);
         }
 
@@ -4131,7 +4212,13 @@
                     this._pickWithRayInverseMatrix = Matrix.Identity();
                 }
                 world.invertToRef(this._pickWithRayInverseMatrix);
-                return Ray.Transform(ray, this._pickWithRayInverseMatrix);
+
+                if (!this._cachedRayForTransform) {
+                    this._cachedRayForTransform = new Ray(Vector3.Zero(), Vector3.Zero());
+                }
+                
+                Ray.TransformToRef(ray, this._pickWithRayInverseMatrix, this._cachedRayForTransform);
+                return this._cachedRayForTransform;
             }, predicate);
         }
 
@@ -4362,8 +4449,8 @@
             return null;
         }
 
-        public createDefaultVRExperience(webVROptions: WebVROptions = {}) {
-            this.VRHelper = new BABYLON.VRExperienceHelper(this, webVROptions);
+        public createDefaultVRExperience(webVROptions: WebVROptions = {}): VRExperienceHelper {
+            return new BABYLON.VRExperienceHelper(this, webVROptions);
         }
 
         // Tags

+ 5 - 4
tests/nullEngine/app.js

@@ -133,9 +133,10 @@ BABYLON.SceneLoader.Load("https://playground.babylonjs.com/scenes/", "skull.baby
     console.log('scene loaded!');
     for (var index = 0; index < scene.meshes.length; index++) {
         console.log(scene.meshes[index].name);
-    }    
-    engine.runRenderLoop(function() {
-        scene.render();
-    });
+    } 
+    engine.dispose();   
+   // engine.runRenderLoop(function() {
+     //   scene.render();
+    //});
   
   }, progress => {}, (scene, err) => console.error('error:', err));