Browse Source

Merge pull request #1556 from Temechon/master

First beta version of Babylon.js inspector
David Catuhe 8 years ago
parent
commit
47c28cd4cc
90 changed files with 108577 additions and 823 deletions
  1. 8 0
      inspector/.gitignore
  2. 151 0
      inspector/Gruntfile.js
  3. 78 0
      inspector/README.md
  4. 0 0
      inspector/dist/.baseDir.d.ts
  5. 2 0
      inspector/dist/.baseDir.js
  6. 906 0
      inspector/dist/inspector.d.ts
  7. 3644 0
      inspector/dist/inspector.js
  8. 15033 0
      inspector/dist/libs/babylon.canvas2d.max.js
  9. 54626 0
      inspector/dist/libs/babylon.max.js
  10. 560 0
      inspector/dist/libs/split.js
  11. 12 0
      inspector/dist/test/Test.d.ts
  12. 133 0
      inspector/dist/test/Test.js
  13. BIN
      inspector/dist/test/assets/albedo.png
  14. BIN
      inspector/dist/test/assets/reflectivity.png
  15. BIN
      inspector/dist/test/assets/room.hdr
  16. BIN
      inspector/dist/test/assets/skybox/snow_nx.jpg
  17. BIN
      inspector/dist/test/assets/skybox/snow_ny.jpg
  18. BIN
      inspector/dist/test/assets/skybox/snow_nz.jpg
  19. BIN
      inspector/dist/test/assets/skybox/snow_px.jpg
  20. BIN
      inspector/dist/test/assets/skybox/snow_py.jpg
  21. BIN
      inspector/dist/test/assets/skybox/snow_pz.jpg
  22. 112 0
      inspector/dist/test/index.html
  23. 12405 0
      inspector/dist/typings/babylon.d.old.d.ts
  24. 1 0
      inspector/dist/typings/babylon.d.old.js
  25. 3 0
      inspector/index.js
  26. 29 0
      inspector/package.json
  27. 146 0
      inspector/sass/_detailPanel.scss
  28. 18 0
      inspector/sass/_resizeBar.scss
  29. 19 0
      inspector/sass/_searchbar.scss
  30. 83 0
      inspector/sass/_tabPanel.scss
  31. 58 0
      inspector/sass/_tabbar.scss
  32. 31 0
      inspector/sass/_toolbar.scss
  33. 19 0
      inspector/sass/_tooltip.scss
  34. 60 0
      inspector/sass/_tree.scss
  35. 16 0
      inspector/sass/_treeTool.scss
  36. 19 0
      inspector/sass/defines.scss
  37. 92 0
      inspector/sass/main.scss
  38. 53 0
      inspector/sass/tabs/_shaderTab.scss
  39. BIN
      inspector/screens/tab_mesh.jpg
  40. BIN
      inspector/screens/tools.jpg
  41. 1 0
      inspector/ts/.baseDir.ts
  42. 183 0
      inspector/ts/inspector/Inspector.ts
  43. 48 0
      inspector/ts/inspector/adapters/Adapter.ts
  44. 71 0
      inspector/ts/inspector/adapters/Canvas2DAdapter.ts
  45. 70 0
      inspector/ts/inspector/adapters/LightAdapter.ts
  46. 54 0
      inspector/ts/inspector/adapters/MaterialAdapter.ts
  47. 133 0
      inspector/ts/inspector/adapters/MeshAdapter.ts
  48. 160 0
      inspector/ts/inspector/details/DetailPanel.ts
  49. 41 0
      inspector/ts/inspector/details/Property.ts
  50. 357 0
      inspector/ts/inspector/details/PropertyLine.ts
  51. 32 0
      inspector/ts/inspector/gui/BasicElement.ts
  52. 36 0
      inspector/ts/inspector/gui/ColorElement.ts
  53. 119 0
      inspector/ts/inspector/gui/CubeTextureElement.ts
  54. 32 0
      inspector/ts/inspector/gui/HDRCubeTextureElement.ts
  55. 42 0
      inspector/ts/inspector/gui/SearchBar.ts
  56. 42 0
      inspector/ts/inspector/gui/TextureElement.ts
  57. 28 0
      inspector/ts/inspector/gui/Tooltip.ts
  58. 108 0
      inspector/ts/inspector/helpers/Helpers.ts
  59. 170 0
      inspector/ts/inspector/properties.ts
  60. 52 0
      inspector/ts/inspector/scheduler/Scheduler.ts
  61. 47 0
      inspector/ts/inspector/tabs/Canvas2DTab.ts
  62. 22 0
      inspector/ts/inspector/tabs/LightTab.ts
  63. 22 0
      inspector/ts/inspector/tabs/MaterialTab.ts
  64. 29 0
      inspector/ts/inspector/tabs/MeshTab.ts
  65. 142 0
      inspector/ts/inspector/tabs/PropertyTab.ts
  66. 182 0
      inspector/ts/inspector/tabs/SceneTab.ts
  67. 180 0
      inspector/ts/inspector/tabs/ShaderTab.ts
  68. 70 0
      inspector/ts/inspector/tabs/Tab.ts
  69. 191 0
      inspector/ts/inspector/tabs/TabBar.ts
  70. 43 0
      inspector/ts/inspector/tools/AbstractTool.ts
  71. 23 0
      inspector/ts/inspector/tools/PauseScheduleTool.ts
  72. 54 0
      inspector/ts/inspector/tools/PickTool.ts
  73. 14 0
      inspector/ts/inspector/tools/PopupTool.ts
  74. 14 0
      inspector/ts/inspector/tools/RefreshTool.ts
  75. 50 0
      inspector/ts/inspector/tools/Toolbar.ts
  76. 180 0
      inspector/ts/inspector/tree/TreeItem.ts
  77. 34 0
      inspector/ts/inspector/treetools/AbstractTreeTool.ts
  78. 43 0
      inspector/ts/inspector/treetools/BoundingBox.ts
  79. 47 0
      inspector/ts/inspector/treetools/Checkbox.ts
  80. 31 0
      inspector/ts/inspector/treetools/DebugArea.ts
  81. 31 0
      inspector/ts/inspector/treetools/Info.ts
  82. 169 0
      inspector/ts/test/Test.ts
  83. 5 0
      inspector/ts/tsconfig.json
  84. 4178 0
      inspector/ts/typings/babylon.canvas2d.d.ts
  85. 12593 0
      inspector/ts/typings/babylon.d.ts
  86. 20 0
      inspector/ts/typings/split.d.ts
  87. 26 0
      inspector/webpack.config.js
  88. 19 816
      src/Debug/babylon.debugLayer.ts
  89. 22 0
      src/Tools/babylon.tools.ts
  90. 0 7
      src/babylon.engine.ts

+ 8 - 0
inspector/.gitignore

@@ -0,0 +1,8 @@
+.idea
+node_modules
+.tmp
+*.map
+.tscache
+.history
+.vscode
+yarn.lock

+ 151 - 0
inspector/Gruntfile.js

@@ -0,0 +1,151 @@
+module.exports = function (grunt) {
+    
+    // load all grunt tasks
+    require('jit-grunt')(grunt);    
+
+    grunt.initConfig({
+        
+        clean: {
+            init: ['dist/libs/inspector.js', 'dist/libs/main.css', 'dist/inspector.js', 'dist/inspector.d.ts'],
+            compilation: ['dist/inspector/', 'dist/libs/inspector.js', 'dist/libs/main.css']
+        },
+
+        // Compilation from TypeScript to ES5²
+        ts: {
+            inspector: {
+                src : ['ts/**/*.ts', 'ts/typings/**/*'],
+                outDir: "dist",
+                options:{
+                    module: 'amd',
+                    target: 'es5',
+                    declaration: true,
+                    sourceMap:true,
+                    removeComments:false
+                }
+            }
+        },
+        // Concat definition files 
+        concat: {
+            inspector: {
+                files: {
+                    'dist/inspector.d.ts': ['dist/inspector/**/*.d.ts']
+                },
+            },
+        },
+        // Watches content related changes
+        watch : {
+            inspector : {
+                files: ['ts/inspector/**/*.ts'],
+                tasks: ['ts']
+            },
+            test : {
+                files: ['ts/test/**/*.ts'],
+                tasks: ['ts']
+            },
+            sass : {
+                files: ['sass/**/*.scss'],
+                tasks: ['sass','postcss']
+            }
+        },
+        // Sass compilation. Produce an extended css file in css folder
+        sass : {
+            options: {
+                sourcemap:'none',
+                style: 'expanded'
+            },
+            dist : {
+                files: {
+                    'dist/libs/main.css': 'sass/main.scss'
+                }
+            }
+        },
+        // Auto prefixer css
+        postcss : {
+            dist: {
+                options: {
+                    processors: [
+                        require('autoprefixer')({browsers: 'last 2 versions'}),
+                        require('cssnano')()
+                    ]
+                },
+                src: 'dist/libs/main.css'
+            }
+        },
+        // Build dist version
+        uglify : {
+            dist: {
+                options: {
+                    compress:false,
+                    beautify: true
+                },
+                files: {
+                    'dist/libs/inspector.js': [
+                        'dist/inspector/gui/BasicElement.js',
+                        'dist/inspector/gui/CubeTextureElement.js',
+                        'dist/inspector/adapters/Adapter.js',
+                        'dist/inspector/tabs/Tab.js',
+                        'dist/inspector/tabs/PropertyTab.js',
+                        'dist/inspector/tools/AbstractTool.js',
+                        'dist/inspector/treeTools/AbstractTreeTool.js',
+                        'dist/inspector/**/*.js']
+                }
+            }
+        },
+        //Server creation
+        connect: {
+            server: {
+                options: {
+                    port: 3000,
+                    base: '.'
+                }
+            },
+            test: {
+                options: {
+                    port: 3000,
+                    base: '.',
+                    keepalive:true
+                }
+            }
+        },
+        // Open default browser
+        open: {
+            local: {
+                path: 'http://localhost:3000/dist/test'
+            }
+        },
+        webpack: {
+            inspector: require("./webpack.config.js")
+        }
+    }); 
+
+    grunt.registerTask('default', 'Compile and watch source files', [
+        'dev',
+        'connect:server',
+        'open',
+        'watch'
+    ]);
+
+    grunt.registerTask('dev', 'build dev version', [
+        'clean:init',
+        'ts',
+        'sass',
+        'postcss', 
+    ]);
+
+    grunt.registerTask('test', 'test dist version', [
+        'open',
+        'connect:test'
+    ]);
+
+    // Compilation and webpack
+    grunt.registerTask('dist', 'build dist version', [
+        'dev',
+        'uglify',
+        'concat',
+        'webpack',
+        'clean:compilation'
+    ]);
+
+};
+
+

+ 78 - 0
inspector/README.md

