Explorar o código

Merge pull request #1573 from Temechon/master

New tab on inspector : Stats ! :sparkles:
David Catuhe %!s(int64=8) %!d(string=hai) anos
pai
achega
1e657905e4

+ 2 - 0
Tools/Gulp/config.json

@@ -533,6 +533,7 @@
         "../../inspector/src/tabs/MeshTab.ts",
         "../../inspector/src/tabs/SceneTab.ts",
         "../../inspector/src/tabs/ShaderTab.ts",
+        "../../inspector/src/tabs/StatsTab.ts",
         "../../inspector/src/tabs/TabBar.ts",
         "../../inspector/src/tools/AbstractTool.ts",
         "../../inspector/src/tools/PauseScheduleTool.ts",
@@ -540,6 +541,7 @@
         "../../inspector/src/tools/PopupTool.ts",
         "../../inspector/src/tools/RefreshTool.ts",
         "../../inspector/src/tools/Toolbar.ts",
+        "../../inspector/src/tools/DisposeTool.ts",
         "../../inspector/src/tree/TreeItem.ts",
         "../../inspector/src/treetools/AbstractTreeTool.ts",
         "../../inspector/src/treetools/BoundingBox.ts",

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 3 - 3
dist/preview release/inspector/babylon.inspector.bundle.js


+ 50 - 18
dist/preview release/inspector/babylon.inspector.css

