Browse Source

Fixed several bugs.
New 'close' tool and new Stats panel !

Temechon 8 năm trước cách đây
mục cha
commit
95bb415aad

+ 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",

+ 418 - 418
dist/preview release/babylon.d.ts

@@ -3440,6 +3440,123 @@ declare module BABYLON {
     }
 }
 
+declare module BABYLON {
+    class BoundingBox implements ICullable {
+        minimum: Vector3;
+        maximum: Vector3;
+        vectors: Vector3[];
+        center: Vector3;
+        extendSize: Vector3;
+        directions: Vector3[];
+        vectorsWorld: Vector3[];
+        minimumWorld: Vector3;
+        maximumWorld: Vector3;
+        private _worldMatrix;
+        constructor(minimum: Vector3, maximum: Vector3);
+        getWorldMatrix(): Matrix;
+        setWorldMatrix(matrix: Matrix): BoundingBox;
+        _update(world: Matrix): void;
+        isInFrustum(frustumPlanes: Plane[]): boolean;
+        isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
+        intersectsPoint(point: Vector3): boolean;
+        intersectsSphere(sphere: BoundingSphere): boolean;
+        intersectsMinMax(min: Vector3, max: Vector3): boolean;
+        static Intersects(box0: BoundingBox, box1: BoundingBox): boolean;
+        static IntersectsSphere(minPoint: Vector3, maxPoint: Vector3, sphereCenter: Vector3, sphereRadius: number): boolean;
+        static IsCompletelyInFrustum(boundingVectors: Vector3[], frustumPlanes: Plane[]): boolean;
+        static IsInFrustum(boundingVectors: Vector3[], frustumPlanes: Plane[]): boolean;
+    }
+}
+
+declare module BABYLON {
+    interface ICullable {
+        isInFrustum(frustumPlanes: Plane[]): boolean;
+        isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
+    }
+    class BoundingInfo implements ICullable {
+        minimum: Vector3;
+        maximum: Vector3;
+        boundingBox: BoundingBox;
+        boundingSphere: BoundingSphere;
+        private _isLocked;
+        constructor(minimum: Vector3, maximum: Vector3);
+        isLocked: boolean;
+        update(world: Matrix): void;
+        isInFrustum(frustumPlanes: Plane[]): boolean;
+        isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
+        _checkCollision(collider: Collider): boolean;
+        intersectsPoint(point: Vector3): boolean;
+        intersects(boundingInfo: BoundingInfo, precise: boolean): boolean;
+    }
+}
+
+declare module BABYLON {
+    class BoundingSphere {
+        minimum: Vector3;
+        maximum: Vector3;
+        center: Vector3;
+        radius: number;
+        centerWorld: Vector3;
+        radiusWorld: number;
+        private _tempRadiusVector;
+        constructor(minimum: Vector3, maximum: Vector3);
+        _update(world: Matrix): void;
+        isInFrustum(frustumPlanes: Plane[]): boolean;
+        intersectsPoint(point: Vector3): boolean;
+        static Intersects(sphere0: BoundingSphere, sphere1: BoundingSphere): boolean;
+    }
+}
+
+declare module BABYLON {
+    class Ray {
+        origin: Vector3;
+        direction: Vector3;
+        length: number;
+        private _edge1;
+        private _edge2;
+        private _pvec;
+        private _tvec;
+        private _qvec;
+        private _renderPoints;
+        private _renderLine;
+        private _renderFunction;
+        private _scene;
+        private _show;
+        private _tmpRay;
+        constructor(origin: Vector3, direction: Vector3, length?: number);
+        intersectsBoxMinMax(minimum: Vector3, maximum: Vector3): boolean;
+        intersectsBox(box: BoundingBox): boolean;
+        intersectsSphere(sphere: BoundingSphere): boolean;
+        intersectsTriangle(vertex0: Vector3, vertex1: Vector3, vertex2: Vector3): IntersectionInfo;
+        intersectsPlane(plane: Plane): number;
+        intersectsMesh(mesh: AbstractMesh, fastCheck?: boolean): PickingInfo;
+        show(scene: Scene, color: Color3): void;
+        hide(): void;
+        private _render();
+        private static smallnum;
+        private static rayl;
+        /**
+         * Intersection test between the ray and a given segment whithin a given tolerance (threshold)
+         * @param sega the first point of the segment to test the intersection against
+         * @param segb the second point of the segment to test the intersection against
+         * @param threshold the tolerance margin, if the ray doesn't intersect the segment but is close to the given threshold, the intersection is successful
+         * @return the distance from the ray origin to the intersection point if there's intersection, or -1 if there's no intersection
+         */
+        intersectionSegment(sega: Vector3, segb: Vector3, threshold: number): number;
+        static CreateNew(x: number, y: number, viewportWidth: number, viewportHeight: number, world: Matrix, view: Matrix, projection: Matrix): Ray;
+        /**
+        * Function will create a new transformed ray starting from origin and ending at the end point. Ray's length will be set, and ray will be
+        * transformed to the given world matrix.
+        * @param origin The origin point
+        * @param end The end point
+        * @param world a matrix to transform the ray to. Default is the identity matrix.
+        */
+        static CreateNewFromTo(origin: Vector3, end: Vector3, world?: Matrix): Ray;
+        static Transform(ray: Ray, matrix: Matrix): Ray;
+        static TransformToRef(ray: Ray, matrix: Matrix, result: Ray): void;
+    }
+}
+
 declare module BABYLON.Debug {
     class AxesViewer {
         private _xline;
@@ -3763,168 +3880,6 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
-    class BoundingBox implements ICullable {
-        minimum: Vector3;
-        maximum: Vector3;
-        vectors: Vector3[];
-        center: Vector3;
-        extendSize: Vector3;
-        directions: Vector3[];
-        vectorsWorld: Vector3[];
-        minimumWorld: Vector3;
-        maximumWorld: Vector3;
-        private _worldMatrix;
-        constructor(minimum: Vector3, maximum: Vector3);
-        getWorldMatrix(): Matrix;
-        setWorldMatrix(matrix: Matrix): BoundingBox;
-        _update(world: Matrix): void;
-        isInFrustum(frustumPlanes: Plane[]): boolean;
-        isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
-        intersectsPoint(point: Vector3): boolean;
-        intersectsSphere(sphere: BoundingSphere): boolean;
-        intersectsMinMax(min: Vector3, max: Vector3): boolean;
-        static Intersects(box0: BoundingBox, box1: BoundingBox): boolean;
-        static IntersectsSphere(minPoint: Vector3, maxPoint: Vector3, sphereCenter: Vector3, sphereRadius: number): boolean;
-        static IsCompletelyInFrustum(boundingVectors: Vector3[], frustumPlanes: Plane[]): boolean;
-        static IsInFrustum(boundingVectors: Vector3[], frustumPlanes: Plane[]): boolean;
-    }
-}
-
-declare module BABYLON {
-    interface ICullable {
-        isInFrustum(frustumPlanes: Plane[]): boolean;
-        isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
-    }
-    class BoundingInfo implements ICullable {
-        minimum: Vector3;
-        maximum: Vector3;
-        boundingBox: BoundingBox;
-        boundingSphere: BoundingSphere;
-        private _isLocked;
-        constructor(minimum: Vector3, maximum: Vector3);
-        isLocked: boolean;
-        update(world: Matrix): void;
-        isInFrustum(frustumPlanes: Plane[]): boolean;
-        isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
-        _checkCollision(collider: Collider): boolean;
-        intersectsPoint(point: Vector3): boolean;
-        intersects(boundingInfo: BoundingInfo, precise: boolean): boolean;
-    }
-}
-
-declare module BABYLON {
-    class BoundingSphere {
-        minimum: Vector3;
-        maximum: Vector3;
-        center: Vector3;
-        radius: number;
-        centerWorld: Vector3;
-        radiusWorld: number;
-        private _tempRadiusVector;
-        constructor(minimum: Vector3, maximum: Vector3);
-        _update(world: Matrix): void;
-        isInFrustum(frustumPlanes: Plane[]): boolean;
-        intersectsPoint(point: Vector3): boolean;
-        static Intersects(sphere0: BoundingSphere, sphere1: BoundingSphere): boolean;
-    }
-}
-
-declare module BABYLON {
-    class Ray {
-        origin: Vector3;
-        direction: Vector3;
-        length: number;
-        private _edge1;
-        private _edge2;
-        private _pvec;
-        private _tvec;
-        private _qvec;
-        private _renderPoints;
-        private _renderLine;
-        private _renderFunction;
-        private _scene;
-        private _show;
-        private _tmpRay;
-        constructor(origin: Vector3, direction: Vector3, length?: number);
-        intersectsBoxMinMax(minimum: Vector3, maximum: Vector3): boolean;
-        intersectsBox(box: BoundingBox): boolean;
-        intersectsSphere(sphere: BoundingSphere): boolean;
-        intersectsTriangle(vertex0: Vector3, vertex1: Vector3, vertex2: Vector3): IntersectionInfo;
-        intersectsPlane(plane: Plane): number;
-        intersectsMesh(mesh: AbstractMesh, fastCheck?: boolean): PickingInfo;
-        show(scene: Scene, color: Color3): void;
-        hide(): void;
-        private _render();
-        private static smallnum;
-        private static rayl;
-        /**
-         * Intersection test between the ray and a given segment whithin a given tolerance (threshold)
-         * @param sega the first point of the segment to test the intersection against
-         * @param segb the second point of the segment to test the intersection against
-         * @param threshold the tolerance margin, if the ray doesn't intersect the segment but is close to the given threshold, the intersection is successful
-         * @return the distance from the ray origin to the intersection point if there's intersection, or -1 if there's no intersection
-         */
-        intersectionSegment(sega: Vector3, segb: Vector3, threshold: number): number;
-        static CreateNew(x: number, y: number, viewportWidth: number, viewportHeight: number, world: Matrix, view: Matrix, projection: Matrix): Ray;
-        /**
-        * Function will create a new transformed ray starting from origin and ending at the end point. Ray's length will be set, and ray will be
-        * transformed to the given world matrix.
-        * @param origin The origin point
-        * @param end The end point
-        * @param world a matrix to transform the ray to. Default is the identity matrix.
-        */
-        static CreateNewFromTo(origin: Vector3, end: Vector3, world?: Matrix): Ray;
-        static Transform(ray: Ray, matrix: Matrix): Ray;
-        static TransformToRef(ray: Ray, matrix: Matrix, result: Ray): void;
-    }
-}
-
-declare module BABYLON {
-    class LensFlare {
-        size: number;
-        position: number;
-        color: Color3;
-        texture: Texture;
-        alphaMode: number;
-        private _system;
-        constructor(size: number, position: number, color: any, imgUrl: string, system: LensFlareSystem);
-        dispose: () => void;
-    }
-}
-
-declare module BABYLON {
-    class LensFlareSystem {
-        name: string;
-        lensFlares: LensFlare[];
-        borderLimit: number;
-        viewportBorder: number;
-        meshesSelectionPredicate: (mesh: Mesh) => boolean;
-        layerMask: number;
-        id: string;
-        private _scene;
-        private _emitter;
-        private _vertexBuffers;
-        private _indexBuffer;
-        private _effect;
-        private _positionX;
-        private _positionY;
-        private _isEnabled;
-        constructor(name: string, emitter: any, scene: Scene);
-        isEnabled: boolean;
-        getScene(): Scene;
-        getEmitter(): any;
-        setEmitter(newEmitter: any): void;
-        getEmitterPosition(): Vector3;
-        computeEffectivePosition(globalViewport: Viewport): boolean;
-        _isVisible(): boolean;
-        render(): boolean;
-        dispose(): void;
-        static Parse(parsedLensFlareSystem: any, scene: Scene, rootUrl: string): LensFlareSystem;
-        serialize(): any;
-    }
-}
-
-declare module BABYLON {
     class DirectionalLight extends Light implements IShadowLight {
         position: Vector3;
         direction: Vector3;
@@ -4083,6 +4038,51 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
+    class LensFlare {
+        size: number;
+        position: number;
+        color: Color3;
+        texture: Texture;
+        alphaMode: number;
+        private _system;
+        constructor(size: number, position: number, color: any, imgUrl: string, system: LensFlareSystem);
+        dispose: () => void;
+    }
+}
+
+declare module BABYLON {
+    class LensFlareSystem {
+        name: string;
+        lensFlares: LensFlare[];
+        borderLimit: number;
+        viewportBorder: number;
+        meshesSelectionPredicate: (mesh: Mesh) => boolean;
+        layerMask: number;
+        id: string;
+        private _scene;
+        private _emitter;
+        private _vertexBuffers;
+        private _indexBuffer;
+        private _effect;
+        private _positionX;
+        private _positionY;
+        private _isEnabled;
+        constructor(name: string, emitter: any, scene: Scene);
+        isEnabled: boolean;
+        getScene(): Scene;
+        getEmitter(): any;
+        setEmitter(newEmitter: any): void;
+        getEmitterPosition(): Vector3;
+        computeEffectivePosition(globalViewport: Viewport): boolean;
+        _isVisible(): boolean;
+        render(): boolean;
+        dispose(): void;
+        static Parse(parsedLensFlareSystem: any, scene: Scene, rootUrl: string): LensFlareSystem;
+        serialize(): any;
+    }
+}
+
+declare module BABYLON {
     interface ISceneLoaderPluginExtensions {
         [extension: string]: {
             isBinary: boolean;
@@ -8952,262 +8952,8 @@ declare module BABYLON {
     }
     interface SpringJointData extends PhysicsJointData {
         length: number;
-        stiffness: number;
-        damping: number;
-    }
-}
-
-declare module BABYLON {
-    class ReflectionProbe {
-        name: string;
-        private _scene;
-        private _renderTargetTexture;
-        private _projectionMatrix;
-        private _viewMatrix;
-        private _target;
-        private _add;
-        private _attachedMesh;
-        invertYAxis: boolean;
-        position: Vector3;
-        constructor(name: string, size: number, scene: Scene, generateMipMaps?: boolean);
-        refreshRate: number;
-        getScene(): Scene;
-        cubeTexture: RenderTargetTexture;
-        renderList: AbstractMesh[];
-        attachToMesh(mesh: AbstractMesh): void;
-        dispose(): void;
-    }
-}
-
-declare module BABYLON {
-    class BoundingBoxRenderer {
-        frontColor: Color3;
-        backColor: Color3;
-        showBackLines: boolean;
-        renderList: SmartArray<BoundingBox>;
-        private _scene;
-        private _colorShader;
-        private _vertexBuffers;
-        private _indexBuffer;
-        constructor(scene: Scene);
-        private _prepareRessources();
-        reset(): void;
-        render(): void;
-        dispose(): void;
-    }
-}
-
-declare module BABYLON {
-    class DepthRenderer {
-        private _scene;
-        private _depthMap;
-        private _effect;
-        private _viewMatrix;
-        private _projectionMatrix;
-        private _transformMatrix;
-        private _worldViewProjection;
-        private _cachedDefines;
-        constructor(scene: Scene, type?: number);
-        isReady(subMesh: SubMesh, useInstances: boolean): boolean;
-        getDepthMap(): RenderTargetTexture;
-        dispose(): void;
-    }
-}
-
-declare module BABYLON {
-    class EdgesRenderer {
-        edgesWidthScalerForOrthographic: number;
-        edgesWidthScalerForPerspective: number;
-        private _source;
-        private _linesPositions;
-        private _linesNormals;
-        private _linesIndices;
-        private _epsilon;
-        private _indicesCount;
-        private _lineShader;
-        private _ib;
-        private _buffers;
-        private _checkVerticesInsteadOfIndices;
-        constructor(source: AbstractMesh, epsilon?: number, checkVerticesInsteadOfIndices?: boolean);
-        private _prepareRessources();
-        dispose(): void;
-        private _processEdgeForAdjacencies(pa, pb, p0, p1, p2);
-        private _processEdgeForAdjacenciesWithVertices(pa, pb, p0, p1, p2);
-        private _checkEdge(faceIndex, edge, faceNormals, p0, p1);
-        _generateEdgesLines(): void;
-        render(): void;
-    }
-}
-
-declare module BABYLON {
-    class OutlineRenderer {
-        private _scene;
-        private _effect;
-        private _cachedDefines;
-        constructor(scene: Scene);
-        render(subMesh: SubMesh, batch: _InstancesBatch, useOverlay?: boolean): void;
-        isReady(subMesh: SubMesh, useInstances: boolean): boolean;
-    }
-}
-
-declare module BABYLON {
-    class RenderingGroup {
-        index: number;
-        private _scene;
-        private _opaqueSubMeshes;
-        private _transparentSubMeshes;
-        private _alphaTestSubMeshes;
-        private _activeVertices;
-        private _opaqueSortCompareFn;
-        private _alphaTestSortCompareFn;
-        private _transparentSortCompareFn;
-        private _renderOpaque;
-        private _renderAlphaTest;
-        private _renderTransparent;
-        onBeforeTransparentRendering: () => void;
-        /**
-         * Set the opaque sort comparison function.
-         * If null the sub meshes will be render in the order they were created
-         */
-        opaqueSortCompareFn: (a: SubMesh, b: SubMesh) => number;
-        /**
-         * Set the alpha test sort comparison function.
-         * If null the sub meshes will be render in the order they were created
-         */
-        alphaTestSortCompareFn: (a: SubMesh, b: SubMesh) => number;
-        /**
-         * Set the transparent sort comparison function.
-         * If null the sub meshes will be render in the order they were created
-         */
-        transparentSortCompareFn: (a: SubMesh, b: SubMesh) => number;
-        /**
-         * Creates a new rendering group.
-         * @param index The rendering group index
-         * @param opaqueSortCompareFn The opaque sort comparison function. If null no order is applied
-         * @param alphaTestSortCompareFn The alpha test sort comparison function. If null no order is applied
-         * @param transparentSortCompareFn The transparent sort comparison function. If null back to front + alpha index sort is applied
-         */
-        constructor(index: number, scene: Scene, opaqueSortCompareFn?: (a: SubMesh, b: SubMesh) => number, alphaTestSortCompareFn?: (a: SubMesh, b: SubMesh) => number, transparentSortCompareFn?: (a: SubMesh, b: SubMesh) => number);
-        /**
-         * Render all the sub meshes contained in the group.
-         * @param customRenderFunction Used to override the default render behaviour of the group.
-         * @returns true if rendered some submeshes.
-         */
-        render(customRenderFunction: (opaqueSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>) => void): boolean;
-        /**
-         * Renders the opaque submeshes in the order from the opaqueSortCompareFn.
-         * @param subMeshes The submeshes to render
-         */
-        private renderOpaqueSorted(subMeshes);
-        /**
-         * Renders the opaque submeshes in the order from the alphatestSortCompareFn.
-         * @param subMeshes The submeshes to render
-         */
-        private renderAlphaTestSorted(subMeshes);
-        /**
-         * Renders the opaque submeshes in the order from the transparentSortCompareFn.
-         * @param subMeshes The submeshes to render
-         */
-        private renderTransparentSorted(subMeshes);
-        /**
-         * Renders the submeshes in a specified order.
-         * @param subMeshes The submeshes to sort before render
-         * @param sortCompareFn The comparison function use to sort
-         * @param cameraPosition The camera position use to preprocess the submeshes to help sorting
-         * @param transparent Specifies to activate blending if true
-         */
-        private static renderSorted(subMeshes, sortCompareFn, cameraPosition, transparent);
-        /**
-         * Renders the submeshes in the order they were dispatched (no sort applied).
-         * @param subMeshes The submeshes to render
-         */
-        private static renderUnsorted(subMeshes);
-        /**
-         * Build in function which can be applied to ensure meshes of a special queue (opaque, alpha test, transparent)
-         * are rendered back to front if in the same alpha index.
-         *
-         * @param a The first submesh
-         * @param b The second submesh
-         * @returns The result of the comparison
-         */
-        static defaultTransparentSortCompare(a: SubMesh, b: SubMesh): number;
-        /**
-         * Build in function which can be applied to ensure meshes of a special queue (opaque, alpha test, transparent)
-         * are rendered back to front.
-         *
-         * @param a The first submesh
-         * @param b The second submesh
-         * @returns The result of the comparison
-         */
-        static backToFrontSortCompare(a: SubMesh, b: SubMesh): number;
-        /**
-         * Build in function which can be applied to ensure meshes of a special queue (opaque, alpha test, transparent)
-         * are rendered front to back (prevent overdraw).
-         *
-         * @param a The first submesh
-         * @param b The second submesh
-         * @returns The result of the comparison
-         */
-        static frontToBackSortCompare(a: SubMesh, b: SubMesh): number;
-        /**
-         * Resets the different lists of submeshes to prepare a new frame.
-         */
-        prepare(): void;
-        /**
-         * Inserts the submesh in its correct queue depending on its material.
-         * @param subMesh The submesh to dispatch
-         */
-        dispatch(subMesh: SubMesh): void;
-    }
-}
-
-declare module BABYLON {
-    class RenderingManager {
-        /**
-         * The max id used for rendering groups (not included)
-         */
-        static MAX_RENDERINGGROUPS: number;
-        /**
-         * The min id used for rendering groups (included)
-         */
-        static MIN_RENDERINGGROUPS: number;
-        private _scene;
-        private _renderingGroups;
-        private _depthStencilBufferAlreadyCleaned;
-        private _currentIndex;
-        private _currentActiveMeshes;
-        private _currentRenderParticles;
-        private _currentRenderSprites;
-        private _autoClearDepthStencil;
-        private _customOpaqueSortCompareFn;
-        private _customAlphaTestSortCompareFn;
-        private _customTransparentSortCompareFn;
-        private _renderinGroupInfo;
-        constructor(scene: Scene);
-        private _renderParticles(index, activeMeshes);
-        private _renderSprites(index);
-        private _clearDepthStencilBuffer();
-        private _renderSpritesAndParticles();
-        render(customRenderFunction: (opaqueSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>) => void, activeMeshes: AbstractMesh[], renderParticles: boolean, renderSprites: boolean): void;
-        reset(): void;
-        dispatch(subMesh: SubMesh): void;
-        /**
-         * Overrides the default sort function applied in the renderging group to prepare the meshes.
-         * This allowed control for front to back rendering or reversly depending of the special needs.
-         *
-         * @param renderingGroupId The rendering group id corresponding to its index
-         * @param opaqueSortCompareFn The opaque queue comparison function use to sort.
-         * @param alphaTestSortCompareFn The alpha test queue comparison function use to sort.
-         * @param transparentSortCompareFn The transparent queue comparison function use to sort.
-         */
-        setRenderingOrder(renderingGroupId: number, opaqueSortCompareFn?: (a: SubMesh, b: SubMesh) => number, alphaTestSortCompareFn?: (a: SubMesh, b: SubMesh) => number, transparentSortCompareFn?: (a: SubMesh, b: SubMesh) => number): void;
-        /**
-         * Specifies whether or not the stencil and depth buffer are cleared between two rendering groups.
-         *
-         * @param renderingGroupId The rendering group id corresponding to its index
-         * @param autoClearDepthStencil Automatically clears depth and stencil between groups if true.
-         */
-        setRenderingAutoClearDepthStencil(renderingGroupId: number, autoClearDepthStencil: boolean): void;
+        stiffness: number;
+        damping: number;
     }
 }
 
@@ -9887,6 +9633,260 @@ declare module BABYLON {
 }
 
 declare module BABYLON {
+    class ReflectionProbe {
+        name: string;
+        private _scene;
+        private _renderTargetTexture;
+        private _projectionMatrix;
+        private _viewMatrix;
+        private _target;
+        private _add;
+        private _attachedMesh;
+        invertYAxis: boolean;
+        position: Vector3;
+        constructor(name: string, size: number, scene: Scene, generateMipMaps?: boolean);
+        refreshRate: number;
+        getScene(): Scene;
+        cubeTexture: RenderTargetTexture;
+        renderList: AbstractMesh[];
+        attachToMesh(mesh: AbstractMesh): void;
+        dispose(): void;
+    }
+}
+
+declare module BABYLON {
+    class BoundingBoxRenderer {
+        frontColor: Color3;
+        backColor: Color3;
+        showBackLines: boolean;
+        renderList: SmartArray<BoundingBox>;
+        private _scene;
+        private _colorShader;
+        private _vertexBuffers;
+        private _indexBuffer;
+        constructor(scene: Scene);
+        private _prepareRessources();
+        reset(): void;
+        render(): void;
+        dispose(): void;
+    }
+}
+
+declare module BABYLON {
+    class DepthRenderer {
+        private _scene;
+        private _depthMap;
+        private _effect;
+        private _viewMatrix;
+        private _projectionMatrix;
+        private _transformMatrix;
+        private _worldViewProjection;
+        private _cachedDefines;
+        constructor(scene: Scene, type?: number);
+        isReady(subMesh: SubMesh, useInstances: boolean): boolean;
+        getDepthMap(): RenderTargetTexture;
+        dispose(): void;
+    }
+}
+
+declare module BABYLON {
+    class EdgesRenderer {
+        edgesWidthScalerForOrthographic: number;
+        edgesWidthScalerForPerspective: number;
+        private _source;
+        private _linesPositions;
+        private _linesNormals;
+        private _linesIndices;
+        private _epsilon;
+        private _indicesCount;
+        private _lineShader;
+        private _ib;
+        private _buffers;
+        private _checkVerticesInsteadOfIndices;
+        constructor(source: AbstractMesh, epsilon?: number, checkVerticesInsteadOfIndices?: boolean);
+        private _prepareRessources();
+        dispose(): void;
+        private _processEdgeForAdjacencies(pa, pb, p0, p1, p2);
+        private _processEdgeForAdjacenciesWithVertices(pa, pb, p0, p1, p2);
+        private _checkEdge(faceIndex, edge, faceNormals, p0, p1);
+        _generateEdgesLines(): void;
+        render(): void;
+    }
+}
+
+declare module BABYLON {
+    class OutlineRenderer {
+        private _scene;
+        private _effect;
+        private _cachedDefines;
+        constructor(scene: Scene);
+        render(subMesh: SubMesh, batch: _InstancesBatch, useOverlay?: boolean): void;
+        isReady(subMesh: SubMesh, useInstances: boolean): boolean;
+    }
+}
+
+declare module BABYLON {
+    class RenderingGroup {
+        index: number;
+        private _scene;
+        private _opaqueSubMeshes;
+        private _transparentSubMeshes;
+        private _alphaTestSubMeshes;
+        private _activeVertices;
+        private _opaqueSortCompareFn;
+        private _alphaTestSortCompareFn;
+        private _transparentSortCompareFn;
+        private _renderOpaque;
+        private _renderAlphaTest;
+        private _renderTransparent;
+        onBeforeTransparentRendering: () => void;
+        /**
+         * Set the opaque sort comparison function.
+         * If null the sub meshes will be render in the order they were created
+         */
+        opaqueSortCompareFn: (a: SubMesh, b: SubMesh) => number;
+        /**
+         * Set the alpha test sort comparison function.
+         * If null the sub meshes will be render in the order they were created
+         */
+        alphaTestSortCompareFn: (a: SubMesh, b: SubMesh) => number;
+        /**
+         * Set the transparent sort comparison function.
+         * If null the sub meshes will be render in the order they were created
+         */
+        transparentSortCompareFn: (a: SubMesh, b: SubMesh) => number;
+        /**
+         * Creates a new rendering group.
+         * @param index The rendering group index
+         * @param opaqueSortCompareFn The opaque sort comparison function. If null no order is applied
+         * @param alphaTestSortCompareFn The alpha test sort comparison function. If null no order is applied
+         * @param transparentSortCompareFn The transparent sort comparison function. If null back to front + alpha index sort is applied
+         */
+        constructor(index: number, scene: Scene, opaqueSortCompareFn?: (a: SubMesh, b: SubMesh) => number, alphaTestSortCompareFn?: (a: SubMesh, b: SubMesh) => number, transparentSortCompareFn?: (a: SubMesh, b: SubMesh) => number);
+        /**
+         * Render all the sub meshes contained in the group.
+         * @param customRenderFunction Used to override the default render behaviour of the group.
+         * @returns true if rendered some submeshes.
+         */
+        render(customRenderFunction: (opaqueSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>) => void): boolean;
+        /**
+         * Renders the opaque submeshes in the order from the opaqueSortCompareFn.
+         * @param subMeshes The submeshes to render
+         */
+        private renderOpaqueSorted(subMeshes);
+        /**
+         * Renders the opaque submeshes in the order from the alphatestSortCompareFn.
+         * @param subMeshes The submeshes to render
+         */
+        private renderAlphaTestSorted(subMeshes);
+        /**
+         * Renders the opaque submeshes in the order from the transparentSortCompareFn.
+         * @param subMeshes The submeshes to render
+         */
+        private renderTransparentSorted(subMeshes);
+        /**
+         * Renders the submeshes in a specified order.
+         * @param subMeshes The submeshes to sort before render
+         * @param sortCompareFn The comparison function use to sort
+         * @param cameraPosition The camera position use to preprocess the submeshes to help sorting
+         * @param transparent Specifies to activate blending if true
+         */
+        private static renderSorted(subMeshes, sortCompareFn, cameraPosition, transparent);
+        /**
+         * Renders the submeshes in the order they were dispatched (no sort applied).
+         * @param subMeshes The submeshes to render
+         */
+        private static renderUnsorted(subMeshes);
+        /**
+         * Build in function which can be applied to ensure meshes of a special queue (opaque, alpha test, transparent)
+         * are rendered back to front if in the same alpha index.
+         *
+         * @param a The first submesh
+         * @param b The second submesh
+         * @returns The result of the comparison
+         */
+        static defaultTransparentSortCompare(a: SubMesh, b: SubMesh): number;
+        /**
+         * Build in function which can be applied to ensure meshes of a special queue (opaque, alpha test, transparent)
+         * are rendered back to front.
+         *
+         * @param a The first submesh
+         * @param b The second submesh
+         * @returns The result of the comparison
+         */
+        static backToFrontSortCompare(a: SubMesh, b: SubMesh): number;
+        /**
+         * Build in function which can be applied to ensure meshes of a special queue (opaque, alpha test, transparent)
+         * are rendered front to back (prevent overdraw).
+         *
+         * @param a The first submesh
+         * @param b The second submesh
+         * @returns The result of the comparison
+         */
+        static frontToBackSortCompare(a: SubMesh, b: SubMesh): number;
+        /**
+         * Resets the different lists of submeshes to prepare a new frame.
+         */
+        prepare(): void;
+        /**
+         * Inserts the submesh in its correct queue depending on its material.
+         * @param subMesh The submesh to dispatch
+         */
+        dispatch(subMesh: SubMesh): void;
+    }
+}
+
+declare module BABYLON {
+    class RenderingManager {
+        /**
+         * The max id used for rendering groups (not included)
+         */
+        static MAX_RENDERINGGROUPS: number;
+        /**
+         * The min id used for rendering groups (included)
+         */
+        static MIN_RENDERINGGROUPS: number;
+        private _scene;
+        private _renderingGroups;
+        private _depthStencilBufferAlreadyCleaned;
+        private _currentIndex;
+        private _currentActiveMeshes;
+        private _currentRenderParticles;
+        private _currentRenderSprites;
+        private _autoClearDepthStencil;
+        private _customOpaqueSortCompareFn;
+        private _customAlphaTestSortCompareFn;
+        private _customTransparentSortCompareFn;
+        private _renderinGroupInfo;
+        constructor(scene: Scene);
+        private _renderParticles(index, activeMeshes);
+        private _renderSprites(index);
+        private _clearDepthStencilBuffer();
+        private _renderSpritesAndParticles();
+        render(customRenderFunction: (opaqueSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>) => void, activeMeshes: AbstractMesh[], renderParticles: boolean, renderSprites: boolean): void;
+        reset(): void;
+        dispatch(subMesh: SubMesh): void;
+        /**
+         * Overrides the default sort function applied in the renderging group to prepare the meshes.
+         * This allowed control for front to back rendering or reversly depending of the special needs.
+         *
+         * @param renderingGroupId The rendering group id corresponding to its index
+         * @param opaqueSortCompareFn The opaque queue comparison function use to sort.
+         * @param alphaTestSortCompareFn The alpha test queue comparison function use to sort.
+         * @param transparentSortCompareFn The transparent queue comparison function use to sort.
+         */
+        setRenderingOrder(renderingGroupId: number, opaqueSortCompareFn?: (a: SubMesh, b: SubMesh) => number, alphaTestSortCompareFn?: (a: SubMesh, b: SubMesh) => number, transparentSortCompareFn?: (a: SubMesh, b: SubMesh) => number): void;
+        /**
+         * Specifies whether or not the stencil and depth buffer are cleared between two rendering groups.
+         *
+         * @param renderingGroupId The rendering group id corresponding to its index
+         * @param autoClearDepthStencil Automatically clears depth and stencil between groups if true.
+         */
+        setRenderingAutoClearDepthStencil(renderingGroupId: number, autoClearDepthStencil: boolean): void;
+    }
+}
+
+declare module BABYLON {
     class Sprite {
         name: string;
         position: Vector3;

+ 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) {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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));
         }
 
         /**