@@ -0,0 +1,78 @@
+# Babylon.js Inspector
+
+An extension to easily debug your Babylon.js application, made with HTML/CSS.
+This extension replaces the old limited debug layer.
+
+## Usage
+### Online method
+Call the method `show` of the scene debugLayer: 
+```
+scene.debugLayer.show();
+```
+This method will retrieve dynamically the library `inspector.js`, download it and add
+it to the html page.
+
+### Offline method
+If you don't have access to internet, the inspector should be imported manually in your HTML page :
+```
+<script src="inspector.js" />
+``` 
+Then, call the method `show` of the scene debugLayer: 
+```
+scene.debugLayer.show();
+```
+
+A right panel will be created containing the Babylon.js inspector.
+
+## Features
+
+The inspector is composed of a set of tabs (Scene, Shader, Mesh...), each one dedicated to a part of 
+the engine. Several tools are also available for specific actions.
+
+##Tabs 
+
+![](https://raw.githubusercontent.com/Temechon/Babylon.js/master/inspector/screens/tab_mesh.jpg)
+
+Tabs are separated in two parts (although some are very specific - `Shader` for example): 
+* a tree panel, presenting a list of object relative to the current tab (meshes, materials...)
+* a properties panel, presenting the list of properties of the selected object
+
+The properties panel is refreshed *almost* in real time (actually each 250ms), 
+and can also be updated manually by clicking on a value and pressing `enter`.
+
+For some tabs, a search bar is also available to look for a specific object.
+
+There are currently 5 tabs: 
+* `Scene`: displays several properties of the class `BABYLON.Scene`, and provides checkbox to 
+manipulate some options (like the old debug layer)
+* `Mesh` : displays the list of all meshes in the current scene. 
+* `Light`: displays the list of all lights in the current scene. 
+* `Shader` : displays the list of custom shaders of the scene. For each shader, the vertex shader and fragment shader is displayed.
+* `Canvas2D` : displays the list of all instances of Canvas2D for the current scene. The parent-children hierarchy is represented in the tree.
+* `Material` : displays the list of all material in the current scene (keep in mind that every property is updated every 250ms.)
+
+Some properties (like colors, texture...) can display 
+### Tools
+![](https://raw.githubusercontent.com/Temechon/Babylon.js/master/inspector/screens/tools.jpg)
+
+Several tools are available (from left to right) : 
+* Refresh - the current tab is refreshed with the last version of the list of objects
+* Pointer - select a mesh in the scene and display its properties in the `Mesh` tab.
+* Popup - Undock the inspector into a separate window.
+* Pause - Pause the automatic update of properties, allowing to manually update one.
+
+## Contribute
+
+```
+npm install
+grunt
+```
+
+## Create the inspector from source
+
+```
+grunt dist
+```
+The library will be in the `dist` folder.
+
+

+ 0 - 0
inspector/dist/.baseDir.d.ts


+ 2 - 0
inspector/dist/.baseDir.js

@@ -0,0 +1,2 @@
+// Ignore this file. See https://github.com/grunt-ts/grunt-ts/issues/77 
+//# sourceMappingURL=.baseDir.js.map

+ 906 - 0
inspector/dist/inspector.d.ts

@@ -0,0 +1,906 @@
+declare module INSPECTOR {
+    interface IHighlight {
+        highlight: (b: boolean) => void;
+    }
+    abstract class Adapter implements IHighlight {
+        protected _obj: any;
+        private static _name;
+        constructor(obj: any);
+        /** Returns the name displayed in the tree */
+        abstract id(): string;
+        /** Returns the type of this object - displayed in the tree */
+        abstract type(): string;
+        /** Returns the list of properties to be displayed for this adapter */
+        abstract getProperties(): Array<PropertyLine>;
+        /** Returns the actual object behind this adapter */
+        actualObject: any;
+        /** Returns true if the given object correspond to this  */
+        correspondsTo(obj: any): boolean;
+        /** Returns the adapter unique name */
+        name: string;
+        /** Returns the list of tools available for this adapter */
+        abstract getTools(): Array<AbstractTreeTool>;
+        /** Should be overriden in subclasses */
+        highlight(b: boolean): void;
+    }
+}
+
+declare module INSPECTOR {
+    class Canvas2DAdapter extends Adapter implements IToolVisible, IToolDebug {
+        constructor(obj: any);
+        /** Returns the name displayed in the tree */
+        id(): string;
+        /** Returns the type of this object - displayed in the tree */
+        type(): string;
+        /** Returns the list of properties to be displayed for this adapter */
+        getProperties(): Array<PropertyLine>;
+        getTools(): Array<AbstractTreeTool>;
+        setVisible(b: boolean): void;
+        isVisible(): boolean;
+        /** Overrides super */
+        debug(b: boolean): void;
+        /** Overrides super.highlight */
+        highlight(b: boolean): void;
+    }
+}
+
+declare module INSPECTOR {
+    class LightAdapter extends Adapter implements IToolVisible {
+        private static _PROPERTIES;
+        constructor(obj: BABYLON.Light);
+        /** Returns the name displayed in the tree */
+        id(): string;
+        /** Returns the type of this object - displayed in the tree */
+        type(): string;
+        /** Returns the list of properties to be displayed for this adapter */
+        getProperties(): Array<PropertyLine>;
+        getTools(): Array<AbstractTreeTool>;
+        setVisible(b: boolean): void;
+        isVisible(): boolean;
+        /** Returns some information about this mesh */
+        /** Overrides super.highlight */
+        highlight(b: boolean): void;
+    }
+}
+
+declare module INSPECTOR {
+    class MaterialAdapter extends Adapter {
+        constructor(obj: BABYLON.Material);
+        /** Returns the name displayed in the tree */
+        id(): string;
+        /** Returns the type of this object - displayed in the tree */
+        type(): string;
+        /** Returns the list of properties to be displayed for this adapter */
+        getProperties(): Array<PropertyLine>;
+        /** No tools for a material adapter */
+        getTools(): Array<AbstractTreeTool>;
+        /** Overrides super.highlight.
+         * Highlighting a material outlines all meshes linked to this material
+         */
+        highlight(b: boolean): void;
+    }
+}
+
+declare module INSPECTOR {
+    class MeshAdapter extends Adapter implements IToolVisible, IToolDebug, IToolBoundingBox, IToolInfo {
+        /** Keep track of the axis of the actual object */
+        private _axis;
+        constructor(obj: BABYLON.AbstractMesh);
+        /** Returns the name displayed in the tree */
+        id(): string;
+        /** Returns the type of this object - displayed in the tree */
+        type(): string;
+        /** Returns the list of properties to be displayed for this adapter */
+        getProperties(): Array<PropertyLine>;
+        getTools(): Array<AbstractTreeTool>;
+        setVisible(b: boolean): void;
+        isVisible(): boolean;
+        isBoxVisible(): boolean;
+        setBoxVisible(b: boolean): boolean;
+        debug(b: boolean): void;
+        /** Returns some information about this mesh */
+        getInfo(): string;
+        /** Overrides super.highlight */
+        highlight(b: boolean): void;
+        /** Draw X, Y and Z axis for the actual object if this adapter.
+         * Should be called only one time as it will fill this._axis
+         */
+        private _drawAxis();
+    }
+}
+
+declare module INSPECTOR {
+    interface SortDirection {
+        [property: string]: number;
+    }
+    class DetailPanel extends BasicElement {
+        private _headerRow;
+        private _detailRows;
+        private _sortDirection;
+        constructor(dr?: Array<PropertyLine>);
+        details: Array<PropertyLine>;
+        protected _build(): void;
+        /** Updates the HTML of the detail panel */
+        update(): void;
+        /** Add all lines in the html div. Does not sort them! */
+        private _addDetails();
+        /**
+         * Sort the details row by comparing the given property of each row
+         */
+        private _sortDetails(property, _direction?);
+        /**
+         * Removes all data in the detail panel but keep the header row
+         */
+        clean(): void;
+        /** Overrides basicelement.dispose */
+        dispose(): void;
+        /**
+         * Creates the header row : name, value, id
+         */
+        private _createHeaderRow();
+    }
+}
+
+declare module INSPECTOR {
+    /**
+     * A property is a link between a data (string) and an object.
+     */
+    class Property {
+        /** The property name */
+        private _property;
+        /** The obj this property refers to */
+        private _obj;
+        constructor(prop: string, obj: any);
+        name: string;
+        value: any;
+        type: string;
+        obj: any;
+    }
+}
+
+declare module INSPECTOR {
+    class PropertyFormatter {
+        /**
+         * Format the value of the given property of the given object.
+         */
+        static format(obj: any, prop: string): string;
+    }
+    /**
+     * A property line represents a line in the detail panel. This line is composed of :
+     * - a name (the property name)
+     * - a value if this property is of a type 'simple' : string, number, boolean, color, texture
+     * - the type of the value if this property is of a complex type (Vector2, Size, ...)
+     * - a ID if defined (otherwise an empty string is displayed)
+     * The original object is sent to the value object who will update it at will.
+     *
+     * A property line can contain OTHER property line objects in the case of a complex type.
+     * If this instance has no link to other instances, its type is ALWAYS a simple one (see above).
+     *
+     */
+    class PropertyLine {
+        private _property;
+        private _div;
+        private _valueDiv;
+        private _children;
+        private static _SIMPLE_TYPE;
+        private static _MARGIN_LEFT;
+        private _level;
+        /** The list of viewer element displayed at the end of the line (color, texture...) */
+        private _elements;
+        /** The property parent of this one. Used to update the value of this property and to retrieve the correct object */
+        private _parent;
+        /** The input element to display if this property is 'simple' in order to update it */
+        private _input;
+        /** Display input handler (stored to be removed afterwards) */
+        private _displayInputHandler;
+        /** Handler used to validate the input by pressing 'enter' */
+        private _validateInputHandler;
+        constructor(prop: Property, parent?: PropertyLine, level?: number);
+        /**
+         * Init the input element and al its handler :
+         * - a click in the window remove the input and restore the old property value
+         * - enters updates the property
+         */
+        private _initInput();
+        /**
+         * On enter : validates the new value and removes the input
+         * On escape : removes the input
+         */
+        private _validateInput(e);
+        /** Removes the input without validating the new value */
+        private _removeInputWithoutValidating();
+        /** Replaces the default display with an input */
+        private _displayInput(e);
+        /** Retrieve the correct object from its parent.
+         * If no parent exists, returns the property value.
+         * This method is used at each update in case the property object is removed from the original object
+         * (example : mesh.position = new BABYLON.Vector3 ; the original vector3 object is deleted from the mesh).
+        */
+        updateObject(): any;
+        name: string;
+        value: any;
+        type: string;
+        /**
+         * Creates elements that wil be displayed on a property line, depending on the
+         * type of the property.
+         */
+        private _createElements();
+        private _displayValueContent();
+        /** Delete properly this property line.
+         * Removes itself from the scheduler.
+         * Dispose all viewer element (color, texture...)
+         */
+        dispose(): void;
+        /** Updates the content of _valueDiv with the value of the property,
+         * and all HTML element correpsonding to this type.
+         * Elements are updated as well
+         */
+        private _updateValue();
+        /**
+         * Update the property division with the new property value.
+         * If this property is complex, update its child, otherwise update its text content
+         */
+        update(): void;
+        /**
+         * Returns true if the given instance is a simple type
+         */
+        private static _IS_TYPE_SIMPLE(inst);
+        /**
+         * Returns true if the type of this property is simple, false otherwise.
+         * Returns true if the value is null
+         */
+        private _isSimple();
+        toHtml(): HTMLElement;
+        /**
+         * Add sub properties in case of a complex type
+         */
+        private _addDetails();
+    }
+}
+
+declare module INSPECTOR {
+    /**
+     * Represents a html div element.
+     * The div is built when an instance of BasicElement is created.
+     */
+    abstract class BasicElement {
+        protected _div: HTMLElement;
+        constructor();
+        /**
+         * Returns the div element
+         */
+        toHtml(): HTMLElement;
+        /**
+         * Build the html element
+         */
+        protected _build(): void;
+        abstract update(data?: any): any;
+        /** Default dispose method if needed */
+        dispose(): void;
+    }
+}
+
+declare module INSPECTOR {
+    /**
+    * Display a very small div corresponding to the given color
+    */
+    class ColorElement extends BasicElement {
+        constructor(color: BABYLON.Color4 | BABYLON.Color3);
+        update(color?: BABYLON.Color4 | BABYLON.Color3): void;
+        private _toRgba(color);
+    }
+}
+
+declare module INSPECTOR {
+    /**
+    * Display a very small div. A new canvas is created, with a new Babylon.js scene, containing only the
+    * cube texture in a cube
+    */
+    class CubeTextureElement extends BasicElement {
+        /** The big div displaying the full image */
+        private _textureDiv;
+        private _engine;
+        protected _scene: BABYLON.Scene;
+        protected _cube: BABYLON.Mesh;
+        private _canvas;
+        protected _textureUrl: string;
+        private _pause;
+        /** The texture given as a parameter should be cube. */
+        constructor(tex: BABYLON.Texture);
+        update(tex?: BABYLON.Texture): void;
+        /** Creates the box  */
+        protected _populateScene(): void;
+        /** Init the babylon engine */
+        private _initEngine();
+        private _showViewer(mode);
+        /** Removes properly the babylon engine */
+        dispose(): void;
+    }
+}
+
+declare module INSPECTOR {
+    /**
+    * Display a very small div. A new canvas is created, with a new Babylon.js scene, containing only the
+    * cube texture in a cube
+    */
+    class HDRCubeTextureElement extends CubeTextureElement {
+        /** The texture given as a parameter should be cube. */
+        constructor(tex: BABYLON.Texture);
+        /** Creates the box  */
+        protected _populateScene(): void;
+    }
+}
+
+declare module INSPECTOR {
+    /**
+     * A search bar can be used to filter elements in the tree panel.
+     * At each keypress on the input, the treepanel will be filtered.
+     */
+    class SearchBar extends BasicElement {
+        private _tab;
+        private _inputElement;
+        constructor(tab: PropertyTab);
+        /** Delete all characters typped in the input element */
+        reset(): void;
+        update(): void;
+    }
+}
+
+declare module INSPECTOR {
+    /**
+    * Display a very small div corresponding to the given texture. On mouse over, display the full image
+    */
+    class TextureElement extends BasicElement {
+        /** The big div displaying the full image */
+        private _textureDiv;
+        constructor(tex: BABYLON.Texture);
+        update(tex?: BABYLON.Texture): void;
+        private _showViewer(mode);
+    }
+}
+
+declare module INSPECTOR {
+    /**
+     * Creates a tooltip for the given html element
+     */
+    class Tooltip {
+        /** The tooltip is displayed for this element */
+        private _elem;
+        /** The tooltip div */
+        private _infoDiv;
+        constructor(elem: HTMLElement, tip: string);
+    }
+}
+
+declare module INSPECTOR {
+    class Helpers {
+        /**
+         * Returns the type of the given object. First
+         * uses getClassName. If nothing is returned, used the type of the constructor
+         */
+        static GET_TYPE(obj: any): string;
+        /**
+         * Returns the name of a function (workaround to get object type for IE11)
+         */
+        private static _GetFnName(fn);
+        /** Send the event which name is given in parameter to the window */
+        static SEND_EVENT(eventName: string): void;
+        /** Returns the given number with 2 decimal number max if a decimal part exists */
+        static Trunc(nb: any): number;
+        /**
+         * Useful function used to create a div
+         */
+        static CreateDiv(className?: string, parent?: HTMLElement): HTMLElement;
+        static CreateElement(element: string, className?: string, parent?: HTMLElement): HTMLElement;
+        /**
+         * Removes all children of the given div.
+         */
+        static CleanDiv(div: HTMLElement): void;
+        static LoadScript(): void;
+    }
+}
+
+declare module INSPECTOR {
+    class Inspector {
+        private _c2diwrapper;
+        /** The panel displayed at the top of the inspector */
+        private _topPanel;
+        /** The div containing the content of the active tab */
+        private _tabPanel;
+        /** The panel containing the list if items */
+        /** The list if tree items displayed in the tree panel. */
+        private _items;
+        private _tabbar;
+        private _scene;
+        /** The HTML document relative to this inspector (the window or the popup depending on its mode) */
+        static DOCUMENT: HTMLDocument;
+        /** True if the inspector is built as a popup tab */
+        private _popupMode;
+        /** The original canvas size, before applying the inspector*/
+        private _canvasSize;
+        /** The inspector is created with the given engine.
+         * If a HTML parent is not given as a parameter, the inspector is created as a right panel on the main window.
+         * If a HTML parent is given, the inspector is created in this element, taking full size of its parent.
+         */
+        constructor(scene: BABYLON.Scene, parent?: HTMLElement);
+        /** Build the inspector panel in the given HTML element */
+        private _buildInspector(parent);
+        scene: BABYLON.Scene;
+        popupMode: boolean;
+        /**
+         * Filter the list of item present in the tree.
+         * All item returned should have the given filter contained in the item id.
+        */
+        filterItem(filter: string): void;
+        /** Display the mesh tab on the given object */
+        displayObjectDetails(mesh: BABYLON.AbstractMesh): void;
+        /** Clean the whole tree of item and rebuilds it */
+        refresh(): void;
+        /** Remove the inspector panel when it's built as a right panel:
+         * remove the right panel and remove the wrapper
+         */
+        private _disposeInspector();
+        /** Open the inspector in a new popup */
+        openPopup(): void;
+    }
+}
+
+declare module INSPECTOR {
+    const PROPERTIES: {
+        format: (obj: any) => any;
+        'Vector2': {
+            properties: string[];
+            format: (vec: BABYLON.Vector2) => string;
+        };
+        'Vector3': {
+            properties: string[];
+            format: (vec: BABYLON.Vector3) => string;
+        };
+        'Color3': {
+            properties: string[];
+            format: (color: BABYLON.Color3) => string;
+        };
+        'Quaternion': {
+            properties: string[];
+        };
+        'Size': {
+            properties: string[];
+            format: (size: BABYLON.Size) => string;
+        };
+        'Texture': {
+            properties: string[];
+        };
+        'ArcRotateCamera': {
+            properties: string[];
+        };
+        'Scene': {
+            properties: string[];
+        };
+        'Mesh': {
+            properties: string[];
+            format: (m: BABYLON.Mesh) => string;
+        };
+        'StandardMaterial': {
+            properties: string[];
+            format: (mat: BABYLON.StandardMaterial) => string;
+        };
+        'PrimitiveAlignment': {
+            properties: string[];
+        };
+        'PrimitiveThickness': {
+            properties: string[];
+        };
+        'BoundingInfo2D': {
+            properties: string[];
+        };
+        'SolidColorBrush2D': {
+            properties: string[];
+        };
+        'GradientColorBrush2D': {
+            properties: string[];
+        };
+        'PBRMaterial': {
+            properties: string[];
+        };
+    };
+}
+
+declare module INSPECTOR {
+    class Scheduler {
+        private static _instance;
+        /** The number of the set interval */
+        private _timer;
+        /** Is this scheduler in pause ? */
+        pause: boolean;
+        /** All properties are refreshed every 250ms */
+        static REFRESH_TIME: number;
+        /** The list of data to update */
+        private _updatableProperties;
+        constructor();
+        static getInstance(): Scheduler;
+        /** Add a property line to be updated every X ms */
+        add(prop: PropertyLine): void;
+        /** Removes the given property from the list of properties to update */
+        remove(prop: PropertyLine): void;
+        private _update();
+    }
+}
+
+declare module INSPECTOR {
+    class Canvas2DTab extends PropertyTab {
+        constructor(tabbar: TabBar, inspector: Inspector);
+        protected _getTree(): Array<TreeItem>;
+    }
+}
+
+declare module INSPECTOR {
+    class LightTab extends PropertyTab {
+        constructor(tabbar: TabBar, inspector: Inspector);
+        protected _getTree(): Array<TreeItem>;
+    }
+}
+
+declare module INSPECTOR {
+    class MaterialTab extends PropertyTab {
+        constructor(tabbar: TabBar, inspector: Inspector);
+        protected _getTree(): Array<TreeItem>;
+    }
+}
+
+declare module INSPECTOR {
+    class MeshTab extends PropertyTab {
+        constructor(tabbar: TabBar, inspector: Inspector);
+        protected _getTree(): Array<TreeItem>;
+    }
+}
+
+declare module INSPECTOR {
+    /**
+     * A Property tab can creates two panels:
+     * a tree panel and a detail panel,
+     * in which properties will be displayed.
+     * Both panels are separated by a resize bar
+     */
+    abstract class PropertyTab extends Tab {
+        protected _inspector: Inspector;
+        /** The panel containing a list of items */
+        protected _treePanel: HTMLElement;
+        /** The panel containing a list if properties corresponding to an item */
+        protected _detailsPanel: DetailPanel;
+        protected _treeItems: Array<TreeItem>;
+        protected _searchBar: SearchBar;
+        constructor(tabbar: TabBar, name: string, insp: Inspector);
+        /** Overrides dispose */
+        dispose(): void;
+        update(_items?: Array<TreeItem>): void;
+        /** Display the details of the given item */
+        displayDetails(item: TreeItem): void;
+        /** Select an item in the tree */
+        select(item: TreeItem): void;
+        /** Highlight the given node, and downplay all others */
+        highlightNode(item?: TreeItem): void;
+        /** Set the given item as active in the tree */
+        activateNode(item: TreeItem): void;
+        /** Returns the treeitem corersponding to the given obj, null if not found */
+        getItemFor(_obj: any): TreeItem;
+        filter(filter: string): void;
+        /** Builds the tree panel */
+        protected abstract _getTree(): Array<TreeItem>;
+    }
+}
+
+declare module INSPECTOR {
+    class SceneTab extends Tab {
+        private _inspector;
+        /** The list of  channels/options that can be activated/deactivated */
+        private _actions;
+        /** The list of skeleton viewer */
+        private _skeletonViewers;
+        /** The detail of the scene */
+        private _detailsPanel;
+        constructor(tabbar: TabBar, insp: Inspector);
+        /** Overrides super.dispose */
+        dispose(): void;
+        /** generates a div which correspond to an option that can be activated/deactivated */
+        private _generateActionLine(name, initValue, action);
+        /**
+         * Add a click action for all given elements :
+         * the clicked element is set as active, all others elements are deactivated
+         */
+        private _generateRadioAction(arr);
+    }
+}
+
+declare module INSPECTOR {
+    class ShaderTab extends Tab {
+        private _inspector;
+        private _vertexPanel;
+        private _fragmentPanel;
+        constructor(tabbar: TabBar, insp: Inspector);
+        private _selectShader(event);
+        /** Overrides super.dispose */
+        dispose(): void;
+        /** Returns the position of the first { and the corresponding } */
+        private _getBracket(str);
+        /**
+         * Beautify the given string : correct indentation
+         */
+        private _beautify(glsl, level?);
+    }
+}
+
+declare module INSPECTOR {
+    abstract class Tab extends BasicElement {
+        protected _tabbar: TabBar;
+        name: string;
+        protected _isActive: boolean;
+        protected _panel: HTMLDivElement;
+        constructor(tabbar: TabBar, name: string);
+        /** True if the tab is active, false otherwise */
+        isActive(): boolean;
+        protected _build(): void;
+        /** Set this tab as active or not, depending on the current state */
+        active(b: boolean): void;
+        update(): void;
+        /** Creates the tab panel for this tab. */
+        getPanel(): HTMLElement;
+        /** Add this in the propertytab with the searchbar */
+        filter(str: string): void;
+        /** Dispose properly this tab */
+        abstract dispose(): any;
+        /**
+         * Returns the total width in pixel of this tab, 0 by default
+        */
+        getPixelWidth(): number;
+    }
+}
+
+declare module INSPECTOR {
+    /**
+     * A tab bar will contains each view the inspector can have : Canvas2D, Meshes...
+     * The default active tab is the first one of the list.
+     */
+    class TabBar extends BasicElement {
+        private _tabs;
+        private _inspector;
+        /** The tab displaying all meshes */
+        private _meshTab;
+        /** The toolbar */
+        private _toolBar;
+        /** The icon displayed at the end of the toolbar displaying a combo box of tabs not displayed */
+        private _moreTabsIcon;
+        /** The panel displayed when the 'more-tab' icon is selected */
+        private _moreTabsPanel;
+        /** The list of tab displayed by clicking on the remainingIcon */
+        private _invisibleTabs;
+        /** The list of tabs visible, displayed in the tab bar */
+        private _visibleTabs;
+        constructor(inspector: Inspector);
+        update(): void;
+        protected _build(): void;
+        /**
+         * Add a tab to the 'more-tabs' panel, displayed by clicking on the
+         * 'more-tabs' icon
+         */
+        private _addInvisibleTabToPanel(tab);
+        /** Dispose the current tab, set the given tab as active, and refresh the treeview */
+        switchTab(tab: Tab): void;
+        /** Display the mesh tab.
+         * If a parameter is given, the given mesh details are displayed
+         */
+        switchMeshTab(mesh?: BABYLON.AbstractMesh): void;
+        /** Returns the active tab */
+        getActiveTab(): Tab;
+        inspector: Inspector;
+        /**
+         * Returns the total width in pixel of the tabbar,
+         * that corresponds to the sum of the width of each visible tab + toolbar width
+        */
+        getPixelWidth(): number;
+        /** Display the remaining icon or not depending on the tabbar width.
+         * This function should be called each time the inspector width is updated
+         */
+        updateWidth(): void;
+    }
+}
+
+declare module INSPECTOR {
+    abstract class AbstractTool {
+        private _elem;
+        protected _inspector: Inspector;
+        constructor(icon: string, parent: HTMLElement, inspector: Inspector, tooltip: string);
+        toHtml(): HTMLElement;
+        /**
+         * Returns the total width in pixel of this tool, 0 by default
+        */
+        getPixelWidth(): number;
+        /**
+         * Updates the icon of this tool with the given string
+         */
+        protected _updateIcon(icon: string): void;
+        abstract action(): any;
+    }
+}
+
+declare module INSPECTOR {
+    class PauseScheduleTool extends AbstractTool {
+        private _isPause;
+        constructor(parent: HTMLElement, inspector: Inspector);
+        action(): void;
+    }
+}
+
+declare module INSPECTOR {
+    class PickTool extends AbstractTool {
+        private _isActive;
+        private _pickHandler;
+        constructor(parent: HTMLElement, inspector: Inspector);
+        action(): void;
+        /** Deactivate this tool */
+        private _deactivate();
+        /** Pick a mesh in the scene */
+        private _pickMesh(evt);
+        private _updatePointerPosition(evt);
+    }
+}
+
+declare module INSPECTOR {
+    class PopupTool extends AbstractTool {
+        constructor(parent: HTMLElement, inspector: Inspector);
+        action(): void;
+    }
+}
+
+declare module INSPECTOR {
+    class RefreshTool extends AbstractTool {
+        constructor(parent: HTMLElement, inspector: Inspector);
+        action(): void;
+    }
+}
+
+declare module INSPECTOR {
+    class Toolbar extends BasicElement {
+        private _inspector;
+        private _tools;
+        constructor(inspector: Inspector);
+        update(): void;
+        protected _build(): void;
+        private _addTools();
+        /**
+         * Returns the total width in pixel of the tabbar,
+         * that corresponds to the sum of the width of each tab + toolbar width
+        */
+        getPixelWidth(): number;
+    }
+}
+
+declare module INSPECTOR {
+    class TreeItem extends BasicElement {
+        private _tab;
+        private _adapter;
+        private _tools;
+        private _children;
+        private _lineContent;
+        constructor(tab: PropertyTab, obj: Adapter);
+        /** Returns the item ID == its adapter ID */
+        id: string;
+        /** Add the given item as a child of this one */
+        add(child: TreeItem): void;
+        /**
+         * Function used to compare this item to another tree item.
+         * Returns the alphabetical sort of the adapter ID
+         */
+        compareTo(item: TreeItem): number;
+        /** Returns true if the given obj correspond to the adapter linked to this tree item */
+        correspondsTo(obj: any): boolean;
+        /** hide all children of this item */
+        fold(): void;
+        /** Show all children of this item */
+        unfold(): void;
+        /** Build the HTML of this item */
+        protected _build(): void;
+        /**
+         * Returns one HTML element (.details) containing all  details of this primitive
+         */
+        getDetails(): Array<PropertyLine>;
+        update(): void;
+        /**
+         * Add an event listener on the item :
+         * - one click display details
+         * - on mouse hover the item is highlighted
+         */
+        protected _addEvent(): void;
+        /** Highlight or downplay this node */
+        highlight(b: boolean): void;
+        /** Returns true if the node is folded, false otherwise */
+        private _isFolded();
+        /** Set this item as active (background lighter) in the tree panel */
+        active(b: boolean): void;
+    }
+}
+
+declare module INSPECTOR {
+    abstract class AbstractTreeTool {
+        protected _elem: HTMLElement;
+        /** Is the tool enabled ? */
+        protected _on: boolean;
+        constructor();
+        toHtml(): HTMLElement;
+        protected _addEvents(): void;
+        /**
+         * Action launched when clicked on this element
+         * Should be overrided
+         */
+        protected action(): void;
+    }
+}
+
+declare module INSPECTOR {
+    /** Any object implementing this interface should
+     * provide methods to toggle its bounding box
+     */
+    interface IToolBoundingBox {
+        isBoxVisible: () => boolean;
+        setBoxVisible: (b: boolean) => void;
+    }
+    /**
+     * Checkbox to display/hide the primitive
+     */
+    class BoundingBox extends AbstractTreeTool {
+        private _obj;
+        constructor(obj: IToolBoundingBox);
+        protected action(): void;
+        private _check();
+    }
+}
+
+declare module INSPECTOR {
+    /** Any object implementing this interface should
+     * provide methods to toggle its visibility
+     */
+    interface IToolVisible {
+        isVisible: () => boolean;
+        setVisible: (b: boolean) => void;
+    }
+    /**
+     * Checkbox to display/hide the primitive
+     */
+    class Checkbox extends AbstractTreeTool {
+        private _obj;
+        constructor(obj: IToolVisible);
+        protected action(): void;
+        private _check();
+    }
+}
+
+declare module INSPECTOR {
+    /** Any object implementing this interface should
+     * provide methods to toggle a debug area
+     */
+    interface IToolDebug {
+        debug: (b: boolean) => void;
+    }
+    class DebugArea extends AbstractTreeTool {
+        private _obj;
+        constructor(obj: IToolDebug);
+        protected action(): void;
+    }
+}
+
+declare module INSPECTOR {
+    /** Any object implementing this interface should
+     * provide methods to retrieve its info
+     */
+    interface IToolInfo {
+        getInfo: () => string;
+    }
+    /**
+     * Checkbox to display/hide the primitive
+     */
+    class Info extends AbstractTreeTool {
+        private _obj;
+        private _tooltip;
+        constructor(obj: IToolInfo);
+        protected action(): void;
+    }
+}

File diff suppressed because it is too large
+ 3644 - 0
inspector/dist/inspector.js


File diff suppressed because it is too large
+ 15033 - 0
inspector/dist/libs/babylon.canvas2d.max.js


File diff suppressed because it is too large
+ 54626 - 0
inspector/dist/libs/babylon.max.js


+ 560 - 0
inspector/dist/libs/split.js

@@ -0,0 +1,560 @@
+// The programming goals of Split.js are to deliver readable, understandable and
+// maintainable code, while at the same time manually optimizing for tiny minified file size,
+// browser compatibility without additional requirements, graceful fallback (IE8 is supported)
+// and very few assumptions about the user's page layout.
+//
+// Make sure all browsers handle this JS library correctly with ES5.
+// More information here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
+'use strict';
+
+// A wrapper function that does a couple things:
+//
+// 1. Doesn't pollute the global namespace. This is important for a library.
+// 2. Allows us to mount the library in different module systems, as well as
+//    directly in the browser.
+(function() {
+
+// Save the global `this` for use later. In this case, since the library only
+// runs in the browser, it will refer to `window`. Also, figure out if we're in IE8
+// or not. IE8 will still render correctly, but will be static instead of draggable.
+//
+// Save a couple long function names that are used frequently.
+// This optimization saves around 400 bytes.
+var global = this
+  , isIE8 = global.attachEvent && !global[addEventListener]
+  , document = global.document
+  , addEventListener = 'addEventListener'
+  , removeEventListener = 'removeEventListener'
+  , getBoundingClientRect = 'getBoundingClientRect'
+
+  // This library only needs two helper functions:
+  //
+  // The first determines which prefixes of CSS calc we need.
+  // We only need to do this once on startup, when this anonymous function is called.
+  // 
+  // Tests -webkit, -moz and -o prefixes. Modified from StackOverflow:
+  // http://stackoverflow.com/questions/16625140/js-feature-detection-to-detect-the-usage-of-webkit-calc-over-calc/16625167#16625167
+  , calc = (function () {
+        var el
+          , prefixes = ["", "-webkit-", "-moz-", "-o-"]
+
+        for (var i = 0; i < prefixes.length; i++) {
+            el = document.createElement('div')
+            el.style.cssText = "width:" + prefixes[i] + "calc(9px)"
+
+            if (el.style.length) {
+                return prefixes[i] + "calc"
+            }
+        }
+    })()
+
+  // The second helper function allows elements and string selectors to be used
+  // interchangeably. In either case an element is returned. This allows us to
+  // do `Split(elem1, elem2)` as well as `Split('#id1', '#id2')`.
+  , elementOrSelector = function (el) {
+        if (typeof el === 'string' || el instanceof String) {
+            return document.querySelector(el)
+        } else {
+            return el
+        }
+    }
+
+  // The main function to initialize a split. Split.js thinks about each pair
+  // of elements as an independant pair. Dragging the gutter between two elements
+  // only changes the dimensions of elements in that pair. This is key to understanding
+  // how the following functions operate, since each function is bound to a pair.
+  // 
+  // A pair object is shaped like this:
+  // 
+  // {
+  //     a: DOM element,
+  //     b: DOM element,
+  //     aMin: Number,
+  //     bMin: Number,
+  //     dragging: Boolean,
+  //     parent: DOM element,
+  //     isFirst: Boolean,
+  //     isLast: Boolean,
+  //     direction: 'horizontal' | 'vertical'
+  // }
+  //
+  // The basic sequence:
+  // 
+  // 1. Set defaults to something sane. `options` doesn't have to be passed at all.
+  // 2. Initialize a bunch of strings based on the direction we're splitting.
+  //    A lot of the behavior in the rest of the library is paramatized down to
+  //    rely on CSS strings and classes.
+  // 3. Define the dragging helper functions, and a few helpers to go with them.
+  // 4. Define a few more functions that "balance" the entire split instance.
+  //    Split.js tries it's best to cope with min sizes that don't add up.
+  // 5. Loop through the elements while pairing them off. Every pair gets an
+  //    `pair` object, a gutter, and special isFirst/isLast properties.
+  // 6. Actually size the pair elements, insert gutters and attach event listeners.
+  // 7. Balance all of the pairs to accomodate min sizes as best as possible.
+  , Split = function (ids, options) {
+    var dimension
+      , i
+      , clientDimension
+      , clientAxis
+      , position
+      , gutterClass
+      , paddingA
+      , paddingB
+      , pairs = []
+
+    // 1. Set defaults to something sane. `options` doesn't have to be passed at all,
+    // so create an options object if none exists. Pixel values 10, 100 and 30 are
+    // arbitrary but feel natural.
+    options = typeof options !== 'undefined' ?  options : {}
+
+    if (typeof options.gutterSize === 'undefined') options.gutterSize = 10
+    if (typeof options.minSize === 'undefined') options.minSize = 100
+    if (typeof options.snapOffset === 'undefined') options.snapOffset = 30
+    if (typeof options.direction === 'undefined') options.direction = 'horizontal'
+
+    // 2. Initialize a bunch of strings based on the direction we're splitting.
+    // A lot of the behavior in the rest of the library is paramatized down to
+    // rely on CSS strings and classes.
+    if (options.direction == 'horizontal') {
+        dimension = 'width'
+        clientDimension = 'clientWidth'
+        clientAxis = 'clientX'
+        position = 'left'
+        gutterClass = 'gutter gutter-horizontal'
+        paddingA = 'paddingLeft'
+        paddingB = 'paddingRight'
+        if (!options.cursor) options.cursor = 'ew-resize'
+    } else if (options.direction == 'vertical') {
+        dimension = 'height'
+        clientDimension = 'clientHeight'
+        clientAxis = 'clientY'
+        position = 'top'
+        gutterClass = 'gutter gutter-vertical'
+        paddingA = 'paddingTop'
+        paddingB = 'paddingBottom'
+        if (!options.cursor) options.cursor = 'ns-resize'
+    }
+
+    // 3. Define the dragging helper functions, and a few helpers to go with them.
+    // Each helper is bound to a pair object that contains it's metadata. This
+    // also makes it easy to store references to listeners that that will be
+    // added and removed.
+    // 
+    // Even though there are no other functions contained in them, aliasing
+    // this to self saves 50 bytes or so since it's used so frequently.
+    //
+    // The pair object saves metadata like dragging state, position and
+    // event listener references.
+    //
+    // startDragging calls `calculateSizes` to store the inital size in the pair object.
+    // It also adds event listeners for mouse/touch events,
+    // and prevents selection while dragging so avoid the selecting text.
+    var startDragging = function (e) {
+            // Alias frequently used variables to save space. 200 bytes.
+            var self = this
+              , a = self.a
+              , b = self.b
+
+            // Call the onDragStart callback.
+            if (!self.dragging && options.onDragStart) {
+                options.onDragStart()
+            }
+
+            // Don't actually drag the element. We emulate that in the drag function.
+            e.preventDefault()
+
+            // Set the dragging property of the pair object.
+            self.dragging = true
+
+            // Create two event listeners bound to the same pair object and store
+            // them in the pair object.
+            self.move = drag.bind(self)
+            self.stop = stopDragging.bind(self)
+
+            // All the binding. `window` gets the stop events in case we drag out of the elements.
+            global[addEventListener]('mouseup', self.stop)
+            global[addEventListener]('touchend', self.stop)
+            global[addEventListener]('touchcancel', self.stop)
+
+            self.parent[addEventListener]('mousemove', self.move)
+            self.parent[addEventListener]('touchmove', self.move)
+
+            // Disable selection. Disable!
+            a[addEventListener]('selectstart', noop)
+            a[addEventListener]('dragstart', noop)
+            b[addEventListener]('selectstart', noop)
+            b[addEventListener]('dragstart', noop)
+
+            a.style.userSelect = 'none'
+            a.style.webkitUserSelect = 'none'
+            a.style.MozUserSelect = 'none'
+            a.style.pointerEvents = 'none'
+
+            b.style.userSelect = 'none'
+            b.style.webkitUserSelect = 'none'
+            b.style.MozUserSelect = 'none'
+            b.style.pointerEvents = 'none'
+
+            // Set the cursor, both on the gutter and the parent element.
+            // Doing only a, b and gutter causes flickering.
+            self.gutter.style.cursor = options.cursor
+            self.parent.style.cursor = options.cursor
+
+            // Cache the initial sizes of the pair.
+            calculateSizes.call(self)
+        }
+
+      // stopDragging is very similar to startDragging in reverse.
+      , stopDragging = function () {
+            var self = this
+              , a = self.a
+              , b = self.b
+
+            if (self.dragging && options.onDragEnd) {
+                options.onDragEnd()
+            }
+
+            self.dragging = false
+
+            // Remove the stored event listeners. This is why we store them.
+            global[removeEventListener]('mouseup', self.stop)
+            global[removeEventListener]('touchend', self.stop)
+            global[removeEventListener]('touchcancel', self.stop)
+
+            self.parent[removeEventListener]('mousemove', self.move)
+            self.parent[removeEventListener]('touchmove', self.move)
+
+            // Delete them once they are removed. I think this makes a difference
+            // in memory usage with a lot of splits on one page. But I don't know for sure.
+            delete self.stop
+            delete self.move
+
+            a[removeEventListener]('selectstart', noop)
+            a[removeEventListener]('dragstart', noop)
+            b[removeEventListener]('selectstart', noop)
+            b[removeEventListener]('dragstart', noop)
+
+            a.style.userSelect = ''
+            a.style.webkitUserSelect = ''
+            a.style.MozUserSelect = ''
+            a.style.pointerEvents = ''
+
+            b.style.userSelect = ''
+            b.style.webkitUserSelect = ''
+            b.style.MozUserSelect = ''
+            b.style.pointerEvents = ''
+
+            self.gutter.style.cursor = ''
+            self.parent.style.cursor = ''
+        }
+
+      // drag, where all the magic happens. The logic is really quite simple:
+      // 
+      // 1. Ignore if the pair is not dragging.
+      // 2. Get the offset of the event.
+      // 3. Snap offset to min if within snappable range (within min + snapOffset).
+      // 4. Actually adjust each element in the pair to offset.
+      // 
+      // ---------------------------------------------------------------------
+      // |    | <- this.aMin               ||              this.bMin -> |    |
+      // |    |  | <- this.snapOffset      ||     this.snapOffset -> |  |    |
+      // |    |  |                         ||                        |  |    |
+      // |    |  |                         ||                        |  |    |
+      // ---------------------------------------------------------------------
+      // | <- this.start                                        this.size -> |
+      , drag = function (e) {
+            var offset
+
+            if (!this.dragging) return
+
+            // Get the offset of the event from the first side of the
+            // pair `this.start`. Supports touch events, but not multitouch, so only the first
+            // finger `touches[0]` is counted.
+            if ('touches' in e) {
+                offset = e.touches[0][clientAxis] - this.start
+            } else {
+                offset = e[clientAxis] - this.start
+            }
+
+            // If within snapOffset of min or max, set offset to min or max.
+            // snapOffset buffers aMin and bMin, so logic is opposite for both.
+            // Include the appropriate gutter sizes to prevent overflows.
+            if (offset <= this.aMin + options.snapOffset + this.aGutterSize) {
+                offset = this.aMin + this.aGutterSize
+            } else if (offset >= this.size - (this.bMin + options.snapOffset + this.bGutterSize)) {
+                offset = this.size - (this.bMin + this.bGutterSize)
+            }
+
+            // Actually adjust the size.
+            adjust.call(this, offset)
+
+            // Call the drag callback continously. Don't do anything too intensive
+            // in this callback.
+            if (options.onDrag) {
+                options.onDrag()
+            }
+        }
+
+      // Cache some important sizes when drag starts, so we don't have to do that
+      // continously:
+      // 
+      // `size`: The total size of the pair. First element + second element + first gutter + second gutter.
+      // `percentage`: The percentage between 0-100 that the pair occupies in the parent.
+      // `start`: The leading side of the first element.
+      //
+      // ------------------------------------------------ - - - - - - - - - - -
+      // |      aGutterSize -> |||                      |                     |
+      // |                     |||                      |                     |
+      // |                     |||                      |                     |
+      // |                     ||| <- bGutterSize       |                     |
+      // ------------------------------------------------ - - - - - - - - - - -
+      // | <- start                             size -> |       parentSize -> |
+      , calculateSizes = function () {
+            // Figure out the parent size minus padding.
+            var computedStyle = global.getComputedStyle(this.parent)
+              , parentSize = this.parent[clientDimension] - parseFloat(computedStyle[paddingA]) - parseFloat(computedStyle[paddingB])
+
+            this.size = this.a[getBoundingClientRect]()[dimension] + this.b[getBoundingClientRect]()[dimension] + this.aGutterSize + this.bGutterSize
+            this.percentage = Math.min(this.size / parentSize * 100, 100)
+            this.start = this.a[getBoundingClientRect]()[position]
+        }
+
+      // Actually adjust the size of elements `a` and `b` to `offset` while dragging.
+      // calc is used to allow calc(percentage + gutterpx) on the whole split instance,
+      // which allows the viewport to be resized without additional logic.
+      // Element a's size is the same as offset. b's size is total size - a size.
+      // Both sizes are calculated from the initial parent percentage, then the gutter size is subtracted.
+      , adjust = function (offset) {
+            this.a.style[dimension] = calc + '(' + (offset / this.size * this.percentage) + '% - ' + this.aGutterSize + 'px)'
+            this.b.style[dimension] = calc + '(' + (this.percentage - (offset / this.size * this.percentage)) + '% - ' + this.bGutterSize + 'px)'
+        }
+
+      // 4. Define a few more functions that "balance" the entire split instance.
+      // Split.js tries it's best to cope with min sizes that don't add up.
+      // At some point this should go away since it breaks out of the calc(% - px) model.
+      // Maybe it's a user error if you pass uncomputable minSizes.
+      , fitMin = function () {
+            var self = this
+              , a = self.a
+              , b = self.b
+
+            if (a[getBoundingClientRect]()[dimension] < self.aMin) {
+                a.style[dimension] = (self.aMin - self.aGutterSize) + 'px'
+                b.style[dimension] = (self.size - self.aMin - self.aGutterSize) + 'px'
+            } else if (b[getBoundingClientRect]()[dimension] < self.bMin) {
+                a.style[dimension] = (self.size - self.bMin - self.bGutterSize) + 'px'
+                b.style[dimension] = (self.bMin - self.bGutterSize) + 'px'
+            }
+        }
+      , fitMinReverse = function () {
+            var self = this
+              , a = self.a
+              , b = self.b
+
+            if (b[getBoundingClientRect]()[dimension] < self.bMin) {
+                a.style[dimension] = (self.size - self.bMin - self.bGutterSize) + 'px'
+                b.style[dimension] = (self.bMin - self.bGutterSize) + 'px'
+            } else if (a[getBoundingClientRect]()[dimension] < self.aMin) {
+                a.style[dimension] = (self.aMin - self.aGutterSize) + 'px'
+                b.style[dimension] = (self.size - self.aMin - self.aGutterSize) + 'px'
+            }
+        }
+      , balancePairs = function (pairs) {
+            for (var i = 0; i < pairs.length; i++) {
+                calculateSizes.call(pairs[i])
+                fitMin.call(pairs[i])
+            }
+
+            for (i = pairs.length - 1; i >= 0; i--) {
+                calculateSizes.call(pairs[i])
+                fitMinReverse.call(pairs[i])
+            }
+        }
+      , setElementSize = function (el, size, gutterSize) {
+            // Split.js allows setting sizes via numbers (ideally), or if you must,
+            // by string, like '300px'. This is less than ideal, because it breaks
+            // the fluid layout that `calc(% - px)` provides. You're on your own if you do that,
+            // make sure you calculate the gutter size by hand.
+            if (typeof size !== 'string' && !(size instanceof String)) {
+                if (!isIE8) {
+                    size = calc + '(' + size + '% - ' + gutterSize + 'px)'
+                } else {
+                    size = options.sizes[i] + '%'
+                }
+            }
+
+            el.style[dimension] = size
+        }
+
+      // No-op function to prevent default. Used to prevent selection.
+      , noop = function () { return false }
+
+      // All DOM elements in the split should have a common parent. We can grab
+      // the first elements parent and hope users read the docs because the
+      // behavior will be whacky otherwise.
+      , parent = elementOrSelector(ids[0]).parentNode
+
+    // Set default options.sizes to equal percentages of the parent element.
+    if (!options.sizes) {
+        var percent = 100 / ids.length
+
+        options.sizes = []
+
+        for (i = 0; i < ids.length; i++) {
+            options.sizes.push(percent)
+        }
+    }
+
+    // Standardize minSize to an array if it isn't already. This allows minSize
+    // to be passed as a number.
+    if (!Array.isArray(options.minSize)) {
+        var minSizes = []
+
+        for (i = 0; i < ids.length; i++) {
+            minSizes.push(options.minSize)
+        }
+
+        options.minSize = minSizes
+    }
+
+    // 5. Loop through the elements while pairing them off. Every pair gets a
+    // `pair` object, a gutter, and isFirst/isLast properties.
+    //
+    // Basic logic:
+    //
+    // - Starting with the second element `i > 0`, create `pair` objects with
+    //   `a = ids[i - 1]` and `b = ids[i]`
+    // - Set gutter sizes based on the _pair_ being first/last. The first and last
+    //   pair have gutterSize / 2, since they only have one half gutter, and not two.
+    // - Create gutter elements and add event listeners.
+    // - Set the size of the elements, minus the gutter sizes.
+    //
+    // -----------------------------------------------------------------------
+    // |     i=0     |         i=1         |        i=2       |      i=3     |
+    // |             |       isFirst       |                  |     isLast   |
+    // |           pair 0                pair 1             pair 2           |
+    // |             |                     |                  |              |
+    // -----------------------------------------------------------------------
+    for (i = 0; i < ids.length; i++) {
+        var el = elementOrSelector(ids[i])
+          , isFirstPair = (i == 1)
+          , isLastPair = (i == ids.length - 1)
+          , size = options.sizes[i]
+          , gutterSize = options.gutterSize
+          , pair
+
+        if (i > 0) {
+            // Create the pair object with it's metadata.
+            pair = {
+                a: elementOrSelector(ids[i - 1]),
+                b: el,
+                aMin: options.minSize[i - 1],
+                bMin: options.minSize[i],
+                dragging: false,
+                parent: parent,
+                isFirst: isFirstPair,
+                isLast: isLastPair,
+                direction: options.direction
+            }
+
+            // For first and last pairs, first and last gutter width is half.
+            pair.aGutterSize = options.gutterSize
+            pair.bGutterSize = options.gutterSize
+
+            if (isFirstPair) {
+                pair.aGutterSize = options.gutterSize / 2
+            }
+
+            if (isLastPair) {
+                pair.bGutterSize = options.gutterSize / 2
+            }
+        }
+
+        // Determine the size of the current element. IE8 is supported by
+        // staticly assigning sizes without draggable gutters. Assigns a string
+        // to `size`.
+        // 
+        // IE9 and above
+        if (!isIE8) {
+            // Create gutter elements for each pair.
+            if (i > 0) {
+                var gutter = document.createElement('div')
+
+                gutter.className = gutterClass
+                gutter.style[dimension] = options.gutterSize + 'px'
+
+                gutter[addEventListener]('mousedown', startDragging.bind(pair))
+                gutter[addEventListener]('touchstart', startDragging.bind(pair))
+
+                parent.insertBefore(gutter, el)
+
+                pair.gutter = gutter
+            }
+
+            // Half-size gutters for first and last elements.
+            if (i === 0 || i == ids.length - 1) {
+                gutterSize = options.gutterSize / 2
+            }
+        }
+
+        // Set the element size to our determined size.
+        setElementSize(el, size, gutterSize)
+
+        // After the first iteration, and we have a pair object, append it to the
+        // list of pairs.
+        if (i > 0) {
+            pairs.push(pair)
+        }
+    }
+
+    // Balance the pairs to try to accomodate min sizes.
+    //balancePairs(pairs)
+
+    return {
+        setSizes: function (sizes) {
+            for (var i = 0; i < sizes.length; i++) {
+                if (i > 0) {
+                    var pair = pairs[i - 1]
+
+                    setElementSize(pair.a, sizes[i - 1], pair.aGutterSize)
+                    setElementSize(pair.b, sizes[i], pair.bGutterSize)
+                }
+            }
+        },
+        collapse: function (i) {
+            var pair
+
+            if (i === pairs.length) {
+                pair = pairs[i - 1]
+
+                calculateSizes.call(pair)
+                adjust.call(pair, pair.size - pair.bGutterSize)
+            } else {
+                pair = pairs[i]
+
+                calculateSizes.call(pair)
+                adjust.call(pair, pair.aGutterSize)
+            }
+        },
+        destroy: function () {
+            for (var i = 0; i < pairs.length; i++) {
+                pairs[i].parent.removeChild(pairs[i].gutter)
+                pairs[i].a.style[dimension] = ''
+                pairs[i].b.style[dimension] = ''
+            }
+        }
+    }
+}
+
+// Play nicely with module systems, and the browser too if you include it raw.
+if (typeof exports !== 'undefined') {
+    if (typeof module !== 'undefined' && module.exports) {
+        exports = module.exports = Split
+    }
+    exports.Split = Split
+} else {
+    global.Split = Split
+}
+
+// Call our wrapper function with the current global. In this case, `window`.
+}).call(window);

+ 12 - 0
inspector/dist/test/Test.d.ts

@@ -0,0 +1,12 @@
+declare class Test {
+    private engine;
+    scene: BABYLON.Scene;
+    constructor(canvasId: string);
+    private _run();
+    private _initScene();
+    private _initGame();
+    /**
+     * Create the canvas2D
+     */
+    private _createCanvas();
+}

+ 133 - 0
inspector/dist/test/Test.js

@@ -0,0 +1,133 @@
+var Test = (function () {
+    function Test(canvasId) {
+        var _this = this;
+        var canvas = document.getElementById(canvasId);
+        this.engine = new BABYLON.Engine(canvas, true);
+        this.scene = null;
+        window.addEventListener("resize", function () {
+            _this.engine.resize();
+        });
+        this._run();
+    }
+    Test.prototype._run = function () {
+        var _this = this;
+        this._initScene();
+        // new INSPECTOR.Inspector(this.scene); 
+        BABYLON.DebugLayer.InspectorURL = 'http://localhost:3000/dist/libs/inspector.js';
+        this.scene.debugLayer.show();
+        this.scene.executeWhenReady(function () {
+            _this._initGame();
+            _this.engine.runRenderLoop(function () {
+                _this.scene.render();
+            });
+        });
+    };
+    Test.prototype._initScene = function () {
+        var scene = new BABYLON.Scene(this.engine);
+        var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 4, Math.PI / 2.5, 200, BABYLON.Vector3.Zero(), scene);
+        camera.attachControl(this.engine.getRenderingCanvas(), true);
+        camera.minZ = 0.1;
+        // Lights
+        var light0 = new BABYLON.PointLight("Omni0", new BABYLON.Vector3(0, 10, 0), scene);
+        var light1 = new BABYLON.PointLight("Omni1", new BABYLON.Vector3(0, -10, 0), scene);
+        var light2 = new BABYLON.PointLight("Omni2", new BABYLON.Vector3(10, 0, 0), scene);
+        var light3 = new BABYLON.DirectionalLight("Dir0", new BABYLON.Vector3(1, -1, 0), scene);
+        var material = new BABYLON.StandardMaterial("kosh", scene);
+        var sphere = BABYLON.Mesh.CreateSphere("Sphere", 16, 3, scene);
+        // Creating light sphere
+        var lightSphere0 = BABYLON.Mesh.CreateSphere("Sphere0", 16, 0.5, scene);
+        var lightSphere1 = BABYLON.Mesh.CreateSphere("Sphere1", 16, 0.5, scene);
+        var lightSphere2 = BABYLON.Mesh.CreateSphere("Sphere2", 16, 0.5, scene);
+        lightSphere0.material = new BABYLON.StandardMaterial("red", scene);
+        lightSphere0.material.diffuseColor = new BABYLON.Color3(0, 0, 0);
+        lightSphere0.material.specularColor = new BABYLON.Color3(0, 0, 0);
+        lightSphere0.material.emissiveColor = new BABYLON.Color3(1, 0, 0);
+        lightSphere1.material = new BABYLON.StandardMaterial("green", scene);
+        lightSphere1.material.diffuseColor = new BABYLON.Color3(0, 0, 0);
+        lightSphere1.material.specularColor = new BABYLON.Color3(0, 0, 0);
+        lightSphere1.material.emissiveColor = new BABYLON.Color3(0, 1, 0);
+        lightSphere2.material = new BABYLON.StandardMaterial("blue", scene);
+        lightSphere2.material.diffuseColor = new BABYLON.Color3(0, 0, 0);
+        lightSphere2.material.specularColor = new BABYLON.Color3(0, 0, 0);
+        lightSphere2.material.emissiveColor = new BABYLON.Color3(0, 0, 1);
+        // Sphere material
+        material.diffuseColor = new BABYLON.Color3(1, 1, 1);
+        sphere.material = material;
+        // Lights colors
+        light0.diffuse = new BABYLON.Color3(1, 0, 0);
+        light0.specular = new BABYLON.Color3(1, 0, 0);
+        light1.diffuse = new BABYLON.Color3(0, 1, 0);
+        light1.specular = new BABYLON.Color3(0, 1, 0);
+        light2.diffuse = new BABYLON.Color3(0, 0, 1);
+        light2.specular = new BABYLON.Color3(0, 0, 1);
+        light3.diffuse = new BABYLON.Color3(1, 1, 1);
+        light3.specular = new BABYLON.Color3(1, 1, 1);
+        BABYLON.Effect.ShadersStore["customVertexShader"] = 'precision highp float;attribute vec3 position;attribute vec2 uv;uniform mat4 worldViewProjection;varying vec2 vUV;varying vec3 vPos;void main(){gl_Position=worldViewProjection*vec4(position,1.),vPos=gl_Position.xyz;if(position.x >2.0) {gl_Position.x = 2.0;} else { gl_Position.y = 1.0;}}';
+        BABYLON.Effect.ShadersStore["customFragmentShader"] = 'precision highp float;varying vec3 vPos;uniform vec3 color;void main(){gl_FragColor=vec4(mix(color,vPos,.05),1.);}';
+        var shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, {
+            vertex: "custom",
+            fragment: "custom",
+        }, {
+            attributes: ["position", "normal", "uv"],
+            uniforms: ["world", "worldView", "worldViewProjection", "view", "projection"]
+        });
+        sphere.material = shaderMaterial;
+        // Animations
+        var alpha = 0;
+        scene.beforeRender = function () {
+            light0.position = new BABYLON.Vector3(10 * Math.sin(alpha), 0, 10 * Math.cos(alpha));
+            light1.position = new BABYLON.Vector3(10 * Math.sin(alpha), 0, -10 * Math.cos(alpha));
+            light2.position = new BABYLON.Vector3(10 * Math.cos(alpha), 0, 10 * Math.sin(alpha));
+            lightSphere0.position = light0.position;
+            lightSphere1.position = light1.position;
+            lightSphere2.position = light2.position;
+            alpha += 0.01;
+        };
+        this.scene = scene;
+    };
+    Test.prototype._initGame = function () {
+        this._createCanvas();
+    };
+    /**
+     * Create the canvas2D
+     */
+    Test.prototype._createCanvas = function () {
+        var canvas = new BABYLON.ScreenSpaceCanvas2D(this.scene, {
+            id: "Hello world SC",
+            size: new BABYLON.Size(300, 100),
+            backgroundFill: "#4040408F",
+            backgroundRoundRadius: 50,
+            children: [
+                new BABYLON.Text2D("Hello World!", {
+                    id: "text",
+                    marginAlignment: "h: center, v:center",
+                    fontName: "20pt Arial",
+                })
+            ]
+        });
+        var infoCanvas = new BABYLON.ScreenSpaceCanvas2D(this.scene, { id: "PINK CUBE SC", size: new BABYLON.Size(500, 500) });
+        var text2 = new BABYLON.Text2D("UnbindTime", { parent: infoCanvas, id: "Text", marginAlignment: "h: left, v: bottom", fontName: "10pt Arial" });
+        canvas = new BABYLON.WorldSpaceCanvas2D(this.scene, new BABYLON.Size(150, 150), {
+            id: "WorldSpaceCanvas",
+            worldPosition: new BABYLON.Vector3(0, 0, 0),
+            worldRotation: BABYLON.Quaternion.RotationYawPitchRoll(Math.PI / 4, Math.PI / 4, 0),
+            enableInteraction: true,
+            backgroundFill: "#C0C0C040",
+            backgroundRoundRadius: 20,
+            children: [
+                new BABYLON.Text2D("World Space Canvas", { fontName: "8pt Arial", marginAlignment: "h: center, v: bottom", fontSuperSample: true })
+            ]
+        });
+        var rect = new BABYLON.Rectangle2D({ parent: canvas, x: 45, y: 45, width: 30, height: 30, fill: null, border: BABYLON.Canvas2D.GetGradientColorBrush(new BABYLON.Color4(0.9, 0.3, 0.9, 1), new BABYLON.Color4(1.0, 1.0, 1.0, 1)), borderThickness: 2 });
+        var buttonRect = new BABYLON.Rectangle2D({ parent: canvas, id: "button", x: 12, y: 12, width: 50, height: 15, fill: "#40C040FF", roundRadius: 2, children: [new BABYLON.Text2D("Click Me!", { fontName: "8pt Arial", marginAlignment: "h: center, v: center", fontSuperSample: true })] });
+        var button2Rect = new BABYLON.Rectangle2D({ parent: canvas, id: "button2", x: 70, y: 12, width: 40, height: 15, fill: "#4040C0FF", roundRadius: 2, isVisible: false, children: [new BABYLON.Text2D("Great!", { fontName: "8pt Arial", marginAlignment: "h: center, v: center", fontSuperSample: true })] });
+        ;
+        buttonRect.pointerEventObservable.add(function (d, s) {
+            button2Rect.levelVisible = !button2Rect.levelVisible;
+        }, BABYLON.PrimitivePointerInfo.PointerUp);
+        var insideRect = new BABYLON.Rectangle2D({ parent: rect, width: 10, height: 10, marginAlignment: "h: center, v: center", fill: "#0040F0FF" });
+        insideRect.roundRadius = 2;
+    };
+    return Test;
+}());
+//# sourceMappingURL=Test.js.map