@@ -37,11 +37,11 @@
       font-size: 1em; }
       .insp-wrapper .insp-right-panel .top-panel .tab-panel-content {
         width: 100%;
-        height: 100%; }
+        height: calc(100% - 32px); }
       .insp-wrapper .insp-right-panel .top-panel .more-tabs-panel {
         position: absolute;
         z-index: 10;
-        top: 30px;
+        top: 32px;
         right: 0;
         width: 100px;
         display: none;
@@ -63,16 +63,13 @@
             background-color: #454545; }
   .insp-wrapper .tooltip {
     position: absolute;
-    bottom: 0;
+    top: 0;
     right: 0;
-    color: #ccc;
-    transform: translateX(100%) translateY(100%);
+    color: #f29766;
     display: none;
     z-index: 4;
     font-family: "Inconsolata", sans-serif;
-    width: 120px;
-    padding: 5px 5px 5px 15px;
-    line-height: 25px;
+    padding: 2px;
     background-color: #242424;
     border: 1px solid #454545; }
   .insp-wrapper .treeTool {
@@ -168,6 +165,39 @@
       text-transform: uppercase;
       line-height: 25px;
       margin-bottom: 10px; }
+  .insp-wrapper .tab-panel.stats-panel {
+    overflow-y: auto; }
+  .insp-wrapper .tab-panel .stat-title1 {
+    font-size: 1.1em;
+    padding: 10px; }
+  .insp-wrapper .tab-panel .stat-title2 {
+    margin: 10px 0 10px 0;
+    font-size: 1.05em;
+    border-bottom: 1px solid #5db0d7;
+    box-sizing: border-box; }
+  .insp-wrapper .tab-panel .stat-label {
+    display: inline-block;
+    width: 80%;
+    padding: 2px;
+    background-color: #2c2c2c;
+    border-bottom: 1px solid #242424;
+    border-top: 1px solid #242424;
+    height: 30px;
+    line-height: 30px;
+    box-sizing: border-box; }
+  .insp-wrapper .tab-panel .stat-value {
+    display: inline-block;
+    width: 20%;
+    padding: 2px;
+    background-color: #2c2c2c;
+    border-top: 1px solid #242424;
+    border-bottom: 1px solid #242424;
+    height: 30px;
+    line-height: 30px;
+    box-sizing: border-box; }
+  .insp-wrapper .tab-panel .stat-infos {
+    width: 100%;
+    padding: 4px; }
   .insp-wrapper .property-type {
     color: #5db0d7; }
   .insp-wrapper .property-name, .insp-wrapper .insp-details .base-row .prop-name, .insp-wrapper .insp-details .row .prop-name, .insp-wrapper .insp-details .header-row .prop-name {
@@ -175,7 +205,7 @@
   .insp-wrapper .insp-tree {
     overflow-y: auto;
     overflow-x: hidden;
-    height: calc(50% - 30px - 30px); }
+    height: calc(50% - 32px - 30px); }
     .insp-wrapper .insp-tree .line {
       cursor: pointer; }
       .insp-wrapper .insp-tree .line:hover {
@@ -279,22 +309,24 @@
           max-width: 110px;
           max-height: 110px; }
   .insp-wrapper .tabbar {
-    height: 30px;
+    height: 32px;
     display: flex;
     align-items: center;
     border-bottom: 1px solid #383838;
     width: 100%;
     overflow-x: auto;
-    overflow-y: hidden; }
+    overflow-y: hidden;
+    box-sizing: border-box; }
     .insp-wrapper .tabbar .tab {
-      height: 30px;
+      height: calc(32px - 2px);
       width: auto;
       padding: 0 10px 0 10px;
       color: #ccc;
-      line-height: 30px;
+      line-height: 32px;
       text-align: center;
       cursor: pointer;
-      margin: 0 5px 0 5px; }
+      margin: 0 5px 0 5px;
+      box-sizing: border-box; }
       .insp-wrapper .tabbar .tab:hover {
         border-bottom: 1px solid #f29766;
         background-color: #2c2c2c; }
@@ -303,8 +335,8 @@
       .insp-wrapper .tabbar .tab.active {
         border-bottom: 1px solid #f29766; }
     .insp-wrapper .tabbar .more-tabs {
-      width: 30px;
-      height: 30px;
+      width: 32px;
+      height: 32px;
       display: flex;
       justify-content: center;
       align-items: center;
@@ -321,8 +353,8 @@
   .insp-wrapper .toolbar {
     display: flex; }
     .insp-wrapper .toolbar .tool {
-      width: 30px;
-      height: 30px;
+      width: 32px;
+      height: 32px;
       display: flex;
       justify-content: center;
       align-items: center;

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

@@ -37,7 +37,7 @@ declare module INSPECTOR {
         /** Remove the inspector panel when it's built as a right panel:
          * remove the right panel and remove the wrapper
          */
-        private _disposeInspector();
+        dispose(): void;
         /** Open the inspector in a new popup */
         openPopup(): void;
     }
@@ -466,7 +466,7 @@ declare module INSPECTOR {
 
 declare module INSPECTOR {
     /**
-     * Creates a tooltip for the given html element
+     * Creates a tooltip for the parent of the given html element
      */
     class Tooltip {
         /** The tooltip is displayed for this element */
@@ -656,6 +656,26 @@ declare module INSPECTOR {
 }
 
 declare module INSPECTOR {
+    class StatsTab extends Tab {
+        private _inspector;
+        /**
+         * Properties in this array will be updated
+         * in a render loop - Mostly stats properties
+         */
+        private _updatableProperties;
+        private _scene;
+        private _engine;
+        private _glInfo;
+        private _updateLoopHandler;
+        constructor(tabbar: TabBar, insp: Inspector);
+        private _createStatLabel(content, parent);
+        /** Update each properties of the stats panel */
+        private _update();
+        dispose(): void;
+    }
+}
+
+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.
@@ -775,6 +795,16 @@ declare module INSPECTOR {
 }
 
 declare module INSPECTOR {
+    /**
+     * Removes the inspector panel
+     */
+    class DisposeTool extends AbstractTool {
+        constructor(parent: HTMLElement, inspector: Inspector);
+        action(): void;
+    }
+}
+
+declare module INSPECTOR {
     class TreeItem extends BasicElement {
         private _tab;
         private _adapter;

+ 295 - 4
dist/preview release/inspector/babylon.inspector.js

@@ -104,7 +104,7 @@ var INSPECTOR;
         /** Remove the inspector panel when it's built as a right panel:
          * remove the right panel and remove the wrapper
          */
-        Inspector.prototype._disposeInspector = function () {
+        Inspector.prototype.dispose = function () {
             if (!this._popupMode) {
                 // Get canvas
                 var canvas = this._scene.getEngine().getRenderingCanvas();
@@ -139,7 +139,7 @@ var INSPECTOR;
                 popup.document.head.appendChild(link);
             }
             // Dispose the right panel
-            this._disposeInspector();
+            this.dispose();
             // set the mode as popup
             this._popupMode = true;
             // Save the HTML document
@@ -1537,13 +1537,13 @@ var INSPECTOR;
 var INSPECTOR;
 (function (INSPECTOR) {
     /**
-     * Creates a tooltip for the given html element
+     * Creates a tooltip for the parent of the given html element
      */
     var Tooltip = (function () {
         function Tooltip(elem, tip) {
             var _this = this;
             this._elem = elem;
-            this._infoDiv = INSPECTOR.Helpers.CreateDiv('tooltip', this._elem);
+            this._infoDiv = INSPECTOR.Helpers.CreateDiv('tooltip', this._elem.parentElement);
             this._elem.addEventListener('mouseover', function () {
                 _this._infoDiv.textContent = tip;
                 _this._infoDiv.style.display = 'block';
@@ -2369,6 +2369,268 @@ var __extends = (this && this.__extends) || function (d, b) {
 };
 var INSPECTOR;
 (function (INSPECTOR) {
+    var StatsTab = (function (_super) {
+        __extends(StatsTab, _super);
+        function StatsTab(tabbar, insp) {
+            var _this = this;
+            _super.call(this, tabbar, 'Stats');
+            /**
+             * Properties in this array will be updated
+             * in a render loop - Mostly stats properties
+             */
+            this._updatableProperties = [];
+            this._inspector = insp;
+            this._scene = this._inspector.scene;
+            this._engine = this._scene.getEngine();
+            this._glInfo = this._engine.getGlInfo();
+            // Build the stats panel: a div that will contains all stats
+            this._panel = INSPECTOR.Helpers.CreateDiv('tab-panel');
+            this._panel.classList.add("stats-panel");
+            var title = INSPECTOR.Helpers.CreateDiv('stat-title1', this._panel);
+            title.innerHTML = "Babylon.js v" + BABYLON.Engine.Version + ' - <b>' + BABYLON.Tools.Format(this._inspector.scene.getEngine().getFps(), 0) + " fps</b>";
+            this._updateLoopHandler = this._update.bind(this);
+            // Count block
+            title = INSPECTOR.Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Count";
+            {
+                var elemLabel = this._createStatLabel("Total meshes", this._panel);
+                var elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.meshes.length.toString(); }
+                });
+                elemLabel = this._createStatLabel("Draw calls", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._engine.drawCalls.toString(); }
+                });
+                elemLabel = this._createStatLabel("Total lights", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.lights.length.toString(); }
+                });
+                elemLabel = this._createStatLabel("Total lights", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.lights.length.toString(); }
+                });
+                elemLabel = this._createStatLabel("Total vertices", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.getTotalVertices().toString(); }
+                });
+                elemLabel = this._createStatLabel("Total materials", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.materials.length.toString(); }
+                });
+                elemLabel = this._createStatLabel("Total textures", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.textures.length.toString(); }
+                });
+                elemLabel = this._createStatLabel("Active meshes", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.getActiveMeshes().length.toString(); }
+                });
+                elemLabel = this._createStatLabel("Active indices", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.getActiveIndices().toString(); }
+                });
+                elemLabel = this._createStatLabel("Active bones", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.getActiveBones().toString(); }
+                });
+                elemLabel = this._createStatLabel("Active particles", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._scene.getActiveParticles().toString(); }
+                });
+            }
+            title = INSPECTOR.Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Duration";
+            {
+                var elemLabel = this._createStatLabel("Meshes selection", this._panel);
+                var elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return BABYLON.Tools.Format(_this._scene.getEvaluateActiveMeshesDuration()); }
+                });
+                elemLabel = this._createStatLabel("Render targets", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return BABYLON.Tools.Format(_this._scene.getRenderTargetsDuration()); }
+                });
+                elemLabel = this._createStatLabel("Particles", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return BABYLON.Tools.Format(_this._scene.getParticlesDuration()); }
+                });
+                elemLabel = this._createStatLabel("Sprites", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return BABYLON.Tools.Format(_this._scene.getSpritesDuration()); }
+                });
+                elemLabel = this._createStatLabel("Render", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return BABYLON.Tools.Format(_this._scene.getRenderDuration()); }
+                });
+                elemLabel = this._createStatLabel("Frame", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return BABYLON.Tools.Format(_this._scene.getLastFrameDuration()); }
+                });
+                elemLabel = this._createStatLabel("Potential FPS", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return BABYLON.Tools.Format(1000.0 / _this._scene.getLastFrameDuration(), 0); }
+                });
+                elemLabel = this._createStatLabel("Resolution", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._engine.getRenderWidth() + "x" + _this._engine.getRenderHeight(); }
+                });
+            }
+            title = INSPECTOR.Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Extensions";
+            {
+                var elemLabel = this._createStatLabel("Std derivatives", this._panel);
+                var elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().standardDerivatives ? "Yes" : "No"); }
+                });
+                elemLabel = this._createStatLabel("Compressed textures", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().s3tc ? "Yes" : "No"); }
+                });
+                elemLabel = this._createStatLabel("Hardware instances", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().instancedArrays ? "Yes" : "No"); }
+                });
+                elemLabel = this._createStatLabel("Texture float", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().textureFloat ? "Yes" : "No"); }
+                });
+                elemLabel = this._createStatLabel("32bits indices", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().uintIndices ? "Yes" : "No"); }
+                });
+                elemLabel = this._createStatLabel("Fragment depth", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().fragmentDepthSupported ? "Yes" : "No"); }
+                });
+                elemLabel = this._createStatLabel("High precision shaders", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().highPrecisionShaderSupported ? "Yes" : "No"); }
+                });
+                elemLabel = this._createStatLabel("Draw buffers", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().drawBuffersExtension ? "Yes" : "No"); }
+                });
+            }
+            title = INSPECTOR.Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Caps.";
+            {
+                var elemLabel = this._createStatLabel("Stencil", this._panel);
+                var elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.isStencilEnable ? "Enabled" : "Disabled"); }
+                });
+                elemLabel = this._createStatLabel("Max textures units", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._engine.getCaps().maxTexturesImageUnits.toString(); }
+                });
+                elemLabel = this._createStatLabel("Max textures size", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._engine.getCaps().maxTextureSize.toString(); }
+                });
+                elemLabel = this._createStatLabel("Max anisotropy", this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._engine.getCaps().maxAnisotropy.toString(); }
+                });
+            }
+            title = INSPECTOR.Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Info";
+            {
+                var elemValue = INSPECTOR.Helpers.CreateDiv('stat-infos', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._engine.webGLVersion + " - " + _this._glInfo.version + " - " + _this._glInfo.renderer; }
+                });
+            }
+            // Register the update loop
+            this._scene.registerAfterRender(this._updateLoopHandler);
+        }
+        StatsTab.prototype._createStatLabel = function (content, parent) {
+            var elem = INSPECTOR.Helpers.CreateDiv('stat-label', parent);
+            elem.textContent = content;
+            return elem;
+        };
+        /** Update each properties of the stats panel */
+        StatsTab.prototype._update = function () {
+            for (var _i = 0, _a = this._updatableProperties; _i < _a.length; _i++) {
+                var prop = _a[_i];
+                prop.elem.textContent = prop.updateFct();
+            }
+        };
+        StatsTab.prototype.dispose = function () {
+            this._scene.unregisterAfterRender(this._updateLoopHandler);
+        };
+        return StatsTab;
+    }(INSPECTOR.Tab));
+    INSPECTOR.StatsTab = StatsTab;
+})(INSPECTOR || (INSPECTOR = {}));
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (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.
@@ -2385,6 +2647,7 @@ var INSPECTOR;
             this._visibleTabs = [];
             this._inspector = inspector;
             this._tabs.push(new INSPECTOR.SceneTab(this, this._inspector));
+            this._tabs.push(new INSPECTOR.StatsTab(this, this._inspector));
             this._meshTab = new INSPECTOR.MeshTab(this, this._inspector);
             this._tabs.push(this._meshTab);
             this._tabs.push(new INSPECTOR.ShaderTab(this, this._inspector));
@@ -2530,6 +2793,8 @@ var INSPECTOR;
                     var 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)) {
@@ -2740,6 +3005,8 @@ var INSPECTOR;
             }
             // Pause schedule
             this._tools.push(new INSPECTOR.PauseScheduleTool(this._div, this._inspector));