BIN
inspector/dist/test/assets/albedo.png


BIN
inspector/dist/test/assets/reflectivity.png


BIN
inspector/dist/test/assets/room.hdr


BIN
inspector/dist/test/assets/skybox/snow_nx.jpg


BIN
inspector/dist/test/assets/skybox/snow_ny.jpg


BIN
inspector/dist/test/assets/skybox/snow_nz.jpg


BIN
inspector/dist/test/assets/skybox/snow_px.jpg


BIN
inspector/dist/test/assets/skybox/snow_py.jpg


BIN
inspector/dist/test/assets/skybox/snow_pz.jpg


+ 112 - 0
inspector/dist/test/index.html

@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Inspector - test</title>
+    <meta charset='utf-8'/>
+    <meta name="viewport" content="width=device-width, user-scalable=no">
+
+    <!--Babylon-->
+    <!--<script src="../libs/split.js"></script>-->
+    <script src="../libs/babylon.max.js"></script>
+    <script src="../libs/babylon.canvas2d.max.js"></script>
+    <!--Dist-->
+    <script src="../inspector.js"></script>
+    
+    <!--Debug-->
+    <!--<link rel="stylesheet" href="../libs/main.css">
+
+    <script src="../inspector/properties.js"></script> 
+    
+    <script src="../inspector/adapters/Adapter.js"></script> 
+    <script src="../inspector/adapters/Canvas2DAdapter.js"></script> 
+    <script src="../inspector/adapters/MeshAdapter.js"></script> 
+    <script src="../inspector/adapters/MaterialAdapter.js"></script> 
+    <script src="../inspector/adapters/LightAdapter.js"></script> 
+
+    <script src="../inspector/gui/BasicElement.js"></script> 
+    <script src="../inspector/gui/ColorElement.js"></script>
+    <script src="../inspector/gui/TextureElement.js"></script>
+    <script src="../inspector/gui/CubeTextureElement.js"></script>
+    <script src="../inspector/gui/HDRCubeTextureElement.js"></script>
+    <script src="../inspector/gui/SearchBar.js"></script>
+    <script src="../inspector/gui/Tooltip.js"></script>
+
+    <script src="../inspector/details/Property.js"></script> 
+    <script src="../inspector/details/DetailPanel.js"></script>
+    <script src="../inspector/details/PropertyLine.js"></script>
+    
+    <script src="../inspector/helpers/Helpers.js"></script>
+    
+    <script src="../inspector/tabs/Tab.js"></script>
+    <script src="../inspector/tabs/PropertyTab.js"></script>
+    <script src="../inspector/tabs/Tabbar.js"></script>
+    
+    <script src="../inspector/tabs/SceneTab.js"></script>
+    <script src="../inspector/tabs/Canvas2DTab.js"></script>
+    <script src="../inspector/tabs/MeshTab.js"></script>
+    <script src="../inspector/tabs/LightTab.js"></script>
+    <script src="../inspector/tabs/MaterialTab.js"></script>
+    <script src="../inspector/tabs/ShaderTab.js"></script>
+
+    <script src="../inspector/tools/AbstractTool.js"></script>
+    <script src="../inspector/tools/PopupTool.js"></script>
+    <script src="../inspector/tools/RefreshTool.js"></script>
+    <script src="../inspector/tools/PauseScheduleTool.js"></script>
+    <script src="../inspector/tools/PickTool.js"></script>
+    <script src="../inspector/tools/Toolbar.js"></script>
+
+    <script src="../inspector/treetools/AbstractTreeTool.js"></script>
+    <script src="../inspector/treetools/Checkbox.js"></script>
+    <script src="../inspector/treetools/DebugArea.js"></script>
+    <script src="../inspector/treetools/BoundingBox.js"></script>
+    <script src="../inspector/treetools/Info.js"></script>
+
+    <script src="../inspector/tree/TreeItem.js"></script>
+    
+    <script src="../inspector/scheduler/Scheduler.js"></script> 
+    
+    <script src="../inspector/Inspector.js"></script> -->
+
+    <script src="Test.js"></script>
+
+    <style>
+        .mini {
+            width:100%;
+            margin:auto;
+            height:100%;
+        }
+        
+        html, body {
+            width: 100%;
+            height:100%;
+            margin:0;
+            padding:0;
+            overflow:hidden;
+            font-family : sans-serif;
+        }
+
+        #game-canvas {
+            width:100%;
+            height:100%; 
+        }
+
+    </style>
+
+</head>
+<body>
+
+    
+    <!--The game canvas-->
+    <div class="mini">
+        <canvas id="game-canvas"></canvas> 
+    </div>
+
+    <!--Starting the game-->
+    <script>
+        window.addEventListener("DOMContentLoaded", function(){
+            new Test('game-canvas');
+        });
+    </script>
+
+</body>
+</html>

File diff suppressed because it is too large
+ 12405 - 0
inspector/dist/typings/babylon.d.old.d.ts


+ 1 - 0
inspector/dist/typings/babylon.d.old.js

@@ -0,0 +1 @@
+//# sourceMappingURL=babylon.d.old.js.map

+ 3 - 0
inspector/index.js

@@ -0,0 +1,3 @@
+require("./dist/libs/main.css");
+window.Split = require("./dist/libs/split.js");
+module.exports = require("exports?INSPECTOR!./dist/libs/inspector.js");

+ 29 - 0
inspector/package.json

@@ -0,0 +1,29 @@
+{
+  "name": "babylonjs-inspector",
+  "version": "1.0.0",
+  "devDependencies": {
+    "autoprefixer": "latest",
+    "css-loader": "^0.25.0",
+    "cssnano": "latest",
+    "exports-loader": "^0.6.3",
+    "grunt": "latest",
+    "grunt-contrib-clean": "^1.0.0",
+    "grunt-contrib-concat": "^1.0.1",
+    "grunt-contrib-connect": "^0.11.2",
+    "grunt-contrib-uglify": "^2.0.0",
+    "grunt-contrib-watch": "latest",
+    "grunt-dts-bundle": "^0.3.0",
+    "grunt-newer": "latest",
+    "grunt-open": "latest",
+    "grunt-postcss": "latest",
+    "grunt-sass": "latest",
+    "grunt-ts": "^5.5.1",
+    "grunt-webpack": "^1.0.18",
+    "imports-loader": "^0.6.5",
+    "jit-grunt": "latest",
+    "style-loader": "^0.13.1",
+    "time-grunt": "latest",
+    "webpack": "^1.13.2",
+    "webpack-dev-server": "^1.16.2"
+  }
+}

+ 146 - 0
inspector/sass/_detailPanel.scss

@@ -0,0 +1,146 @@
+// The main class of the detail panel
+.insp-details {
+  background-color: $background;
+  overflow-y: auto;
+  overflow-x: hidden;
+  color:$color;
+  font-family: $font;
+
+  // Common defintion between header row and detail row
+  .base-row {      
+    display:flex;
+    width:100%;
+
+    .base-property {
+        word-wrap: break-word;
+        padding: 2px 0 2px 0;
+    }
+    // Property name
+    .prop-name {
+        @extend .property-name;
+        @extend .base-property;
+        width:35%;
+    }
+    // property value
+    .prop-value {
+        @extend .base-property;
+        width:59%;
+        padding-left:10px;
+        &.clickable {
+            cursor:pointer;
+            &:hover {
+                background-color:$background-lighter2;
+            }
+            &:after {
+                font-family: 'FontAwesome', sans-serif;
+                content: "\00a0 \00a0 \00a0 \f054"; // 4 space before + chevron right
+            }
+        }   
+    }
+    
+    // input {
+    //     border:0;
+    //     font-family: $font;
+    //     background-color: $background-lighter;
+    //     color:$color;
+    //     // Remove blue border on focus
+    //     &:focus {
+    //         outline:none;
+    //     }
+    // }
+  }
+  
+  // A line of details composed of name, type, value, id, flagId
+  .row {
+    @extend .base-row;
+    &:nth-child(even) {
+        background-color:$background-lighter;
+    }
+    &.unfolded {          
+        .prop-value.clickable:after {
+            font-family: 'FontAwesome', sans-serif;
+            content: "\00a0 \00a0 \00a0 \f078"; // 4 space before + chevron down
+        }
+    }
+  }
+
+  
+  // Name, type, value, Id, FlagId
+  .header-row {
+    @extend .base-row;
+    background-color: $background-lighter; 
+    color           : $color;
+    width           : 100%;
+    // Max-width is set in js
+    
+    // Special definition for text color: the color is the default one
+    // All header columns
+    & > * {
+        color:$color !important;
+        padding:5px 0 5px 5px !important;
+        cursor: pointer;
+        &:hover {
+            background-color:$background-lighter2;
+        }
+    }
+
+    .header-col {
+        display:flex;
+        justify-content: space-between;
+        align-items: center;
+
+        .sort-direction {
+            margin-right:5px;
+        }
+    }
+  }
+  
+  // A div used to view a property in the property line (color, texture...)
+  .element-viewer {
+      position:relative;
+      width:10px;
+      height:10px;
+      display:inline-block;
+      margin-left:5px;
+  }
+
+  // The div displaying a color
+  .color-element {
+      @extend .element-viewer;
+  }
+
+  // The div displaying a texture element
+  .texture-element {
+      @extend .element-viewer;
+      color:$color-top;
+      margin-left:10px; 
+
+      .texture-viewer {
+          
+          color:$color;
+          position:absolute;
+          z-index:10;
+          bottom:0;
+          right:0;
+          display:block;
+          width:150px;
+          height:150px;
+          border: 1px solid $background-lighter3;
+          background-color: $background;
+          transform: translateX(100%) translateY(100%);
+          
+          display: none;
+          flex-direction: column;
+          justify-content: flex-start;
+          align-items: center;
+
+          .texture-viewer-img {
+              margin:10px 0 10px 0;
+              max-width:110px;
+              max-height:110px;
+          }
+      }
+  }
+   
+
+}

+ 18 - 0
inspector/sass/_resizeBar.scss

@@ -0,0 +1,18 @@
+.c2di-resize-bar-v {
+  height:100%;
+  width:$resizebar-width;
+  background-color: $background;
+  cursor: col-resize;
+  flex-shrink: 0;
+  border-left:1px solid $background-lighter;
+  border-right:1px solid $background-lighter;
+}
+.c2di-resize-bar-h {
+  width:100%;
+  height:$resizebar-width;
+  background-color: $background;
+  cursor: row-resize;
+  flex-shrink: 0;
+  border-top: 1px solid $background-lighter;
+  border-bottom: 1px solid $background-lighter;
+}

+ 19 - 0
inspector/sass/_searchbar.scss

@@ -0,0 +1,19 @@
+.searchbar {
+    
+    border       : 1px solid $background-lighter;
+    margin-bottom: 5px;
+    display      : flex;
+    align-items  : center;
+    color:darken($color, 10%);
+    
+    input {
+        background-color: $background;
+        border          : none;
+        width           : 100%;
+        outline         : none;
+        font-family     : $font;
+        color           : darken($color, 10%);
+        padding         : 3px 0 3px 10px;
+        margin          : 6px 0 6px 0;
+    }
+}

+ 83 - 0
inspector/sass/_tabPanel.scss

@@ -0,0 +1,83 @@
+.tab-panel {
+    height:100%;     
+    
+    .scene-actions {
+        overflow-y: auto;
+        
+        // Action title : Textures/Options/Viewer
+        .actions-title {
+            font-size     : 1.1em;
+            padding-bottom: 10px;
+            border-bottom : 1px solid $color-bot;
+            margin        : 10px 0 10px 0;
+        }
+
+        .defaut-action {            
+            height     : 20px;
+            line-height: 20px;
+            width      : 100%;
+            cursor     : pointer;   
+            
+            &:hover {
+                background-color:$background-lighter;
+            }
+            &:active {                
+                background-color: $background-lighter2;
+            }
+        }
+
+        // Radio button
+        .action-radio {
+            @extend .defaut-action;
+            &:before {
+                width      : 1em;
+                height     : 1em;
+                line-height: 1em;
+                display    : inline-block;
+                font-family: 'FontAwesome', sans-serif;
+                content    : "\f10c";
+                margin-right:10px;
+            }
+            // radio button active
+            &.active {
+                &:before {
+                    width      : 1em;
+                    height     : 1em;
+                    line-height: 1em;
+                    display    : inline-block;
+                    font-family: 'FontAwesome', sans-serif;
+                    content    : "\f192";
+                    color      : $color-bot;
+                    margin-right:10px;
+                }
+            }
+        }
+
+        // Check button
+        .action {             
+            @extend .defaut-action;
+            &:before {
+                width      : 1em;
+                height     : 1em;
+                line-height: 1em;
+                display    : inline-block;
+                font-family: 'FontAwesome', sans-serif;
+                content    : "\f096";
+                margin-right:10px;
+            }
+                
+            &.active {
+                &:before {
+                    width      : 1em;
+                    height     : 1em;
+                    line-height: 1em;
+                    display    : inline-block;
+                    font-family: 'FontAwesome', sans-serif;
+                    content    : "\f14a";
+                    color      : $color-bot;
+                    margin-right:10px;
+                }
+            }
+        }
+    }
+}

+ 58 - 0
inspector/sass/_tabbar.scss

@@ -0,0 +1,58 @@
+.tabbar {
+    height:$tabbar-height;
+    
+    display:flex;
+    align-items: center;
+    border-bottom: 1px solid $background-lighter2;
+    width:100%;
+    overflow-x:auto;
+    overflow-y:hidden;
+    
+    .tab {
+        height:$tabbar-height;
+        width:auto;
+        padding: 0 10px 0 10px;
+        color:$color;
+        line-height: $tabbar-height;
+        text-align: center;
+        cursor: pointer;
+        margin: 0 5px 0 5px;
+       
+        // Hover on it
+        &:hover {
+            border-bottom: 1px solid $color-top;
+            background-color: $background-lighter;
+        }
+        // Clic on it
+        &:active {
+            background-color: $background-lighter2;
+        }
+        // tab selected
+        &.active {
+            border-bottom: 1px solid $color-top;
+        }
+    }
+
+    .more-tabs {
+        width           : $tabbar-height;
+        height          : $tabbar-height;
+        display         : flex;
+        justify-content : center;
+        align-items     : center;
+        cursor          : pointer;
+        position        : relative;
+        border-right    : 1px solid $background-lighter2;
+        &:hover {
+            background-color: $background-lighter2;
+        }
+        &:active {
+            color:$color-top;
+            background-color: $background-lighter3;
+        }
+        // This tool is activated
+        &.active {
+            color:$color-top;
+        }
+
+    }
+}

+ 31 - 0
inspector/sass/_toolbar.scss

@@ -0,0 +1,31 @@
+/**
+ * The toolbar contains : 
+ * - a refresh tool - refresh the whole panel
+ * - a popup tool - Open the inspector in a new panel
+ * ...
+ */   
+.toolbar {   
+    display:flex;
+     
+    .tool {
+        width          : $tabbar-height;
+        height         : $tabbar-height;
+        display        : flex;
+        justify-content: center;
+        align-items    : center;
+        cursor         : pointer;
+        position       : relative;
+        border-right   : 1px solid $background-lighter2;
+        &:hover {
+            background-color: $background-lighter2;
+        }
+        &:active {
+            color:$color-top;
+            background-color: $background-lighter3;
+        }
+        // This tool is activated
+        &.active {
+            color:$color-top;
+        }
+    }
+}

+ 19 - 0
inspector/sass/_tooltip.scss

@@ -0,0 +1,19 @@
+// Tooltip used when hovering on divisions
+
+.tooltip {
+    position        : absolute;
+    bottom          : 0;
+    right           : 0;
+    color           : $color;
+    transform       : translateX(100%) translateY(100%);    
+    display         : none;
+    z-index         : 4;
+    font-family     : $font;
+    
+    
+    width           : 120px;
+    padding         : 5px 5px 5px 15px;
+    line-height     : 25px;
+    background-color: $background;
+    border          : 1px solid $background-lighter3;
+}

+ 60 - 0
inspector/sass/_tree.scss

@@ -0,0 +1,60 @@
+
+// Color for a property type
+.property-type {
+  color:$color-bot;
+}
+
+.property-name {
+  color:$color-top;
+}
+
+.insp-tree {
+  overflow-y: auto;
+  overflow-x: hidden;
+  height    : calc(50% - #{$tabbar-height} - #{$searchbar-height});
+  
+    
+    .line {
+        cursor:pointer;
+        // Hover
+        &:hover {
+            background-color:$background-lighter;
+        }
+        // Line active (selected)
+        &.active {
+            background-color:$background-lighter3;
+            .line-content {
+                background-color:$background;
+            }
+        }
+        // > before title names
+        &.unfolded:before{            
+            width      : 1em;
+            height     : 1em;
+            line-height: 1em;
+            display    : inline-block;
+            font-family: 'FontAwesome', sans-serif;
+            content    : "\f078";
+        }
+        &.folded:before{            
+            width      : 1em;
+            height     : 1em;
+            line-height: 1em;
+            display    : inline-block;
+            font-family: 'FontAwesome', sans-serif;
+            content    : "\f054";
+        }
+
+        // Sub lines
+        .line-content {
+            padding-left:15px;
+            &:hover { 
+                background-color:$background;
+            }
+            .line:hover:first-child {            
+                background-color:$background-lighter2;
+            }
+            
+        }
+    }
+}

+ 16 - 0
inspector/sass/_treeTool.scss

@@ -0,0 +1,16 @@
+/**
+ * A tool contained in the tree panel (available for each item of the tree)
+ */
+.treeTool {
+    margin : 3px 8px 3px 3px;
+    cursor:pointer;
+    position:relative;
+   
+    &:hover {        
+        color:$color-bot;
+    }
+    
+    &.active {
+        color:$color-bot;
+    }
+}

+ 19 - 0
inspector/sass/defines.scss

@@ -0,0 +1,19 @@
+@import url(https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css);
+@import url(http://fonts.googleapis.com/css?family=Inconsolata);
+
+$font               : 'Inconsolata', sans-serif;
+
+
+$color              : #ccc;
+$background         : #242424;
+$background-active  : #2c2c2c;
+$color-top         : #f29766;
+$color-bot         : #5db0d7;
+
+$background-lighter : lighten($color: $background, $amount         : 3%);
+$background-lighter2: lighten($color: $background-lighter, $amount : 5%);
+$background-lighter3: lighten($color: $background-lighter2, $amount: 5%);
+
+$resizebar-width    : 10px;
+$tabbar-height      : 30px;
+$searchbar-height   : 30px;

+ 92 - 0
inspector/sass/main.scss

@@ -0,0 +1,92 @@
+
+.insp-wrapper {
+  // Import variables, color and font files
+  @import "defines";
+
+  // Style for resize bar
+  .gutter {
+    background-color: $background-lighter;
+    &.gutter-vertical {
+      cursor        : ns-resize;
+    }
+    &.gutter-horizontal {
+      cursor        : ew-resize;
+    }
+  }
+
+  user-select: none; 
+  display:flex;
+  font-size:0.9em;
+  font-family     : $font;
+
+  // The panel containing the two subpanel : tree and details
+  .insp-right-panel {
+    width: 750px;
+    display:flex; 
+    flex-direction: column;
+    flex-shrink: 0;
+
+    // The tree panel
+    .top-panel {
+      width:100%;
+      height:100%;
+      position:relative;
+      background-color: $background;
+      color           : $color;
+      font-size       : 1em;
+      
+      // The div that will contain the tab div
+      .tab-panel-content {
+        width:100%;
+        height:100%;
+      }   
+
+      // The div displaying all invisible tabs (when the tabbar width is small)
+      .more-tabs-panel {
+          position:absolute;
+          z-index:10;
+          top:$tabbar-height;
+          right:0;
+          width:100px;
+          display:none;
+          flex-direction: column;
+          align-items: center;
+          justify-content: center;
+          border: 1px solid $background-lighter3;
+          background-color: $background;
+
+          .invisible-tab {
+            height     : 25px;
+            width      : 100%;
+            line-height: 25px;
+            text-align : center;
+            background-color: $background-lighter;
+            cursor:pointer;
+
+            // Hover on it
+            &:hover {
+              background-color: $background-lighter2;
+            }
+            // Clic on it
+            &:active {
+              background-color: $background-lighter3;
+            }
+          }
+
+      }
+    }
+  }
+
+  @import 'tooltip';
+
+  // All tools used on object in the tree
+  @import 'treeTool';
+  @import "tabPanel";
+  @import "tabs/shaderTab";
+
+  @import "tree";
+  @import "detailPanel";
+  @import "tabbar";
+  @import "toolbar";
+  @import "searchbar";
+}

+ 53 - 0
inspector/sass/tabs/_shaderTab.scss

@@ -0,0 +1,53 @@
+.tab-panel {
+
+    .shader-tree-panel {
+        height                  : 30px;
+        
+        // The combo box listing all shaders
+        select {
+            height              : 30px;
+            background-color    : transparent;
+            color               : #ccc;
+            height              : 30px;
+            width               : 100%;
+            max-width           : 300px;
+            padding-left        : 15px;
+            border              : 1px solid $background-lighter;
+            outline             : 1px solid $background-lighter3;
+            option {
+                padding         : 5px;
+                color           : darken($color: $color, $amount: 30%)
+            }
+        }
+        
+    }
+
+    .shader-panel {
+        min-height              : 100px;
+        user-select             : text;
+        box-sizing              : border-box;
+        padding                 : 0 15px;
+        
+        // Shader code style - the syntax highlightinh is done by highlight.js, loaded dynamically
+        pre {
+            margin              : 0;
+            white-space         : pre-wrap;
+            code {
+                // to cancel the background color from zenburn theme of highlight js
+                background-color: $background !important;
+                padding         : 0;
+                margin          : 0;                    
+            }            
+        }
+        
+        .shader-panel-title {
+            height              : 25px;
+            border-bottom       : 1px solid $background-lighter2;
+            text-transform      : uppercase;
+            line-height         : 25px;
+            margin-bottom       : 10px;
+        }
+        
+        
+    }
+}

BIN
inspector/screens/tab_mesh.jpg


BIN
inspector/screens/tools.jpg


+ 1 - 0
inspector/ts/.baseDir.ts

@@ -0,0 +1 @@
+// Ignore this file. See https://github.com/grunt-ts/grunt-ts/issues/77

+ 183 - 0
inspector/ts/inspector/Inspector.ts

@@ -0,0 +1,183 @@
+module INSPECTOR {
+    export class Inspector {
+
+        private _c2diwrapper    : HTMLElement;
+        // private _detailsPanel: DetailPanel;
+        /** The panel displayed at the top of the inspector */
+        private _topPanel       : HTMLElement;
+        /** The div containing the content of the active tab */
+        private _tabPanel       : HTMLElement;
+        /** The panel containing the list if items */
+        // private _treePanel   : HTMLElement;
+        /** The list if tree items displayed in the tree panel. */
+        private _items          : Array<TreeItem>;
+        private _tabbar         : TabBar;
+        private _scene          : BABYLON.Scene;
+        /** The HTML document relative to this inspector (the window or the popup depending on its mode) */
+        public static DOCUMENT  : HTMLDocument;
+        /** True if the inspector is built as a popup tab */
+        private _popupMode      : boolean = false;
+        /** The original canvas size, before applying the inspector*/
+        private _canvasSize : {width:string, height:string};
+
+        /** The inspector is created with the given engine.
+         * If a HTML parent is not given as a parameter, the inspector is created as a right panel on the main window.
+         * If a HTML parent is given, the inspector is created in this element, taking full size of its parent.
+         */
+        constructor(scene: BABYLON.Scene, parent?:HTMLElement) {
+
+            // get canvas parent only if needed.
+            this._scene     = scene;
+            
+            // Save HTML document
+            Inspector.DOCUMENT = window.document;                       
+            
+            // POPUP MODE if parent is defined
+            if (parent) {    
+                // Build the inspector in the given parent
+                this._buildInspector(parent);
+            } else {        
+                // Get canvas and its DOM parent
+                let canvas            = this._scene.getEngine().getRenderingCanvas();            
+                let canvasParent      = canvas.parentElement;                                
+                // resize canvas
+                // canvas.style.width = 'calc(100% - 750px - 12px)';
+
+                // get canvas style                
+                let canvasStyle       = window.getComputedStyle(canvas);
+                this._canvasSize = { width:canvasStyle.width, height:canvasStyle.height};
+
+                // Create c2di wrapper
+                this._c2diwrapper  = Helpers.CreateDiv('insp-wrapper', canvasParent);
+                this._c2diwrapper.style.width = this._canvasSize.width;
+                this._c2diwrapper.style.height = this._canvasSize.height;
+                // Add canvas to the wrapper
+                this._c2diwrapper.appendChild(canvas);
+                // add inspector     
+                let inspector      = Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
+                // Add split bar
+                Split([canvas, inspector], {
+                    direction:'horizontal',
+                    sizes : [75, 25],
+                    onDrag : () => { 
+                        Helpers.SEND_EVENT('resize');
+                        if (this._tabbar) {
+                            this._tabbar.updateWidth()
+                        }
+                    }
+                });
+
+                // Build the inspector
+                this._buildInspector(inspector);   
+                // Send resize event to the window
+                Helpers.SEND_EVENT('resize');
+            }
+
+            // Refresh the inspector
+            this.refresh();
+        }
+        
+        /** Build the inspector panel in the given HTML element */
+        private _buildInspector(parent:HTMLElement) {            
+            // tabbar
+            this._tabbar = new TabBar(this);
+
+            // Top panel
+            this._topPanel = Helpers.CreateDiv('top-panel', parent);
+            // Add tabbar
+            this._topPanel.appendChild(this._tabbar.toHtml());
+            this._tabbar.updateWidth();
+            
+            // Tab panel
+            this._tabPanel = Helpers.CreateDiv('tab-panel-content', this._topPanel);
+            
+        }
+
+        public get scene() : BABYLON.Scene {
+            return this._scene;
+        }
+        public get popupMode() : boolean {
+            return this._popupMode;
+        }
+
+        /**  
+         * Filter the list of item present in the tree.
+         * All item returned should have the given filter contained in the item id.
+        */
+        public filterItem(filter:string){
+            this._tabbar.getActiveTab().filter(filter);
+        }
+        
+        /** Display the mesh tab on the given object */
+        public displayObjectDetails(mesh:BABYLON.AbstractMesh) {
+            this._tabbar.switchMeshTab(mesh);
+        }
+
+        /** Clean the whole tree of item and rebuilds it */
+        public refresh() {
+            // Clean top panel
+            Helpers.CleanDiv(this._tabPanel);
+
+            // Get the active tab and its items
+            let activeTab = this._tabbar.getActiveTab();
+            activeTab.update();
+            this._tabPanel.appendChild(activeTab.getPanel());            
+            Helpers.SEND_EVENT('resize');
+            
+        }        
+        
+        /** Remove the inspector panel when it's built as a right panel:
+         * remove the right panel and remove the wrapper
+         */
+        private _disposeInspector() {
+            if (!this._popupMode) {
+                // Get canvas
+                let canvas         = this._scene.getEngine().getRenderingCanvas(); 
+                // restore canvas size
+                canvas.style.width = this._canvasSize.width;
+                canvas.style.height = this._canvasSize.height;
+                // Get parent of the wrapper           
+                let canvasParent   = canvas.parentElement.parentElement;  
+                canvasParent.appendChild(canvas);                              
+                // Remove wrapper
+                Helpers.CleanDiv(this._c2diwrapper);
+                this._c2diwrapper.remove();                   
+                // Send resize event to the window
+                Helpers.SEND_EVENT('resize');              
+            }
+        }
+        
+        /** Open the inspector in a new popup */
+        public openPopup() {    
+            // Create popup
+            let popup = window.open('', 'Babylon.js INSPECTOR', 'toolbar=no,resizable=yes,menubar=no,width=750,height=1000');
+            popup.document.title = 'Babylon.js INSPECTOR';
+            // Get the inspector style      
+            let styles = Inspector.DOCUMENT.querySelectorAll('style');
+            for (let s = 0; s<styles.length; s++) {
+                popup.document.body.appendChild(styles[s].cloneNode(true));              
+            } 
+            let links = document.querySelectorAll('link');
+            for (let l = 0; l<links.length; l++) {
+                let link  = popup.document.createElement("link");
+                link.rel  = "stylesheet";
+                link.href = (links[l] as HTMLLinkElement).href;
+                popup.document.head.appendChild(link);              
+            } 
+            // Dispose the right panel
+            this._disposeInspector();
+            // set the mode as popup
+            this._popupMode = true;
+            // Save the HTML document
+            Inspector.DOCUMENT = popup.document;
+            // Build the inspector wrapper
+            this._c2diwrapper  = Helpers.CreateDiv('insp-wrapper', popup.document.body);
+            // add inspector     
+            let inspector      = Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
+            // and build it in the popup  
+            this._buildInspector(inspector); 
+            // Rebuild it
+            this.refresh();              
+        }
+    }
+}

+ 48 - 0
inspector/ts/inspector/adapters/Adapter.ts

@@ -0,0 +1,48 @@
+module INSPECTOR {
+    
+    export interface IHighlight {
+        highlight : (b:boolean) => void
+    }
+
+    export abstract class Adapter implements IHighlight {
+        
+        protected _obj      : any;
+        // a unique name for this adapter, to retrieve its own key in the local storage
+        private static _name: string = BABYLON.Geometry.RandomId();
+        
+        constructor(obj:any) {
+            this._obj = obj;
+        }
+        
+
+        /** Returns the name displayed in the tree */
+        public abstract id()           : string;
+        
+        /** Returns the type of this object - displayed in the tree */
+        public abstract type()         : string;
+        
+        /** Returns the list of properties to be displayed for this adapter */
+        public abstract getProperties(): Array<PropertyLine>;
+        
+        /** Returns the actual object behind this adapter */
+        public get actualObject()      : any {
+            return this._obj; 
+        }
+        
+        /** Returns true if the given object correspond to this  */
+        public correspondsTo(obj:any) {
+            return obj === this._obj;
+        }
+
+        /** Returns the adapter unique name */
+        public get name(): string {
+            return Adapter._name;
+        }
+
+        /** Returns the list of tools available for this adapter */
+        public abstract getTools() : Array<AbstractTreeTool>;
+
+        /** Should be overriden in subclasses */
+        public highlight(b:boolean) {};
+    }
+}

+ 71 - 0
inspector/ts/inspector/adapters/Canvas2DAdapter.ts

@@ -0,0 +1,71 @@
+module INSPECTOR {
+    
+    export class Canvas2DAdapter 
+        extends Adapter 
+        implements IToolVisible, IToolDebug {
+        
+        constructor(obj:any) {
+            super(obj);
+        }
+        
+        /** Returns the name displayed in the tree */
+        public id() : string {
+            let str = '';
+            if (this._obj.id) {
+                str = this._obj.id;
+            } // otherwise nothing displayed        
+            return str;
+        }
+        
+        /** Returns the type of this object - displayed in the tree */
+        public type() : string{
+            return Helpers.GET_TYPE(this._obj);
+        }
+        
+        /** Returns the list of properties to be displayed for this adapter */
+        public getProperties() : Array<PropertyLine> {
+            let propertiesLines = [];
+            if (this._obj.propDic) {
+                let dico = this._obj.propDic as BABYLON.StringDictionary<BABYLON.Prim2DPropInfo>;
+                dico.forEach((name, propInfo) => {
+                    let property = new Property(name, this.actualObject);
+                    propertiesLines.push(new PropertyLine(property));
+                });
+            }            
+            // TODO REMOVE THIS WHEN PROPERTIES WILL BE DECORATED
+            let toAddDirty = [
+                'actualZOffset', 'isSizeAuto', 'layoutArea', 'layoutAreaPos', 'contentArea', 
+                'marginOffset', 'paddingOffset', 'isPickable', 'isContainer', 'boundingInfo', 
+                'levelBoundingInfo', 'isSizedByContent', 'isPositionAuto', 'actualScale', 'layoutBoundingInfo'];
+            for (let dirty of toAddDirty) {
+                let infos = new Property(dirty, this.actualObject);
+                propertiesLines.push(new PropertyLine(infos));
+            }
+            return propertiesLines; 
+        }
+
+        public getTools() : Array<AbstractTreeTool> {
+            let tools = [];
+            tools.push(new Checkbox(this));
+            tools.push(new DebugArea(this));   
+            return tools;
+        }
+
+        /// TOOLS ///
+        public setVisible(b:boolean) {
+            this._obj.levelVisible = b;
+        }
+        public isVisible():boolean{
+            return this._obj.levelVisible;
+        }
+        /** Overrides super */
+        public debug(b:boolean) {
+            this._obj["displayDebugAreas"] = b;
+        }
+        /** Overrides super.highlight */
+        public highlight(b:boolean) {
+        }
+        
+        
+    }
+}

+ 70 - 0
inspector/ts/inspector/adapters/LightAdapter.ts

@@ -0,0 +1,70 @@
+module INSPECTOR {
+    
+    export class LightAdapter 
+        extends Adapter 
+        implements IToolVisible{
+
+        private static _PROPERTIES = [
+            'position',
+            'diffuse', 
+            'intensity', 
+            'radius', 
+            'range', 
+            'specular'
+        ];
+        
+        constructor(obj:BABYLON.Light) {
+            super(obj);
+        }
+        
+        /** Returns the name displayed in the tree */
+        public id() : string {
+            let str = '';
+            if (this._obj.name) {
+                str = this._obj.name;
+            } // otherwise nothing displayed        
+            return str;
+        }
+        
+        /** Returns the type of this object - displayed in the tree */
+        public type() : string{
+            return Helpers.GET_TYPE(this._obj);
+        }
+        
+        /** Returns the list of properties to be displayed for this adapter */
+        public getProperties() : Array<PropertyLine> {
+           let propertiesLines : Array<PropertyLine> = [];
+                
+            for (let dirty of LightAdapter._PROPERTIES) {
+                let infos = new Property(dirty, this._obj);
+                propertiesLines.push(new PropertyLine(infos));
+            }
+            return propertiesLines; 
+        }
+        
+        public getTools() : Array<AbstractTreeTool> {
+            let tools = [];
+            tools.push(new Checkbox(this));
+            return tools;
+        }
+        
+        public setVisible(b:boolean) {
+            this._obj.setEnabled(b);
+        }        
+        public isVisible() : boolean {
+            return this._obj.isEnabled(); 
+        }
+
+        /** Returns some information about this mesh */
+        // public getInfo() : string {
+        //     return `${(this._obj as BABYLON.AbstractMesh).getTotalVertices()} vertices`;
+        // }
+        
+        /** Overrides super.highlight */
+        public highlight(b:boolean) {
+            this.actualObject.renderOutline = b;
+			this.actualObject.outlineWidth = 0.25;
+			this.actualObject.outlineColor = BABYLON.Color3.Yellow();
+        }
+    }
+}

+ 54 - 0
inspector/ts/inspector/adapters/MaterialAdapter.ts

@@ -0,0 +1,54 @@
+module INSPECTOR {
+    
+    export class MaterialAdapter 
+        extends Adapter {
+
+        constructor(obj:BABYLON.Material) {
+            super(obj);
+        }
+        
+        /** Returns the name displayed in the tree */
+        public id() : string {
+            let str = '';
+            if (this._obj.name) {
+                str = this._obj.name;
+            } // otherwise nothing displayed        
+            return str;
+        }
+        
+        /** Returns the type of this object - displayed in the tree */
+        public type() : string{
+            return Helpers.GET_TYPE(this._obj);
+        }
+        
+        /** Returns the list of properties to be displayed for this adapter */
+        public getProperties() : Array<PropertyLine> {
+            let propertiesLines : Array<PropertyLine> = [];
+            let propToDisplay =  PROPERTIES[this.type()].properties;
+
+            for (let dirty of propToDisplay) {
+                let infos = new Property(dirty, this._obj);
+                propertiesLines.push(new PropertyLine(infos));
+            }
+            return propertiesLines; 
+        }
+        
+        /** No tools for a material adapter */
+        public getTools() : Array<AbstractTreeTool> {
+            return [];
+        }
+
+        /** Overrides super.highlight.
+         * Highlighting a material outlines all meshes linked to this material
+         */
+        public highlight(b:boolean) {
+            let material = this.actualObject as BABYLON.Material;
+            let meshes = material.getBindedMeshes();
+            for (let mesh of meshes) {
+                mesh.renderOutline = b;
+                mesh.outlineWidth = 0.25;
+                mesh.outlineColor = BABYLON.Color3.Yellow();
+            }
+        }
+    }
+}

+ 133 - 0
inspector/ts/inspector/adapters/MeshAdapter.ts

@@ -0,0 +1,133 @@
+module INSPECTOR {
+    
+    export class MeshAdapter 
+        extends Adapter 
+        implements IToolVisible, IToolDebug, IToolBoundingBox, IToolInfo{
+
+        /** Keep track of the axis of the actual object */ 
+        private _axis : Array<BABYLON.Mesh> = [];
+        
+        constructor(obj:BABYLON.AbstractMesh) {
+            super(obj);
+        }
+        
+        /** Returns the name displayed in the tree */
+        public id() : string {
+            let str = '';
+            if (this._obj.name) {
+                str = this._obj.name;
+            } // otherwise nothing displayed        
+            return str;
+        }
+        
+        /** Returns the type of this object - displayed in the tree */
+        public type() : string{
+            return Helpers.GET_TYPE(this._obj);
+        }
+        
+        /** Returns the list of properties to be displayed for this adapter */
+        public getProperties() : Array<PropertyLine> {
+           let propertiesLines : Array<PropertyLine> = [];
+                
+            for (let dirty of PROPERTIES['Mesh'].properties) {
+                let infos = new Property(dirty, this._obj);
+                propertiesLines.push(new PropertyLine(infos));
+            }
+            return propertiesLines; 
+        }
+        
+        public getTools() : Array<AbstractTreeTool> {
+            let tools = [];
+            tools.push(new Checkbox(this));
+            tools.push(new DebugArea(this));  
+            tools.push(new BoundingBox(this));   
+            tools.push(new Info(this));   
+            return tools;
+        }
+        
+        public setVisible(b:boolean) {
+            this._obj.setEnabled(b);
+            this._obj.isVisible = b;
+        }        
+        public isVisible() : boolean {
+            return this._obj.isEnabled() && this._obj.isVisible;
+        }
+        public isBoxVisible() : boolean {
+            return (this._obj as BABYLON.AbstractMesh).showBoundingBox;
+        }
+        public setBoxVisible(b:boolean) {            
+            return (this._obj as BABYLON.AbstractMesh).showBoundingBox = b;
+        }
+ 
+        public debug(b:boolean) {
+            // Draw axis the first time
+            if (this._axis.length == 0) {
+                this._drawAxis();
+            }
+            // Display or hide axis
+            for (let ax of this._axis) {
+                ax.setEnabled(b);
+            }
+        }
+
+        /** Returns some information about this mesh */
+        public getInfo() : string {
+            return `${(this._obj as BABYLON.AbstractMesh).getTotalVertices()} vertices`;
+        }
+        
+        /** Overrides super.highlight */
+        public highlight(b:boolean) {
+            this.actualObject.renderOutline = b;
+			this.actualObject.outlineWidth = 0.25;
+			this.actualObject.outlineColor = BABYLON.Color3.Yellow();
+        }
+        /** Draw X, Y and Z axis for the actual object if this adapter.
+         * Should be called only one time as it will fill this._axis
+         */
+        private _drawAxis() {
+            this._obj.computeWorldMatrix();
+            var m = this._obj.getWorldMatrix();
+            
+            // Axis
+            var x = new BABYLON.Vector3(8,0,0);
+            var y = new BABYLON.Vector3(0,8,0);
+            var z = new BABYLON.Vector3(0,0,8);
+            
+            // Draw an axis of the given color
+            let _drawAxis = (color, start, end) : BABYLON.LinesMesh => {
+                let axis = BABYLON.Mesh.CreateLines("###axis###", [
+                    start,
+                    end
+                ], this._obj.getScene());
+                axis.color = color;
+                axis.renderingGroupId = 1;
+                return axis;
+            };
+            
+            // X axis
+            let xAxis = _drawAxis(
+                    BABYLON.Color3.Red(), 
+                    this._obj.getAbsolutePosition(),
+                    BABYLON.Vector3.TransformCoordinates(x, m));
+            xAxis.position.subtractInPlace(this._obj.position);
+            xAxis.parent = this._obj;
+            this._axis.push(xAxis);
+            // Y axis        
+            let yAxis = _drawAxis(
+                    BABYLON.Color3.Green(), 
+                    this._obj.getAbsolutePosition(),
+                    BABYLON.Vector3.TransformCoordinates(y, m));
+            yAxis.parent = this._obj;
+            yAxis.position.subtractInPlace(this._obj.position);
+            this._axis.push(yAxis);
+            // Z axis
+            let zAxis = _drawAxis(
+                    BABYLON.Color3.Blue(), 
+                    this._obj.getAbsolutePosition(),
+                    BABYLON.Vector3.TransformCoordinates(z, m));
+            zAxis.parent = this._obj;
+            zAxis.position.subtractInPlace(this._obj.position);
+            this._axis.push(zAxis);
+        }
+    }
+}

+ 160 - 0
inspector/ts/inspector/details/DetailPanel.ts

@@ -0,0 +1,160 @@
+ module INSPECTOR {
+    export interface SortDirection {
+        [property: string]: number;
+    }
+
+    export class DetailPanel extends BasicElement {
+
+        // The header row
+        private _headerRow : HTMLElement;
+        // Contains all details rows that belongs to the item above
+        private _detailRows : Array<PropertyLine> = [];
+        // Store the sort direction of each header column
+        private _sortDirection : SortDirection = {};
+
+        constructor(dr? : Array<PropertyLine>) {
+            super();
+            this._build();
+            
+            if (dr) {
+                this._detailRows = dr;
+                this.update();
+            }
+        }
+
+        set details(detailsRow : Array<PropertyLine>) {
+            this.clean();   
+            this._detailRows = detailsRow;
+            
+            // Refresh HTML
+            this.update();
+        }
+        protected _build() {            
+            this._div.className = 'insp-details';
+            this._div.id = 'insp-details';
+            
+            // Create header row
+            this._createHeaderRow();
+            this._div.appendChild(this._headerRow);
+            
+            window.addEventListener('resize', (e) => {
+                // adapt the header row max width according to its parent size;
+                this._headerRow.style.maxWidth = this._headerRow.parentElement.clientWidth+'px';
+            });
+        }
+        
+        /** Updates the HTML of the detail panel */
+        public update() {                 
+            this._sortDetails('name', 1);  
+            this._addDetails();
+        }
+
+        /** Add all lines in the html div. Does not sort them! */
+        private _addDetails() {
+            let details = Helpers.CreateDiv('details', this._div);
+            for (let row of this._detailRows) {
+                details.appendChild(row.toHtml());
+            }
+        }
+
+        /**
+         * Sort the details row by comparing the given property of each row
+         */
+        private _sortDetails(property:string, _direction?:number) {
+                
+            // Clean header
+            let elems = Inspector.DOCUMENT.querySelectorAll('.sort-direction');
+            for (let e=0; e<elems.length; e++) {
+                elems[e].classList.remove('fa-chevron-up');
+                elems[e].classList.remove('fa-chevron-down');
+            }
+
+
+            if (_direction || !this._sortDirection[property]) {
+                this._sortDirection[property] = _direction || 1;
+            } else {
+                this._sortDirection[property] *= -1;
+            }
+            let direction = this._sortDirection[property];
+            if (direction == 1) {
+                this._headerRow.querySelector(`#sort-direction-${property}`).classList.remove('fa-chevron-down');
+                this._headerRow.querySelector(`#sort-direction-${property}`).classList.add('fa-chevron-up');
+            } else {
+                this._headerRow.querySelector(`#sort-direction-${property}`).classList.remove('fa-chevron-up');
+                this._headerRow.querySelector(`#sort-direction-${property}`).classList.add('fa-chevron-down');
+            }
+
+            let isString = (s) => {
+                return typeof(s) === 'string' || s instanceof String;
+            };
+
+            this._detailRows.sort((detail1, detail2) : number => {
+                let str1 = String(detail1[property]);
+                let str2 = String(detail2[property]);
+                if (!isString(str1)) {
+                    str1 = detail1[property].toString();
+                }
+                if (!isString(str2)) {
+                    str2 = detail2[property].toString();
+                }
+                // Compare numbers as numbers and string as string with 'numeric=true'
+                return str1.localeCompare(str2, [], {numeric:true}) * direction;
+            });
+        }
+
+        /**
+         * Removes all data in the detail panel but keep the header row
+         */
+        public clean() {   
+                        
+            // Delete all details row
+            for (let pline of this._detailRows) {
+                pline.dispose();
+            }
+            
+            Helpers.CleanDiv(this._div);
+            // Header row
+            this._div.appendChild(this._headerRow);
+        }
+
+        /** Overrides basicelement.dispose */
+        public dispose() {
+            // Delete all details row
+            for (let pline of this._detailRows) {
+                pline.dispose();
+            }
+        }
+
+        /**
+         * Creates the header row : name, value, id
+         */
+        private _createHeaderRow() {   
+            this._headerRow = Helpers.CreateDiv('header-row');
+
+            let createDiv = (name:string, cssClass:string)  : HTMLElement => {
+                let div =  Helpers.CreateDiv(cssClass+' header-col');
+
+                // Column title - first letter in uppercase
+                let spanName = Inspector.DOCUMENT.createElement('span');
+                spanName.textContent = name.charAt(0).toUpperCase() + name.slice(1); 
+                
+                // sort direction
+                let spanDirection = Inspector.DOCUMENT.createElement('i');
+                spanDirection.className = 'sort-direction fa';
+                spanDirection.id = 'sort-direction-'+name; 
+
+                div.appendChild(spanName);
+                div.appendChild(spanDirection);
+
+                div.addEventListener('click', (e) => {
+                    this._sortDetails(name);
+                    this._addDetails();
+                });
+                return div;
+            };
+
+            this._headerRow.appendChild(createDiv('name', 'prop-name'));
+            this._headerRow.appendChild(createDiv('value', 'prop-value'));
+        }
+    }
+ }

+ 41 - 0
inspector/ts/inspector/details/Property.ts

@@ -0,0 +1,41 @@
+module INSPECTOR {
+    
+    /**
+     * A property is a link between a data (string) and an object.
+     */
+    export class Property {
+        
+        /** The property name */
+        private _property   : string;
+        /** The obj this property refers to */
+        private _obj        : any;
+        
+        constructor(prop:string, obj:any) {
+            this._property = prop;
+            this._obj = obj;
+        }
+        
+        public get name() : string {
+            return this._property;
+        }
+        
+        public get value() : any {
+            return this._obj[this._property];
+        }
+        public set value(newValue:any) {
+            this._obj[this._property] = newValue;
+        }
+        
+        public get type() :string {
+            return Helpers.GET_TYPE(this.value);
+        }
+        
+        public get obj() : any {
+            return this._obj;
+        }
+        public set obj(newObj : any)  {
+            this._obj = newObj;
+        }
+        
+    }
+}

+ 357 - 0
inspector/ts/inspector/details/PropertyLine.ts

@@ -0,0 +1,357 @@
+module INSPECTOR {
+
+    export class PropertyFormatter {
+
+        /**
+         * Format the value of the given property of the given object.
+         */
+        public static format(obj: any, prop:string) : string {
+            // Get original value;
+            let value = obj[prop];
+            // PrimitiveAlignment
+            if (obj instanceof BABYLON.PrimitiveAlignment) {
+                if (prop === 'horizontal') {
+                    switch(value) {
+                        case BABYLON.PrimitiveAlignment.AlignLeft:
+                            return 'left';
+                        case BABYLON.PrimitiveAlignment.AlignRight:
+                            return 'right';
+                        case BABYLON.PrimitiveAlignment.AlignCenter:
+                            return 'center';
+                        case BABYLON.PrimitiveAlignment.AlignStretch:
+                            return 'stretch';
+                    }
+                } else if (prop === 'vertical') {
+                    switch(value) {
+                        case BABYLON.PrimitiveAlignment.AlignTop:
+                            return 'top';
+                        case BABYLON.PrimitiveAlignment.AlignBottom:
+                            return 'bottom';
+                        case BABYLON.PrimitiveAlignment.AlignCenter:
+                            return 'center';
+                        case BABYLON.PrimitiveAlignment.AlignStretch:
+                            return 'stretch';
+                    }
+                }
+            }
+            return value;
+        }
+
+    }
+
+    /**
+     * A property line represents a line in the detail panel. This line is composed of : 
+     * - a name (the property name)
+     * - a value if this property is of a type 'simple' : string, number, boolean, color, texture
+     * - the type of the value if this property is of a complex type (Vector2, Size, ...)
+     * - a ID if defined (otherwise an empty string is displayed)
+     * The original object is sent to the value object who will update it at will.
+     * 
+     * A property line can contain OTHER property line objects in the case of a complex type.
+     * If this instance has no link to other instances, its type is ALWAYS a simple one (see above).
+     * 
+     */
+    export class PropertyLine {
+        
+        // The property can be of any type (Property internally can have any type), relative to this._obj
+        private _property : Property;    
+        //The HTML element corresponding to this line
+        private _div : HTMLElement;
+        // The div containing the value to display. Used to update dynamically the property
+        private _valueDiv : HTMLElement;
+        // If the type is complex, this property will have child to update
+        private _children : Array<PropertyLine> = [];        
+        // Array representing the simple type. All others are considered 'complex'
+        private static _SIMPLE_TYPE = ['number', 'string', 'boolean'];
+        // The number of pixel at each children step
+        private static _MARGIN_LEFT = 15;
+        // The margin-left used to display to row
+        private _level : number;
+        /** The list of viewer element displayed at the end of the line (color, texture...) */
+        private _elements : Array<BasicElement> = [];
+        /** The property parent of this one. Used to update the value of this property and to retrieve the correct object */
+        private _parent : PropertyLine;
+        /** The input element to display if this property is 'simple' in order to update it */
+        private _input : HTMLInputElement;
+        /** Display input handler (stored to be removed afterwards) */
+        private _displayInputHandler : EventListener;
+        /** Handler used to validate the input by pressing 'enter' */
+        private _validateInputHandler : EventListener;
+        
+        constructor(prop : Property, parent?: PropertyLine, level:number=0) {
+            this._property = prop;
+            this._level    = level;       
+            this._parent   = parent;   
+                         
+            this._div = Helpers.CreateDiv('row');
+            this._div.style.marginLeft = `${this._level}px`;
+            
+            // Property name
+            let propName : HTMLElement = Helpers.CreateDiv('prop-name', this._div);
+            propName.textContent = `${this.name}`;
+
+            // Value
+            this._valueDiv = Helpers.CreateDiv('prop-value', this._div);
+            this._valueDiv.textContent = this._displayValueContent() || '-'; // Init value text node
+            
+            this._createElements();
+            
+            for (let elem of this._elements) {
+                this._valueDiv.appendChild(elem.toHtml());
+            }
+            
+            this._updateValue();
+
+            // If the property type is not simple, add click event to unfold its children
+            if (!this._isSimple()) { 
+                this._valueDiv.classList.add('clickable');
+                this._valueDiv.addEventListener('click', this._addDetails.bind(this));
+            } else {
+                this._initInput();
+                this._valueDiv.addEventListener('click', this._displayInputHandler);
+                this._input.addEventListener('keypress', this._validateInputHandler);
+            }
+            
+            // Add this property to the scheduler
+            Scheduler.getInstance().add(this);
+        }
+
+        /** 
+         * Init the input element and al its handler : 
+         * - a click in the window remove the input and restore the old property value
+         * - enters updates the property
+         */
+        private _initInput() {
+            
+            // Create the input element
+            this._input = document.createElement('input') as HTMLInputElement;
+            this._input.setAttribute('type', 'text');
+
+            // if the property is 'simple', add an event listener to create an input
+            this._displayInputHandler  = this._displayInput.bind(this);
+            this._validateInputHandler = this._validateInput.bind(this);
+        }
+
+        /** 
+         * On enter : validates the new value and removes the input
+         * On escape : removes the input
+         */
+        private _validateInput(e : KeyboardEvent) {
+            if (e.keyCode == 13) {
+                // Enter : validate the new value
+                let newValue = this._input.value;
+                this.updateObject();
+                this._property.value = newValue;
+                // Remove input
+                this.update();
+                // resume scheduler
+                Scheduler.getInstance().pause = false;
+                
+            } else if (e.keyCode == 27) { 
+                // Esc : remove input
+                this.update();
+            }
+        }
+
+        /** Removes the input without validating the new value */
+        private _removeInputWithoutValidating() {
+            Helpers.CleanDiv(this._valueDiv);
+            this._valueDiv.textContent = "-";
+            // restore elements
+            for (let elem of this._elements) {
+                this._valueDiv.appendChild(elem.toHtml());
+            }            
+            this._valueDiv.addEventListener('click', this._displayInputHandler);
+        }
+
+        /** Replaces the default display with an input */
+        private _displayInput(e) {
+            // Remove the displayInput event listener
+            this._valueDiv.removeEventListener('click', this._displayInputHandler);
+
+            // Set input value
+            let valueTxt = this._valueDiv.textContent;
+            this._valueDiv.textContent = "";
+            this._input.value = valueTxt;
+            this._valueDiv.appendChild(this._input);
+            
+            // Pause the scheduler
+            Scheduler.getInstance().pause = true;
+        }
+
+        /** Retrieve the correct object from its parent. 
+         * If no parent exists, returns the property value.
+         * This method is used at each update in case the property object is removed from the original object 
+         * (example : mesh.position = new BABYLON.Vector3 ; the original vector3 object is deleted from the mesh).
+        */
+        public updateObject() {
+            if (!this._parent) {
+                return this._property.value;
+            }
+            else {
+                this._property.obj = this._parent.updateObject();
+            } 
+        }
+
+        // Returns the property name
+        public get name() : string {
+            return this._property.name;
+        }
+
+        // Returns the value of the property
+        public get value() : any {
+            return PropertyFormatter.format(this._property.obj, this._property.name);
+        }
+
+        // Returns the type of the property
+        public get type() : string {
+            return this._property.type;
+        }
+        
+        /**
+         * Creates elements that wil be displayed on a property line, depending on the
+         * type of the property.
+         */
+        private _createElements() {
+            
+            // Colors
+            if (this.type == 'Color3' ||this.type == 'Color4') {
+                this._elements.push(new ColorElement(this.value));
+            } 
+            // Texture
+            if (this.type == 'Texture') {
+                this._elements.push(new TextureElement(this.value));
+            }   
+            // HDR Texture
+            if (this.type == 'HDRCubeTexture') {
+                this._elements.push(new HDRCubeTextureElement(this.value));
+            }      
+            if (this.type == 'CubeTexture') {
+                this._elements.push(new CubeTextureElement(this.value));
+            }      
+        }
+
+        // Returns the text displayed on the left of the property name : 
+        // - If the type is simple, display its value
+        // - If the type is complex, but instance of Vector2, Size, display the type and its tostring
+        // - If the type is another one, display the Type
+        private _displayValueContent () {
+            
+            let value = this.value;
+            // If the value is a number, truncate it if needed
+            if (typeof value === 'number') {
+                return Helpers.Trunc(value);                
+            } 
+            // If it's a string or a boolean, display its value
+            if (typeof value === 'string' || typeof value === 'boolean') {
+                return value;          
+            } 
+            return PROPERTIES.format(value);
+            
+        }
+        
+        /** Delete properly this property line. 
+         * Removes itself from the scheduler.
+         * Dispose all viewer element (color, texture...)
+         */
+        public dispose() {
+            // console.log('delete properties', this.name);
+            Scheduler.getInstance().remove(this); 
+            for (let child of this._children) {
+                // console.log('delete properties', child.name);
+                Scheduler.getInstance().remove(child); 
+            }
+            for (let elem of this._elements) {
+                elem.dispose();
+            }
+            this._elements = [];
+        }
+
+        /** Updates the content of _valueDiv with the value of the property, 
+         * and all HTML element correpsonding to this type.
+         * Elements are updated as well
+         */
+        private _updateValue() {
+            // Update the property object first
+            this.updateObject();
+            // Then update its value
+            // this._valueDiv.textContent = " "; // TOFIX this removes the elements after
+            this._valueDiv.childNodes[0].nodeValue = this._displayValueContent();
+            for (let elem of this._elements) {
+                elem.update(this.value);
+            }
+        }
+        
+        /**
+         * Update the property division with the new property value. 
+         * If this property is complex, update its child, otherwise update its text content
+         */
+        public update() {            
+           this._removeInputWithoutValidating();
+           this._updateValue();
+        }
+        
+        /**
+         * Returns true if the given instance is a simple type  
+         */
+        private static _IS_TYPE_SIMPLE(inst:any) {
+            let type = Helpers.GET_TYPE(inst);
+            return PropertyLine._SIMPLE_TYPE.indexOf(type) != -1;
+        }
+
+        /**
+         * Returns true if the type of this property is simple, false otherwise.
+         * Returns true if the value is null
+         */
+        private _isSimple() : boolean {
+            if (this.value != null) {
+                if (PropertyLine._SIMPLE_TYPE.indexOf(this.type) == -1) {
+                    // complex type : return the type name
+                    return false;
+                } else {
+                    // simple type : return value
+                    return true; 
+                }
+            } else {
+                return true;
+            }
+        }
+
+        public toHtml() : HTMLElement {
+            return this._div;
+        }
+
+        /**
+         * Add sub properties in case of a complex type
+         */
+        private _addDetails() {
+            if (this._div.classList.contains('unfolded')) {            
+                // Remove class unfolded
+                this._div.classList.remove('unfolded');
+                // remove html children
+                for (let child of this._children) {
+                    this._div.parentNode.removeChild(child.toHtml());
+                }
+            } else {
+                // if children does not exists, generate it
+                this._div.classList.toggle('unfolded');
+                if (this._children.length ==0) {                    
+                    let objToDetail = this.value;
+                    let propToDisplay = PROPERTIES[Helpers.GET_TYPE(objToDetail)].properties.reverse();
+                    let propertyLine = null;
+                    
+                    for (let prop of propToDisplay) {
+                        let infos = new Property(prop, this._property.value);
+                        let child = new PropertyLine(infos, this, this._level+PropertyLine._MARGIN_LEFT);
+                        this._children.push(child);
+                    }
+                } 
+                // otherwise display it                    
+                for (let child of this._children) {
+                    this._div.parentNode.insertBefore(child.toHtml(), this._div.nextSibling);
+                }
+            }            
+        }        
+    } 
+    
+}

+ 32 - 0
inspector/ts/inspector/gui/BasicElement.ts

@@ -0,0 +1,32 @@
+ module INSPECTOR {
+    /**
+     * Represents a html div element. 
+     * The div is built when an instance of BasicElement is created.
+     */
+    export abstract class BasicElement {
+        
+        protected _div : HTMLElement;
+        
+        constructor() {        
+            this._div = Helpers.CreateDiv();  
+        }
+            
+        /**
+         * Returns the div element
+         */
+        public toHtml() : HTMLElement {
+            return this._div;
+        }
+        
+        /**
+         * Build the html element
+         */
+        protected _build(){};
+        
+        public abstract update(data?:any);
+        
+        /** Default dispose method if needed */
+        public dispose() {};
+        
+    }
+}

+ 36 - 0
inspector/ts/inspector/gui/ColorElement.ts

@@ -0,0 +1,36 @@
+ module INSPECTOR {
+     /**
+     * Display a very small div corresponding to the given color
+     */
+    export class ColorElement extends BasicElement{
+                
+        // The color as hexadecimal string
+        constructor(color:BABYLON.Color4|BABYLON.Color3) {
+            super();
+            this._div.className = 'color-element';
+            this._div.style.backgroundColor = this._toRgba(color);
+        }
+        
+        public update(color?:BABYLON.Color4|BABYLON.Color3) {
+            if (color) {
+                this._div.style.backgroundColor = this._toRgba(color);
+            }
+        }
+        
+        
+        private _toRgba(color:BABYLON.Color4|BABYLON.Color3) : string {
+            if (color) {
+                let r = (color.r * 255) | 0;
+                let g = (color.g * 255) | 0;
+                let b = (color.b * 255) | 0;
+                let a = 1;
+                if (color instanceof BABYLON.Color4) {
+                    let a = (color as BABYLON.Color4).a;
+                }
+                return `rgba(${r}, ${g}, ${b}, ${a})`;
+            } else {
+                return '';
+            }
+        }
+    }
+ }

+ 119 - 0
inspector/ts/inspector/gui/CubeTextureElement.ts

@@ -0,0 +1,119 @@
+ module INSPECTOR {
+     /**
+     * Display a very small div. A new canvas is created, with a new Babylon.js scene, containing only the 
+     * cube texture in a cube
+     */
+    export class CubeTextureElement extends BasicElement{
+
+        /** The big div displaying the full image */
+        private _textureDiv: HTMLElement;
+        
+        private _engine    : BABYLON.Engine;
+        protected _scene     : BABYLON.Scene;
+        protected _cube      : BABYLON.Mesh;
+        private _canvas    : HTMLCanvasElement;
+        protected _textureUrl: string;
+        
+        // On pause the engine will not render anything
+        private _pause     : boolean = false;
+            
+        /** The texture given as a parameter should be cube. */
+        constructor(tex : BABYLON.Texture) {
+            super();
+            this._div.className    = 'fa fa-search texture-element';
+            
+            // Create the texture viewer
+            this._textureDiv       = Helpers.CreateDiv('texture-viewer', this._div);
+            // canvas
+            this._canvas             = Helpers.CreateElement('canvas', 'texture-viewer-img', this._textureDiv) as HTMLCanvasElement;
+            
+            if (tex) {
+                this._textureUrl = tex.name;
+            }
+
+            this._div.addEventListener('mouseover', this._showViewer.bind(this, 'flex'));
+            this._div.addEventListener('mouseout', this._showViewer.bind(this, 'none')); 
+
+        }
+        
+        public update(tex?:BABYLON.Texture) {
+            if (tex && tex.url === this._textureUrl) {
+                // Nothing to do, as the old texture is the same as the old one
+            } else {                    
+                if (tex) {
+                    this._textureUrl = tex.name;
+                }
+                if (this._engine) {
+                    // Dispose old material and cube
+                    this._cube.material.dispose(true, true);
+                    this._cube.dispose();
+                } else {
+                    this._initEngine();
+                }
+                // and create it again
+                this._populateScene();
+            }       
+        }
+        
+        /** Creates the box  */
+        protected _populateScene() {
+            // Create the hdr texture
+            let hdrTexture                      = new BABYLON.CubeTexture(this._textureUrl, this._scene);
+            hdrTexture.coordinatesMode          = BABYLON.Texture.SKYBOX_MODE;
+            
+            this._cube                          = BABYLON.Mesh.CreateBox("hdrSkyBox", 10.0, this._scene);
+            let hdrSkyboxMaterial               = new BABYLON.StandardMaterial("skyBox", this._scene);
+            hdrSkyboxMaterial.backFaceCulling   = false;
+            hdrSkyboxMaterial.reflectionTexture = hdrTexture;
+            hdrSkyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;        
+            hdrSkyboxMaterial.disableLighting   = true;
+            this._cube.material                 = hdrSkyboxMaterial;
+            this._cube.registerBeforeRender(() => {
+                this._cube.rotation.y += 0.01;
+            })
+        }
+        
+        /** Init the babylon engine */
+        private _initEngine() {
+            this._engine           = new BABYLON.Engine(this._canvas);
+            this._scene            = new BABYLON.Scene(this._engine);
+            this._scene.clearColor = new BABYLON.Color4(0,0,0, 0);
+            let cam                = new BABYLON.FreeCamera('cam', new BABYLON.Vector3(0,0,-20), this._scene);
+            let light              = new BABYLON.HemisphericLight('', new BABYLON.Vector3(0,1,0), this._scene);
+            
+            this._engine.runRenderLoop(() => {
+                if (!this._pause) {
+                    this._scene.render();
+                }
+            });
+            
+            this._canvas.setAttribute('width', '110');
+            this._canvas.setAttribute('height', '110');
+        }
+
+        private _showViewer(mode:string) {
+            // If displaying...
+            if (mode != 'none') {
+                // ... and the canvas is not initialized                
+                if (!this._engine) {
+                    this._initEngine();
+                    this._populateScene();
+                }
+                // In every cases, unpause the engine
+                this._pause = false;
+            } else {
+                // hide : pause the engine
+                this._pause = true;
+            }
+            this._textureDiv.style.display = mode;
+        }
+        
+        /** Removes properly the babylon engine */
+        public dispose () {
+            if (this._engine) {
+                this._engine.dispose();
+                this._engine = null;
+            }
+        }
+    }
+ }

+ 32 - 0
inspector/ts/inspector/gui/HDRCubeTextureElement.ts

@@ -0,0 +1,32 @@
+ module INSPECTOR {
+     /**
+     * Display a very small div. A new canvas is created, with a new Babylon.js scene, containing only the 
+     * cube texture in a cube
+     */
+    export class HDRCubeTextureElement extends CubeTextureElement{
+
+            
+        /** The texture given as a parameter should be cube. */
+        constructor(tex : BABYLON.Texture) {
+            super(tex);
+        }
+        
+        /** Creates the box  */
+        protected _populateScene() {
+            // Create the hdr texture
+            let hdrTexture                      = new BABYLON.HDRCubeTexture(this._textureUrl, this._scene, 512);
+            hdrTexture.coordinatesMode          = BABYLON.Texture.SKYBOX_MODE;
+            
+            this._cube                          = BABYLON.Mesh.CreateBox("hdrSkyBox", 10.0, this._scene);
+            let hdrSkyboxMaterial               = new BABYLON.PBRMaterial("skyBox", this._scene);
+            hdrSkyboxMaterial.backFaceCulling   = false;
+            hdrSkyboxMaterial.reflectionTexture = hdrTexture;
+            hdrSkyboxMaterial.microSurface      = 1.0;
+            hdrSkyboxMaterial.disableLighting   = true;
+            this._cube.material                 = hdrSkyboxMaterial;
+            this._cube.registerBeforeRender(() => {
+                this._cube.rotation.y += 0.01;
+            })
+        }        
+    }
+ }

+ 42 - 0
inspector/ts/inspector/gui/SearchBar.ts

@@ -0,0 +1,42 @@
+module INSPECTOR {
+
+    /**
+     * A search bar can be used to filter elements in the tree panel.
+     * At each keypress on the input, the treepanel will be filtered.
+     */
+    export class SearchBar extends BasicElement {
+
+        private _tab   : PropertyTab;
+        private _inputElement: HTMLInputElement;
+
+        constructor(tab:PropertyTab) {
+            super();
+            this._tab = tab;
+            this._div.classList.add('searchbar');
+            
+            let filter = Inspector.DOCUMENT.createElement('i');
+            filter.className = 'fa fa-search';
+            
+            this._div.appendChild(filter);
+            // Create input
+            this._inputElement = Inspector.DOCUMENT.createElement('input');
+            this._inputElement.placeholder = 'Filter by name...';
+            this._div.appendChild(this._inputElement);
+            
+            this._inputElement.addEventListener('keyup', (evt : KeyboardEvent)=> {
+                let filter = this._inputElement.value;
+                this._tab.filter(filter);
+            })
+        }
+
+        /** Delete all characters typped in the input element */
+        public reset() {
+            this._inputElement.value = '';
+        }
+
+        public update() {
+            // Nothing to update
+        }
+
+    }
+}

+ 42 - 0
inspector/ts/inspector/gui/TextureElement.ts

@@ -0,0 +1,42 @@
+ module INSPECTOR {
+     /**
+     * Display a very small div corresponding to the given texture. On mouse over, display the full image
+     */
+    export class TextureElement extends BasicElement{
+
+        /** The big div displaying the full image */
+        private _textureDiv : HTMLElement;
+            
+        constructor(tex : BABYLON.Texture) {
+            super();
+            this._div.className = 'fa fa-search texture-element';
+
+            // Create the texture viewer
+            this._textureDiv = Helpers.CreateDiv('texture-viewer', this._div);
+            // Img
+            let imgDiv = Helpers.CreateDiv('texture-viewer-img', this._textureDiv);
+
+            // Texture size
+            let sizeDiv = Helpers.CreateDiv(null, this._textureDiv);
+            
+            if (tex) {
+                sizeDiv.textContent = `${tex.getBaseSize().width}px x ${tex.getBaseSize().height}px`;
+                imgDiv.style.backgroundImage = `url('${tex.url}')`;     
+                imgDiv.style.width = `${tex.getBaseSize().width}px`;
+                imgDiv.style.height = `${tex.getBaseSize().height}px`;
+            }
+
+            this._div.addEventListener('mouseover', this._showViewer.bind(this, 'flex'));
+            this._div.addEventListener('mouseout', this._showViewer.bind(this, 'none')); 
+
+        }
+        
+        public update(tex?:BABYLON.Texture) {
+            
+        }
+
+        private _showViewer(mode:string) {
+            this._textureDiv.style.display = mode;
+        }
+    }
+ }

+ 28 - 0
inspector/ts/inspector/gui/Tooltip.ts

@@ -0,0 +1,28 @@
+module INSPECTOR {
+    
+    /**
+     * Creates a tooltip for the given html element
+     */
+    export class Tooltip {
+        
+        /** The tooltip is displayed for this element */
+        private _elem : HTMLElement;
+        
+        /** The tooltip div */
+        private _infoDiv : HTMLDivElement;
+        
+        constructor(elem: HTMLElement, tip:string) {
+            
+            this._elem = elem;
+            
+            this._infoDiv = Helpers.CreateDiv('tooltip', this._elem) as HTMLDivElement;
+            
+
+            this._elem.addEventListener('mouseover', () => { 
+                this._infoDiv.textContent = tip;
+                this._infoDiv.style.display = 'block'
+            });
+            this._elem.addEventListener('mouseout', () => { this._infoDiv.style.display = 'none'});
+        }
+    }
+}

+ 108 - 0
inspector/ts/inspector/helpers/Helpers.ts

@@ -0,0 +1,108 @@
+module INSPECTOR {
+    export class Helpers {
+        
+        
+        /** 
+         * Returns the type of the given object. First
+         * uses getClassName. If nothing is returned, used the type of the constructor
+         */
+        public static GET_TYPE(obj:any) : string{
+            if (obj != null && obj != undefined) {
+                let classname = BABYLON.Tools.getClassName(obj);
+                if (!classname || classname === 'object'){
+                    classname = obj.constructor.name;
+                    // classname is undefined in IE11
+                    if (!classname) {
+                        classname = this._GetFnName(obj.constructor);
+                    }
+                }
+                return classname;
+            } else {
+                return '';
+            }
+        }
+
+        /**
+         * Returns the name of a function (workaround to get object type for IE11)
+         */
+        private static _GetFnName(fn) {
+            var f = typeof fn == 'function';
+            var s = f && ((fn.name && ['', fn.name]) || fn.toString().match(/function ([^\(]+)/));
+            return (!f && 'not a function') || (s && s[1] || 'anonymous');
+        }
+        
+        /** Send the event which name is given in parameter to the window */
+        public static SEND_EVENT(eventName:string){
+            let event;
+            if (Inspector.DOCUMENT.createEvent) {
+                event = Inspector.DOCUMENT.createEvent('HTMLEvents');
+                event.initEvent(eventName, true, true);
+            } else {
+                event = new Event(eventName);
+            }
+            window.dispatchEvent(event);
+        }
+        
+        /** Returns the given number with 2 decimal number max if a decimal part exists */
+        public static Trunc(nb) :number {
+            if(Math.round(nb) !== nb) {
+                return nb.toFixed(2);
+            }
+            return nb;
+        };
+        
+        /**
+         * Useful function used to create a div
+         */
+        public static CreateDiv(className?:string, parent?: HTMLElement) : HTMLElement{
+            return Helpers.CreateElement('div', className, parent);
+        }
+        
+        public static CreateElement(element:string, className?:string, parent?: HTMLElement) : HTMLElement{
+            let elem = Inspector.DOCUMENT.createElement(element);
+            
+            if (className) {
+                elem.className = className;
+            }
+            if (parent) {
+                parent.appendChild(elem);
+            }
+            return elem;
+        }
+        
+        /**
+         * Removes all children of the given div.
+         */
+        public static CleanDiv(div:HTMLElement) {
+            while ( div.firstChild ) {
+                div.removeChild(div.firstChild);
+            }
+        }
+        
+        public static LoadScript() {
+            BABYLON.Tools.LoadFile("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/highlight.min.js", (elem) => {
+                let script = Helpers.CreateElement('script', '', Inspector.DOCUMENT.body);
+                script.textContent = elem;                
+                
+                // Load glsl detection
+                BABYLON.Tools.LoadFile("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/languages/glsl.min.js", (elem) => {
+                    let script = Helpers.CreateElement('script', '', Inspector.DOCUMENT.body);
+                    script.textContent = elem;                    
+                    
+                    // Load css style
+                    BABYLON.Tools.LoadFile("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/zenburn.min.css", (elem) => {
+                        let style = Helpers.CreateElement('style', '', Inspector.DOCUMENT.body);
+                        style.textContent = elem;
+                    });
+                }, null, null, null, () => {
+                    console.log("erreur");
+                });      
+                
+            }, null, null, null, () => {
+                console.log("erreur");
+            });
+            
+        }
+
+    }
+}

+ 170 - 0
inspector/ts/inspector/properties.ts

@@ -0,0 +1,170 @@
+module INSPECTOR {
+    
+    export const PROPERTIES = {
+        /** Format the given object : 
+         * If a format function exists, returns the result of this function.
+         * If this function doesn't exists, return the object type instead */
+        format : (obj:any) => {
+            let type = Helpers.GET_TYPE(obj) ||  'default';
+            if (PROPERTIES[type] && PROPERTIES[type].format) {
+                return PROPERTIES[type].format(obj);
+            } else {
+                return Helpers.GET_TYPE(obj);
+            }
+        },
+        
+        'Vector2' : {
+            properties: ['x', 'y'],
+            format: (vec : BABYLON.Vector2) => {return `x:${Helpers.Trunc(vec.x)}, y:${Helpers.Trunc(vec.y)}`;}
+        },
+        'Vector3' : {
+            properties: ['x', 'y', 'z'],
+            format: (vec : BABYLON.Vector3) => {return `x:${Helpers.Trunc(vec.x)}, y:${Helpers.Trunc(vec.y)}, z:${Helpers.Trunc(vec.z)}`} 
+        },
+        'Color3' : {
+            properties : ['r', 'g', 'b'],
+            format: (color: BABYLON.Color3) => { return `R:${color.r}, G:${color.g}, B:${color.b}`}
+        },
+        'Quaternion' : {
+            properties : ['x', 'y', 'z', 'w']
+        },
+        'Size' : {
+            properties :['width', 'height'],
+            format: (size:BABYLON.Size) => { return `Size - w:${Helpers.Trunc(size.width)}, h:${Helpers.Trunc(size.height)}`} 
+        },
+        'Texture' : {
+            properties :[
+                'hasAlpha', 
+                'level', 
+                'name', 
+                'wrapU', 
+                'wrapV', 
+                'uScale', 
+                'vScale', 
+                'uAng', 
+                'vAng', 
+                'wAng', 
+                'uOffset', 
+                'vOffset'
+            ],
+        },
+        
+        'ArcRotateCamera' : {
+            properties : ['alpha', 'beta', 'radius']  
+        },
+        
+        'Scene' : {
+            properties:['actionManager', 'activeCamera', 'ambientColor', 'clearColor']  
+        },
+        'Mesh': {
+            properties : [
+                'name', 
+                'position', 
+                'rotation', 
+                'rotationQuaternion', 
+                'absolutePosition', 
+                'material'
+            ],
+            format : (m:BABYLON.Mesh) : string => {return m.name;}
+        },        
+        'StandardMaterial' : {
+            properties : [
+                'name', 
+                'alpha',
+                'alphaMode', 
+                'wireframe', 
+                'isFrozen', 
+                'zOffset',
+                
+                'ambientColor', 
+                'emissiveColor', 
+                'diffuseColor', 
+                'specularColor',
+                
+                'specularPower',       
+                'useAlphaFromDiffuseTexture', 
+                'linkEmissiveWithDiffuse',
+                'useSpecularOverAlpha',
+                
+                'diffuseFresnelParameters', 
+                'opacityFresnelParameters', 
+                'reflectionFresnelParameters', 
+                'refractionFresnelParameters', 
+                'emissiveFresnelParameters',
+                
+                'diffuseTexture', 
+                'emissiveTexture', 
+                'specularTexture', 
+                'ambientTexture',
+                'bumpTexture',
+                'lightMapTexture', 
+                'opacityTexture', 
+                'reflectionTexture',
+                'refractionTexture'                
+            ],
+            format : (mat:BABYLON.StandardMaterial) : string => {return mat.name;}
+        },
+        'PrimitiveAlignment':{
+            properties:['horizontal', 'vertical']
+        },
+        'PrimitiveThickness':{
+            properties:['topPixels', 'leftPixels', 'rightPixels', 'bottomPixels']
+        },
+        'BoundingInfo2D':{
+            properties:['radius','center', 'extent']
+        },
+        'SolidColorBrush2D':{
+            properties:['color']
+        },
+        'GradientColorBrush2D':{
+            properties:['color1', 'color2', 'translation', 'rotation', 'scale']
+        },
+        'PBRMaterial' : {
+            properties: [
+                'name', 
+                'albedoColor', 
+                'albedoTexture', 
+
+                'opacityTexture', 
+                'reflectionTexture', 
+                'emissiveTexture', 
+                'bumpTexture', 
+                'lightmapTexture', 
+
+                'opacityFresnelParameters', 
+                'emissiveFresnelParameters', 
+
+                'linkEmissiveWithAlbedo', 
+                'useLightmapAsShadowmap', 
+
+                'useAlphaFromAlbedoTexture', 
+                'useSpecularOverAlpha', 
+                'useAutoMicroSurfaceFromReflectivityMap', 
+                'useLogarithmicDepth', 
+
+                'reflectivityColor',
+                'reflectivityTexture',
+                'reflectionTexture',
+                'reflectionColor',
+                
+                'alpha',
+                'linkRefractionWithTransparency',
+                'indexOfRefraction',
+
+                'microSurface',
+                'useMicroSurfaceFromReflectivityMapAlpha',
+
+                'directIntensity',
+                'emissiveIntensity',
+                'specularIntensity',
+                'environmentIntensity',
+                'cameraExposure',
+                'cameraContrast',
+                'cameraColorGradingTexture',
+                'cameraColorCurves'
+            ]
+        }
+        
+    }
+    
+}

+ 52 - 0
inspector/ts/inspector/scheduler/Scheduler.ts

@@ -0,0 +1,52 @@
+module INSPECTOR {
+
+    export class Scheduler {
+
+        private static _instance: Scheduler;
+
+        /** The number of the set interval */
+        private _timer          : number;
+        /** Is this scheduler in pause ? */
+        public pause            : boolean = false;
+
+        /** All properties are refreshed every 250ms */
+        public static REFRESH_TIME  : number = 250;
+
+        /** The list of data to update */
+        private _updatableProperties: Array<PropertyLine> = [];
+
+        constructor () {
+            this._timer = setInterval(this._update.bind(this), Scheduler.REFRESH_TIME);
+        }
+
+        public static getInstance() : Scheduler {
+            if (!Scheduler._instance) {
+                Scheduler._instance = new Scheduler();
+                console.log('create '); 
+            }
+            return Scheduler._instance;
+        }
+
+        /** Add a property line to be updated every X ms */
+        public add(prop:PropertyLine) {
+            this._updatableProperties.push(prop);
+        }
+        
+        /** Removes the given property from the list of properties to update */
+        public remove(prop:PropertyLine) {
+            let index = this._updatableProperties.indexOf(prop);
+            if (index != -1) {
+                this._updatableProperties.splice(index, 1);
+            }
+        }
+
+        private _update() {
+            // If not in pause, update 
+            if (!this.pause) {
+                for (let prop of this._updatableProperties) {
+                    prop.update();
+                }
+            }
+        }
+    }
+}

+ 47 - 0
inspector/ts/inspector/tabs/Canvas2DTab.ts

@@ -0,0 +1,47 @@
+module INSPECTOR{
+    
+    export class Canvas2DTab extends PropertyTab {
+                
+        constructor(tabbar:TabBar, inspector:Inspector) {
+            super(tabbar, 'Canvas2D', inspector); 
+        }
+
+        /* Overrides */
+        protected _getTree() : Array<TreeItem> {
+            let arr = [];
+            
+            // get all canvas2D
+            let instances = BABYLON.Canvas2D.instances || [];
+                    
+            // Returns true if the id of the given object starts and ends with '###'
+            let shouldExcludeThisPrim = (obj:BABYLON.Prim2DBase) : boolean => {
+                return (obj.id && obj.id.indexOf('###') == 0 && obj.id.lastIndexOf('###', 0) === 0);
+            }
+            
+            // Recursive method building the tree panel
+            let createNode = (obj : BABYLON.Prim2DBase) => {
+                if (obj.children && obj.children.length > 0) {
+                    let node = new TreeItem(this, new Canvas2DAdapter(obj));
+                    for (let child of obj.children) {     
+                        if (!shouldExcludeThisPrim(child)) {  
+                            let n = createNode(child);
+                            node.add(n); 
+                        }
+                    }
+                    node.update();
+                    return node;
+                } else {
+                    return new TreeItem(this, new Canvas2DAdapter(obj));
+                }
+            };
+            
+            for (let inst of instances) {
+                let c2d : BABYLON.Canvas2D = inst as BABYLON.Canvas2D;
+                let nodes = createNode(c2d);
+                arr.push(nodes);
+            }
+            return arr;
+        }
+    }
+    
+}

+ 22 - 0
inspector/ts/inspector/tabs/LightTab.ts

@@ -0,0 +1,22 @@
+module INSPECTOR{
+    
+    export class LightTab extends PropertyTab {
+                
+        constructor(tabbar:TabBar, inspector:Inspector) {
+            super(tabbar, 'Light', inspector); 
+        }
+
+        /* Overrides super */
+        protected _getTree() : Array<TreeItem> {
+            let arr = [];
+                        
+            // get all lights from the first scene
+            let instances = this._inspector.scene;
+            for (let light of instances.lights) {
+                arr.push(new TreeItem(this, new LightAdapter(light)));
+            }
+            return arr;
+        }  
+    }
+    
+}

+ 22 - 0
inspector/ts/inspector/tabs/MaterialTab.ts

@@ -0,0 +1,22 @@
+module INSPECTOR{
+    
+    export class MaterialTab extends PropertyTab {
+                
+        constructor(tabbar:TabBar, inspector:Inspector) {
+            super(tabbar, 'Material', inspector); 
+        }
+
+        /* Overrides super */
+        protected _getTree() : Array<TreeItem> {
+            let arr = [];
+            
+            // get all meshes from the first scene
+            let instances = this._inspector.scene;
+            for (let mat of instances.materials) {
+                arr.push(new TreeItem(this, new MaterialAdapter(mat)))
+            }
+            return arr;
+        }
+    }
+    
+}

+ 29 - 0
inspector/ts/inspector/tabs/MeshTab.ts

@@ -0,0 +1,29 @@
+module INSPECTOR{
+    
+    export class MeshTab extends PropertyTab {
+                
+        constructor(tabbar:TabBar, inspector:Inspector) {
+            super(tabbar, 'Mesh', inspector); 
+        }
+
+        /* Overrides super */
+        protected _getTree() : Array<TreeItem> {
+            let arr = [];
+            
+            // Returns true if the id of the given object starts and ends with '###'
+            let shouldExcludeThisMesh = (obj:BABYLON.AbstractMesh) : boolean => {
+                return (obj.name && obj.name.indexOf('###') == 0 && obj.name.lastIndexOf('###', 0) === 0);
+            };
+            
+            // get all meshes from the first scene
+            let instances = this._inspector.scene;
+            for (let mesh of instances.meshes) {
+                if (!shouldExcludeThisMesh(mesh)){
+                    arr.push(new TreeItem(this, new MeshAdapter(mesh)));
+                }
+            }
+            return arr;
+        }  
+    }
+    
+}

+ 142 - 0
inspector/ts/inspector/tabs/PropertyTab.ts

@@ -0,0 +1,142 @@
+module INSPECTOR{
+    
+    /**
+     * A Property tab can creates two panels: 
+     * a tree panel and a detail panel, 
+     * in which properties will be displayed.
+     * Both panels are separated by a resize bar
+     */
+    export abstract class PropertyTab extends Tab {
+        
+        protected _inspector : Inspector;
+        /** The panel containing a list of items */
+        protected _treePanel    : HTMLElement;
+        /** The panel containing a list if properties corresponding to an item */
+        protected _detailsPanel    : DetailPanel;
+        protected _treeItems: Array<TreeItem> = [];
+        protected _searchBar : SearchBar;
+        
+        constructor(tabbar:TabBar, name:string, insp:Inspector) {
+            super(tabbar, name);
+            
+            this._inspector = insp;  
+            
+            // Build the properties panel : a div that will contains the tree and the detail panel
+            this._panel = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
+            
+            // Search bar
+            this._searchBar = new SearchBar(this);
+            // Add searchbar
+            this._panel.appendChild(this._searchBar.toHtml());
+            
+            // Build the treepanel
+            this._treePanel = Helpers.CreateDiv('insp-tree', this._panel);
+            
+            // Build the detail panel
+            this._detailsPanel = new DetailPanel();
+            this._panel.appendChild(this._detailsPanel.toHtml());
+            
+            Split([this._treePanel, this._detailsPanel.toHtml()], {direction:'vertical'});       
+            
+            this.update();   
+        }
+
+        /** Overrides dispose */
+        public dispose() {
+            this._detailsPanel.dispose();
+        }
+        
+        public update(_items?:Array<TreeItem>) {
+
+            let items;   
+            if (_items) {
+                items = _items;
+            } else {
+                // Rebuild the tree
+                this._treeItems = this._getTree();
+                items = this._treeItems;                
+            }
+            // Clean the tree
+            Helpers.CleanDiv(this._treePanel); 
+            // Clean the detail properties
+            this._detailsPanel.clean();
+            
+            
+            // Sort items alphabetically
+            items.sort((item1, item2) => {
+                return item1.compareTo(item2);
+            });
+            
+            // Display items
+            for (let item of items) {
+                this._treePanel.appendChild(item.toHtml());
+            }
+        }        
+        
+        /** Display the details of the given item */
+        public displayDetails(item:TreeItem) {
+            // Remove active state on all items
+            this.activateNode(item);
+            // Update the detail panel
+            this._detailsPanel.details = item.getDetails();
+        }
+        
+        /** Select an item in the tree */
+        public select(item:TreeItem) {            
+            // Remove the node highlight
+            this.highlightNode();
+            // Active the node
+            this.activateNode(item);
+            // Display its details
+            this.displayDetails(item); 
+        }
+        
+
+        /** Highlight the given node, and downplay all others */
+         public highlightNode(item?:TreeItem) {
+            if (this._treeItems) {
+                for (let node of this._treeItems) {
+                    node.highlight(false);
+                }
+            }
+            if (item) {
+                item.highlight(true);
+            }
+        }
+
+        /** Set the given item as active in the tree */
+        public activateNode(item:TreeItem) {
+            if (this._treeItems) {
+                for (let node of this._treeItems) {
+                    node.active(false);
+                }
+            }
+            item.active(true);
+        }
+        
+        /** Returns the treeitem corersponding to the given obj, null if not found */
+        public getItemFor(_obj:any) : TreeItem{
+            let obj = _obj as BABYLON.AbstractMesh;
+            for (let item of this._treeItems) {
+                if (item.correspondsTo(obj)) {
+                    return item;
+                }
+            }
+            return null;
+        }      
+        
+        public filter(filter:string) {            
+            let items = [];
+            
+            for (let item of this._treeItems) {
+                if (item.id.toLowerCase().indexOf(filter.toLowerCase()) != -1) {
+                    items.push(item);
+                }
+            }    
+            this.update(items);
+        }
+        
+        /** Builds the tree panel */
+        protected abstract _getTree() : Array<TreeItem>;
+    } 
+}

+ 182 - 0
inspector/ts/inspector/tabs/SceneTab.ts

@@ -0,0 +1,182 @@
+module INSPECTOR {
+    
+    export class SceneTab extends Tab {
+        
+        private _inspector : Inspector;
+        /** The list of  channels/options that can be activated/deactivated */
+        private _actions   : HTMLDivElement;
+
+        /** The list of skeleton viewer */
+        private _skeletonViewers : Array<BABYLON.Debug.SkeletonViewer> = [];
+
+        /** The detail of the scene */
+        private _detailsPanel : DetailPanel;        
+                
+        constructor(tabbar:TabBar, insp:Inspector) {
+            super(tabbar, 'Scene');            
+            this._inspector = insp;  
+            
+            // Build the properties panel : a div that will contains the tree and the detail panel
+            this._panel   = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
+            
+            this._actions = Helpers.CreateDiv('scene-actions', this._panel) as HTMLDivElement;
+           
+            this._detailsPanel = new DetailPanel();
+            this._panel.appendChild(this._detailsPanel.toHtml());
+            
+            // build propertiesline
+            let details = [];
+            for (let prop of PROPERTIES['Scene'].properties) {
+                details.push(new PropertyLine(new Property(prop, this._inspector.scene)));
+            }
+            this._detailsPanel.details = details;
+            
+            Split([this._actions, this._detailsPanel.toHtml()], {  
+                sizes:[50, 50],
+                direction:'vertical'
+            });  
+            
+            // Build actions
+            {
+                
+                // Rendering mode
+                let title = Helpers.CreateDiv('actions-title', this._actions);
+                title.textContent = 'Rendering mode';
+                let point             = Helpers.CreateDiv('action-radio', this._actions);
+                let wireframe         = Helpers.CreateDiv('action-radio', this._actions);
+                let solid             = Helpers.CreateDiv('action-radio', this._actions);
+                point.textContent     = 'Point';
+                wireframe.textContent = 'Wireframe';
+                solid.textContent     = 'Solid';
+                if (this._inspector.scene.forcePointsCloud) {
+                    point.classList.add('active');
+                } else if (this._inspector.scene.forceWireframe) {
+                    wireframe.classList.add('active');
+                } else {
+                    solid.classList.add('active');
+                }
+                this._generateRadioAction([point, wireframe, solid]);
+                point.addEventListener('click', () => {this._inspector.scene.forcePointsCloud = true; this._inspector.scene.forceWireframe = false;});
+                wireframe.addEventListener('click', () => {this._inspector.scene.forcePointsCloud = false; this._inspector.scene.forceWireframe = true; });
+                solid.addEventListener('click',  () => {this._inspector.scene.forcePointsCloud = false; this._inspector.scene.forceWireframe = false; });
+
+                // Textures
+                title = Helpers.CreateDiv('actions-title', this._actions);
+                title.textContent = 'Textures channels';
+                this._generateActionLine('Diffuse Texture', BABYLON.StandardMaterial.DiffuseTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.DiffuseTextureEnabled = b });
+                this._generateActionLine('Ambient Texture', BABYLON.StandardMaterial.AmbientTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.AmbientTextureEnabled = b });
+                this._generateActionLine('Specular Texture', BABYLON.StandardMaterial.SpecularTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.SpecularTextureEnabled = b });
+                this._generateActionLine('Emissive Texture', BABYLON.StandardMaterial.EmissiveTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.EmissiveTextureEnabled = b });
+                this._generateActionLine('Bump Texture', BABYLON.StandardMaterial.BumpTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.BumpTextureEnabled = b });
+                this._generateActionLine('Opacity Texture', BABYLON.StandardMaterial.OpacityTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.OpacityTextureEnabled = b });
+                this._generateActionLine('Reflection Texture', BABYLON.StandardMaterial.ReflectionTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.ReflectionTextureEnabled = b });
+                this._generateActionLine('Refraction Texture', BABYLON.StandardMaterial.RefractionTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.RefractionTextureEnabled = b });
+                this._generateActionLine('ColorGrading', BABYLON.StandardMaterial.ColorGradingTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.ColorGradingTextureEnabled = b });
+                this._generateActionLine('Lightmap Texture', BABYLON.StandardMaterial.LightmapTextureEnabled, (b:boolean) => {BABYLON.StandardMaterial.LightmapTextureEnabled = b });
+                this._generateActionLine('Fresnel', BABYLON.StandardMaterial.FresnelEnabled, (b:boolean) => {BABYLON.StandardMaterial.FresnelEnabled = b });
+
+                // Options
+                title = Helpers.CreateDiv('actions-title', this._actions);
+                title.textContent = 'Options'; 
+                this._generateActionLine('Animations', this._inspector.scene.animationsEnabled, (b:boolean) => {this._inspector.scene.animationsEnabled = b });
+                this._generateActionLine('Collisions', this._inspector.scene.collisionsEnabled, (b:boolean) => {this._inspector.scene.collisionsEnabled = b });
+                this._generateActionLine('Fog', this._inspector.scene.fogEnabled, (b:boolean) => {this._inspector.scene.fogEnabled = b });
+                this._generateActionLine('Lens flares', this._inspector.scene.lensFlaresEnabled, (b:boolean) => {this._inspector.scene.lensFlaresEnabled = b });
+                this._generateActionLine('Lights', this._inspector.scene.lightsEnabled, (b:boolean) => {this._inspector.scene.lightsEnabled = b });
+                this._generateActionLine('Particles', this._inspector.scene.particlesEnabled, (b:boolean) => {this._inspector.scene.particlesEnabled = b });
+                this._generateActionLine('Post-processes', this._inspector.scene.postProcessesEnabled, (b:boolean) => {this._inspector.scene.postProcessesEnabled = b });
+                this._generateActionLine('Probes', this._inspector.scene.probesEnabled, (b:boolean) => {this._inspector.scene.probesEnabled = b });
+                this._generateActionLine('Procedural textures', this._inspector.scene.proceduralTexturesEnabled, (b:boolean) => {this._inspector.scene.proceduralTexturesEnabled = b });
+                this._generateActionLine('Render targets', this._inspector.scene.renderTargetsEnabled, (b:boolean) => {this._inspector.scene.renderTargetsEnabled = b });
+                this._generateActionLine('Shadows', this._inspector.scene.shadowsEnabled, (b:boolean) => {this._inspector.scene.shadowsEnabled = b });
+                this._generateActionLine('Skeletons', this._inspector.scene.skeletonsEnabled, (b:boolean) => {this._inspector.scene.skeletonsEnabled = b });
+                this._generateActionLine('Sprites', this._inspector.scene.spritesEnabled, (b:boolean) => {this._inspector.scene.spritesEnabled = b });
+                this._generateActionLine('Textures', this._inspector.scene.texturesEnabled, (b:boolean) => {this._inspector.scene.texturesEnabled = b });
+                
+                // Audio
+                title = Helpers.CreateDiv('actions-title', this._actions);
+                title.textContent         = 'Audio'; 
+                let headphones            = Helpers.CreateDiv('action-radio', this._actions);
+                let normalSpeaker         = Helpers.CreateDiv('action-radio', this._actions);
+                this._generateActionLine('Disable audio', !this._inspector.scene.audioEnabled, (b:boolean) => {this._inspector.scene.audioEnabled = !b });
+                headphones.textContent    = 'Headphones';
+                normalSpeaker.textContent = 'Normal speakers';                
+                this._generateRadioAction([headphones, normalSpeaker]);
+                if (this._inspector.scene.headphone) {
+                    headphones.classList.add('active');
+                } else {
+                    normalSpeaker.classList.add('active');
+                }
+                headphones.addEventListener('click', () => {this._inspector.scene.headphone = true;});
+                normalSpeaker.addEventListener('click', () => {this._inspector.scene.headphone = false;});
+                
+                // Viewers
+                title = Helpers.CreateDiv('actions-title', this._actions);
+                title.textContent = 'Viewer';
+                this._generateActionLine('Skeletons', false, (b:boolean) => {
+                    if (b) {
+                        for (var index = 0; index < this._inspector.scene.meshes.length; index++) {
+                            var mesh = this._inspector.scene.meshes[index];
+                            if (mesh.skeleton) {
+                                var found = false;
+                                for (var sIndex = 0; sIndex < this._skeletonViewers.length; sIndex++) {
+                                    if (this._skeletonViewers[sIndex].skeleton === mesh.skeleton) {
+                                        found = true;
+                                        break;
+                                    }
+                                }
+                                if (found) {
+                                    continue;
+                                }
+                                var viewer = new BABYLON.Debug.SkeletonViewer(mesh.skeleton, mesh, this._inspector.scene);
+                                viewer.isEnabled = true;
+                                this._skeletonViewers.push(viewer);
+                            }
+                        }
+                    } else {
+                        for (var index = 0; index < this._skeletonViewers.length; index++) {
+                            this._skeletonViewers[index].dispose();
+                        }
+                        this._skeletonViewers = [];
+                    }
+                });                
+            }            
+        }
+
+        /** Overrides super.dispose */
+        public dispose() {
+            this._detailsPanel.dispose();
+        }
+        
+        /** generates a div which correspond to an option that can be activated/deactivated */
+        private _generateActionLine(name:string, initValue:boolean, action:(b:boolean) => void) {
+            let div = Helpers.CreateDiv('scene-actions', this._actions);
+            div.textContent = name;
+            div.classList.add('action');
+            if (initValue) {
+                div.classList.add('active');
+            }
+            div.addEventListener('click', (e) => {
+                div.classList.toggle('active');      
+                let isActivated = div.classList.contains('active');
+                action(isActivated);          
+            })
+        }
+
+        /** 
+         * Add a click action for all given elements : 
+         * the clicked element is set as active, all others elements are deactivated
+         */
+        private _generateRadioAction(arr:Array<HTMLElement>) {
+            let active = (elem, evt) => {
+                for (let e of arr) {
+                    e.classList.remove('active');
+                }
+                elem.classList.add('active');
+            }
+            for (let elem of arr) {
+                elem.addEventListener('click', active.bind(this, elem));
+            }
+        }
+    }
+}