+            // Pause schedule
+            this._tools.push(new INSPECTOR.DisposeTool(this._div, this._inspector));
         };
         /**
          * Returns the total width in pixel of the tabbar,
@@ -2765,6 +3032,30 @@ var __extends = (this && this.__extends) || function (d, b) {
 };
 var INSPECTOR;
 (function (INSPECTOR) {
+    /**
+     * Removes the inspector panel
+     */
+    var DisposeTool = (function (_super) {
+        __extends(DisposeTool, _super);
+        function DisposeTool(parent, inspector) {
+            _super.call(this, 'fa-times', parent, inspector, 'Close the inspector panel');
+        }
+        // Action : refresh the whole panel
+        DisposeTool.prototype.action = function () {
+            this._inspector.dispose();
+        };
+        return DisposeTool;
+    }(INSPECTOR.AbstractTool));
+    INSPECTOR.DisposeTool = DisposeTool;
+})(INSPECTOR || (INSPECTOR = {}));
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (INSPECTOR) {
     var TreeItem = (function (_super) {
         __extends(TreeItem, _super);
         function TreeItem(tab, obj) {

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 2 - 2
dist/preview release/inspector/babylon.inspector.min.js


+ 3 - 1
inspector/sass/_tabbar.scss

@@ -7,9 +7,10 @@
     width:100%;
     overflow-x:auto;
     overflow-y:hidden;
+    box-sizing: border-box;
     
     .tab {
-        height:$tabbar-height;
+        height:calc(#{$tabbar-height} - 2px);
         width:auto;
         padding: 0 10px 0 10px;
         color:$color;
@@ -17,6 +18,7 @@
         text-align: center;
         cursor: pointer;
         margin: 0 5px 0 5px;
+        box-sizing: border-box;
        
         // Hover on it
         &:hover {

+ 5 - 6
inspector/sass/_tooltip.scss

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

+ 1 - 1
inspector/sass/defines.scss

@@ -15,5 +15,5 @@ $background-lighter2: lighten($color: $background-lighter, $amount : 5%);
 $background-lighter3: lighten($color: $background-lighter2, $amount: 5%);
 
 $resizebar-width    : 10px;
-$tabbar-height      : 30px;
+$tabbar-height      : 32px;
 $searchbar-height   : 30px;

+ 2 - 1
inspector/sass/main.scss

@@ -38,7 +38,7 @@
       // The div that will contain the tab div
       .tab-panel-content {
         width:100%;
-        height:100%;
+        height:calc(100% - #{$tabbar-height});   
       }   
 
       // The div displaying all invisible tabs (when the tabbar width is small)
@@ -83,6 +83,7 @@
   @import 'treeTool';
   @import "tabPanel";
   @import "tabs/shaderTab";
+  @import "tabs/statsTab";
 
   @import "tree";
   @import "detailPanel";

+ 47 - 0
inspector/sass/tabs/_statsTab.scss

@@ -0,0 +1,47 @@
+.tab-panel {
+
+    &.stats-panel {
+        overflow-y      : auto;
+    }
+
+    .stat-title1 {        
+        font-size       : 1.1em;
+        padding         : 10px;
+    }
+
+    .stat-title2 {
+        margin          : 10px 0 10px 0;
+        font-size       : 1.05em; 
+        border-bottom   : 1px solid $color-bot;
+        box-sizing      : border-box;
+    }
+
+    .stat-label {
+        display         : inline-block;
+        width           : 80%;
+        padding         : 2px;
+        background-color: $background-lighter;
+        border-bottom   : 1px solid $background;
+        border-top      : 1px solid $background;
+        height          : 30px;
+        line-height     : 30px;
+        box-sizing      : border-box;
+        
+    }
+    .stat-value {
+        display         : inline-block;
+        width           : 20%;
+        padding         : 2px;
+        background-color: $background-lighter;
+        border-top      : 1px solid $background;
+        border-bottom   : 1px solid $background;
+        height          : 30px;
+        line-height     : 30px;
+        box-sizing      : border-box;
+    }
+
+    .stat-infos {
+        width           : 100%;
+        padding         : 4px;
+    }
+}

+ 2 - 2
inspector/src/Inspector.ts

@@ -129,7 +129,7 @@ module INSPECTOR {
         /** Remove the inspector panel when it's built as a right panel:
          * remove the right panel and remove the wrapper
          */
-        private _disposeInspector() {
+        public dispose() {
             if (!this._popupMode) {
                 // Get canvas
                 let canvas         = this._scene.getEngine().getRenderingCanvas(); 
@@ -165,7 +165,7 @@ module INSPECTOR {
                 popup.document.head.appendChild(link);              
             } 
             // Dispose the right panel
-            this._disposeInspector();
+            this.dispose();
             // set the mode as popup
             this._popupMode = true;
             // Save the HTML document

+ 2 - 2
inspector/src/gui/Tooltip.ts

@@ -1,7 +1,7 @@
 module INSPECTOR {
     
     /**
-     * Creates a tooltip for the given html element
+     * Creates a tooltip for the parent of the given html element
      */
     export class Tooltip {
         
@@ -15,7 +15,7 @@ module INSPECTOR {
             
             this._elem = elem;
             
-            this._infoDiv = Helpers.CreateDiv('tooltip', this._elem) as HTMLDivElement;
+            this._infoDiv = Helpers.CreateDiv('tooltip', this._elem.parentElement) as HTMLDivElement;
             
 
             this._elem.addEventListener('mouseover', () => { 

+ 284 - 0
inspector/src/tabs/StatsTab.ts

@@ -0,0 +1,284 @@
+module INSPECTOR {
+
+    export class StatsTab extends Tab {
+
+        private _inspector : Inspector;
+
+        /** 
+         * Properties in this array will be updated
+         * in a render loop - Mostly stats properties
+         */
+        private _updatableProperties : Array<{elem:HTMLElement, updateFct : () => string}> = [];
+
+        private _scene : BABYLON.Scene;
+        private _engine : BABYLON.Engine;
+        private _glInfo : any;
+
+        private _updateLoopHandler : any;
+
+        constructor(tabbar:TabBar, insp:Inspector) {
+            super(tabbar, 'Stats');        
+
+            this._inspector         = insp;  
+
+            this._scene             = this._inspector.scene;
+            this._engine            = this._scene.getEngine();
+            this._glInfo            = this._engine.getGlInfo();
+
+            // Build the stats panel: a div that will contains all stats
+            this._panel             = Helpers.CreateDiv('tab-panel') as HTMLDivElement; 
+            this._panel.classList.add("stats-panel")
+            
+            let title               = Helpers.CreateDiv('stat-title1', this._panel);
+            title.innerHTML         = "Babylon.js v" + BABYLON.Engine.Version + ' - <b>' +BABYLON.Tools.Format(this._inspector.scene.getEngine().getFps(), 0) + " fps</b>";
+            
+            this._updateLoopHandler = this._update.bind(this);
+
+            // Count block
+            title = Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Count";
+            {                
+                let elemLabel = this._createStatLabel("Total meshes", this._panel);
+                let elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.meshes.length.toString()}
+                });
+
+                elemLabel = this._createStatLabel("Draw calls", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._engine.drawCalls.toString()}
+                });
+
+                elemLabel = this._createStatLabel("Total lights", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.lights.length.toString()}
+                });
+
+                elemLabel = this._createStatLabel("Total lights", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.lights.length.toString()}
+                });
+
+                elemLabel = this._createStatLabel("Total vertices", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.getTotalVertices().toString()}
+                });
+
+                elemLabel = this._createStatLabel("Total materials", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.materials.length.toString()}
+                });
+
+                elemLabel = this._createStatLabel("Total textures", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.textures.length.toString()}
+                });
+
+                elemLabel = this._createStatLabel("Active meshes", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.getActiveMeshes().length.toString()}
+                });
+
+                elemLabel = this._createStatLabel("Active indices", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.getActiveIndices().toString()}
+                });
+
+                elemLabel = this._createStatLabel("Active bones", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.getActiveBones().toString()}
+                });
+
+                elemLabel = this._createStatLabel("Active particles", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._scene.getActiveParticles().toString()}
+                });
+            }            
+            
+            title = Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Duration";
+            {
+                let elemLabel = this._createStatLabel("Meshes selection", this._panel);
+                let elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return BABYLON.Tools.Format(this._scene.getEvaluateActiveMeshesDuration())}
+                });
+                elemLabel = this._createStatLabel("Render targets", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return BABYLON.Tools.Format(this._scene.getRenderTargetsDuration())}
+                });
+                elemLabel = this._createStatLabel("Particles", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return BABYLON.Tools.Format(this._scene.getParticlesDuration())}
+                });
+                elemLabel = this._createStatLabel("Sprites", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return BABYLON.Tools.Format(this._scene.getSpritesDuration())}
+                });
+                elemLabel = this._createStatLabel("Render", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return BABYLON.Tools.Format(this._scene.getRenderDuration())}
+                });
+                elemLabel = this._createStatLabel("Frame", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return BABYLON.Tools.Format(this._scene.getLastFrameDuration())}
+                });
+                elemLabel = this._createStatLabel("Potential FPS", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return BABYLON.Tools.Format(1000.0 / this._scene.getLastFrameDuration(), 0)}
+                });
+                elemLabel = this._createStatLabel("Resolution", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._engine.getRenderWidth() + "x" + this._engine.getRenderHeight()}
+                });
+            }
+            
+            title = Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Extensions";
+            {
+                let elemLabel = this._createStatLabel("Std derivatives", this._panel);
+                let elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.getCaps().standardDerivatives ? "Yes" : "No")}
+                });
+                elemLabel = this._createStatLabel("Compressed textures", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.getCaps().s3tc ? "Yes" : "No")}
+                });
+                elemLabel = this._createStatLabel("Hardware instances", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.getCaps().instancedArrays ? "Yes" : "No")}
+                });
+                elemLabel = this._createStatLabel("Texture float", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.getCaps().textureFloat ? "Yes" : "No")}
+                });
+                elemLabel = this._createStatLabel("32bits indices", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.getCaps().uintIndices ? "Yes" : "No")}
+                });
+                elemLabel = this._createStatLabel("Fragment depth", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.getCaps().fragmentDepthSupported ? "Yes" : "No")}
+                });
+                elemLabel = this._createStatLabel("High precision shaders", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.getCaps().highPrecisionShaderSupported ? "Yes" : "No")}
+                });
+                elemLabel = this._createStatLabel("Draw buffers", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.getCaps().drawBuffersExtension ? "Yes" : "No")}
+                }); 
+            }
+
+            title = Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Caps.";
+            {
+                let elemLabel = this._createStatLabel("Stencil", this._panel);
+                let elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.isStencilEnable ? "Enabled" : "Disabled")}
+                });
+                elemLabel = this._createStatLabel("Max textures units", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._engine.getCaps().maxTexturesImageUnits.toString()}
+                });
+                elemLabel = this._createStatLabel("Max textures size", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._engine.getCaps().maxTextureSize.toString()}
+                });
+                elemLabel = this._createStatLabel("Max anisotropy", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._engine.getCaps().maxAnisotropy.toString()}
+                });
+            }
+            title = Helpers.CreateDiv('stat-title2', this._panel);
+            title.textContent = "Info";
+            {
+                let elemValue = Helpers.CreateDiv('stat-infos', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return this._engine.webGLVersion + " - " + this._glInfo.version + " - "+this._glInfo.renderer}
+                });
+            }
+
+
+            // Register the update loop
+            this._scene.registerAfterRender(this._updateLoopHandler);
+        }
+        private _createStatLabel(content:string, parent: HTMLElement) : HTMLElement {
+            let elem = Helpers.CreateDiv('stat-label', parent);
+            elem.textContent = content;
+            return elem;
+        }
+
+        /** Update each properties of the stats panel */
+        private _update() {
+            for (let prop of this._updatableProperties) {
+                prop.elem.textContent = prop.updateFct();
+            }
+        }
+
+        public dispose() {
+            this._scene.unregisterAfterRender(this._updateLoopHandler);
+        }
+    }
+}