+ 180 - 0
inspector/ts/inspector/tabs/ShaderTab.ts

@@ -0,0 +1,180 @@
+module INSPECTOR {
+
+    export class ShaderTab extends Tab {
+
+        private _inspector : Inspector;
+        
+        private _vertexPanel : HTMLElement;
+        private _fragmentPanel : HTMLElement;
+
+        constructor(tabbar:TabBar, insp:Inspector) {
+            super(tabbar, 'Shader');            
+            this._inspector = insp;
+
+            // Build the shaders panel : a div that will contains the shaders tree and both shaders panels
+            this._panel         = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
+
+            let shaderPanel     = Helpers.CreateDiv('shader-tree-panel') as HTMLDivElement;
+            this._vertexPanel   = Helpers.CreateDiv('shader-panel') as HTMLDivElement;
+            this._fragmentPanel = Helpers.CreateDiv('shader-panel') as HTMLDivElement;
+
+            this._panel.appendChild(shaderPanel);
+            this._panel.appendChild(this._vertexPanel);
+            this._panel.appendChild(this._fragmentPanel);
+            
+            Helpers.LoadScript();
+            
+            Split([this._vertexPanel, this._fragmentPanel], {
+                sizes:[50, 50],
+                direction:'vertical'}
+            );  
+            
+            let comboBox = Helpers.CreateElement('select', '', shaderPanel);
+            comboBox.addEventListener('change', this._selectShader.bind(this));
+            
+            let option = Helpers.CreateElement('option', '', comboBox);
+            option.textContent = 'Select a shader';
+            option.setAttribute('value', "");
+            option.setAttribute('disabled', 'true');
+            option.setAttribute('selected', 'true');            
+            
+            // Build shaders combobox
+            for (let mat of this._inspector.scene.materials) {
+                if (mat instanceof BABYLON.ShaderMaterial) {
+                    let option = Helpers.CreateElement('option', '', comboBox);
+                    option.setAttribute('value', mat.id);
+                    option.textContent = `${mat.name} - ${mat.id}`;
+                    
+                }
+            }
+
+        }
+        
+        private _selectShader(event:Event) {
+            let id = (event.target as HTMLSelectElement).value;
+            let mat = this._inspector.scene.getMaterialByID(id); 
+            
+            // Clean shader panel
+            Helpers.CleanDiv(this._vertexPanel);
+            // add the title - vertex shader
+            let title = Helpers.CreateDiv('shader-panel-title', this._vertexPanel);
+            title.textContent = 'Vertex shader';
+            // add code
+            let code = Helpers.CreateElement('code', 'glsl',  Helpers.CreateElement('pre', '', this._vertexPanel));
+            code.textContent = this._beautify(mat.getEffect().getVertexShaderSource());
+            
+            Helpers.CleanDiv(this._fragmentPanel);
+            // add the title - fragment shader
+            title = Helpers.CreateDiv('shader-panel-title', this._fragmentPanel);
+            title.textContent = 'Frgament shader';
+            // add code
+            code = Helpers.CreateElement('code', 'glsl',  Helpers.CreateElement('pre', '', this._fragmentPanel));
+            code.textContent = this._beautify(mat.getEffect().getFragmentShaderSource());         
+                                    
+            // Init the syntax highlighting
+            let styleInit = Helpers.CreateElement('script', '', Inspector.DOCUMENT.body);
+            styleInit.textContent = 'hljs.initHighlighting();';
+            
+        }
+
+        /** Overrides super.dispose */
+        public dispose() {
+        }
+
+        /** Returns the position of the first { and the corresponding } */
+        private _getBracket(str) {
+            let fb = str.indexOf('{');
+            let arr = str.substr(fb+1).split('');
+            let counter = 1;
+            let currentPosInString  = fb;
+            let lastBracketIndex = 0;
+            for (let char of arr) {
+                currentPosInString++;
+                if (char === '{') {
+                    counter++
+                }
+                if (char === '}') {
+                    counter--
+                }
+                if (counter == 0) {
+                    lastBracketIndex = currentPosInString;
+                    break;         
+                }
+            }
+
+            return {firstBracket : fb, lastBracket:lastBracketIndex};
+        }
+
+        /** 
+         * Beautify the given string : correct indentation
+         */
+        private _beautify(glsl:string, level: number = 0) {
+            
+            // return condition : no brackets at all
+            let brackets = this._getBracket(glsl);
+            let firstBracket       = brackets.firstBracket;
+            let lastBracket        = brackets.lastBracket;
+
+            let spaces = "";
+            for (let i=0 ;i<level; i++) {
+                spaces += "    "; // 4 spaces
+            }
+            // If no brackets, return the indented string
+            if (firstBracket == -1) {
+                glsl = spaces+glsl; // indent first line
+                glsl = glsl
+                    .replace(/;./g, x => '\n'+x.substr(1)) // new line after ;  except the last one
+                    glsl = glsl.replace(/=/g, " = ") // space around =
+                    glsl = glsl.replace(/\n/g, "\n"+spaces); // indentation
+                return glsl;
+            } else {
+                // if brackets, beautify the inside                                 
+                // let insideWithBrackets = glsl.substr(firstBracket, lastBracket-firstBracket+1);
+                let left   = glsl.substr(0, firstBracket);
+                let right  = glsl.substr(lastBracket+1, glsl.length); 
+                let inside = glsl.substr(firstBracket+1, lastBracket-firstBracket-1);
+                inside     = this._beautify(inside, level+1);
+                return this._beautify(left, level)+'{\n'+inside+'\n'+spaces+'}\n'+this._beautify(right, level);
+
+            }
+
+                
+                // // Replace bracket with @1 and @2 with correct indentation
+                // let newInside          = "@1\n\t" + inside + "\n@2";
+                // newInside              = newInside.replace(/;\n/g, ";\n\t");
+                
+                // glsl                   = glsl.replace(insideWithBrackets, newInside);
+
+                // firstBracket       = glsl.indexOf('{');
+                // lastBracket        = glsl.lastIndexOf('}');
+
+            // }
+
+            // console.log(glsl);
+
+            // let regex = /(\{(?:\{??[^\{]*?}))+/gmi;
+
+            // let tmp = glsl;
+
+            // let m;
+            // while ((m = regex.exec(tmp)) !== null) {
+            //     // This is necessary to avoid infinite loops with zero-width matches
+            //     if (m.index === regex.lastIndex) {
+            //         regex.lastIndex++;
+            //     }                
+            //     // The result can be accessed through the `m`-variable.
+            //     m.forEach((match, groupIndex) => {
+            //         // Remove the first and the last bracket only
+            //         let matchWithoutBrackets = match.replace(/{/, "").replace(/}/, "");
+            //         // Indent the content inside brackets with tabs
+            //         glsl = glsl.replace(match, `{\n\t${matchWithoutBrackets}\n}\n`);
+            //         // removes the match from tmp
+            //         tmp = tmp.replace(match, "");
+            //         // and continue
+            //     });
+            // }
+            // return 
+        }
+    }
+
+}

+ 70 - 0
inspector/ts/inspector/tabs/Tab.ts

@@ -0,0 +1,70 @@
+module INSPECTOR{
+    
+    export abstract class Tab extends BasicElement {
+        protected _tabbar  : TabBar;
+        // The tab name displayed in the tabbar
+        public name        : string;        
+        protected _isActive: boolean = false;
+        
+        // The whole panel corresponding to this tab. It's what is displayed when the tab is activacted
+        protected _panel : HTMLDivElement;
+        
+        constructor(tabbar:TabBar, name:string) {
+            super();      
+            this._tabbar = tabbar;      
+            this.name   = name;
+            this._build();
+        }
+        
+        /** True if the tab is active, false otherwise */
+        public isActive() : boolean {
+            return this._isActive;
+        }
+        
+        protected _build() {            
+            this._div.className = 'tab';  
+            this._div.textContent = this.name;     
+            
+            this._div.addEventListener('click', (evt) => {
+                // Set this tab as active
+                this._tabbar.switchTab(this);
+            });
+        }
+        
+        /** Set this tab as active or not, depending on the current state */
+        public active(b:boolean) {
+            if (b) {                
+                this._div.classList.add('active');
+            } else {
+                this._div.classList.remove('active');
+            }
+            this._isActive = b;
+        }
+        
+        public update() {
+            // Nothing for the moment
+        }
+        
+        /** Creates the tab panel for this tab. */   
+        public getPanel() : HTMLElement {
+            return this._panel;
+        }
+        
+        /** Add this in the propertytab with the searchbar */
+        public filter(str:string) {};
+
+        /** Dispose properly this tab */
+        public abstract dispose();
+
+        /** 
+         * Returns the total width in pixel of this tab, 0 by default
+        */
+        public getPixelWidth() : number {
+            let style = window.getComputedStyle(this._div);
+            let left = parseFloat(style.marginLeft.substr(0,style.marginLeft.length-2)) ||0;
+            let right = parseFloat(style.marginRight.substr(0,style.marginRight.length-2)) ||0;
+            return (this._div.clientWidth || 0) + left + right;
+        }
+    }
+    
+}

+ 191 - 0
inspector/ts/inspector/tabs/TabBar.ts

@@ -0,0 +1,191 @@
+module INSPECTOR {
+    /**
+     * A tab bar will contains each view the inspector can have : Canvas2D, Meshes...
+     * The default active tab is the first one of the list.
+     */
+    export class TabBar extends BasicElement {
+        
+        // The list of available tabs
+        private _tabs         : Array<Tab> = [];
+        private _inspector    : Inspector;
+        /** The tab displaying all meshes */
+        private _meshTab      : MeshTab;
+        /** The toolbar */
+        private _toolBar      : Toolbar;
+        /** The icon displayed at the end of the toolbar displaying a combo box of tabs not displayed */
+        private _moreTabsIcon : HTMLElement;
+        /** The panel displayed when the 'more-tab' icon is selected */
+        private _moreTabsPanel: HTMLElement;
+        /** The list of tab displayed by clicking on the remainingIcon */
+        private _invisibleTabs: Array<Tab> = [];
+        /** The list of tabs visible, displayed in the tab bar */
+        private _visibleTabs  : Array<Tab> = [];
+                
+        constructor(inspector:Inspector) {
+            super();
+            this._inspector = inspector;
+            this._tabs.push(new SceneTab(this, this._inspector));
+            this._meshTab = new MeshTab(this, this._inspector);
+            this._tabs.push(this._meshTab);
+            this._tabs.push(new ShaderTab(this, this._inspector));
+            this._tabs.push(new LightTab(this, this._inspector));
+            this._tabs.push(new Canvas2DTab(this, this._inspector));
+            this._tabs.push(new MaterialTab(this, this._inspector));
+
+            this._toolBar = new Toolbar(this._inspector);
+
+            this._build();
+            // Active the first tab
+            this._tabs[0].active(true); 
+
+            // set all tab as visible
+            for (let tab of this._tabs) {
+                this._visibleTabs.push(tab);
+            }
+        }
+        
+        // No update
+        public update() {}
+        
+        protected _build() {            
+            this._div.className = 'tabbar';
+            
+            this._div.appendChild(this._toolBar.toHtml());
+            for (let tab of this._tabs) {
+                this._div.appendChild(tab.toHtml());
+            }
+
+
+            this._moreTabsIcon = Helpers.CreateElement('i', 'fa fa-angle-double-right more-tabs');
+         
+            this._moreTabsPanel = Helpers.CreateDiv('more-tabs-panel');
+
+            this._moreTabsIcon.addEventListener('click', () => {
+                // Hide the 'more-tabs-panel' if already displayed 
+                if (this._moreTabsPanel.style.display == 'flex') {
+                    this._moreTabsPanel.style.display = 'none';
+                } else {
+                    // Attach more-tabs-panel if not attached yet
+                    let topPanel = this._div.parentNode as HTMLElement;
+                    if (! topPanel.contains(this._moreTabsPanel)) {
+                        topPanel.appendChild(this._moreTabsPanel);
+                    }
+                    // Clean the 'more-tabs-panel'
+                    Helpers.CleanDiv(this._moreTabsPanel);
+                    // Add each invisible tabs to this panel
+                    for (let tab of this._invisibleTabs) {
+                        this._addInvisibleTabToPanel(tab);
+                    }        
+                    // And display it
+                    this._moreTabsPanel.style.display = 'flex';
+                }
+            });
+        }
+
+        /** 
+         * Add a tab to the 'more-tabs' panel, displayed by clicking on the 
+         * 'more-tabs' icon
+         */
+        private _addInvisibleTabToPanel(tab:Tab) {
+            let div = Helpers.CreateDiv('invisible-tab', this._moreTabsPanel);
+            div.textContent = tab.name;
+            div.addEventListener('click', () => {
+                this._moreTabsPanel.style.display = 'none';
+                this.switchTab(tab);
+            });
+        }
+        
+        /** Dispose the current tab, set the given tab as active, and refresh the treeview */
+        public switchTab(tab:Tab) {
+            // Dispose the active tab
+            this.getActiveTab().dispose();
+
+            // Deactivate all tabs
+            for (let t of this._tabs) {
+                t.active(false);
+            }
+            // activate the given tab
+            tab.active(true);
+            
+            // Refresh the inspector
+            this._inspector.refresh();
+        }
+        
+        /** Display the mesh tab.
+         * If a parameter is given, the given mesh details are displayed
+         */
+        public switchMeshTab(mesh?:BABYLON.AbstractMesh) {
+            this.switchTab(this._meshTab);
+            if (mesh) {
+                let item = this._meshTab.getItemFor(mesh);
+                this._meshTab.select(item);
+            }
+        }
+        
+        /** Returns the active tab */
+        public getActiveTab() : Tab {
+            for (let tab of this._tabs) {
+                if (tab.isActive()) {
+                    return tab;
+                }
+            }
+        }
+
+        public get inspector() : Inspector {
+            return this._inspector;
+        }
+
+        /** 
+         * Returns the total width in pixel of the tabbar, 
+         * that corresponds to the sum of the width of each visible tab + toolbar width
+        */
+        public getPixelWidth() : number {
+            let sum = 0;
+            for (let tab of this._visibleTabs) {
+                sum += tab.getPixelWidth();
+            }
+            sum += this._toolBar.getPixelWidth();
+            if (this._div.contains(this._moreTabsIcon)) {
+                sum += 30; // $tabbarheight
+            }
+            return sum;
+        }
+
+        /** Display the remaining icon or not depending on the tabbar width.
+         * This function should be called each time the inspector width is updated
+         */
+        public updateWidth() {
+            let parentSize = this._div.parentElement.clientWidth;
+            let lastTabWidth = 75;
+            let currentSize = this.getPixelWidth(); 
+
+            // Check if a tab should be removed : if the tab bar width is greater than
+            // its parent width
+            while (this._visibleTabs.length > 0 && currentSize > parentSize) {
+                // Start by the last element
+                let tab = this._visibleTabs.pop();
+                // set it invisible
+                this._invisibleTabs.push(tab);
+                // and removes it from the DOM
+                this._div.removeChild(tab.toHtml());
+                currentSize = this.getPixelWidth() + lastTabWidth;                
+            }
+
+            // Check if a tab can be added to the tab bar : if the tab bar width
+            // + 100 (at least 100px is needed to add a tab) is less than its parent width
+            if (this._invisibleTabs.length > 0) {
+                if (currentSize + lastTabWidth < parentSize) {
+                    let lastTab = this._invisibleTabs.pop();
+                    this._div.appendChild(lastTab.toHtml());
+                    this._visibleTabs.push(lastTab);
+                    // Update more-tab icon in last position if needed
+                    this._div.removeChild(this._moreTabsIcon);
+                }
+            }
+            if (this._invisibleTabs.length > 0 && !this._div.contains(this._moreTabsIcon)) {
+                this._div.appendChild(this._moreTabsIcon);
+            }
+        }
+        
+    }
+}

+ 43 - 0
inspector/ts/inspector/tools/AbstractTool.ts

@@ -0,0 +1,43 @@
+ module INSPECTOR {
+    export abstract class AbstractTool {
+        private _elem : HTMLElement;
+        protected _inspector: Inspector;
+
+        constructor(icon:string, parent:HTMLElement, inspector:Inspector, tooltip:string) {
+            this._inspector = inspector;
+
+            this._elem = Inspector.DOCUMENT.createElement('i');
+            this._elem.className = `tool fa ${icon}`;
+            parent.appendChild(this._elem);
+
+            this._elem.addEventListener('click', (e) => {
+                this.action();
+            });
+            
+            new Tooltip(this._elem, tooltip);
+        }
+
+        public toHtml() : HTMLElement {
+            return this._elem;
+        }
+
+        /** 
+         * Returns the total width in pixel of this tool, 0 by default
+        */
+        public getPixelWidth() : number {
+            let style = window.getComputedStyle(this._elem);
+            let left = parseFloat(style.marginLeft.substr(0,style.marginLeft.length-2)) ||0;
+            let right = parseFloat(style.marginRight.substr(0,style.marginRight.length-2)) ||0;
+            return (this._elem.clientWidth || 0) + left + right;
+        }
+        
+        /** 
+         * Updates the icon of this tool with the given string
+         */
+        protected _updateIcon(icon:string) {
+            this._elem.className = `tool fa ${icon}`;
+        }
+
+        public abstract action(); 
+    }
+ }

+ 23 - 0
inspector/ts/inspector/tools/PauseScheduleTool.ts

@@ -0,0 +1,23 @@
+module INSPECTOR {
+     
+    export class PauseScheduleTool extends AbstractTool {
+        
+        private _isPause : boolean = false;
+
+        constructor(parent:HTMLElement, inspector:Inspector) {
+            super('fa-pause', parent, inspector, 'Pause the automatic update of properties');
+        }
+
+        // Action : refresh the whole panel
+        public action() {
+            if (this._isPause) {
+                Scheduler.getInstance().pause = false;
+                this._updateIcon('fa-pause');
+            } else {
+                Scheduler.getInstance().pause = true;
+                this._updateIcon('fa-play');
+            }
+            this._isPause = !this._isPause;
+        }
+    }
+}

+ 54 - 0
inspector/ts/inspector/tools/PickTool.ts

@@ -0,0 +1,54 @@
+module INSPECTOR {
+     
+    export class PickTool extends AbstractTool {
+        
+        private _isActive : boolean = false;
+        private _pickHandler;
+
+        constructor(parent:HTMLElement, inspector:Inspector) {
+            super('fa-mouse-pointer', parent, inspector, 'Pick a mesh in the scene to display its details');
+            
+            // Create handler
+            this._pickHandler = this._pickMesh.bind(this);
+        }
+
+        // Action : find the corresponding tree item in the correct tab and display it
+        public action() {
+            if (this._isActive) {
+                this._deactivate();
+            } else {                 
+                this.toHtml().classList.add('active');            
+                // Add event handler : pick on a mesh in the scene
+                this._inspector.scene.getEngine().getRenderingCanvas().addEventListener('click', this._pickHandler);
+                this._isActive = true;
+            }
+        }
+        
+        /** Deactivate this tool */
+        private _deactivate() {
+            this.toHtml().classList.remove('active');            
+            // Remove event handler
+            this._inspector.scene.getEngine().getRenderingCanvas().removeEventListener('click', this._pickHandler);
+            this._isActive = false;
+        }
+        
+        /** Pick a mesh in the scene */
+        private _pickMesh(evt) {
+            let pos = this._updatePointerPosition(evt);
+            let pi = this._inspector.scene.pick(pos.x, pos.y, (mesh:BABYLON.AbstractMesh) => {return true});
+            
+            if (pi.pickedMesh) {
+                console.log(pi.pickedMesh.name);
+                this._inspector.displayObjectDetails(pi.pickedMesh);
+            }
+            this._deactivate();
+        }
+        
+        private _updatePointerPosition(evt) : {x:number, y:number}{
+            let canvasRect =  this._inspector.scene.getEngine().getRenderingCanvasClientRect();
+            let pointerX = evt.clientX - canvasRect.left;
+            let pointerY = evt.clientY - canvasRect.top;
+            return {x:pointerX, y:pointerY};
+        };
+    }
+}

+ 14 - 0
inspector/ts/inspector/tools/PopupTool.ts

@@ -0,0 +1,14 @@
+module INSPECTOR {
+     
+    export class PopupTool extends AbstractTool {
+
+        constructor(parent:HTMLElement, inspector:Inspector) {
+            super('fa-external-link', parent, inspector, 'Creates the inspector in an external popup');
+        }
+
+        // Action : refresh the whole panel
+        public action() {
+            this._inspector.openPopup();
+        }
+    }
+}