+ 2 - 1
inspector/src/tabs/TabBar.ts

@@ -25,6 +25,7 @@ module INSPECTOR {
             super();
             this._inspector = inspector;
             this._tabs.push(new SceneTab(this, this._inspector));
+            this._tabs.push(new StatsTab(this, this._inspector));
             this._meshTab = new MeshTab(this, this._inspector);
             this._tabs.push(this._meshTab);
             this._tabs.push(new ShaderTab(this, this._inspector));
@@ -179,7 +180,7 @@ module INSPECTOR {
                     this._div.appendChild(lastTab.toHtml());
                     this._visibleTabs.push(lastTab);
                     // Update more-tab icon in last position if needed
-                    //this._div.removeChild(this._moreTabsIcon);
+                    this._div.removeChild(this._moreTabsIcon);
                 }
             }
             if (this._invisibleTabs.length > 0 && !this._div.contains(this._moreTabsIcon)) {

+ 17 - 0
inspector/src/tools/DisposeTool.ts

@@ -0,0 +1,17 @@
+module INSPECTOR {
+     
+    /**
+     * Removes the inspector panel
+     */
+    export class DisposeTool extends AbstractTool {
+
+        constructor(parent:HTMLElement, inspector:Inspector) {
+            super('fa-times', parent, inspector, 'Close the inspector panel');
+        }
+
+        // Action : refresh the whole panel
+        public action() {
+            this._inspector.dispose();
+        }
+    }
+}

+ 3 - 0
inspector/src/tools/Toolbar.ts

@@ -32,6 +32,9 @@
             }
             // Pause schedule
             this._tools.push(new PauseScheduleTool(this._div, this._inspector));
+            
+            // Pause schedule
+            this._tools.push(new DisposeTool(this._div, this._inspector));
         }
 
         /** 

+ 1 - 1
inspector/test/index.js

@@ -6,7 +6,7 @@ var Test = (function () {
         var _this = this;
         var canvas = document.getElementById(canvasId);
         this.engine = new BABYLON.Engine(canvas, true);					
-		BABYLONDEVTOOLS.Loader.debugShortcut(engine);
+		BABYLONDEVTOOLS.Loader.debugShortcut(this.engine);
         this.scene = null;
         window.addEventListener("resize", function () {
             _this.engine.resize();