+ 14 - 0
inspector/ts/inspector/tools/RefreshTool.ts

@@ -0,0 +1,14 @@
+module INSPECTOR {
+     
+    export class RefreshTool extends AbstractTool {
+
+        constructor(parent:HTMLElement, inspector:Inspector) {
+            super('fa-refresh', parent, inspector, 'Refresh the current tab');
+        }
+
+        // Action : refresh the whole panel
+        public action() {
+            this._inspector.refresh();
+        }
+    }
+}

+ 50 - 0
inspector/ts/inspector/tools/Toolbar.ts

@@ -0,0 +1,50 @@
+ module INSPECTOR {
+
+    export class Toolbar extends BasicElement {
+
+        private _inspector: Inspector;
+        private _tools : Array<AbstractTool> = []
+
+        constructor (inspector:Inspector) {
+            super();
+            this._inspector = inspector;
+            this._build(); 
+
+            this._addTools();
+        }
+        
+        // A toolbar cannot be updated
+        public update() {};
+        
+        protected _build() {            
+            this._div.className = 'toolbar';
+        };
+
+        private _addTools() {
+            // Refresh
+            this._tools.push(new RefreshTool(this._div, this._inspector));            
+            // Pick object
+            this._tools.push(new PickTool(this._div, this._inspector));
+            
+            // Add the popup mode only if the inspector is not in popup mode
+            if (!this._inspector.popupMode) {
+                this._tools.push(new PopupTool(this._div, this._inspector));
+            }
+            // Pause schedule
+            this._tools.push(new PauseScheduleTool(this._div, this._inspector));
+        }
+
+        /** 
+         * Returns the total width in pixel of the tabbar, 
+         * that corresponds to the sum of the width of each tab + toolbar width
+        */
+        public getPixelWidth() : number {
+            let sum = 0;
+            for (let tool of this._tools) {
+                sum += tool.getPixelWidth();
+            }
+            
+            return sum;
+        }
+    }
+ }

+ 180 - 0
inspector/ts/inspector/tree/TreeItem.ts

@@ -0,0 +1,180 @@
+ module INSPECTOR {
+     
+    export class TreeItem extends BasicElement{
+    
+        // Reference to the tab
+        private _tab        : PropertyTab;
+        // The object this item is linked to (should be a primitive or a canvas) TODO should be superclass of all primitives
+        private _adapter    : Adapter;
+        private _tools      : Array<AbstractTreeTool>;
+        private _children   : Array<TreeItem> = [];
+        // Div element that contains all children of this node.
+        private _lineContent: HTMLElement;  
+
+        constructor(tab:PropertyTab, obj:Adapter ) {
+            super();
+            this._tab = tab;
+            this._adapter = obj;
+            
+            this._tools = this._adapter.getTools();
+            
+            this._build();
+            
+        }
+
+        /** Returns the item ID == its adapter ID */
+        public get id() : string {
+            return this._adapter.id();
+        }
+
+        /** Add the given item as a child of this one */
+        public add(child:TreeItem) {
+            this._children.push(child);
+            this.update();
+        }
+
+        /**
+         * Function used to compare this item to another tree item.
+         * Returns the alphabetical sort of the adapter ID
+         */
+        public compareTo(item:TreeItem) : number {
+            let str1 = this.id;
+            let str2 = item.id;
+            return str1.localeCompare(str2, [], {numeric:true});
+        }
+        
+        /** Returns true if the given obj correspond to the adapter linked to this tree item */
+        public correspondsTo(obj:any) : boolean {
+            return this._adapter.correspondsTo(obj);
+        }
+
+        /** hide all children of this item */
+        public fold() {
+            // Do nothing id no children
+            if (this._children.length > 0) {
+                for (let elem of this._children) {
+                    elem.toHtml().style.display = 'none';
+                }
+                this._div.classList.add('folded');
+                this._div.classList.remove('unfolded');
+            }
+        }
+        /** Show all children of this item */
+        public unfold() {
+            // Do nothing id no children
+            if (this._children.length > 0) {
+                for (let elem of this._children) {
+                    elem.toHtml().style.display = 'block';
+                }
+                this._div.classList.add('unfolded');
+                this._div.classList.remove('folded');
+            }
+        }
+        
+        /** Build the HTML of this item */
+        protected _build() {
+            this._div.className = 'line'; 
+            
+            
+            for (let tool of this._tools) { 
+                this._div.appendChild(tool.toHtml());
+            }
+            
+            
+            // Id
+            let text = Inspector.DOCUMENT.createElement('span');        
+            text.textContent = this._adapter.id();
+            this._div.appendChild(text);
+            
+            // Type
+            let type = Inspector.DOCUMENT.createElement('span'); 
+            type.className = 'property-type';
+            type.textContent = ' - '+this._adapter.type(); 
+            this._div.appendChild(type);
+            
+            this._lineContent = Helpers.CreateDiv('line-content', this._div);
+
+            this._addEvent();
+        }
+
+        /**
+         * Returns one HTML element (.details) containing all  details of this primitive
+         */
+        public getDetails() : Array<PropertyLine> {
+            return this._adapter.getProperties();
+        }
+        
+        public update() {
+            // Clean division holding all children
+            Helpers.CleanDiv(this._lineContent);
+
+            for (let child of this._children) {
+                let elem = child.toHtml();
+                this._lineContent.appendChild(elem);
+            }
+            if (this._children.length > 0) {
+                // Check if folded or not
+                if (!this._div.classList.contains('folded') && !this._div.classList.contains('unfolded')) {
+                    this._div.classList.add('folded');
+                }
+            }
+            this.fold();
+        }
+        
+        /**
+         * Add an event listener on the item : 
+         * - one click display details
+         * - on mouse hover the item is highlighted
+         */
+        protected _addEvent() {
+            this._div.addEventListener('click', (e) => {
+                this._tab.select(this);
+                // Fold/unfold the tree
+                if (this._isFolded()) {
+                    this.unfold();        
+                } else {
+                    this.fold();
+                }                   
+                e.stopPropagation();
+            });
+
+            // Highlight on mouse over
+            this._div.addEventListener('mouseover', (e) => {
+                this._tab.highlightNode(this);
+                e.stopPropagation();
+            });
+            // Remove highlight on mouse out
+            this._div.addEventListener('mouseout', (e) => {
+                this._tab.highlightNode();
+            });
+        }
+
+        /** Highlight or downplay this node */
+        public highlight(b:boolean) {
+            // Remove highlight for all children 
+            if (!b) {
+                for (let child of this._children) {
+                    child._adapter.highlight(b);
+                }
+            }
+            // Highlight this node
+            this._adapter.highlight(b);
+        }
+        
+        /** Returns true if the node is folded, false otherwise */
+        private _isFolded() : boolean {
+            return !this._div.classList.contains('unfolded');
+        }
+
+        /** Set this item as active (background lighter) in the tree panel */
+        public active(b:boolean) {
+            this._div.classList.remove('active');
+            for (let child of this._children) {
+                child.active(false);
+            }
+            if (b) {
+                this._div.classList.add('active');
+            }
+        }
+    }
+ }

+ 34 - 0
inspector/ts/inspector/treetools/AbstractTreeTool.ts

@@ -0,0 +1,34 @@
+ module INSPECTOR {
+     
+    export abstract class AbstractTreeTool {
+        protected _elem: HTMLElement;
+        /** Is the tool enabled ? */
+        protected _on  : boolean = false;
+        
+        constructor() {
+            this._elem = Inspector.DOCUMENT.createElement('i');
+            this._elem.className = 'treeTool fa';
+            this._addEvents();
+        }    
+
+        public toHtml() : HTMLElement {
+            return this._elem;
+        }
+
+        protected _addEvents() {
+            this._elem.addEventListener('click', (e) => {
+                this.action();
+                e.stopPropagation();
+            });
+        }
+
+        /**
+         * Action launched when clicked on this element
+         * Should be overrided
+         */
+        protected action() {
+            this._on = !this._on;
+        }
+
+    }
+ }

+ 43 - 0
inspector/ts/inspector/treetools/BoundingBox.ts

@@ -0,0 +1,43 @@
+module INSPECTOR{
+
+    /** Any object implementing this interface should 
+     * provide methods to toggle its bounding box
+     */
+    export interface IToolBoundingBox {
+        isBoxVisible   : () => boolean,
+        setBoxVisible  : (b:boolean) => void;
+    }
+    /**
+     * Checkbox to display/hide the primitive
+     */
+    export class BoundingBox extends AbstractTreeTool{
+
+        private _obj : IToolBoundingBox;
+
+        constructor(obj:IToolBoundingBox) {
+            super (); 
+            this._obj = obj;
+            this._elem.classList.add('fa-square-o');
+            this._on = this._obj.isBoxVisible();
+            this._check();
+        }
+
+        // For a checkbox, set visible/invisible the corresponding prim
+        protected action() {     
+            super.action();
+            // update object and gui according to the new status
+            this._check();
+        }
+
+        private _check() {
+             if (this._on) {
+                // set icon eye
+                this._elem.classList.add('active');
+            }else {
+                // set icon eye-slash
+                this._elem.classList.remove('active');
+            }
+            this._obj.setBoxVisible(this._on);
+        }
+    }
+}

+ 47 - 0
inspector/ts/inspector/treetools/Checkbox.ts

@@ -0,0 +1,47 @@
+module INSPECTOR{
+
+    /** Any object implementing this interface should 
+     * provide methods to toggle its visibility
+     */
+    export interface IToolVisible {
+        isVisible   : () => boolean,
+        setVisible  : (b:boolean) => void;
+    }
+    /**
+     * Checkbox to display/hide the primitive
+     */
+    export class Checkbox extends AbstractTreeTool{
+
+        private _obj : IToolVisible;
+
+        constructor(obj:IToolVisible) {
+            super (); 
+            this._obj = obj;
+            this._elem.classList.add('fa-eye');
+            this._on = this._obj.isVisible();
+            this._check();
+        }
+
+        // For a checkbox, set visible/invisible the corresponding prim
+        protected action() {     
+            super.action();
+            // update object and gui according to the new status
+            this._check();
+        }
+
+        private _check() {
+             if (this._on) {
+                // set icon eye
+                this._elem.classList.add('fa-eye');
+                this._elem.classList.add('active');
+                this._elem.classList.remove('fa-eye-slash');
+            }else {
+                // set icon eye-slash
+                this._elem.classList.remove('fa-eye');
+                this._elem.classList.remove('active');
+                this._elem.classList.add('fa-eye-slash');
+            }
+            this._obj.setVisible(this._on);
+        }
+    }
+}

+ 31 - 0
inspector/ts/inspector/treetools/DebugArea.ts

@@ -0,0 +1,31 @@
+module INSPECTOR {
+    /** Any object implementing this interface should 
+     * provide methods to toggle a debug area
+     */
+    export interface IToolDebug {
+        debug  : (b:boolean) => void;
+    }
+
+    export class DebugArea extends AbstractTreeTool {
+
+        private _obj : IToolDebug;
+
+        constructor(obj:IToolDebug) {
+            super();
+            this._obj = obj;
+            this._elem.classList.add('fa-wrench');
+        }
+
+        protected action() {     
+            super.action(); 
+            if (this._on) {
+                // set icon activated
+                this._elem.classList.add('active');
+            }else {
+                // set icon deactivated
+                this._elem.classList.remove('active');
+            }
+            this._obj.debug(this._on);
+        }
+    }
+}

+ 31 - 0
inspector/ts/inspector/treetools/Info.ts

@@ -0,0 +1,31 @@
+module INSPECTOR{
+
+    /** Any object implementing this interface should 
+     * provide methods to retrieve its info
+     */
+    export interface IToolInfo {
+        getInfo  : () => string;
+    }
+    /**
+     * Checkbox to display/hide the primitive
+     */
+    export class Info extends AbstractTreeTool{
+
+        private _obj : IToolInfo;
+
+        private _tooltip : Tooltip;
+
+        constructor(obj:IToolInfo) {
+            super (); 
+            this._obj = obj;
+            this._elem.classList.add('fa-info-circle');
+
+            this._tooltip = new Tooltip(this._elem, this._obj.getInfo());
+        }
+
+        // Nothing to do on click
+        protected action() {     
+            super.action();
+        }
+    }
+}

+ 169 - 0
inspector/ts/test/Test.ts

@@ -0,0 +1,169 @@
+class Test { 
+
+    private engine: BABYLON.Engine;
+    public scene: BABYLON.Scene;
+
+    constructor(canvasId: string) {
+
+        let canvas: HTMLCanvasElement = <HTMLCanvasElement>document.getElementById(canvasId);
+        this.engine = new BABYLON.Engine(canvas, true);
+
+        this.scene = null;
+
+        window.addEventListener("resize", () => {
+            this.engine.resize();
+        });
+
+        this._run();
+
+    }
+
+    private _run() {
+        
+        this._initScene();
+
+        // new INSPECTOR.Inspector(this.scene); 
+        BABYLON.DebugLayer.InspectorURL = 'http://localhost:3000/dist/libs/inspector.js';
+        this.scene.debugLayer.show();
+        this.scene.executeWhenReady(() => {
+            
+            this._initGame();
+
+            this.engine.runRenderLoop(() => {
+                this.scene.render();
+            });
+        });
+    }
+
+    private _initScene() {
+
+        var scene = new BABYLON.Scene(this.engine);
+        var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 4, Math.PI / 2.5, 200, BABYLON.Vector3.Zero(), scene);
+        camera.attachControl(this.engine.getRenderingCanvas(), true);
+        camera.minZ = 0.1;
+    // Lights
+        var light0 = new BABYLON.PointLight("Omni0", new BABYLON.Vector3(0, 10, 0), scene);
+        var light1 = new BABYLON.PointLight("Omni1", new BABYLON.Vector3(0, -10, 0), scene);
+        var light2 = new BABYLON.PointLight("Omni2", new BABYLON.Vector3(10, 0, 0), scene);
+        var light3 = new BABYLON.DirectionalLight("Dir0", new BABYLON.Vector3(1, -1, 0), scene);
+
+        var material = new BABYLON.StandardMaterial("kosh", scene);
+        var sphere = BABYLON.Mesh.CreateSphere("Sphere", 16, 3, scene);
+
+        // Creating light sphere
+        var lightSphere0 = BABYLON.Mesh.CreateSphere("Sphere0", 16, 0.5, scene);
+        var lightSphere1 = BABYLON.Mesh.CreateSphere("Sphere1", 16, 0.5, scene);
+        var lightSphere2 = BABYLON.Mesh.CreateSphere("Sphere2", 16, 0.5, scene);
+
+        lightSphere0.material = new BABYLON.StandardMaterial("red", scene);
+        (lightSphere0.material as BABYLON.StandardMaterial).diffuseColor = new BABYLON.Color3(0, 0, 0);
+        (lightSphere0.material as BABYLON.StandardMaterial).specularColor = new BABYLON.Color3(0, 0, 0);
+        (lightSphere0.material as BABYLON.StandardMaterial).emissiveColor = new BABYLON.Color3(1, 0, 0);
+
+        lightSphere1.material = new BABYLON.StandardMaterial("green", scene);
+        (lightSphere1.material as BABYLON.StandardMaterial).diffuseColor = new BABYLON.Color3(0, 0, 0);
+        (lightSphere1.material as BABYLON.StandardMaterial).specularColor = new BABYLON.Color3(0, 0, 0);
+        (lightSphere1.material as BABYLON.StandardMaterial).emissiveColor = new BABYLON.Color3(0, 1, 0);
+
+        lightSphere2.material = new BABYLON.StandardMaterial("blue", scene);
+        (lightSphere2.material as BABYLON.StandardMaterial).diffuseColor = new BABYLON.Color3(0, 0, 0);
+        (lightSphere2.material as BABYLON.StandardMaterial).specularColor = new BABYLON.Color3(0, 0, 0);
+        (lightSphere2.material as BABYLON.StandardMaterial).emissiveColor = new BABYLON.Color3(0, 0, 1);
+
+        // Sphere material
+        material.diffuseColor = new BABYLON.Color3(1, 1, 1);
+        sphere.material = material;
+
+        // Lights colors
+        light0.diffuse = new BABYLON.Color3(1, 0, 0);
+        light0.specular = new BABYLON.Color3(1, 0, 0);
+
+        light1.diffuse = new BABYLON.Color3(0, 1, 0);
+        light1.specular = new BABYLON.Color3(0, 1, 0);
+
+        light2.diffuse = new BABYLON.Color3(0, 0, 1);
+        light2.specular = new BABYLON.Color3(0, 0, 1);
+
+        light3.diffuse = new BABYLON.Color3(1, 1, 1);
+        light3.specular = new BABYLON.Color3(1, 1, 1);
+
+        BABYLON.Effect.ShadersStore["customVertexShader"] = 'precision highp float;attribute vec3 position;attribute vec2 uv;uniform mat4 worldViewProjection;varying vec2 vUV;varying vec3 vPos;void main(){gl_Position=worldViewProjection*vec4(position,1.),vPos=gl_Position.xyz;if(position.x >2.0) {gl_Position.x = 2.0;} else { gl_Position.y = 1.0;}}';
+        BABYLON.Effect.ShadersStore["customFragmentShader"] = 'precision highp float;varying vec3 vPos;uniform vec3 color;void main(){gl_FragColor=vec4(mix(color,vPos,.05),1.);}';
+        
+        var shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, {
+            vertex: "custom",
+            fragment: "custom",
+        },
+        {
+            attributes: ["position", "normal", "uv"],
+            uniforms: ["world", "worldView", "worldViewProjection", "view", "projection"]
+            });
+        sphere.material = shaderMaterial;
+
+        // Animations
+        var alpha = 0;
+        scene.beforeRender = function () {
+            light0.position = new BABYLON.Vector3(10 * Math.sin(alpha), 0, 10 * Math.cos(alpha));
+            light1.position = new BABYLON.Vector3(10 * Math.sin(alpha), 0, -10 * Math.cos(alpha));
+            light2.position = new BABYLON.Vector3(10 * Math.cos(alpha), 0, 10 * Math.sin(alpha));
+
+            lightSphere0.position = light0.position;
+            lightSphere1.position = light1.position;
+            lightSphere2.position = light2.position;
+
+            alpha += 0.01;
+        };
+        
+        this.scene = scene;        
+    }
+
+
+    private _initGame () {
+        
+        
+        this._createCanvas();
+    }
+
+    /**
+     * Create the canvas2D 
+     */
+    private _createCanvas() {
+            var canvas = new BABYLON.ScreenSpaceCanvas2D(this.scene, {
+            id: "Hello world SC",
+            size: new BABYLON.Size(300, 100),
+            backgroundFill: "#4040408F",
+            backgroundRoundRadius: 50,
+            children: [
+                new BABYLON.Text2D("Hello World!", {
+                    id: "text",
+                    marginAlignment: "h: center, v:center",
+                    fontName: "20pt Arial",
+                })
+            ]
+        });
+
+        var infoCanvas = new BABYLON.ScreenSpaceCanvas2D(this.scene, { id: "PINK CUBE SC", size: new BABYLON.Size(500, 500) });
+    var text2 = new BABYLON.Text2D("UnbindTime", { parent: infoCanvas, id: "Text", marginAlignment: "h: left, v: bottom", fontName: "10pt Arial" });
+    canvas = new BABYLON.WorldSpaceCanvas2D(this.scene, new BABYLON.Size(150, 150), {
+        id: "WorldSpaceCanvas",
+        worldPosition: new BABYLON.Vector3(0, 0, 0),
+        worldRotation: BABYLON.Quaternion.RotationYawPitchRoll(Math.PI / 4, Math.PI / 4, 0),
+        enableInteraction: true,
+        backgroundFill: "#C0C0C040",
+        backgroundRoundRadius: 20,
+        children: [
+            new BABYLON.Text2D("World Space Canvas", { fontName: "8pt Arial", marginAlignment: "h: center, v: bottom", fontSuperSample: true })
+        ]
+    });
+    var rect = new BABYLON.Rectangle2D({ parent: canvas, x: 45, y: 45, width: 30, height: 30, fill: null, border: BABYLON.Canvas2D.GetGradientColorBrush(new BABYLON.Color4(0.9, 0.3, 0.9, 1), new BABYLON.Color4(1.0, 1.0, 1.0, 1)), borderThickness: 2 });
+    var buttonRect = new BABYLON.Rectangle2D({ parent: canvas, id: "button", x: 12, y: 12, width: 50, height: 15, fill: "#40C040FF", roundRadius: 2, children: [new BABYLON.Text2D("Click Me!", { fontName: "8pt Arial", marginAlignment: "h: center, v: center", fontSuperSample: true })] });
+    var button2Rect = new BABYLON.Rectangle2D({ parent: canvas, id: "button2", x: 70, y: 12, width: 40, height: 15, fill: "#4040C0FF", roundRadius: 2, isVisible: false, children: [new BABYLON.Text2D("Great!", { fontName: "8pt Arial", marginAlignment: "h: center, v: center", fontSuperSample: true })] });
+    ;
+    buttonRect.pointerEventObservable.add(function (d, s) {
+        button2Rect.levelVisible = !button2Rect.levelVisible;
+    }, BABYLON.PrimitivePointerInfo.PointerUp);
+    var insideRect = new BABYLON.Rectangle2D({ parent: rect, width: 10, height: 10, marginAlignment: "h: center, v: center", fill: "#0040F0FF" });
+    insideRect.roundRadius = 2;
+
+    }
+}

+ 5 - 0
inspector/ts/tsconfig.json

@@ -0,0 +1,5 @@
+{
+    "compilerOptions": {
+        "target": "es5"
+    }
+}

File diff suppressed because it is too large
+ 4178 - 0
inspector/ts/typings/babylon.canvas2d.d.ts


File diff suppressed because it is too large
+ 12593 - 0
inspector/ts/typings/babylon.d.ts


+ 20 - 0
inspector/ts/typings/split.d.ts

@@ -0,0 +1,20 @@
+
+
+interface ISplit {
+    setSizes(sizes:Array<number>);
+    collapse(index:number);
+    destroy();
+}
+
+
+declare function Split (element : Array<HTMLElement> | Array<string>, options:{
+    sizes?      : Array<number>, 
+    minSize?    : number,
+    gutterSize? : number,
+    snapOffset? : number,
+    direction?  : string
+    cursor?     : string
+    onDrag?     : Function,
+    onDragStart?: Function,
+    onDragEnd?  : Function,        
+}) : ISplit;

+ 26 - 0
inspector/webpack.config.js

@@ -0,0 +1,26 @@
+var webpack = require("webpack");
+
+module.exports = {
+    entry: "./index.js",
+    output: {
+        path: __dirname + '/dist',        
+        filename: "inspector.js",
+        libraryTarget: "umd",
+        library: "INSPECTOR",
+        umdNamedDefine: true
+    },
+    module: {
+        loaders: [
+            { test: /\.css$/, loader: "style!css" }
+        ]
+        
+    },
+    // plugins: [
+    //     new webpack.optimize.UglifyJsPlugin({
+    //         compress: {
+    //             warnings: false
+    //         },
+    //         mangle:false
+    //     })
+    // ]
+};

+ 19 - 816
src/Debug/babylon.debugLayer.ts

@@ -1,836 +1,39 @@
-module BABYLON {
-    export class DebugLayer {
-        private _scene: Scene;
-        private _camera: Camera;
-        private _transformationMatrix = Matrix.Identity();
-        private _enabled: boolean = false;
-        private _labelsEnabled: boolean = false;
-        private _displayStatistics = true;
-        private _displayTree = false;
-        private _displayLogs = false;
-        private _globalDiv: HTMLDivElement;
-        private _statsDiv: HTMLDivElement;
-        private _statsSubsetDiv: HTMLDivElement;
-        private _optionsDiv: HTMLDivElement;
-        private _optionsSubsetDiv: HTMLDivElement;
-        private _logDiv: HTMLDivElement;
-        private _logSubsetDiv: HTMLDivElement;
-        private _treeDiv: HTMLDivElement;
-        private _treeSubsetDiv: HTMLDivElement;
-        private _drawingCanvas: HTMLCanvasElement;
-        private _drawingContext: CanvasRenderingContext2D;
-        private _rootElement: HTMLElement;
-
-        private _skeletonViewers = new Array<Debug.SkeletonViewer>();
-
-        public _syncPositions: () => void;
-        private _syncData: () => void;
-        private _syncUI: () => void;
-        private _onCanvasClick: (evt: MouseEvent) => void;
-
-        private _clickPosition: any;
-        private _ratio: number;
-
-        private _identityMatrix = Matrix.Identity();
+module BABYLON {
 
-        private _showUI: boolean;
-        private _needToRefreshMeshesTree: boolean;
+    // declare INSPECTOR namespace for compilation issue
+    declare var INSPECTOR : any;
 
-        public shouldDisplayLabel: (node: Node) => boolean;
-        public shouldDisplayAxis: (mesh: Mesh) => boolean;
-
-        public axisRatio = 0.02;
-
-        public accentColor = "orange";
-
-        public customStatsFunction: () => string;
+    export class DebugLayer {
+        private _scene: Scene;
+        public static InspectorURL = 'http://www.babylonjs.com/inspector.js'
 
         constructor(scene: Scene) {
             this._scene = scene;
-
-            this._syncPositions = (): void => {
-                var engine = this._scene.getEngine();
-                var canvasRect = engine.getRenderingCanvasClientRect();
-
-                if (this._showUI) {
-                    this._statsDiv.style.left = (canvasRect.width - 410) + "px";
-                    this._statsDiv.style.top = (canvasRect.height - 290) + "px";
-                    this._statsDiv.style.width = "400px";
-                    this._statsDiv.style.height = "auto";
-                    this._statsSubsetDiv.style.maxHeight = "240px";
-
-                    this._optionsDiv.style.left = "0px";
-                    this._optionsDiv.style.top = "10px";
-                    this._optionsDiv.style.width = "200px";
-                    this._optionsDiv.style.height = "auto";
-                    this._optionsSubsetDiv.style.maxHeight = (canvasRect.height - 225) + "px";
-
-                    this._logDiv.style.left = "0px";
-                    this._logDiv.style.top = (canvasRect.height - 170) + "px";
-                    this._logDiv.style.width = "600px";
-                    this._logDiv.style.height = "160px";
-
-                    this._treeDiv.style.left = (canvasRect.width - 310) + "px";
-                    this._treeDiv.style.top = "10px";
-                    this._treeDiv.style.width = "300px";
-                    this._treeDiv.style.height = "auto";
-                    this._treeSubsetDiv.style.maxHeight = (canvasRect.height - 340) + "px";
-                }
-
-                this._globalDiv.style.left = canvasRect.left + "px";
-                this._globalDiv.style.top = canvasRect.top + "px";
-
-                this._drawingCanvas.style.left = "0px";
-                this._drawingCanvas.style.top = "0px";
-                this._drawingCanvas.style.width = engine.getRenderWidth() + "px";
-                this._drawingCanvas.style.height = engine.getRenderHeight() + "px";
-
-                var devicePixelRatio = window.devicePixelRatio || 1;
-                var context = <any>this._drawingContext;
-                var backingStoreRatio = context.webkitBackingStorePixelRatio ||
-                    context.mozBackingStorePixelRatio ||
-                    context.msBackingStorePixelRatio ||
-                    context.oBackingStorePixelRatio ||
-                    context.backingStorePixelRatio || 1;
-
-                this._ratio = devicePixelRatio / backingStoreRatio;
-
-                this._drawingCanvas.width = engine.getRenderWidth() * this._ratio;
-                this._drawingCanvas.height = engine.getRenderHeight() * this._ratio;
-            }
-
-            this._onCanvasClick = (evt: MouseEvent): void => {
-                this._clickPosition = {
-                    x: evt.clientX * this._ratio,
-                    y: evt.clientY * this._ratio
-                };
-            }
-
-            this._syncUI = (): void => {
-                if (this._showUI) {
-                    if (this._displayStatistics) {
-                        this._displayStats();
-                        this._statsDiv.style.display = "";
-                    } else {
-                        this._statsDiv.style.display = "none";
-                    }
-
-                    if (this._displayLogs) {
-                        this._logDiv.style.display = "";
-                    } else {
-                        this._logDiv.style.display = "none";
-                    }
-
-                    if (this._displayTree) {
-                        this._treeDiv.style.display = "";
-
-                        if (this._needToRefreshMeshesTree) {
-                            this._needToRefreshMeshesTree = false;
-
-                            this._refreshMeshesTreeContent();
-                        }
-
-                    } else {
-                        this._treeDiv.style.display = "none";
-                    }
-                }
-            }
-
-            this._syncData = (): void => {
-                if (this._labelsEnabled || !this._showUI) {
-
-                    this._camera.getViewMatrix().multiplyToRef(this._camera.getProjectionMatrix(), this._transformationMatrix);
-
-                    this._drawingContext.clearRect(0, 0, this._drawingCanvas.width, this._drawingCanvas.height);
-
-                    var engine = this._scene.getEngine();
-                    var viewport = this._camera.viewport;
-                    var globalViewport = viewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
-
-                    // Meshes
-                    var meshes = this._camera.getActiveMeshes();
-                    var index: number;
-                    var projectedPosition: Vector3;
-                    for (index = 0; index < meshes.length; index++) {
-                        var mesh = meshes.data[index];
-
-                        var position = mesh.getBoundingInfo().boundingSphere.center;
-                        projectedPosition = Vector3.Project(position, mesh.getWorldMatrix(), this._transformationMatrix, globalViewport);
-                        if (mesh.renderOverlay || this.shouldDisplayAxis && this.shouldDisplayAxis(mesh)) {
-                            this._renderAxis(projectedPosition, mesh, globalViewport);
-                        }
-
-                        if (!this.shouldDisplayLabel || this.shouldDisplayLabel(mesh)) {
-                            this._renderLabel(mesh.name, projectedPosition, 12,
-                                () => { mesh.renderOverlay = !mesh.renderOverlay },
-                                () => { return mesh.renderOverlay ? 'red' : 'black'; });
-                        }
-                    }
-
-                    // Cameras
-                    var cameras = this._scene.cameras;
-                    for (index = 0; index < cameras.length; index++) {
-                        var camera = cameras[index];
-
-                        if (camera === this._camera) {
-                            continue;
-                        }
-
-                        projectedPosition = Vector3.Project(Vector3.Zero(), camera.getWorldMatrix(), this._transformationMatrix, globalViewport);
-
-                        if (!this.shouldDisplayLabel || this.shouldDisplayLabel(camera)) {
-                            this._renderLabel(camera.name, projectedPosition, 12,
-                                () => {
-                                    this._camera.detachControl(engine.getRenderingCanvas());
-                                    this._camera = camera;
-                                    this._camera.attachControl(engine.getRenderingCanvas());
-                                },
-                                () => { return "purple"; });
-                        }
-                    }
-
-                    // Lights
-                    var lights = this._scene.lights;
-                    for (index = 0; index < lights.length; index++) {
-                        var light = <any>lights[index];
-
-                        if (light.position) {
-
-                            projectedPosition = Vector3.Project(light.getAbsolutePosition(), this._identityMatrix, this._transformationMatrix, globalViewport);
-
-                            if (!this.shouldDisplayLabel || this.shouldDisplayLabel(light)) {
-                                this._renderLabel(light.name, projectedPosition, -20,
-                                    () => {
-                                        light.setEnabled(!light.isEnabled());
-                                    },
-                                    () => { return light.isEnabled() ? "orange" : "gray"; });
-                            }
-
-                        }
-                    }
-                }
-
-                this._clickPosition = undefined;
-            }
-        }
-
-        private _refreshMeshesTreeContent(): void {
-            while (this._treeSubsetDiv.hasChildNodes()) {
-                this._treeSubsetDiv.removeChild(this._treeSubsetDiv.lastChild);
-            }
-
-            // Add meshes
-            var sortedArray = this._scene.meshes.slice(0, this._scene.meshes.length);
-
-            sortedArray.sort((a, b) => {
-                if (a.name === b.name) {
-                    return 0;
-                }
-
-                return (a.name > b.name) ? 1 : -1;
-            });
-
-            for (var index = 0; index < sortedArray.length; index++) {
-                var mesh = sortedArray[index];
-
-                if (!mesh.isEnabled()) {
-                    continue;
-                }
-
-                this._generateAdvancedCheckBox(this._treeSubsetDiv, mesh.name, mesh.getTotalVertices() + " verts", mesh.isVisible, (element, m) => {
-                    m.isVisible = element.checked;
-                }, mesh);
-            }
-        }
-
-        private _renderSingleAxis(zero: Vector3, unit: Vector3, unitText: Vector3, label: string, color: string) {
-            this._drawingContext.beginPath();
-            this._drawingContext.moveTo(zero.x, zero.y);
-            this._drawingContext.lineTo(unit.x, unit.y);
-
-            this._drawingContext.strokeStyle = color;
-            this._drawingContext.lineWidth = 4;
-            this._drawingContext.stroke();
-
-            this._drawingContext.font = "normal 14px Segoe UI";
-            this._drawingContext.fillStyle = color;
-            this._drawingContext.fillText(label, unitText.x, unitText.y);
-        }
-
-        private _renderAxis(projectedPosition: Vector3, mesh: Mesh, globalViewport: Viewport) {
-            var position = mesh.getBoundingInfo().boundingSphere.center;
-            var worldMatrix = mesh.getWorldMatrix();
-
-            var unprojectedVector = Vector3.UnprojectFromTransform(projectedPosition.add(new Vector3(this._drawingCanvas.width * this.axisRatio, 0, 0)), globalViewport.width, globalViewport.height, worldMatrix, this._transformationMatrix);
-            var unit = (unprojectedVector.subtract(position)).length();
-
-            var xAxis = Vector3.Project(position.add(new Vector3(unit, 0, 0)), worldMatrix, this._transformationMatrix, globalViewport);
-            var xAxisText = Vector3.Project(position.add(new Vector3(unit * 1.5, 0, 0)), worldMatrix, this._transformationMatrix, globalViewport);
-
-            this._renderSingleAxis(projectedPosition, xAxis, xAxisText, "x", "#FF0000");
-
-            var yAxis = Vector3.Project(position.add(new Vector3(0, unit, 0)), worldMatrix, this._transformationMatrix, globalViewport);
-            var yAxisText = Vector3.Project(position.add(new Vector3(0, unit * 1.5, 0)), worldMatrix, this._transformationMatrix, globalViewport);
-
-            this._renderSingleAxis(projectedPosition, yAxis, yAxisText, "y", "#00FF00");
-
-            var zAxis = Vector3.Project(position.add(new Vector3(0, 0, unit)), worldMatrix, this._transformationMatrix, globalViewport);
-            var zAxisText = Vector3.Project(position.add(new Vector3(0, 0, unit * 1.5)), worldMatrix, this._transformationMatrix, globalViewport);
-
-            this._renderSingleAxis(projectedPosition, zAxis, zAxisText, "z", "#0000FF");
         }
 
-        private _renderLabel(text: string, projectedPosition: Vector3, labelOffset: number, onClick: () => void, getFillStyle: () => string): void {
-            if (projectedPosition.z > 0 && projectedPosition.z < 1.0) {
-                this._drawingContext.font = "normal 12px Segoe UI";
-                var textMetrics = this._drawingContext.measureText(text);
-                var centerX = projectedPosition.x - textMetrics.width / 2;
-                var centerY = projectedPosition.y;
-                var clientRect = this._drawingCanvas.getBoundingClientRect();
-
-                if (this._showUI && this._isClickInsideRect(clientRect.left * this._ratio + centerX - 5, clientRect.top * this._ratio + centerY - labelOffset - 12, textMetrics.width + 10, 17)) {
-                    onClick();
-                }
-
-                this._drawingContext.beginPath();
-                this._drawingContext.rect(centerX - 5, centerY - labelOffset - 12, textMetrics.width + 10, 17);
-                this._drawingContext.fillStyle = getFillStyle();
-                this._drawingContext.globalAlpha = 0.5;
-                this._drawingContext.fill();
-                this._drawingContext.globalAlpha = 1.0;
-
-                this._drawingContext.strokeStyle = '#FFFFFF';
-                this._drawingContext.lineWidth = 1;
-                this._drawingContext.stroke();
-
-                this._drawingContext.fillStyle = "#FFFFFF";
-                this._drawingContext.fillText(text, centerX, centerY - labelOffset);
-
-                this._drawingContext.beginPath();
-                this._drawingContext.arc(projectedPosition.x, centerY, 5, 0, 2 * Math.PI, false);
-                this._drawingContext.fill();
-            }
-        }
-
-        private _isClickInsideRect(x: number, y: number, width: number, height: number): boolean {
-            if (!this._clickPosition) {
-                return false;
-            }
-
-            if (this._clickPosition.x < x || this._clickPosition.x > x + width) {
-                return false;
-            }
-
-            if (this._clickPosition.y < y || this._clickPosition.y > y + height) {
-                return false;
-            }
-
-            return true;
+        /** Creates the inspector window. */
+        private _createInspector() {
+            new INSPECTOR.Inspector(this._scene);
         }
 
+        
         public isVisible(): boolean {
-            return this._enabled;
+            return true;
         }
 
         public hide() {
-            if (!this._enabled) {
-                return;
-            }
-
-            this._enabled = false;
-
-            var engine = this._scene.getEngine();
-
-            this._scene.unregisterBeforeRender(this._syncData);
-            this._scene.unregisterAfterRender(this._syncUI);
-            this._rootElement.removeChild(this._globalDiv);
-
-            this._scene.forceShowBoundingBoxes = false;
-            this._scene.forceWireframe = false;
-
-            StandardMaterial.DiffuseTextureEnabled = true;
-            StandardMaterial.AmbientTextureEnabled = true;
-            StandardMaterial.SpecularTextureEnabled = true;
-            StandardMaterial.EmissiveTextureEnabled = true;
-            StandardMaterial.BumpTextureEnabled = true;
-            StandardMaterial.OpacityTextureEnabled = true;
-            StandardMaterial.ReflectionTextureEnabled = true;
-            StandardMaterial.LightmapTextureEnabled = true;
-            StandardMaterial.RefractionTextureEnabled = true;            
-            StandardMaterial.ColorGradingTextureEnabled = true;
-
-            this._scene.shadowsEnabled = true;
-            this._scene.particlesEnabled = true;
-            this._scene.postProcessesEnabled = true;
-            this._scene.collisionsEnabled = true;
-            this._scene.lightsEnabled = true;
-            this._scene.texturesEnabled = true;
-            this._scene.lensFlaresEnabled = true;
-            this._scene.proceduralTexturesEnabled = true;
-            this._scene.renderTargetsEnabled = true;
-            this._scene.probesEnabled = true;
-
-            engine.getRenderingCanvas().removeEventListener("click", this._onCanvasClick);
-
-            this._clearSkeletonViewers();
-        }
-
-        private _clearSkeletonViewers(): void {
-            for (var index = 0; index < this._skeletonViewers.length; index++) {
-                this._skeletonViewers[index].dispose();
-            }
-
-            this._skeletonViewers = [];
+            console.warn('')
         }
-
-        public show(showUI: boolean = true, camera: Camera = null, rootElement: HTMLElement = null) {
-            if (this._enabled) {
-                return;
-            }
-
-            this._enabled = true;
-
-            if (camera) {
-                this._camera = camera;
+        
+        public show() {
+            if (typeof INSPECTOR == 'undefined') {
+                // Load inspector and add it to the DOM
+                Tools.LoadScript(DebugLayer.InspectorURL, this._createInspector.bind(this));
             } else {
-                this._camera = this._scene.activeCamera;
-            }
-
-            this._showUI = showUI;
-
-            var engine = this._scene.getEngine();
-
-            this._globalDiv = document.createElement("div");
-
-            this._rootElement = rootElement || document.body;
-
-            this._rootElement.appendChild(this._globalDiv);
-
-            this._generateDOMelements();
-
-            engine.getRenderingCanvas().addEventListener("click", this._onCanvasClick);
-
-            this._syncPositions();
-            this._scene.registerBeforeRender(this._syncData);
-            this._scene.registerAfterRender(this._syncUI);
-        }
-
-        private _clearLabels(): void {
-            this._drawingContext.clearRect(0, 0, this._drawingCanvas.width, this._drawingCanvas.height);
-
-            for (var index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-                mesh.renderOverlay = false;
-            }
-        }
-
-        private _generateheader(root: HTMLDivElement, text: string): void {
-            var header = document.createElement("div");
-            header.innerHTML = text + "&nbsp;";
-
-            header.style.textAlign = "right";
-            header.style.width = "100%";
-            header.style.color = "white";
-            header.style.backgroundColor = "Black";
-            header.style.padding = "5px 5px 4px 0px";
-            header.style.marginLeft = "-5px";
-            header.style.fontWeight = "bold";
-
-            root.appendChild(header);
-        }
-
-        private _generateTexBox(root: HTMLDivElement, title: string, color: string): void {
-            var label = document.createElement("label");
-            label.style.display = "inline";
-            label.innerHTML = title;
-            label.style.color = color;
-
-            root.appendChild(label);
-            root.appendChild(document.createElement("br"));
-        }
-
-        private _generateAdvancedCheckBox(root: HTMLDivElement, leftTitle: string, rightTitle: string, initialState: boolean, task: (element, tag) => void, tag: any = null): void {
-            var label = document.createElement("label");
-            label.style.display = "inline";
-
-            var boundingBoxesCheckbox = document.createElement("input");
-            boundingBoxesCheckbox.type = "checkbox";
-            boundingBoxesCheckbox.checked = initialState;
-            boundingBoxesCheckbox.style.display = "inline";
-            boundingBoxesCheckbox.style.margin = "0px 5px 0px 0px";
-            boundingBoxesCheckbox.style.verticalAlign = "sub";
-
-            boundingBoxesCheckbox.addEventListener("change", (evt: Event) => {
-                task(evt.target, tag);
-            });
-
-            label.appendChild(boundingBoxesCheckbox);
-            var container = document.createElement("span");
-            var leftPart = document.createElement("span");
-            var rightPart = document.createElement("span");
-
-            rightPart.style.cssFloat = "right";
-
-            leftPart.innerHTML = leftTitle;
-            rightPart.innerHTML = rightTitle;
-            rightPart.style.fontSize = "12px";
-            rightPart.style.maxWidth = "200px";
-
-            container.appendChild(leftPart);
-            container.appendChild(rightPart);
-
-            label.appendChild(container);
-            root.appendChild(label);
-            root.appendChild(document.createElement("br"));
-        }
-
-        private _generateCheckBox(root: HTMLDivElement, title: string, initialState: boolean, task: (element, tag) => void, tag: any = null): void {
-            var label = document.createElement("label");
-            label.style.display = "inline";
-
-            var checkBox = document.createElement("input");
-            checkBox.type = "checkbox";
-            checkBox.checked = initialState;
-            checkBox.style.display = "inline";
-            checkBox.style.margin = "0px 5px 0px 0px";
-            checkBox.style.verticalAlign = "sub";
-
-            checkBox.addEventListener("change", (evt: Event) => {
-                task(evt.target, tag);
-            });
-
-            label.appendChild(checkBox);
-            label.appendChild(document.createTextNode(title));
-            root.appendChild(label);
-            root.appendChild(document.createElement("br"));
-        }
-
-        private _generateButton(root: HTMLDivElement, title: string, task: (element, tag) => void, tag: any = null): void {
-            var button = document.createElement("button");
-            button.innerHTML = title;
-            button.style.height = "24px";
-            button.style.width = "150px";
-            button.style.marginBottom = "5px";
-            button.style.color = "#444444";
-            button.style.border = "1px solid white";
-            button.className = "debugLayerButton";
-
-            button.addEventListener("click", (evt: Event) => {
-                task(evt.target, tag);
-            });
-
-            root.appendChild(button);
-            root.appendChild(document.createElement("br"));
-        }
-
-        private _generateRadio(root: HTMLDivElement, title: string, name: string, initialState: boolean, task: (element, tag) => void, tag: any = null): void {
-            var label = document.createElement("label");
-
-            label.style.display = "inline";
-
-            var boundingBoxesRadio = document.createElement("input");
-            boundingBoxesRadio.type = "radio";
-            boundingBoxesRadio.name = name;
-            boundingBoxesRadio.checked = initialState;
-            boundingBoxesRadio.style.display = "inline";
-            boundingBoxesRadio.style.margin = "0px 5px 0px 0px";
-            boundingBoxesRadio.style.verticalAlign = "sub";
-
-            boundingBoxesRadio.addEventListener("change", (evt: Event) => {
-                task(evt.target, tag);
-            });
-
-            label.appendChild(boundingBoxesRadio);
-            label.appendChild(document.createTextNode(title));
-            root.appendChild(label);
-            root.appendChild(document.createElement("br"));
-        }
-
-        private _generateDOMelements(): void {
-            this._globalDiv.id = "DebugLayer";
-            this._globalDiv.style.position = "absolute";
-
-            this._globalDiv.style.fontFamily = "Segoe UI, Arial";
-            this._globalDiv.style.fontSize = "14px";
-            this._globalDiv.style.color = "white";
-
-            // Drawing canvas
-            this._drawingCanvas = document.createElement("canvas");
-            this._drawingCanvas.id = "DebugLayerDrawingCanvas";
-            this._drawingCanvas.style.position = "absolute";
-            this._drawingCanvas.style.pointerEvents = "none";
-            this._drawingCanvas.style.backgroundColor = "transparent";
-            this._drawingContext = this._drawingCanvas.getContext("2d");
-            this._globalDiv.appendChild(this._drawingCanvas);
-
-            if (this._showUI) {
-                var background = "rgba(128, 128, 128, 0.4)";
-                var border = "rgb(180, 180, 180) solid 1px";
-
-                // Stats
-                this._statsDiv = document.createElement("div");
-                this._statsDiv.id = "DebugLayerStats";
-                this._statsDiv.style.border = border;
-                this._statsDiv.style.position = "absolute";
-                this._statsDiv.style.background = background;
-                this._statsDiv.style.padding = "0px 0px 0px 5px";
-                this._generateheader(this._statsDiv, "STATISTICS");
-                this._statsSubsetDiv = document.createElement("div");
-                this._statsSubsetDiv.style.paddingTop = "5px";
-                this._statsSubsetDiv.style.paddingBottom = "5px";
-                this._statsSubsetDiv.style.overflowY = "auto";
-                this._statsDiv.appendChild(this._statsSubsetDiv);
-
-                // Tree
-                this._treeDiv = document.createElement("div");
-                this._treeDiv.id = "DebugLayerTree";
-                this._treeDiv.style.border = border;
-                this._treeDiv.style.position = "absolute";
-                this._treeDiv.style.background = background;
-                this._treeDiv.style.padding = "0px 0px 0px 5px";
-                this._treeDiv.style.display = "none";
-                this._generateheader(this._treeDiv, "MESHES TREE");
-                this._treeSubsetDiv = document.createElement("div");
-                this._treeSubsetDiv.style.paddingTop = "5px";
-                this._treeSubsetDiv.style.paddingRight = "5px";
-                this._treeSubsetDiv.style.overflowY = "auto";
-                this._treeSubsetDiv.style.maxHeight = "300px";
-                this._treeDiv.appendChild(this._treeSubsetDiv);
-                this._needToRefreshMeshesTree = true;
-
-                // Logs
-                this._logDiv = document.createElement("div");
-                this._logDiv.style.border = border;
-                this._logDiv.id = "DebugLayerLogs";
-                this._logDiv.style.position = "absolute";
-                this._logDiv.style.background = background;
-                this._logDiv.style.padding = "0px 0px 0px 5px";
-                this._logDiv.style.display = "none";
-                this._generateheader(this._logDiv, "LOGS");
-                this._logSubsetDiv = document.createElement("div");
-                this._logSubsetDiv.style.height = "127px";
-                this._logSubsetDiv.style.paddingTop = "5px";
-                this._logSubsetDiv.style.overflowY = "auto";
-                this._logSubsetDiv.style.fontSize = "12px";
-                this._logSubsetDiv.style.fontFamily = "consolas";
-                this._logSubsetDiv.innerHTML = Tools.LogCache;
-                this._logDiv.appendChild(this._logSubsetDiv);
-                Tools.OnNewCacheEntry = (entry: string) => {
-                    this._logSubsetDiv.innerHTML = entry + this._logSubsetDiv.innerHTML;
-                }
-
-                // Options
-                this._optionsDiv = document.createElement("div");
-                this._optionsDiv.id = "DebugLayerOptions";
-                this._optionsDiv.style.border = border;
-                this._optionsDiv.style.position = "absolute";
-                this._optionsDiv.style.background = background;
-                this._optionsDiv.style.padding = "0px 0px 0px 5px";
-                this._optionsDiv.style.overflowY = "auto";
-                this._generateheader(this._optionsDiv, "OPTIONS");
-                this._optionsSubsetDiv = document.createElement("div");
-                this._optionsSubsetDiv.style.paddingTop = "5px";
-                this._optionsSubsetDiv.style.paddingBottom = "5px";
-                this._optionsSubsetDiv.style.overflowY = "auto";
-                this._optionsSubsetDiv.style.maxHeight = "200px";
-                this._optionsDiv.appendChild(this._optionsSubsetDiv);
-
-                this._generateTexBox(this._optionsSubsetDiv, "<b>Windows:</b>", this.accentColor);
-                this._generateCheckBox(this._optionsSubsetDiv, "Statistics", this._displayStatistics, (element) => { this._displayStatistics = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Logs", this._displayLogs, (element) => { this._displayLogs = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Meshes tree", this._displayTree, (element) => {
-                    this._displayTree = element.checked;
-                    this._needToRefreshMeshesTree = true;
-                });
-                this._optionsSubsetDiv.appendChild(document.createElement("br"));
-                this._generateTexBox(this._optionsSubsetDiv, "<b>General:</b>", this.accentColor);
-                this._generateCheckBox(this._optionsSubsetDiv, "Bounding boxes", this._scene.forceShowBoundingBoxes, (element) => { this._scene.forceShowBoundingBoxes = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Clickable labels", this._labelsEnabled, (element) => {
-                    this._labelsEnabled = element.checked;
-                    if (!this._labelsEnabled) {
-                        this._clearLabels();
-                    }
-                });
-                this._generateCheckBox(this._optionsSubsetDiv, "Generate user marks (F12)", Tools.PerformanceLogLevel === Tools.PerformanceUserMarkLogLevel,
-                    (element) => {
-                        if (element.checked) {
-                            Tools.PerformanceLogLevel = Tools.PerformanceUserMarkLogLevel;
-                        } else {
-                            Tools.PerformanceLogLevel = Tools.PerformanceNoneLogLevel;
-                        }
-                    });
-                ;
-                this._optionsSubsetDiv.appendChild(document.createElement("br"));
-                this._generateTexBox(this._optionsSubsetDiv, "<b>Rendering mode:</b>", this.accentColor);
-                this._generateRadio(this._optionsSubsetDiv, "Solid", "renderMode", !this._scene.forceWireframe && !this._scene.forcePointsCloud, (element) => {
-                    if (element.checked) {
-                        this._scene.forceWireframe = false;
-                        this._scene.forcePointsCloud = false;
-                    }
-                });
-                this._generateRadio(this._optionsSubsetDiv, "Wireframe", "renderMode", this._scene.forceWireframe, (element) => {
-                    if (element.checked) {
-                        this._scene.forceWireframe = true;
-                        this._scene.forcePointsCloud = false;
-                    }
-                });
-                this._generateRadio(this._optionsSubsetDiv, "Point", "renderMode", this._scene.forcePointsCloud, (element) => {
-                    if (element.checked) {
-                        this._scene.forceWireframe = false;
-                        this._scene.forcePointsCloud = true;
-                    }
-                });
-                this._optionsSubsetDiv.appendChild(document.createElement("br"));
-                this._generateTexBox(this._optionsSubsetDiv, "<b>Texture channels:</b>", this.accentColor);
-                this._generateCheckBox(this._optionsSubsetDiv, "Diffuse", StandardMaterial.DiffuseTextureEnabled, (element) => { StandardMaterial.DiffuseTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Ambient", StandardMaterial.AmbientTextureEnabled, (element) => { StandardMaterial.AmbientTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Specular", StandardMaterial.SpecularTextureEnabled, (element) => { StandardMaterial.SpecularTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Emissive", StandardMaterial.EmissiveTextureEnabled, (element) => { StandardMaterial.EmissiveTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Bump", StandardMaterial.BumpTextureEnabled, (element) => { StandardMaterial.BumpTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Opacity", StandardMaterial.OpacityTextureEnabled, (element) => { StandardMaterial.OpacityTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Reflection", StandardMaterial.ReflectionTextureEnabled, (element) => { StandardMaterial.ReflectionTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Refraction", StandardMaterial.RefractionTextureEnabled, (element) => { StandardMaterial.RefractionTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "ColorGrading", StandardMaterial.ColorGradingTextureEnabled, (element) => { StandardMaterial.ColorGradingTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Lightmap", StandardMaterial.LightmapTextureEnabled, (element) => { StandardMaterial.LightmapTextureEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Fresnel", StandardMaterial.FresnelEnabled, (element) => { StandardMaterial.FresnelEnabled = element.checked });
-                this._optionsSubsetDiv.appendChild(document.createElement("br"));
-                this._generateTexBox(this._optionsSubsetDiv, "<b>Options:</b>", this.accentColor);
-                this._generateCheckBox(this._optionsSubsetDiv, "Animations", this._scene.animationsEnabled, (element) => { this._scene.animationsEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Collisions", this._scene.collisionsEnabled, (element) => { this._scene.collisionsEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Fog", this._scene.fogEnabled, (element) => { this._scene.fogEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Lens flares", this._scene.lensFlaresEnabled, (element) => { this._scene.lensFlaresEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Lights", this._scene.lightsEnabled, (element) => { this._scene.lightsEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Particles", this._scene.particlesEnabled, (element) => { this._scene.particlesEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Post-processes", this._scene.postProcessesEnabled, (element) => { this._scene.postProcessesEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Probes", this._scene.probesEnabled, (element) => { this._scene.probesEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Procedural textures", this._scene.proceduralTexturesEnabled, (element) => { this._scene.proceduralTexturesEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Render targets", this._scene.renderTargetsEnabled, (element) => { this._scene.renderTargetsEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Shadows", this._scene.shadowsEnabled, (element) => { this._scene.shadowsEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Skeletons", this._scene.skeletonsEnabled, (element) => { this._scene.skeletonsEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Sprites", this._scene.spritesEnabled, (element) => { this._scene.spritesEnabled = element.checked });
-                this._generateCheckBox(this._optionsSubsetDiv, "Textures", this._scene.texturesEnabled, (element) => { this._scene.texturesEnabled = element.checked });
-                if (AudioEngine && Engine.audioEngine.canUseWebAudio) {
-                    this._optionsSubsetDiv.appendChild(document.createElement("br"));
-                    this._generateTexBox(this._optionsSubsetDiv, "<b>Audio:</b>", this.accentColor);
-                    this._generateRadio(this._optionsSubsetDiv, "Headphones", "panningModel", this._scene.headphone, (element) => {
-                        if (element.checked) {
-                            this._scene.headphone = true;
-                        }
-                    });
-                    this._generateRadio(this._optionsSubsetDiv, "Normal Speakers", "panningModel", !this._scene.headphone, (element) => {
-                        if (element.checked) {
-                            this._scene.headphone = false;
-                        }
-                    });
-                    this._generateCheckBox(this._optionsSubsetDiv, "Disable audio", !this._scene.audioEnabled, (element) => {
-                        this._scene.audioEnabled = !element.checked;
-                    });
-                }
-                this._optionsSubsetDiv.appendChild(document.createElement("br"));
-                this._generateTexBox(this._optionsSubsetDiv, "<b>Viewers:</b>", this.accentColor);
-                this._generateCheckBox(this._optionsSubsetDiv, "Skeletons", false, (element) => {
-
-                    if (!element.checked) {
-                        this._clearSkeletonViewers();
-                        return;
-                    }
-
-                    for (var index = 0; index < this._scene.meshes.length; index++) {
-                        var mesh = this._scene.meshes[index];
-
-                        if (mesh.skeleton) {
-                            var found = false;
-                            for (var sIndex = 0; sIndex < this._skeletonViewers.length; sIndex++) {
-                                if (this._skeletonViewers[sIndex].skeleton === mesh.skeleton) {
-                                    found = true;
-                                    break;
-                                }
-                            }
-
-                            if (found) {
-                                continue;
-                            }
-
-                            var viewer = new BABYLON.Debug.SkeletonViewer(mesh.skeleton, mesh, this._scene);
-                            viewer.isEnabled = true;
-                            this._skeletonViewers.push(viewer);
-                        }
-                    }
-                });
-                this._optionsSubsetDiv.appendChild(document.createElement("br"));
-                this._generateTexBox(this._optionsSubsetDiv, "<b>Tools:</b>", this.accentColor);
-                this._generateButton(this._optionsSubsetDiv, "Dump rendertargets", (element) => { this._scene.dumpNextRenderTargets = true; });
-                this._generateButton(this._optionsSubsetDiv, "Run SceneOptimizer", (element) => { SceneOptimizer.OptimizeAsync(this._scene); });
-                this._generateButton(this._optionsSubsetDiv, "Log camera object", (element) => {
-                    if (this._camera) {
-                        console.log(this._camera);
-                    } else {
-                        console.warn("No camera defined, or debug layer created before camera creation!");
-                    }
-                });
-                this._optionsSubsetDiv.appendChild(document.createElement("br"));
-
-                this._globalDiv.appendChild(this._statsDiv);
-                this._globalDiv.appendChild(this._logDiv);
-                this._globalDiv.appendChild(this._optionsDiv);
-                this._globalDiv.appendChild(this._treeDiv);
+                // Otherwise creates the inspector
+                this._createInspector();
             }
         }
 
-        private _displayStats() {
-            var scene = this._scene;
-            var engine = scene.getEngine();
-            var glInfo = engine.getGlInfo();
-
-            this._statsSubsetDiv.innerHTML = "Babylon.js v" + Engine.Version + " - <b>" + Tools.Format(engine.getFps(), 0) + " fps</b><br><br>"
-                + "<div style='column-count: 2;-moz-column-count:2;-webkit-column-count:2'>"
-                + "<b>Count</b><br>"
-                + "Total meshes: " + scene.meshes.length + "<br>"
-                + "Total lights: " + scene.lights.length + "<br>"
-                + "Total vertices: " + scene.getTotalVertices() + "<br>"
-                + "Total materials: " + scene.materials.length + "<br>"
-                + "Total textures: " + scene.textures.length + "<br>"
-                + "Active meshes: " + scene.getActiveMeshes().length + "<br>"
-                + "Active indices: " + scene.getActiveIndices() + "<br>"
-                + "Active bones: " + scene.getActiveBones() + "<br>"
-                + "Active particles: " + scene.getActiveParticles() + "<br>"
-                + "<b>Draw calls: " + engine.drawCalls + "</b><br><br>"
-                + "<b>Duration</b><br>"
-                + "Meshes selection:</i> " + Tools.Format(scene.getEvaluateActiveMeshesDuration()) + " ms<br>"
-                + "Render Targets: " + Tools.Format(scene.getRenderTargetsDuration()) + " ms<br>"
-                + "Particles: " + Tools.Format(scene.getParticlesDuration()) + " ms<br>"
-                + "Sprites: " + Tools.Format(scene.getSpritesDuration()) + " ms<br><br>"
-                + "Render: <b>" + Tools.Format(scene.getRenderDuration()) + " ms</b><br>"
-                + "Frame: " + Tools.Format(scene.getLastFrameDuration()) + " ms<br>"
-                + "Potential FPS: " + Tools.Format(1000.0 / scene.getLastFrameDuration(), 0) + "<br>"
-                + "Resolution: " + engine.getRenderWidth() + "x" + engine.getRenderHeight() + "<br><br>"
-                + "</div>"
-                + "<div style='column-count: 2;-moz-column-count:2;-webkit-column-count:2'>"
-                + "<b>Extensions</b><br>"
-                + "Std derivatives: " + (engine.getCaps().standardDerivatives ? "Yes" : "No") + "<br>"
-                + "Compressed textures: " + (engine.getCaps().s3tc ? "Yes" : "No") + "<br>"
-                + "Hardware instances: " + (engine.getCaps().instancedArrays ? "Yes" : "No") + "<br>"
-                + "Texture float: " + (engine.getCaps().textureFloat ? "Yes" : "No") + "<br><br>"
-                + "32bits indices: " + (engine.getCaps().uintIndices ? "Yes" : "No") + "<br>"
-                + "Fragment depth: " + (engine.getCaps().fragmentDepthSupported ? "Yes" : "No") + "<br>"
-                + "High precision shaders: " + (engine.getCaps().highPrecisionShaderSupported ? "Yes" : "No") + "<br>"
-                + "Draw buffers: " + (engine.getCaps().drawBuffersExtension ? "Yes" : "No") + "<br>"
-                + "</div><br>"
-                + "<div style='column-count: 2;-moz-column-count:2;-webkit-column-count:2'>"
-                + "<b>Caps.</b><br>"
-                + "Stencil: " + (engine.isStencilEnable ? "Enabled" : "Disabled") + "<br>"
-                + "Max textures units: " + engine.getCaps().maxTexturesImageUnits + "<br>"
-                + "Max textures size: " + engine.getCaps().maxTextureSize + "<br>"
-                + "Max anisotropy: " + engine.getCaps().maxAnisotropy + "<br>"
-                + "<b>Info</b><br>"
-                + "WebGL feature level: " + engine.webGLVersion + "<br>"
-                + glInfo.version + "<br>"
-                + "</div><br>"
-                + glInfo.renderer + "<br>";
-
-            if (this.customStatsFunction) {
-                this._statsSubsetDiv.innerHTML += this.customStatsFunction();
-            }
-        }
     }
 }

+ 22 - 0
src/Tools/babylon.tools.ts

@@ -439,6 +439,28 @@
             }
         }
 
+        /** 
+         * Load a script (identified by an url). When the url returns, the 
+         * content of this file is added into a new script element, attached to the DOM (body element)
+         */
+        public static LoadScript(
+            scriptUrl:string, 
+            callback: (data?: any) => void, 
+            progressCallBack?: () => void,
+            onError?: () => void) {
+                // Load file
+                BABYLON.Tools.LoadFile(scriptUrl, (scriptContent:string) => {
+                    // Create script element
+                    let scriptElem         = window.document.createElement('script');
+                    scriptElem.textContent = scriptContent;        
+                    // attach the script to the body
+                    let body               = window.document.body;
+                    body.appendChild(scriptElem);
+                    callback();
+                }, progressCallBack, null, null, onError);
+
+        }
+
         public static ReadFileAsDataURL(fileToLoad, callback, progressCallback): void {
             var reader = new FileReader();
             reader.onload = e => {

+ 0 - 7
src/babylon.engine.ts

@@ -1059,13 +1059,6 @@
             var height = navigator.isCocoonJS ? window.innerHeight : this._renderingCanvas.clientHeight;
 
             this.setSize(width / this._hardwareScalingLevel, height / this._hardwareScalingLevel);
-
-            for (var index = 0; index < this.scenes.length; index++) {
-                var scene = this.scenes[index];
-                if (DebugLayer && scene.debugLayer.isVisible()) {
-                    scene.debugLayer._syncPositions();
-                }
-            }
         }
 
         /**