Browse Source

update from upstream

nockawa 9 năm trước cách đây
mục cha
commit
e9f596dc98

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 24 - 24
dist/preview release/babylon.core.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3945 - 3855
dist/preview release/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 38 - 38
dist/preview release/babylon.js


+ 345 - 95
dist/preview release/babylon.max.js

@@ -5355,6 +5355,135 @@ var BABYLON;
     })();
     })();
     BABYLON.Tools = Tools;
     BABYLON.Tools = Tools;
     /**
     /**
+     * This class is used to track a performance counter which is number based.
+     * The user has access to many properties which give statistics of different nature
+     *
+     * The implementer can track two kinds of Performance Counter: time and count
+     * For time you can optionally call fetchNewFrame() to notify the start of a new frame to monitor, then call beginMonitoring() to start and endMonitoring() to record the lapsed time. endMonitoring takes a newFrame parameter for you to specify if the monitored time should be set for a new frame or accumulated to the current frame being monitored.
+     * For count you first have to call fetchNewFrame() to notify the start of a new frame to monitor, then call addCount() how many time required to increment the count value you monitor.
+     */
+    var PerfCounter = (function () {
+        function PerfCounter() {
+            this._startMonitoringTime = 0;
+            this._min = 0;
+            this._max = 0;
+            this._average = 0;
+            this._lastSecAverage = 0;
+            this._current = 0;
+            this._totalValueCount = 0;
+            this._totalAccumulated = 0;
+            this._lastSecAccumulated = 0;
+            this._lastSecTime = 0;
+            this._lastSecValueCount = 0;
+        }
+        Object.defineProperty(PerfCounter.prototype, "min", {
+            /**
+             * Returns the smallest value ever
+             */
+            get: function () {
+                return this._min;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PerfCounter.prototype, "max", {
+            /**
+             * Returns the biggest value ever
+             */
+            get: function () {
+                return this._max;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PerfCounter.prototype, "average", {
+            /**
+             * Returns the average value since the performance counter is running
+             */
+            get: function () {
+                return this._average;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PerfCounter.prototype, "lastSecAverage", {
+            /**
+             * Returns the average value of the last second the counter was monitored
+             */
+            get: function () {
+                return this._lastSecAverage;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PerfCounter.prototype, "current", {
+            /**
+             * Returns the current value
+             */
+            get: function () {
+                return this._current;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * Call this method to start monitoring a new frame.
+         * This scenario is typically used when you accumulate monitoring time many times for a single frame, you call this method at the start of the frame, then beginMonitoring to start recording and endMonitoring(false) to accumulated the recorded time to the PerfCounter or addCount() to accumulate a monitored count.
+         */
+        PerfCounter.prototype.fetchNewFrame = function () {
+            this._totalValueCount++;
+            this._current = 0;
+        };
+        /**
+         * Call this method to monitor a count of something (e.g. mesh drawn in viewport count)
+         * @param newCount the count value to add to the monitored count
+         * @param fetchResult true when it's the last time in the frame you add to the counter and you wish to update the statistics properties (min/max/average), false if you only want to update statistics.
+         */
+        PerfCounter.prototype.addCount = function (newCount, fetchResult) {
+            this._current += newCount;
+            if (fetchResult) {
+                this._fetchResult();
+            }
+        };
+        /**
+         * Start monitoring this performance counter
+         */
+        PerfCounter.prototype.beginMonitoring = function () {
+            this._startMonitoringTime = Tools.Now;
+        };
+        /**
+         * Compute the time lapsed since the previous beginMonitoring() call.
+         * @param newFrame true by default to fetch the result and monitor a new frame, if false the time monitored will be added to the current frame counter
+         */
+        PerfCounter.prototype.endMonitoring = function (newFrame) {
+            if (newFrame === void 0) { newFrame = true; }
+            if (newFrame) {
+                this.fetchNewFrame();
+            }
+            var currentTime = Tools.Now;
+            this._current = currentTime - this._startMonitoringTime;
+            if (newFrame) {
+                this._fetchResult();
+            }
+        };
+        PerfCounter.prototype._fetchResult = function () {
+            this._totalAccumulated += this._current;
+            // Min/Max update
+            this._min = Math.min(this._min, this._current);
+            this._max = Math.max(this._max, this._current);
+            this._average = this._totalAccumulated / this._totalValueCount;
+            // Reset last sec?
+            if ((this._startMonitoringTime - this._lastSecTime) > 1000) {
+                this._lastSecAverage = this._lastSecAccumulated / this._lastSecValueCount;
+                this._lastSecTime = this._startMonitoringTime;
+                this._lastSecAccumulated = 0;
+                this._lastSecValueCount = 0;
+            }
+        };
+        return PerfCounter;
+    })();
+    BABYLON.PerfCounter = PerfCounter;
+    /**
      * Use this className as a decorator on a given class definition to add it a name.
      * Use this className as a decorator on a given class definition to add it a name.
      * You can then use the Tools.getClassName(obj) on an instance to retrieve its class name.
      * You can then use the Tools.getClassName(obj) on an instance to retrieve its class name.
      * This method is the only way to get it done in all cases, even if the .js file declaring the class is minified
      * This method is the only way to get it done in all cases, even if the .js file declaring the class is minified
@@ -5843,7 +5972,7 @@ var BABYLON;
             this.scenes = new Array();
             this.scenes = new Array();
             this._windowIsBackground = false;
             this._windowIsBackground = false;
             this._webGLVersion = "1.0";
             this._webGLVersion = "1.0";
-            this._drawCalls = 0;
+            this._drawCalls = new BABYLON.PerfCounter();
             this._renderingQueueLaunched = false;
             this._renderingQueueLaunched = false;
             this._activeRenderLoops = [];
             this._activeRenderLoops = [];
             // FPS
             // FPS
@@ -6190,15 +6319,18 @@ var BABYLON;
         };
         };
         Object.defineProperty(Engine.prototype, "drawCalls", {
         Object.defineProperty(Engine.prototype, "drawCalls", {
             get: function () {
             get: function () {
+                return this._drawCalls.current;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Engine.prototype, "drawCallsPerfCounter", {
+            get: function () {
                 return this._drawCalls;
                 return this._drawCalls;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
-        // Methods
-        Engine.prototype.resetDrawCalls = function () {
-            this._drawCalls = 0;
-        };
         Engine.prototype.getDepthFunction = function () {
         Engine.prototype.getDepthFunction = function () {
             return this._depthCullingState.depthFunc;
             return this._depthCullingState.depthFunc;
         };
         };
@@ -6687,7 +6819,7 @@ var BABYLON;
         Engine.prototype.draw = function (useTriangles, indexStart, indexCount, instancesCount) {
         Engine.prototype.draw = function (useTriangles, indexStart, indexCount, instancesCount) {
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             // Render
             // Render
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
@@ -6700,7 +6832,7 @@ var BABYLON;
         Engine.prototype.drawPointClouds = function (verticesStart, verticesCount, instancesCount) {
         Engine.prototype.drawPointClouds = function (verticesStart, verticesCount, instancesCount) {
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             if (instancesCount) {
             if (instancesCount) {
                 this._caps.instancedArrays.drawArraysInstancedANGLE(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
                 this._caps.instancedArrays.drawArraysInstancedANGLE(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
                 return;
                 return;
@@ -6710,7 +6842,7 @@ var BABYLON;
         Engine.prototype.drawUnIndexed = function (useTriangles, verticesStart, verticesCount, instancesCount) {
         Engine.prototype.drawUnIndexed = function (useTriangles, verticesStart, verticesCount, instancesCount) {
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             if (instancesCount) {
             if (instancesCount) {
                 this._caps.instancedArrays.drawArraysInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, verticesStart, verticesCount, instancesCount);
                 this._caps.instancedArrays.drawArraysInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, verticesStart, verticesCount, instancesCount);
                 return;
                 return;
@@ -9671,18 +9803,20 @@ var BABYLON;
             this.computeWorldMatrix(true);
             this.computeWorldMatrix(true);
             this.position = BABYLON.Vector3.TransformCoordinates(vector3, this._localWorld);
             this.position = BABYLON.Vector3.TransformCoordinates(vector3, this._localWorld);
         };
         };
-        AbstractMesh.prototype.lookAt = function (targetPoint, yawCor, pitchCor, rollCor) {
+        AbstractMesh.prototype.lookAt = function (targetPoint, yawCor, pitchCor, rollCor, space) {
             /// <summary>Orients a mesh towards a target point. Mesh must be drawn facing user.</summary>
             /// <summary>Orients a mesh towards a target point. Mesh must be drawn facing user.</summary>
             /// <param name="targetPoint" type="Vector3">The position (must be in same space as current mesh) to look at</param>
             /// <param name="targetPoint" type="Vector3">The position (must be in same space as current mesh) to look at</param>
             /// <param name="yawCor" type="Number">optional yaw (y-axis) correction in radians</param>
             /// <param name="yawCor" type="Number">optional yaw (y-axis) correction in radians</param>
             /// <param name="pitchCor" type="Number">optional pitch (x-axis) correction in radians</param>
             /// <param name="pitchCor" type="Number">optional pitch (x-axis) correction in radians</param>
             /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param>
             /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param>
             /// <returns>Mesh oriented towards targetMesh</returns>
             /// <returns>Mesh oriented towards targetMesh</returns>
-            yawCor = yawCor || 0; // default to zero if undefined
-            pitchCor = pitchCor || 0;
-            rollCor = rollCor || 0;
+            if (yawCor === void 0) { yawCor = 0; }
+            if (pitchCor === void 0) { pitchCor = 0; }
+            if (rollCor === void 0) { rollCor = 0; }
+            if (space === void 0) { space = BABYLON.Space.LOCAL; }
             var dv = AbstractMesh._lookAtVectorCache;
             var dv = AbstractMesh._lookAtVectorCache;
-            targetPoint.subtractToRef(this.position, dv);
+            var pos = space === BABYLON.Space.LOCAL ? this.position : this.getAbsolutePosition();
+            targetPoint.subtractToRef(pos, dv);
             var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
             var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
             var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
             var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
             var pitch = Math.atan2(dv.y, len);
             var pitch = Math.atan2(dv.y, len);
@@ -9706,15 +9840,9 @@ var BABYLON;
         AbstractMesh.prototype.isInFrustum = function (frustumPlanes) {
         AbstractMesh.prototype.isInFrustum = function (frustumPlanes) {
             return this._boundingInfo.isInFrustum(frustumPlanes);
             return this._boundingInfo.isInFrustum(frustumPlanes);
         };
         };
-        AbstractMesh.prototype.isCompletelyInFrustum = function (camera) {
-            if (!camera) {
-                camera = this.getScene().activeCamera;
-            }
-            var transformMatrix = camera.getViewMatrix().multiply(camera.getProjectionMatrix());
-            if (!this._boundingInfo.isCompletelyInFrustum(BABYLON.Frustum.GetPlanes(transformMatrix))) {
-                return false;
-            }
-            return true;
+        AbstractMesh.prototype.isCompletelyInFrustum = function (frustumPlanes) {
+            return this._boundingInfo.isCompletelyInFrustum(frustumPlanes);
+            ;
         };
         };
         AbstractMesh.prototype.intersectsMesh = function (mesh, precise) {
         AbstractMesh.prototype.intersectsMesh = function (mesh, precise) {
             if (!this._boundingInfo || !mesh._boundingInfo) {
             if (!this._boundingInfo || !mesh._boundingInfo) {
@@ -11589,8 +11717,10 @@ var BABYLON;
             this._computedViewMatrix = BABYLON.Matrix.Identity();
             this._computedViewMatrix = BABYLON.Matrix.Identity();
             this._projectionMatrix = new BABYLON.Matrix();
             this._projectionMatrix = new BABYLON.Matrix();
             this._postProcesses = new Array();
             this._postProcesses = new Array();
+            this._transformMatrix = BABYLON.Matrix.Zero();
             this._activeMeshes = new BABYLON.SmartArray(256);
             this._activeMeshes = new BABYLON.SmartArray(256);
             this._globalPosition = BABYLON.Vector3.Zero();
             this._globalPosition = BABYLON.Vector3.Zero();
+            this._refreshFrustumPlanes = true;
             scene.addCamera(this);
             scene.addCamera(this);
             if (!scene.activeCamera) {
             if (!scene.activeCamera) {
                 scene.activeCamera = this;
                 scene.activeCamera = this;
@@ -11861,6 +11991,7 @@ var BABYLON;
             if (!force && this._isSynchronizedViewMatrix()) {
             if (!force && this._isSynchronizedViewMatrix()) {
                 return this._computedViewMatrix;
                 return this._computedViewMatrix;
             }
             }
+            this._refreshFrustumPlanes = true;
             if (!this.parent || !this.parent.getWorldMatrix) {
             if (!this.parent || !this.parent.getWorldMatrix) {
                 this._globalPosition.copyFrom(this.position);
                 this._globalPosition.copyFrom(this.position);
             }
             }
@@ -11889,6 +12020,7 @@ var BABYLON;
             if (!force && this._isSynchronizedProjectionMatrix()) {
             if (!force && this._isSynchronizedProjectionMatrix()) {
                 return this._projectionMatrix;
                 return this._projectionMatrix;
             }
             }
+            this._refreshFrustumPlanes = true;
             var engine = this.getEngine();
             var engine = this.getEngine();
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
                 if (this.minZ <= 0) {
                 if (this.minZ <= 0) {
@@ -11902,6 +12034,31 @@ var BABYLON;
             BABYLON.Matrix.OrthoOffCenterLHToRef(this.orthoLeft || -halfWidth, this.orthoRight || halfWidth, this.orthoBottom || -halfHeight, this.orthoTop || halfHeight, this.minZ, this.maxZ, this._projectionMatrix);
             BABYLON.Matrix.OrthoOffCenterLHToRef(this.orthoLeft || -halfWidth, this.orthoRight || halfWidth, this.orthoBottom || -halfHeight, this.orthoTop || halfHeight, this.minZ, this.maxZ, this._projectionMatrix);
             return this._projectionMatrix;
             return this._projectionMatrix;
         };
         };
+        Camera.prototype.getTranformationMatrix = function () {
+            this._computedViewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
+            return this._transformMatrix;
+        };
+        Camera.prototype.updateFrustumPlanes = function () {
+            if (!this._refreshFrustumPlanes) {
+                return;
+            }
+            this.getTranformationMatrix();
+            if (!this._frustumPlanes) {
+                this._frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
+            }
+            else {
+                BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
+            }
+            this._refreshFrustumPlanes = false;
+        };
+        Camera.prototype.isInFrustum = function (target) {
+            this.updateFrustumPlanes();
+            return target.isInFrustum(this._frustumPlanes);
+        };
+        Camera.prototype.isCompletelyInFrustum = function (target) {
+            this.updateFrustumPlanes();
+            return target.isCompletelyInFrustum(this._frustumPlanes);
+        };
         Camera.prototype.dispose = function () {
         Camera.prototype.dispose = function () {
             // Animations
             // Animations
             this.getScene().stopAnimation(this);
             this.getScene().stopAnimation(this);
@@ -14555,7 +14712,7 @@ var BABYLON;
             }
             }
             // Particles
             // Particles
             var activeCamera = this._scene.activeCamera;
             var activeCamera = this._scene.activeCamera;
-            var beforeParticlesDate = BABYLON.Tools.Now;
+            this._scene._particlesDuration.beginMonitoring();
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
                 if (particleSystem.renderingGroupId !== index) {
                 if (particleSystem.renderingGroupId !== index) {
@@ -14566,10 +14723,10 @@ var BABYLON;
                 }
                 }
                 this._clearDepthBuffer();
                 this._clearDepthBuffer();
                 if (!particleSystem.emitter.position || !activeMeshes || activeMeshes.indexOf(particleSystem.emitter) !== -1) {
                 if (!particleSystem.emitter.position || !activeMeshes || activeMeshes.indexOf(particleSystem.emitter) !== -1) {
-                    this._scene._activeParticles += particleSystem.render();
+                    this._scene._activeParticles.addCount(particleSystem.render(), false);
                 }
                 }
             }
             }
-            this._scene._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
+            this._scene._particlesDuration.endMonitoring(false);
         };
         };
         RenderingManager.prototype._renderSprites = function (index) {
         RenderingManager.prototype._renderSprites = function (index) {
             if (!this._scene.spritesEnabled || this._scene.spriteManagers.length === 0) {
             if (!this._scene.spritesEnabled || this._scene.spriteManagers.length === 0) {
@@ -14577,7 +14734,7 @@ var BABYLON;
             }
             }
             // Sprites       
             // Sprites       
             var activeCamera = this._scene.activeCamera;
             var activeCamera = this._scene.activeCamera;
-            var beforeSpritessDate = BABYLON.Tools.Now;
+            this._scene._spritesDuration.beginMonitoring();
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
                 var spriteManager = this._scene.spriteManagers[id];
                 var spriteManager = this._scene.spriteManagers[id];
                 if (spriteManager.renderingGroupId === index && ((activeCamera.layerMask & spriteManager.layerMask) !== 0)) {
                 if (spriteManager.renderingGroupId === index && ((activeCamera.layerMask & spriteManager.layerMask) !== 0)) {
@@ -14585,7 +14742,7 @@ var BABYLON;
                     spriteManager.render();
                     spriteManager.render();
                 }
                 }
             }
             }
-            this._scene._spritesDuration += BABYLON.Tools.Now - beforeSpritessDate;
+            this._scene._spritesDuration.endMonitoring(false);
         };
         };
         RenderingManager.prototype._clearDepthBuffer = function () {
         RenderingManager.prototype._clearDepthBuffer = function () {
             if (this._depthBufferAlreadyCleaned) {
             if (this._depthBufferAlreadyCleaned) {
@@ -15028,16 +15185,22 @@ var BABYLON;
             this.soundTracks = new Array();
             this.soundTracks = new Array();
             this._audioEnabled = true;
             this._audioEnabled = true;
             this._headphone = false;
             this._headphone = false;
-            this._totalVertices = 0;
-            this._activeIndices = 0;
-            this._activeParticles = 0;
-            this._lastFrameDuration = 0;
-            this._evaluateActiveMeshesDuration = 0;
-            this._renderTargetsDuration = 0;
-            this._particlesDuration = 0;
-            this._renderDuration = 0;
-            this._spritesDuration = 0;
-            this._animationRatio = 0;
+            // Performance counters
+            this._totalMeshesCounter = new BABYLON.PerfCounter();
+            this._totalLightsCounter = new BABYLON.PerfCounter();
+            this._totalMaterialsCounter = new BABYLON.PerfCounter();
+            this._totalTexturesCounter = new BABYLON.PerfCounter();
+            this._totalVertices = new BABYLON.PerfCounter();
+            this._activeIndices = new BABYLON.PerfCounter();
+            this._activeParticles = new BABYLON.PerfCounter();
+            this._lastFrameDuration = new BABYLON.PerfCounter();
+            this._evaluateActiveMeshesDuration = new BABYLON.PerfCounter();
+            this._renderTargetsDuration = new BABYLON.PerfCounter();
+            this._particlesDuration = new BABYLON.PerfCounter();
+            this._renderDuration = new BABYLON.PerfCounter();
+            this._spritesDuration = new BABYLON.PerfCounter();
+            this._animationRatio = new BABYLON.PerfCounter();
+            this._activeBones = new BABYLON.PerfCounter();
             this._renderId = 0;
             this._renderId = 0;
             this._executeWhenReadyTimeoutId = -1;
             this._executeWhenReadyTimeoutId = -1;
             this._intermediateRendering = false;
             this._intermediateRendering = false;
@@ -15049,7 +15212,6 @@ var BABYLON;
             this._activeParticleSystems = new BABYLON.SmartArray(256);
             this._activeParticleSystems = new BABYLON.SmartArray(256);
             this._activeSkeletons = new BABYLON.SmartArray(32);
             this._activeSkeletons = new BABYLON.SmartArray(32);
             this._softwareSkinnedMeshes = new BABYLON.SmartArray(32);
             this._softwareSkinnedMeshes = new BABYLON.SmartArray(32);
-            this._activeBones = 0;
             this._activeAnimatables = new Array();
             this._activeAnimatables = new Array();
             this._transformMatrix = BABYLON.Matrix.Zero();
             this._transformMatrix = BABYLON.Matrix.Zero();
             this._edgesRenderers = new BABYLON.SmartArray(16);
             this._edgesRenderers = new BABYLON.SmartArray(16);
@@ -15239,40 +15401,106 @@ var BABYLON;
             return this._engine;
             return this._engine;
         };
         };
         Scene.prototype.getTotalVertices = function () {
         Scene.prototype.getTotalVertices = function () {
-            return this._totalVertices;
+            return this._totalVertices.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "totalVerticesPerfCounter", {
+            get: function () {
+                return this._totalVertices;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getActiveIndices = function () {
         Scene.prototype.getActiveIndices = function () {
-            return this._activeIndices;
+            return this._activeIndices.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "totalActiveIndicesPerfCounter", {
+            get: function () {
+                return this._activeIndices;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getActiveParticles = function () {
         Scene.prototype.getActiveParticles = function () {
-            return this._activeParticles;
+            return this._activeParticles.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "activeParticlesPerfCounter", {
+            get: function () {
+                return this._activeParticles;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getActiveBones = function () {
         Scene.prototype.getActiveBones = function () {
-            return this._activeBones;
+            return this._activeBones.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "activeBonesPerfCounter", {
+            get: function () {
+                return this._activeBones;
+            },
+            enumerable: true,
+            configurable: true
+        });
         // Stats
         // Stats
         Scene.prototype.getLastFrameDuration = function () {
         Scene.prototype.getLastFrameDuration = function () {
-            return this._lastFrameDuration;
+            return this._lastFrameDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "lastFramePerfCounter", {
+            get: function () {
+                return this._lastFrameDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getEvaluateActiveMeshesDuration = function () {
         Scene.prototype.getEvaluateActiveMeshesDuration = function () {
-            return this._evaluateActiveMeshesDuration;
+            return this._evaluateActiveMeshesDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "evaluateActiveMeshesDurationPerfCounter", {
+            get: function () {
+                return this._evaluateActiveMeshesDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getActiveMeshes = function () {
         Scene.prototype.getActiveMeshes = function () {
             return this._activeMeshes;
             return this._activeMeshes;
         };
         };
         Scene.prototype.getRenderTargetsDuration = function () {
         Scene.prototype.getRenderTargetsDuration = function () {
-            return this._renderTargetsDuration;
+            return this._renderTargetsDuration.current;
         };
         };
         Scene.prototype.getRenderDuration = function () {
         Scene.prototype.getRenderDuration = function () {
-            return this._renderDuration;
+            return this._renderDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "renderDurationPerfCounter", {
+            get: function () {
+                return this._renderDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getParticlesDuration = function () {
         Scene.prototype.getParticlesDuration = function () {
-            return this._particlesDuration;
+            return this._particlesDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "particlesDurationPerfCounter", {
+            get: function () {
+                return this._particlesDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getSpritesDuration = function () {
         Scene.prototype.getSpritesDuration = function () {
-            return this._spritesDuration;
+            return this._spritesDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "spriteDuractionPerfCounter", {
+            get: function () {
+                return this._spritesDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getAnimationRatio = function () {
         Scene.prototype.getAnimationRatio = function () {
+            return this._animationRatio.current;
+        };
+        Scene.prototype.animationRatioPerfCounter = function () {
             return this._animationRatio;
             return this._animationRatio;
         };
         };
         Scene.prototype.getRenderId = function () {
         Scene.prototype.getRenderId = function () {
@@ -15734,6 +15962,13 @@ var BABYLON;
             this._viewMatrix = view;
             this._viewMatrix = view;
             this._projectionMatrix = projection;
             this._projectionMatrix = projection;
             this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
             this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
+            // Update frustum
+            if (!this._frustumPlanes) {
+                this._frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
+            }
+            else {
+                BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
+            }
         };
         };
         // Methods
         // Methods
         Scene.prototype.addMesh = function (newMesh) {
         Scene.prototype.addMesh = function (newMesh) {
@@ -16218,7 +16453,7 @@ var BABYLON;
                         }
                         }
                     }
                     }
                     // Dispatch
                     // Dispatch
-                    this._activeIndices += subMesh.indexCount;
+                    this._activeIndices.addCount(subMesh.indexCount, false);
                     this._renderingManager.dispatch(subMesh);
                     this._renderingManager.dispatch(subMesh);
                 }
                 }
             }
             }
@@ -16236,12 +16471,6 @@ var BABYLON;
             this._softwareSkinnedMeshes.reset();
             this._softwareSkinnedMeshes.reset();
             this._boundingBoxRenderer.reset();
             this._boundingBoxRenderer.reset();
             this._edgesRenderers.reset();
             this._edgesRenderers.reset();
-            if (!this._frustumPlanes) {
-                this._frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
-            }
-            else {
-                BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
-            }
             // Meshes
             // Meshes
             var meshes;
             var meshes;
             var len;
             var len;
@@ -16259,7 +16488,7 @@ var BABYLON;
                 if (mesh.isBlocked) {
                 if (mesh.isBlocked) {
                     continue;
                     continue;
                 }
                 }
-                this._totalVertices += mesh.getTotalVertices();
+                this._totalVertices.addCount(mesh.getTotalVertices(), false);
                 if (!mesh.isReady() || !mesh.isEnabled()) {
                 if (!mesh.isReady() || !mesh.isEnabled()) {
                     continue;
                     continue;
                 }
                 }
@@ -16282,6 +16511,7 @@ var BABYLON;
                 }
                 }
             }
             }
             // Particle systems
             // Particle systems
+            this._particlesDuration.beginMonitoring();
             var beforeParticlesDate = BABYLON.Tools.Now;
             var beforeParticlesDate = BABYLON.Tools.Now;
             if (this.particlesEnabled) {
             if (this.particlesEnabled) {
                 BABYLON.Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
                 BABYLON.Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
@@ -16297,7 +16527,7 @@ var BABYLON;
                 }
                 }
                 BABYLON.Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
                 BABYLON.Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
             }
             }
-            this._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
+            this._particlesDuration.endMonitoring(false);
         };
         };
         Scene.prototype._activeMesh = function (mesh) {
         Scene.prototype._activeMesh = function (mesh) {
             if (mesh.skeleton && this.skeletonsEnabled) {
             if (mesh.skeleton && this.skeletonsEnabled) {
@@ -16338,6 +16568,7 @@ var BABYLON;
         };
         };
         Scene.prototype._renderForCamera = function (camera) {
         Scene.prototype._renderForCamera = function (camera) {
             var engine = this._engine;
             var engine = this._engine;
+            var startTime = BABYLON.Tools.Now;
             this.activeCamera = camera;
             this.activeCamera = camera;
             if (!this.activeCamera)
             if (!this.activeCamera)
                 throw new Error("Active camera not set");
                 throw new Error("Active camera not set");
@@ -16350,10 +16581,10 @@ var BABYLON;
             this.updateTransformMatrix();
             this.updateTransformMatrix();
             this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
             this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
             // Meshes
             // Meshes
-            var beforeEvaluateActiveMeshesDate = BABYLON.Tools.Now;
+            this._evaluateActiveMeshesDuration.beginMonitoring();
             BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
             BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
             this._evaluateActiveMeshes();
             this._evaluateActiveMeshes();
-            this._evaluateActiveMeshesDuration += BABYLON.Tools.Now - beforeEvaluateActiveMeshesDate;
+            this._evaluateActiveMeshesDuration.endMonitoring(false);
             BABYLON.Tools.EndPerformanceCounter("Active meshes evaluation");
             BABYLON.Tools.EndPerformanceCounter("Active meshes evaluation");
             // Software skinning
             // Software skinning
             for (var softwareSkinnedMeshIndex = 0; softwareSkinnedMeshIndex < this._softwareSkinnedMeshes.length; softwareSkinnedMeshIndex++) {
             for (var softwareSkinnedMeshIndex = 0; softwareSkinnedMeshIndex < this._softwareSkinnedMeshes.length; softwareSkinnedMeshIndex++) {
@@ -16361,6 +16592,7 @@ var BABYLON;
                 mesh.applySkeleton(mesh.skeleton);
                 mesh.applySkeleton(mesh.skeleton);
             }
             }
             // Render targets
             // Render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
                 this._intermediateRendering = true;
                 this._intermediateRendering = true;
@@ -16378,10 +16610,10 @@ var BABYLON;
                 this._renderId++;
                 this._renderId++;
                 engine.restoreDefaultFramebuffer(); // Restore back buffer
                 engine.restoreDefaultFramebuffer(); // Restore back buffer
             }
             }
-            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring(false);
             // Prepare Frame
             // Prepare Frame
             this.postProcessManager._prepareFrame();
             this.postProcessManager._prepareFrame();
-            var beforeRenderDate = BABYLON.Tools.Now;
+            this._renderDuration.beginMonitoring();
             // Backgrounds
             // Backgrounds
             var layerIndex;
             var layerIndex;
             var layer;
             var layer;
@@ -16427,7 +16659,7 @@ var BABYLON;
                 }
                 }
                 engine.setDepthBuffer(true);
                 engine.setDepthBuffer(true);
             }
             }
-            this._renderDuration += BABYLON.Tools.Now - beforeRenderDate;
+            this._renderDuration.endMonitoring(false);
             // Finalize frame
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
             // Update camera
             // Update camera
@@ -16486,17 +16718,17 @@ var BABYLON;
             }
             }
         };
         };
         Scene.prototype.render = function () {
         Scene.prototype.render = function () {
-            var startDate = BABYLON.Tools.Now;
-            this._particlesDuration = 0;
-            this._spritesDuration = 0;
-            this._activeParticles = 0;
-            this._renderDuration = 0;
-            this._renderTargetsDuration = 0;
-            this._evaluateActiveMeshesDuration = 0;
-            this._totalVertices = 0;
-            this._activeIndices = 0;
-            this._activeBones = 0;
-            this.getEngine().resetDrawCalls();
+            this._lastFrameDuration.beginMonitoring();
+            this._particlesDuration.fetchNewFrame();
+            this._spritesDuration.fetchNewFrame();
+            this._activeParticles.fetchNewFrame();
+            this._renderDuration.fetchNewFrame();
+            this._renderTargetsDuration.fetchNewFrame();
+            this._evaluateActiveMeshesDuration.fetchNewFrame();
+            this._totalVertices.fetchNewFrame();
+            this._activeIndices.fetchNewFrame();
+            this._activeBones.fetchNewFrame();
+            this.getEngine().drawCallsPerfCounter.fetchNewFrame();
             this._meshesForIntersections.reset();
             this._meshesForIntersections.reset();
             this.resetCachedMaterial();
             this.resetCachedMaterial();
             BABYLON.Tools.StartPerformanceCounter("Scene rendering");
             BABYLON.Tools.StartPerformanceCounter("Scene rendering");
@@ -16510,7 +16742,7 @@ var BABYLON;
             }
             }
             // Animations
             // Animations
             var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
             var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
-            this._animationRatio = deltaTime * (60.0 / 1000.0);
+            this._animationRatio.addCount(deltaTime * (60.0 / 1000.0), true);
             this._animate();
             this._animate();
             // Physics
             // Physics
             if (this._physicsEngine) {
             if (this._physicsEngine) {
@@ -16521,6 +16753,7 @@ var BABYLON;
             // Before render
             // Before render
             this.onBeforeRenderObservable.notifyObservers(this);
             this.onBeforeRenderObservable.notifyObservers(this);
             // Customs render targets
             // Customs render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var engine = this.getEngine();
             var engine = this.getEngine();
             var currentActiveCamera = this.activeCamera;
             var currentActiveCamera = this.activeCamera;
@@ -16546,7 +16779,7 @@ var BABYLON;
             if (this.customRenderTargets.length > 0) {
             if (this.customRenderTargets.length > 0) {
                 engine.restoreDefaultFramebuffer();
                 engine.restoreDefaultFramebuffer();
             }
             }
-            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring();
             this.activeCamera = currentActiveCamera;
             this.activeCamera = currentActiveCamera;
             // Procedural textures
             // Procedural textures
             if (this.proceduralTexturesEnabled) {
             if (this.proceduralTexturesEnabled) {
@@ -16615,7 +16848,14 @@ var BABYLON;
                 this.dumpNextRenderTargets = false;
                 this.dumpNextRenderTargets = false;
             }
             }
             BABYLON.Tools.EndPerformanceCounter("Scene rendering");
             BABYLON.Tools.EndPerformanceCounter("Scene rendering");
-            this._lastFrameDuration = BABYLON.Tools.Now - startDate;
+            this._lastFrameDuration.endMonitoring();
+            this._totalMeshesCounter.addCount(this.meshes.length, true);
+            this._totalLightsCounter.addCount(this.lights.length, true);
+            this._totalMaterialsCounter.addCount(this.materials.length, true);
+            this._totalTexturesCounter.addCount(this.textures.length, true);
+            this._activeBones.addCount(0, true);
+            this._activeIndices.addCount(0, true);
+            this._activeParticles.addCount(0, true);
         };
         };
         Scene.prototype._updateAudioParameters = function () {
         Scene.prototype._updateAudioParameters = function () {
             if (!this.audioEnabled || (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 1)) {
             if (!this.audioEnabled || (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 1)) {
@@ -18290,7 +18530,7 @@ var BABYLON;
         /**
         /**
          * Sets the mesh indices.
          * Sets the mesh indices.
          * Expects an array populated with integers or a Int32Array.
          * Expects an array populated with integers or a Int32Array.
-         * If the mesh has no geometry, a new `Geometry` object is created and set to the mesh.
+         * If the mesh has no geometry, a new Geometry object is created and set to the mesh.
          * This method creates a new index buffer each call.
          * This method creates a new index buffer each call.
          */
          */
         Mesh.prototype.setIndices = function (indices, totalVertices) {
         Mesh.prototype.setIndices = function (indices, totalVertices) {
@@ -18742,7 +18982,7 @@ var BABYLON;
          * Returns a new Mesh object generated from the current mesh properties.
          * Returns a new Mesh object generated from the current mesh properties.
          * This method must not get confused with createInstance().
          * This method must not get confused with createInstance().
          * The parameter `name` is a string, the name given to the new mesh.
          * The parameter `name` is a string, the name given to the new mesh.
-         * The optional parameter `newParent` can be any `Node` object (default `null`).
+         * The optional parameter `newParent` can be any Node object (default `null`).
          * The optional parameter `doNotCloneChildren` (default `false`) allows/denies the recursive cloning of the original mesh children if any.
          * The optional parameter `doNotCloneChildren` (default `false`) allows/denies the recursive cloning of the original mesh children if any.
          * The parameter `clonePhysicsImpostor` (default `true`)  allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any.
          * The parameter `clonePhysicsImpostor` (default `true`)  allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any.
          */
          */
@@ -19542,7 +19782,6 @@ var BABYLON;
          * Creates lathe mesh.
          * Creates lathe mesh.
          * The lathe is a shape with a symetry axis : a 2D model shape is rotated around this axis to design the lathe.
          * The lathe is a shape with a symetry axis : a 2D model shape is rotated around this axis to design the lathe.
          * Please consider using the same method from the MeshBuilder class instead.
          * Please consider using the same method from the MeshBuilder class instead.
-         *
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be rotated in its local space : the shape must be designed in the xOy plane and will be
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be rotated in its local space : the shape must be designed in the xOy plane and will be
          * rotated around the Y axis. It's usually a 2D shape, so the Vector3 z coordinates are often set to zero.
          * rotated around the Y axis. It's usually a 2D shape, so the Vector3 z coordinates are often set to zero.
          * The parameter `radius` (positive float, default 1) is the radius value of the lathe.
          * The parameter `radius` (positive float, default 1) is the radius value of the lathe.
@@ -19650,7 +19889,6 @@ var BABYLON;
         /**
         /**
          * Creates a tube mesh.
          * Creates a tube mesh.
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.
-         *
          * Please consider using the same method from the MeshBuilder class instead.
          * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.
          * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.
          * The parameter `radius` (positive float, default 1) sets the tube radius size.
          * The parameter `radius` (positive float, default 1) sets the tube radius size.
@@ -19685,7 +19923,6 @@ var BABYLON;
         };
         };
         /**
         /**
          * Creates a polyhedron mesh.
          * Creates a polyhedron mesh.
-         *
          * Please consider using the same method from the MeshBuilder class instead.
          * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `type` (positive integer, max 14, default 0) sets the polyhedron type to build among the 15 embbeded types. Please refer to the type sheet in the tutorial
          * The parameter `type` (positive integer, max 14, default 0) sets the polyhedron type to build among the 15 embbeded types. Please refer to the type sheet in the tutorial
          *  to choose the wanted type.
          *  to choose the wanted type.
@@ -19722,7 +19959,7 @@ var BABYLON;
          * Please consider using the same method from the MeshBuilder class instead.
          * Please consider using the same method from the MeshBuilder class instead.
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.
          * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.
          * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.
-         * The parameter `normal` (Vector3, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.
+         * The parameter `normal` (Vector3, default Vector3.Up) sets the normal of the mesh where the decal is applied onto in World coordinates.
          * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.
          * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.
          */
          */
@@ -19849,7 +20086,7 @@ var BABYLON;
         };
         };
         // Tools
         // Tools
         /**
         /**
-         * Returns an object `{min: Vector3, max: Vector3}`
+         * Returns an object `{min:` Vector3`, max:` Vector3`}`
          * This min and max Vector3 are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
          * This min and max Vector3 are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
          */
          */
         Mesh.MinMax = function (meshes) {
         Mesh.MinMax = function (meshes) {
@@ -19872,7 +20109,7 @@ var BABYLON;
             };
             };
         };
         };
         /**
         /**
-         * Returns a `Vector3`, the center of the `{min: Vector3, max: Vector3}` or the center of MinMax vector3 computed from a mesh array.
+         * Returns a Vector3, the center of the `{min:` Vector3`, max:` Vector3`}` or the center of MinMax vector3 computed from a mesh array.
          */
          */
         Mesh.Center = function (meshesOrMinMaxVector) {
         Mesh.Center = function (meshesOrMinMaxVector) {
             var minMaxVector = meshesOrMinMaxVector.min !== undefined ? meshesOrMinMaxVector : Mesh.MinMax(meshesOrMinMaxVector);
             var minMaxVector = meshesOrMinMaxVector.min !== undefined ? meshesOrMinMaxVector : Mesh.MinMax(meshesOrMinMaxVector);
@@ -20036,6 +20273,9 @@ var BABYLON;
         SubMesh.prototype.isInFrustum = function (frustumPlanes) {
         SubMesh.prototype.isInFrustum = function (frustumPlanes) {
             return this.getBoundingInfo().isInFrustum(frustumPlanes);
             return this.getBoundingInfo().isInFrustum(frustumPlanes);
         };
         };
+        SubMesh.prototype.isCompletelyInFrustum = function (frustumPlanes) {
+            return this.getBoundingInfo().isCompletelyInFrustum(frustumPlanes);
+        };
         SubMesh.prototype.render = function (enableAlphaMode) {
         SubMesh.prototype.render = function (enableAlphaMode) {
             this._renderingMesh.render(this, enableAlphaMode);
             this._renderingMesh.render(this, enableAlphaMode);
         };
         };
@@ -21932,7 +22172,7 @@ var BABYLON;
                         mesh._activate(sceneRenderId);
                         mesh._activate(sceneRenderId);
                         for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
                         for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
                             var subMesh = mesh.subMeshes[subIndex];
                             var subMesh = mesh.subMeshes[subIndex];
-                            scene._activeIndices += subMesh.indexCount;
+                            scene._activeIndices.addCount(subMesh.indexCount, false);
                             this._renderingManager.dispatch(subMesh);
                             this._renderingManager.dispatch(subMesh);
                         }
                         }
                     }
                     }
@@ -27975,7 +28215,7 @@ var BABYLON;
                 this._computeTransformMatrices(this._transformMatrices, null);
                 this._computeTransformMatrices(this._transformMatrices, null);
             }
             }
             this._isDirty = false;
             this._isDirty = false;
-            this._scene._activeBones += this.bones.length;
+            this._scene._activeBones.addCount(this.bones.length, false);
         };
         };
         Skeleton.prototype.getAnimatables = function () {
         Skeleton.prototype.getAnimatables = function () {
             if (!this._animatables || this._animatables.length !== this.bones.length) {
             if (!this._animatables || this._animatables.length !== this.bones.length) {
@@ -35880,7 +36120,9 @@ var BABYLON;
                 // Small heuristic... We don't want to allocate totalElementCount right away because it may have 50 for 3 used elements, but on the other side we don't want to allocate just 3 when we just need 2, so double this value to give us some air to breath...
                 // Small heuristic... We don't want to allocate totalElementCount right away because it may have 50 for 3 used elements, but on the other side we don't want to allocate just 3 when we just need 2, so double this value to give us some air to breath...
                 var newCount = Math.min(this.totalElementCount, count * 2);
                 var newCount = Math.min(this.totalElementCount, count * 2);
                 this._sortTable = new Array(newCount);
                 this._sortTable = new Array(newCount);
-                this._sortedTable = new Array(newCount);
+            }
+            if (!this._sortTable || this._sortTable.length !== count) {
+                this._sortedTable = new Array(count);
             }
             }
             // Because, you know...
             // Because, you know...
             this.pack();
             this.pack();
@@ -41671,12 +41913,14 @@ var BABYLON;
             }
             }
             else {
             else {
                 context.partDataStartIndex = 0;
                 context.partDataStartIndex = 0;
-                // Find the first valid object to get the count
-                var i = 0;
-                while (!context.groupInfoPartData[i]) {
-                    i++;
+                if (context.groupInfoPartData.length > 0) {
+                    // Find the first valid object to get the count
+                    var i = 0;
+                    while (!context.groupInfoPartData[i]) {
+                        i++;
+                    }
+                    context.partDataEndIndex = context.groupInfoPartData[i]._partData.usedElementCount;
                 }
                 }
-                context.partDataEndIndex = context.groupInfoPartData[i]._partData.usedElementCount;
             }
             }
             return renderCount;
             return renderCount;
         };
         };
@@ -45367,9 +45611,15 @@ var BABYLON;
                 group.y = Math.round(rh - proj.y);
                 group.y = Math.round(rh - proj.y);
             }
             }
         };
         };
-        Canvas2D.prototype._updateCanvasState = function () {
+        /**
+         * Call this method change you want to have layout related data computed and up to date (layout area, primitive area, local/global transformation matrices)
+         */
+        Canvas2D.prototype.updateCanvasLayout = function (forceRecompute) {
+            this._updateCanvasState(forceRecompute);
+        };
+        Canvas2D.prototype._updateCanvasState = function (forceRecompute) {
             // Check if the update has already been made for this render Frame
             // Check if the update has already been made for this render Frame
-            if (this.scene.getRenderId() === this._updateRenderId) {
+            if (!forceRecompute && this.scene.getRenderId() === this._updateRenderId) {
                 return;
                 return;
             }
             }
             // Detect a change of rendering size
             // Detect a change of rendering size
@@ -45404,7 +45654,7 @@ var BABYLON;
          */
          */
         Canvas2D.prototype._render = function () {
         Canvas2D.prototype._render = function () {
             this._updateTrackedNodes();
             this._updateTrackedNodes();
-            this._updateCanvasState();
+            this._updateCanvasState(false);
             if (this._primPointerInfo.canvasPointerPos) {
             if (this._primPointerInfo.canvasPointerPos) {
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
                 this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!
                 this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 40 - 40
dist/preview release/babylon.noworker.js


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

@@ -3,11 +3,13 @@
 ### Major updates
 ### Major updates
     
     
 ### Updates
 ### Updates
+- Added `camera.isInFrustum` and `camera.isCompletelyInFrustum`. Can be used with meshes, submeshes and boundingInfo ([deltakosh](https://github.com/deltakosh)) 
 - Several memory allocation reduction ([benaadams](https://github.com/benaadams)) 
 - Several memory allocation reduction ([benaadams](https://github.com/benaadams)) 
 - Several GPU state change reduction ([benaadams](https://github.com/benaadams)) 
 - Several GPU state change reduction ([benaadams](https://github.com/benaadams)) 
 - MapTexture: add `supersample` mode to double font quality. ([nockawa](https://github.com/nockawa))
 - MapTexture: add `supersample` mode to double font quality. ([nockawa](https://github.com/nockawa))
 - new `invertUV` parameter an all ribbon based shapes : ribbon, tube, lathe, basic and custom extrusion
 - new `invertUV` parameter an all ribbon based shapes : ribbon, tube, lathe, basic and custom extrusion
 - Text2D: new `fontSuperSample` setting to use high quality font.
 - Text2D: new `fontSuperSample` setting to use high quality font.
+- PerfCounter class added to monitor time/counter and expose min/max/average/lastSecondAverage/current metrics. Updated engine/scene current counter to use this class, exposing new properties as well to access the PerfCounter object. ([nockawa](https://github.com/nockawa))
 
 
 ### Exporters
 ### Exporters
     
     

+ 1 - 1
src/Bones/babylon.skeleton.js

@@ -220,7 +220,7 @@ var BABYLON;
                 this._computeTransformMatrices(this._transformMatrices, null);
                 this._computeTransformMatrices(this._transformMatrices, null);
             }
             }
             this._isDirty = false;
             this._isDirty = false;
-            this._scene._activeBones += this.bones.length;
+            this._scene._activeBones.addCount(this.bones.length, false);
         };
         };
         Skeleton.prototype.getAnimatables = function () {
         Skeleton.prototype.getAnimatables = function () {
             if (!this._animatables || this._animatables.length !== this.bones.length) {
             if (!this._animatables || this._animatables.length !== this.bones.length) {

+ 1 - 1
src/Bones/babylon.skeleton.ts

@@ -258,7 +258,7 @@
 
 
             this._isDirty = false;
             this._isDirty = false;
 
 
-            this._scene._activeBones += this.bones.length;
+            this._scene._activeBones.addCount(this.bones.length, false);
         }
         }
 
 
         public getAnimatables(): IAnimatable[] {
         public getAnimatables(): IAnimatable[] {

+ 29 - 0
src/Cameras/babylon.camera.js

@@ -36,8 +36,10 @@ var BABYLON;
             this._computedViewMatrix = BABYLON.Matrix.Identity();
             this._computedViewMatrix = BABYLON.Matrix.Identity();
             this._projectionMatrix = new BABYLON.Matrix();
             this._projectionMatrix = new BABYLON.Matrix();
             this._postProcesses = new Array();
             this._postProcesses = new Array();
+            this._transformMatrix = BABYLON.Matrix.Zero();
             this._activeMeshes = new BABYLON.SmartArray(256);
             this._activeMeshes = new BABYLON.SmartArray(256);
             this._globalPosition = BABYLON.Vector3.Zero();
             this._globalPosition = BABYLON.Vector3.Zero();
+            this._refreshFrustumPlanes = true;
             scene.addCamera(this);
             scene.addCamera(this);
             if (!scene.activeCamera) {
             if (!scene.activeCamera) {
                 scene.activeCamera = this;
                 scene.activeCamera = this;
@@ -308,6 +310,7 @@ var BABYLON;
             if (!force && this._isSynchronizedViewMatrix()) {
             if (!force && this._isSynchronizedViewMatrix()) {
                 return this._computedViewMatrix;
                 return this._computedViewMatrix;
             }
             }
+            this._refreshFrustumPlanes = true;
             if (!this.parent || !this.parent.getWorldMatrix) {
             if (!this.parent || !this.parent.getWorldMatrix) {
                 this._globalPosition.copyFrom(this.position);
                 this._globalPosition.copyFrom(this.position);
             }
             }
@@ -336,6 +339,7 @@ var BABYLON;
             if (!force && this._isSynchronizedProjectionMatrix()) {
             if (!force && this._isSynchronizedProjectionMatrix()) {
                 return this._projectionMatrix;
                 return this._projectionMatrix;
             }
             }
+            this._refreshFrustumPlanes = true;
             var engine = this.getEngine();
             var engine = this.getEngine();
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
                 if (this.minZ <= 0) {
                 if (this.minZ <= 0) {
@@ -349,6 +353,31 @@ var BABYLON;
             BABYLON.Matrix.OrthoOffCenterLHToRef(this.orthoLeft || -halfWidth, this.orthoRight || halfWidth, this.orthoBottom || -halfHeight, this.orthoTop || halfHeight, this.minZ, this.maxZ, this._projectionMatrix);
             BABYLON.Matrix.OrthoOffCenterLHToRef(this.orthoLeft || -halfWidth, this.orthoRight || halfWidth, this.orthoBottom || -halfHeight, this.orthoTop || halfHeight, this.minZ, this.maxZ, this._projectionMatrix);
             return this._projectionMatrix;
             return this._projectionMatrix;
         };
         };
+        Camera.prototype.getTranformationMatrix = function () {
+            this._computedViewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
+            return this._transformMatrix;
+        };
+        Camera.prototype.updateFrustumPlanes = function () {
+            if (!this._refreshFrustumPlanes) {
+                return;
+            }
+            this.getTranformationMatrix();
+            if (!this._frustumPlanes) {
+                this._frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
+            }
+            else {
+                BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
+            }
+            this._refreshFrustumPlanes = false;
+        };
+        Camera.prototype.isInFrustum = function (target) {
+            this.updateFrustumPlanes();
+            return target.isInFrustum(this._frustumPlanes);
+        };
+        Camera.prototype.isCompletelyInFrustum = function (target) {
+            this.updateFrustumPlanes();
+            return target.isCompletelyInFrustum(this._frustumPlanes);
+        };
         Camera.prototype.dispose = function () {
         Camera.prototype.dispose = function () {
             // Animations
             // Animations
             this.getScene().stopAnimation(this);
             this.getScene().stopAnimation(this);

+ 40 - 0
src/Cameras/babylon.camera.ts

@@ -120,10 +120,13 @@
         public _projectionMatrix = new Matrix();
         public _projectionMatrix = new Matrix();
         private _worldMatrix: Matrix;
         private _worldMatrix: Matrix;
         public _postProcesses = new Array<PostProcess>();
         public _postProcesses = new Array<PostProcess>();
+        private _transformMatrix = Matrix.Zero();
 
 
         public _activeMeshes = new SmartArray<Mesh>(256);
         public _activeMeshes = new SmartArray<Mesh>(256);
 
 
         private _globalPosition = Vector3.Zero();
         private _globalPosition = Vector3.Zero();
+        private _frustumPlanes: Plane[];
+        private _refreshFrustumPlanes = true;
 
 
         constructor(name: string, position: Vector3, scene: Scene) {
         constructor(name: string, position: Vector3, scene: Scene) {
             super(name, scene);
             super(name, scene);
@@ -366,6 +369,8 @@
                 return this._computedViewMatrix;
                 return this._computedViewMatrix;
             }
             }
 
 
+            this._refreshFrustumPlanes = true;
+
             if (!this.parent || !this.parent.getWorldMatrix) {
             if (!this.parent || !this.parent.getWorldMatrix) {
                 this._globalPosition.copyFrom(this.position);
                 this._globalPosition.copyFrom(this.position);
             } else {
             } else {
@@ -404,6 +409,8 @@
                 return this._projectionMatrix;
                 return this._projectionMatrix;
             }
             }
 
 
+            this._refreshFrustumPlanes = true;
+
             var engine = this.getEngine();
             var engine = this.getEngine();
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
                 if (this.minZ <= 0) {
                 if (this.minZ <= 0) {
@@ -420,6 +427,39 @@
             return this._projectionMatrix;
             return this._projectionMatrix;
         }
         }
 
 
+        public getTranformationMatrix(): Matrix {
+            this._computedViewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
+            return this._transformMatrix;
+        }
+
+        private updateFrustumPlanes(): void {
+            if (!this._refreshFrustumPlanes) {
+                return;
+            }
+
+            this.getTranformationMatrix();
+
+            if (!this._frustumPlanes) {
+                this._frustumPlanes = Frustum.GetPlanes(this._transformMatrix);
+            } else {
+                Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
+            }
+
+            this._refreshFrustumPlanes = false;
+        }
+
+        public isInFrustum(target: ICullable): boolean {
+            this.updateFrustumPlanes();
+
+            return target.isInFrustum(this._frustumPlanes);
+        }
+
+        public isCompletelyInFrustum(target: ICullable): boolean {
+            this.updateFrustumPlanes();
+
+            return target.isCompletelyInFrustum(this._frustumPlanes);
+        }
+
         public dispose(): void {
         public dispose(): void {
             // Animations
             // Animations
             this.getScene().stopAnimation(this);
             this.getScene().stopAnimation(this);

+ 9 - 3
src/Canvas2d/babylon.canvas2d.js

@@ -751,9 +751,15 @@ var BABYLON;
                 group.y = Math.round(rh - proj.y);
                 group.y = Math.round(rh - proj.y);
             }
             }
         };
         };
-        Canvas2D.prototype._updateCanvasState = function () {
+        /**
+         * Call this method change you want to have layout related data computed and up to date (layout area, primitive area, local/global transformation matrices)
+         */
+        Canvas2D.prototype.updateCanvasLayout = function (forceRecompute) {
+            this._updateCanvasState(forceRecompute);
+        };
+        Canvas2D.prototype._updateCanvasState = function (forceRecompute) {
             // Check if the update has already been made for this render Frame
             // Check if the update has already been made for this render Frame
-            if (this.scene.getRenderId() === this._updateRenderId) {
+            if (!forceRecompute && this.scene.getRenderId() === this._updateRenderId) {
                 return;
                 return;
             }
             }
             // Detect a change of rendering size
             // Detect a change of rendering size
@@ -788,7 +794,7 @@ var BABYLON;
          */
          */
         Canvas2D.prototype._render = function () {
         Canvas2D.prototype._render = function () {
             this._updateTrackedNodes();
             this._updateTrackedNodes();
-            this._updateCanvasState();
+            this._updateCanvasState(false);
             if (this._primPointerInfo.canvasPointerPos) {
             if (this._primPointerInfo.canvasPointerPos) {
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
                 this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!
                 this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!

+ 20 - 20
src/Canvas2d/babylon.canvas2d.ts

@@ -1428,27 +1428,27 @@
          */
          */
         constructor(scene: Scene, settings?: {
         constructor(scene: Scene, settings?: {
 
 
-            children                 ?: Array<Prim2DBase>,
-            id                       ?: string,
-            x                        ?: number,
-            y                        ?: number,
-            position                 ?: Vector2,
-            origin                   ?: Vector2,
-            width                    ?: number,
-            height                   ?: number,
-            size                     ?: Size,
-            cachingStrategy          ?: number,
-            enableInteraction        ?: boolean,
-            isVisible                ?: boolean,
-            backgroundRoundRadius    ?: number,
-            backgroundFill           ?: IBrush2D | string,
-            backgroundBorder         ?: IBrush2D | string,
+            children?: Array<Prim2DBase>,
+            id?: string,
+            x?: number,
+            y?: number,
+            position?: Vector2,
+            origin?: Vector2,
+            width?: number,
+            height?: number,
+            size?: Size,
+            cachingStrategy?: number,
+            enableInteraction?: boolean,
+            isVisible?: boolean,
+            backgroundRoundRadius?: number,
+            backgroundFill?: IBrush2D | string,
+            backgroundBorder?: IBrush2D | string,
             backgroundBorderThickNess?: number,
             backgroundBorderThickNess?: number,
-            paddingTop               ?: number | string,
-            paddingLeft              ?: number | string,
-            paddingRight             ?: number | string,
-            paddingBottom            ?: number | string,
-            padding                  ?: string,
+            paddingTop?: number | string,
+            paddingLeft?: number | string,
+            paddingRight?: number | string,
+            paddingBottom?: number | string,
+            padding?: string,
 
 
         }) {
         }) {
             Prim2DBase._isCanvasInit = true;
             Prim2DBase._isCanvasInit = true;

+ 7 - 5
src/Canvas2d/babylon.group2d.js

@@ -556,12 +556,14 @@ var BABYLON;
             }
             }
             else {
             else {
                 context.partDataStartIndex = 0;
                 context.partDataStartIndex = 0;
-                // Find the first valid object to get the count
-                var i = 0;
-                while (!context.groupInfoPartData[i]) {
-                    i++;
+                if (context.groupInfoPartData.length > 0) {
+                    // Find the first valid object to get the count
+                    var i = 0;
+                    while (!context.groupInfoPartData[i]) {
+                        i++;
+                    }
+                    context.partDataEndIndex = context.groupInfoPartData[i]._partData.usedElementCount;
                 }
                 }
-                context.partDataEndIndex = context.groupInfoPartData[i]._partData.usedElementCount;
             }
             }
             return renderCount;
             return renderCount;
         };
         };

+ 1 - 1
src/Culling/babylon.boundingBox.ts

@@ -1,5 +1,5 @@
 module BABYLON {
 module BABYLON {
-    export class BoundingBox {
+    export class BoundingBox implements ICullable {
         public vectors: Vector3[] = new Array<Vector3>();
         public vectors: Vector3[] = new Array<Vector3>();
         public center: Vector3;
         public center: Vector3;
         public extendSize: Vector3;
         public extendSize: Vector3;

+ 6 - 1
src/Culling/babylon.boundingInfo.ts

@@ -22,7 +22,12 @@
         return extentsOverlap(result0.min, result0.max, result1.min, result1.max);
         return extentsOverlap(result0.min, result0.max, result1.min, result1.max);
     }
     }
 
 
-    export class BoundingInfo {
+    export interface ICullable {
+        isInFrustum(frustumPlanes: Plane[]): boolean;
+        isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
+    }
+
+    export class BoundingInfo implements ICullable {
         public boundingBox: BoundingBox;
         public boundingBox: BoundingBox;
         public boundingSphere: BoundingSphere;
         public boundingSphere: BoundingSphere;
 
 

+ 1 - 1
src/Materials/Textures/babylon.renderTargetTexture.js

@@ -234,7 +234,7 @@ var BABYLON;
                         mesh._activate(sceneRenderId);
                         mesh._activate(sceneRenderId);
                         for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
                         for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
                             var subMesh = mesh.subMeshes[subIndex];
                             var subMesh = mesh.subMeshes[subIndex];
-                            scene._activeIndices += subMesh.indexCount;
+                            scene._activeIndices.addCount(subMesh.indexCount, false);
                             this._renderingManager.dispatch(subMesh);
                             this._renderingManager.dispatch(subMesh);
                         }
                         }
                     }
                     }

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

@@ -254,7 +254,7 @@
 
 
                         for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
                         for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
                             var subMesh = mesh.subMeshes[subIndex];
                             var subMesh = mesh.subMeshes[subIndex];
-                            scene._activeIndices += subMesh.indexCount;
+                            scene._activeIndices.addCount(subMesh.indexCount, false);
                             this._renderingManager.dispatch(subMesh);
                             this._renderingManager.dispatch(subMesh);
                         }
                         }
                     }
                     }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1050 - 1055
src/Mesh/babylon.abstractMesh.js


+ 6 - 19
src/Mesh/babylon.abstractMesh.ts

@@ -1,5 +1,5 @@
 module BABYLON {
 module BABYLON {
-    export class AbstractMesh extends Node implements IDisposable {
+    export class AbstractMesh extends Node implements IDisposable, ICullable {
         // Statics
         // Statics
         private static _BILLBOARDMODE_NONE = 0;
         private static _BILLBOARDMODE_NONE = 0;
         private static _BILLBOARDMODE_X = 1;
         private static _BILLBOARDMODE_X = 1;
@@ -746,7 +746,7 @@
         }
         }
 
 
         private static _lookAtVectorCache = new Vector3(0,0,0);
         private static _lookAtVectorCache = new Vector3(0,0,0);
-        public lookAt(targetPoint: Vector3, yawCor: number, pitchCor: number, rollCor: number): void {
+        public lookAt(targetPoint: Vector3, yawCor: number = 0, pitchCor: number = 0, rollCor: number = 0, space: Space = Space.LOCAL): void {
             /// <summary>Orients a mesh towards a target point. Mesh must be drawn facing user.</summary>
             /// <summary>Orients a mesh towards a target point. Mesh must be drawn facing user.</summary>
             /// <param name="targetPoint" type="Vector3">The position (must be in same space as current mesh) to look at</param>
             /// <param name="targetPoint" type="Vector3">The position (must be in same space as current mesh) to look at</param>
             /// <param name="yawCor" type="Number">optional yaw (y-axis) correction in radians</param>
             /// <param name="yawCor" type="Number">optional yaw (y-axis) correction in radians</param>
@@ -754,12 +754,9 @@
             /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param>
             /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param>
             /// <returns>Mesh oriented towards targetMesh</returns>
             /// <returns>Mesh oriented towards targetMesh</returns>
 
 
-            yawCor = yawCor || 0; // default to zero if undefined
-            pitchCor = pitchCor || 0;
-            rollCor = rollCor || 0;
-
             var dv = AbstractMesh._lookAtVectorCache;
             var dv = AbstractMesh._lookAtVectorCache;
-            targetPoint.subtractToRef(this.position, dv);
+            var pos = space === Space.LOCAL ? this.position : this.getAbsolutePosition();
+            targetPoint.subtractToRef(pos, dv);
             var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
             var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
             var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
             var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
             var pitch = Math.atan2(dv.y, len);
             var pitch = Math.atan2(dv.y, len);
@@ -789,18 +786,8 @@
             return this._boundingInfo.isInFrustum(frustumPlanes);
             return this._boundingInfo.isInFrustum(frustumPlanes);
         }
         }
 
 
-        public isCompletelyInFrustum(camera?: Camera): boolean {
-            if (!camera) {
-                camera = this.getScene().activeCamera;
-            }
-
-            var transformMatrix = camera.getViewMatrix().multiply(camera.getProjectionMatrix());
-
-            if (!this._boundingInfo.isCompletelyInFrustum(Frustum.GetPlanes(transformMatrix))) {
-                return false;
-            }
-
-            return true;
+        public isCompletelyInFrustum(frustumPlanes: Plane[]): boolean {
+            return this._boundingInfo.isCompletelyInFrustum(frustumPlanes);;
         }
         }
 
 
         public intersectsMesh(mesh: AbstractMesh, precise?: boolean): boolean {
         public intersectsMesh(mesh: AbstractMesh, precise?: boolean): boolean {

+ 5 - 8
src/Mesh/babylon.mesh.js

@@ -718,7 +718,7 @@ var BABYLON;
         /**
         /**
          * Sets the mesh indices.
          * Sets the mesh indices.
          * Expects an array populated with integers or a Int32Array.
          * Expects an array populated with integers or a Int32Array.
-         * If the mesh has no geometry, a new `Geometry` object is created and set to the mesh.
+         * If the mesh has no geometry, a new Geometry object is created and set to the mesh.
          * This method creates a new index buffer each call.
          * This method creates a new index buffer each call.
          */
          */
         Mesh.prototype.setIndices = function (indices, totalVertices) {
         Mesh.prototype.setIndices = function (indices, totalVertices) {
@@ -1170,7 +1170,7 @@ var BABYLON;
          * Returns a new Mesh object generated from the current mesh properties.
          * Returns a new Mesh object generated from the current mesh properties.
          * This method must not get confused with createInstance().
          * This method must not get confused with createInstance().
          * The parameter `name` is a string, the name given to the new mesh.
          * The parameter `name` is a string, the name given to the new mesh.
-         * The optional parameter `newParent` can be any `Node` object (default `null`).
+         * The optional parameter `newParent` can be any Node object (default `null`).
          * The optional parameter `doNotCloneChildren` (default `false`) allows/denies the recursive cloning of the original mesh children if any.
          * The optional parameter `doNotCloneChildren` (default `false`) allows/denies the recursive cloning of the original mesh children if any.
          * The parameter `clonePhysicsImpostor` (default `true`)  allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any.
          * The parameter `clonePhysicsImpostor` (default `true`)  allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any.
          */
          */
@@ -1970,7 +1970,6 @@ var BABYLON;
          * Creates lathe mesh.
          * Creates lathe mesh.
          * The lathe is a shape with a symetry axis : a 2D model shape is rotated around this axis to design the lathe.
          * The lathe is a shape with a symetry axis : a 2D model shape is rotated around this axis to design the lathe.
          * Please consider using the same method from the MeshBuilder class instead.
          * Please consider using the same method from the MeshBuilder class instead.
-         *
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be rotated in its local space : the shape must be designed in the xOy plane and will be
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be rotated in its local space : the shape must be designed in the xOy plane and will be
          * rotated around the Y axis. It's usually a 2D shape, so the Vector3 z coordinates are often set to zero.
          * rotated around the Y axis. It's usually a 2D shape, so the Vector3 z coordinates are often set to zero.
          * The parameter `radius` (positive float, default 1) is the radius value of the lathe.
          * The parameter `radius` (positive float, default 1) is the radius value of the lathe.
@@ -2078,7 +2077,6 @@ var BABYLON;
         /**
         /**
          * Creates a tube mesh.
          * Creates a tube mesh.
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.
-         *
          * Please consider using the same method from the MeshBuilder class instead.
          * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.
          * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.
          * The parameter `radius` (positive float, default 1) sets the tube radius size.
          * The parameter `radius` (positive float, default 1) sets the tube radius size.
@@ -2113,7 +2111,6 @@ var BABYLON;
         };
         };
         /**
         /**
          * Creates a polyhedron mesh.
          * Creates a polyhedron mesh.
-         *
          * Please consider using the same method from the MeshBuilder class instead.
          * Please consider using the same method from the MeshBuilder class instead.
          * The parameter `type` (positive integer, max 14, default 0) sets the polyhedron type to build among the 15 embbeded types. Please refer to the type sheet in the tutorial
          * The parameter `type` (positive integer, max 14, default 0) sets the polyhedron type to build among the 15 embbeded types. Please refer to the type sheet in the tutorial
          *  to choose the wanted type.
          *  to choose the wanted type.
@@ -2150,7 +2147,7 @@ var BABYLON;
          * Please consider using the same method from the MeshBuilder class instead.
          * Please consider using the same method from the MeshBuilder class instead.
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.
          * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.
          * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.
-         * The parameter `normal` (Vector3, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.
+         * The parameter `normal` (Vector3, default Vector3.Up) sets the normal of the mesh where the decal is applied onto in World coordinates.
          * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.
          * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.
          */
          */
@@ -2277,7 +2274,7 @@ var BABYLON;
         };
         };
         // Tools
         // Tools
         /**
         /**
-         * Returns an object `{min: Vector3, max: Vector3}`
+         * Returns an object `{min:` Vector3`, max:` Vector3`}`
          * This min and max Vector3 are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
          * This min and max Vector3 are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
          */
          */
         Mesh.MinMax = function (meshes) {
         Mesh.MinMax = function (meshes) {
@@ -2300,7 +2297,7 @@ var BABYLON;
             };
             };
         };
         };
         /**
         /**
-         * Returns a `Vector3`, the center of the `{min: Vector3, max: Vector3}` or the center of MinMax vector3 computed from a mesh array.
+         * Returns a Vector3, the center of the `{min:` Vector3`, max:` Vector3`}` or the center of MinMax vector3 computed from a mesh array.
          */
          */
         Mesh.Center = function (meshesOrMinMaxVector) {
         Mesh.Center = function (meshesOrMinMaxVector) {
             var minMaxVector = meshesOrMinMaxVector.min !== undefined ? meshesOrMinMaxVector : Mesh.MinMax(meshesOrMinMaxVector);
             var minMaxVector = meshesOrMinMaxVector.min !== undefined ? meshesOrMinMaxVector : Mesh.MinMax(meshesOrMinMaxVector);

+ 5 - 8
src/Mesh/babylon.mesh.ts

@@ -762,7 +762,7 @@
         /**
         /**
          * Sets the mesh indices.  
          * Sets the mesh indices.  
          * Expects an array populated with integers or a Int32Array.
          * Expects an array populated with integers or a Int32Array.
-         * If the mesh has no geometry, a new `Geometry` object is created and set to the mesh. 
+         * If the mesh has no geometry, a new Geometry object is created and set to the mesh. 
          * This method creates a new index buffer each call.
          * This method creates a new index buffer each call.
          */
          */
         public setIndices(indices: number[] | Int32Array, totalVertices?: number): void {
         public setIndices(indices: number[] | Int32Array, totalVertices?: number): void {
@@ -1309,7 +1309,7 @@
          * Returns a new Mesh object generated from the current mesh properties.
          * Returns a new Mesh object generated from the current mesh properties.
          * This method must not get confused with createInstance().  
          * This method must not get confused with createInstance().  
          * The parameter `name` is a string, the name given to the new mesh. 
          * The parameter `name` is a string, the name given to the new mesh. 
-         * The optional parameter `newParent` can be any `Node` object (default `null`).  
+         * The optional parameter `newParent` can be any Node object (default `null`).  
          * The optional parameter `doNotCloneChildren` (default `false`) allows/denies the recursive cloning of the original mesh children if any.
          * The optional parameter `doNotCloneChildren` (default `false`) allows/denies the recursive cloning of the original mesh children if any.
          * The parameter `clonePhysicsImpostor` (default `true`)  allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any. 
          * The parameter `clonePhysicsImpostor` (default `true`)  allows/denies the cloning in the same time of the original mesh `body` used by the physics engine, if any. 
          */
          */
@@ -2223,7 +2223,6 @@
          * Creates lathe mesh.  
          * Creates lathe mesh.  
          * The lathe is a shape with a symetry axis : a 2D model shape is rotated around this axis to design the lathe.      
          * The lathe is a shape with a symetry axis : a 2D model shape is rotated around this axis to design the lathe.      
          * Please consider using the same method from the MeshBuilder class instead.    
          * Please consider using the same method from the MeshBuilder class instead.    
-         *
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be rotated in its local space : the shape must be designed in the xOy plane and will be
          * The parameter `shape` is a required array of successive Vector3. This array depicts the shape to be rotated in its local space : the shape must be designed in the xOy plane and will be
          * rotated around the Y axis. It's usually a 2D shape, so the Vector3 z coordinates are often set to zero.    
          * rotated around the Y axis. It's usually a 2D shape, so the Vector3 z coordinates are often set to zero.    
          * The parameter `radius` (positive float, default 1) is the radius value of the lathe.        
          * The parameter `radius` (positive float, default 1) is the radius value of the lathe.        
@@ -2337,7 +2336,6 @@
         /**
         /**
          * Creates a tube mesh.    
          * Creates a tube mesh.    
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.    
          * The tube is a parametric shape :  http://doc.babylonjs.com/tutorials/Parametric_Shapes.  It has no predefined shape. Its final shape will depend on the input parameters.    
-         *
          * Please consider using the same method from the MeshBuilder class instead.    
          * Please consider using the same method from the MeshBuilder class instead.    
          * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.        
          * The parameter `path` is a required array of successive Vector3. It is the curve used as the axis of the tube.        
          * The parameter `radius` (positive float, default 1) sets the tube radius size.    
          * The parameter `radius` (positive float, default 1) sets the tube radius size.    
@@ -2372,7 +2370,6 @@
         }
         }
         /**
         /**
          * Creates a polyhedron mesh.  
          * Creates a polyhedron mesh.  
-         * 
          * Please consider using the same method from the MeshBuilder class instead.    
          * Please consider using the same method from the MeshBuilder class instead.    
          * The parameter `type` (positive integer, max 14, default 0) sets the polyhedron type to build among the 15 embbeded types. Please refer to the type sheet in the tutorial
          * The parameter `type` (positive integer, max 14, default 0) sets the polyhedron type to build among the 15 embbeded types. Please refer to the type sheet in the tutorial
          *  to choose the wanted type.  
          *  to choose the wanted type.  
@@ -2410,7 +2407,7 @@
          * Please consider using the same method from the MeshBuilder class instead.    
          * Please consider using the same method from the MeshBuilder class instead.    
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.  
          * A decal is a mesh usually applied as a model onto the surface of another mesh. So don't forget the parameter `sourceMesh` depicting the decal.  
          * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.  
          * The parameter `position` (Vector3, default `(0, 0, 0)`) sets the position of the decal in World coordinates.  
-         * The parameter `normal` (Vector3, default `Vector3.Up`) sets the normal of the mesh where the decal is applied onto in World coordinates.  
+         * The parameter `normal` (Vector3, default Vector3.Up) sets the normal of the mesh where the decal is applied onto in World coordinates.  
          * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.  
          * The parameter `size` (Vector3, default `(1, 1, 1)`) sets the decal scaling.  
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.  
          * The parameter `angle` (float in radian, default 0) sets the angle to rotate the decal.  
          */
          */
@@ -2561,7 +2558,7 @@
 
 
         // Tools
         // Tools
         /**
         /**
-         * Returns an object `{min: Vector3, max: Vector3}`
+         * Returns an object `{min:` Vector3`, max:` Vector3`}`
          * This min and max Vector3 are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
          * This min and max Vector3 are the minimum and maximum vectors of each mesh bounding box from the passed array, in the World system
          */
          */
         public static MinMax(meshes: AbstractMesh[]): { min: Vector3; max: Vector3 } {
         public static MinMax(meshes: AbstractMesh[]): { min: Vector3; max: Vector3 } {
@@ -2585,7 +2582,7 @@
             };
             };
         }
         }
         /**
         /**
-         * Returns a `Vector3`, the center of the `{min: Vector3, max: Vector3}` or the center of MinMax vector3 computed from a mesh array.
+         * Returns a Vector3, the center of the `{min:` Vector3`, max:` Vector3`}` or the center of MinMax vector3 computed from a mesh array.
          */
          */
         public static Center(meshesOrMinMaxVector): Vector3 {
         public static Center(meshesOrMinMaxVector): Vector3 {
             var minMaxVector = meshesOrMinMaxVector.min !== undefined ? meshesOrMinMaxVector : Mesh.MinMax(meshesOrMinMaxVector);
             var minMaxVector = meshesOrMinMaxVector.min !== undefined ? meshesOrMinMaxVector : Mesh.MinMax(meshesOrMinMaxVector);

+ 3 - 0
src/Mesh/babylon.subMesh.js

@@ -84,6 +84,9 @@ var BABYLON;
         SubMesh.prototype.isInFrustum = function (frustumPlanes) {
         SubMesh.prototype.isInFrustum = function (frustumPlanes) {
             return this.getBoundingInfo().isInFrustum(frustumPlanes);
             return this.getBoundingInfo().isInFrustum(frustumPlanes);
         };
         };
+        SubMesh.prototype.isCompletelyInFrustum = function (frustumPlanes) {
+            return this.getBoundingInfo().isCompletelyInFrustum(frustumPlanes);
+        };
         SubMesh.prototype.render = function (enableAlphaMode) {
         SubMesh.prototype.render = function (enableAlphaMode) {
             this._renderingMesh.render(this, enableAlphaMode);
             this._renderingMesh.render(this, enableAlphaMode);
         };
         };

+ 5 - 1
src/Mesh/babylon.subMesh.ts

@@ -1,5 +1,5 @@
 module BABYLON {
 module BABYLON {
-    export class SubMesh {
+    export class SubMesh implements ICullable {
         public linesIndexCount: number;
         public linesIndexCount: number;
 
 
         private _mesh: AbstractMesh;
         private _mesh: AbstractMesh;
@@ -107,6 +107,10 @@
             return this.getBoundingInfo().isInFrustum(frustumPlanes);
             return this.getBoundingInfo().isInFrustum(frustumPlanes);
         }
         }
 
 
+        public isCompletelyInFrustum(frustumPlanes: Plane[]): boolean {
+            return this.getBoundingInfo().isCompletelyInFrustum(frustumPlanes);
+        }
+
         public render(enableAlphaMode: boolean): void {
         public render(enableAlphaMode: boolean): void {
             this._renderingMesh.render(this, enableAlphaMode);
             this._renderingMesh.render(this, enableAlphaMode);
         }
         }

+ 5 - 5
src/Rendering/babylon.renderingManager.js

@@ -11,7 +11,7 @@ var BABYLON;
             }
             }
             // Particles
             // Particles
             var activeCamera = this._scene.activeCamera;
             var activeCamera = this._scene.activeCamera;
-            var beforeParticlesDate = BABYLON.Tools.Now;
+            this._scene._particlesDuration.beginMonitoring();
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
                 if (particleSystem.renderingGroupId !== index) {
                 if (particleSystem.renderingGroupId !== index) {
@@ -22,10 +22,10 @@ var BABYLON;
                 }
                 }
                 this._clearDepthBuffer();
                 this._clearDepthBuffer();
                 if (!particleSystem.emitter.position || !activeMeshes || activeMeshes.indexOf(particleSystem.emitter) !== -1) {
                 if (!particleSystem.emitter.position || !activeMeshes || activeMeshes.indexOf(particleSystem.emitter) !== -1) {
-                    this._scene._activeParticles += particleSystem.render();
+                    this._scene._activeParticles.addCount(particleSystem.render(), false);
                 }
                 }
             }
             }
-            this._scene._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
+            this._scene._particlesDuration.endMonitoring(false);
         };
         };
         RenderingManager.prototype._renderSprites = function (index) {
         RenderingManager.prototype._renderSprites = function (index) {
             if (!this._scene.spritesEnabled || this._scene.spriteManagers.length === 0) {
             if (!this._scene.spritesEnabled || this._scene.spriteManagers.length === 0) {
@@ -33,7 +33,7 @@ var BABYLON;
             }
             }
             // Sprites       
             // Sprites       
             var activeCamera = this._scene.activeCamera;
             var activeCamera = this._scene.activeCamera;
-            var beforeSpritessDate = BABYLON.Tools.Now;
+            this._scene._spritesDuration.beginMonitoring();
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
                 var spriteManager = this._scene.spriteManagers[id];
                 var spriteManager = this._scene.spriteManagers[id];
                 if (spriteManager.renderingGroupId === index && ((activeCamera.layerMask & spriteManager.layerMask) !== 0)) {
                 if (spriteManager.renderingGroupId === index && ((activeCamera.layerMask & spriteManager.layerMask) !== 0)) {
@@ -41,7 +41,7 @@ var BABYLON;
                     spriteManager.render();
                     spriteManager.render();
                 }
                 }
             }
             }
-            this._scene._spritesDuration += BABYLON.Tools.Now - beforeSpritessDate;
+            this._scene._spritesDuration.endMonitoring(false);
         };
         };
         RenderingManager.prototype._clearDepthBuffer = function () {
         RenderingManager.prototype._clearDepthBuffer = function () {
             if (this._depthBufferAlreadyCleaned) {
             if (this._depthBufferAlreadyCleaned) {

+ 5 - 5
src/Rendering/babylon.renderingManager.ts

@@ -22,7 +22,7 @@
 
 
             // Particles
             // Particles
             var activeCamera = this._scene.activeCamera;
             var activeCamera = this._scene.activeCamera;
-            var beforeParticlesDate = Tools.Now;
+            this._scene._particlesDuration.beginMonitoring();
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
 
 
@@ -37,10 +37,10 @@
                 this._clearDepthBuffer();
                 this._clearDepthBuffer();
 
 
                 if (!particleSystem.emitter.position || !activeMeshes || activeMeshes.indexOf(particleSystem.emitter) !== -1) {
                 if (!particleSystem.emitter.position || !activeMeshes || activeMeshes.indexOf(particleSystem.emitter) !== -1) {
-                    this._scene._activeParticles += particleSystem.render();
+                    this._scene._activeParticles.addCount(particleSystem.render(), false);
                 }
                 }
             }
             }
-            this._scene._particlesDuration += Tools.Now - beforeParticlesDate;
+            this._scene._particlesDuration.endMonitoring(false);
         }
         }
 
 
         private _renderSprites(index: number): void {
         private _renderSprites(index: number): void {
@@ -50,7 +50,7 @@
 
 
             // Sprites       
             // Sprites       
             var activeCamera = this._scene.activeCamera;
             var activeCamera = this._scene.activeCamera;
-            var beforeSpritessDate = Tools.Now;
+            this._scene._spritesDuration.beginMonitoring();
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
                 var spriteManager = this._scene.spriteManagers[id];
                 var spriteManager = this._scene.spriteManagers[id];
 
 
@@ -59,7 +59,7 @@
                     spriteManager.render();
                     spriteManager.render();
                 }
                 }
             }
             }
-            this._scene._spritesDuration += Tools.Now - beforeSpritessDate;
+            this._scene._spritesDuration.endMonitoring(false);
         }
         }
 
 
         private _clearDepthBuffer(): void {
         private _clearDepthBuffer(): void {

+ 3 - 1
src/Tools/babylon.dynamicFloatArray.js

@@ -225,7 +225,9 @@ var BABYLON;
                 // Small heuristic... We don't want to allocate totalElementCount right away because it may have 50 for 3 used elements, but on the other side we don't want to allocate just 3 when we just need 2, so double this value to give us some air to breath...
                 // Small heuristic... We don't want to allocate totalElementCount right away because it may have 50 for 3 used elements, but on the other side we don't want to allocate just 3 when we just need 2, so double this value to give us some air to breath...
                 var newCount = Math.min(this.totalElementCount, count * 2);
                 var newCount = Math.min(this.totalElementCount, count * 2);
                 this._sortTable = new Array(newCount);
                 this._sortTable = new Array(newCount);
-                this._sortedTable = new Array(newCount);
+            }
+            if (!this._sortTable || this._sortTable.length !== count) {
+                this._sortedTable = new Array(count);
             }
             }
             // Because, you know...
             // Because, you know...
             this.pack();
             this.pack();

+ 129 - 0
src/Tools/babylon.tools.js

@@ -914,6 +914,135 @@ var BABYLON;
     })();
     })();
     BABYLON.Tools = Tools;
     BABYLON.Tools = Tools;
     /**
     /**
+     * This class is used to track a performance counter which is number based.
+     * The user has access to many properties which give statistics of different nature
+     *
+     * The implementer can track two kinds of Performance Counter: time and count
+     * For time you can optionally call fetchNewFrame() to notify the start of a new frame to monitor, then call beginMonitoring() to start and endMonitoring() to record the lapsed time. endMonitoring takes a newFrame parameter for you to specify if the monitored time should be set for a new frame or accumulated to the current frame being monitored.
+     * For count you first have to call fetchNewFrame() to notify the start of a new frame to monitor, then call addCount() how many time required to increment the count value you monitor.
+     */
+    var PerfCounter = (function () {
+        function PerfCounter() {
+            this._startMonitoringTime = 0;
+            this._min = 0;
+            this._max = 0;
+            this._average = 0;
+            this._lastSecAverage = 0;
+            this._current = 0;
+            this._totalValueCount = 0;
+            this._totalAccumulated = 0;
+            this._lastSecAccumulated = 0;
+            this._lastSecTime = 0;
+            this._lastSecValueCount = 0;
+        }
+        Object.defineProperty(PerfCounter.prototype, "min", {
+            /**
+             * Returns the smallest value ever
+             */
+            get: function () {
+                return this._min;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PerfCounter.prototype, "max", {
+            /**
+             * Returns the biggest value ever
+             */
+            get: function () {
+                return this._max;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PerfCounter.prototype, "average", {
+            /**
+             * Returns the average value since the performance counter is running
+             */
+            get: function () {
+                return this._average;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PerfCounter.prototype, "lastSecAverage", {
+            /**
+             * Returns the average value of the last second the counter was monitored
+             */
+            get: function () {
+                return this._lastSecAverage;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(PerfCounter.prototype, "current", {
+            /**
+             * Returns the current value
+             */
+            get: function () {
+                return this._current;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * Call this method to start monitoring a new frame.
+         * This scenario is typically used when you accumulate monitoring time many times for a single frame, you call this method at the start of the frame, then beginMonitoring to start recording and endMonitoring(false) to accumulated the recorded time to the PerfCounter or addCount() to accumulate a monitored count.
+         */
+        PerfCounter.prototype.fetchNewFrame = function () {
+            this._totalValueCount++;
+            this._current = 0;
+        };
+        /**
+         * Call this method to monitor a count of something (e.g. mesh drawn in viewport count)
+         * @param newCount the count value to add to the monitored count
+         * @param fetchResult true when it's the last time in the frame you add to the counter and you wish to update the statistics properties (min/max/average), false if you only want to update statistics.
+         */
+        PerfCounter.prototype.addCount = function (newCount, fetchResult) {
+            this._current += newCount;
+            if (fetchResult) {
+                this._fetchResult();
+            }
+        };
+        /**
+         * Start monitoring this performance counter
+         */
+        PerfCounter.prototype.beginMonitoring = function () {
+            this._startMonitoringTime = Tools.Now;
+        };
+        /**
+         * Compute the time lapsed since the previous beginMonitoring() call.
+         * @param newFrame true by default to fetch the result and monitor a new frame, if false the time monitored will be added to the current frame counter
+         */
+        PerfCounter.prototype.endMonitoring = function (newFrame) {
+            if (newFrame === void 0) { newFrame = true; }
+            if (newFrame) {
+                this.fetchNewFrame();
+            }
+            var currentTime = Tools.Now;
+            this._current = currentTime - this._startMonitoringTime;
+            if (newFrame) {
+                this._fetchResult();
+            }
+        };
+        PerfCounter.prototype._fetchResult = function () {
+            this._totalAccumulated += this._current;
+            // Min/Max update
+            this._min = Math.min(this._min, this._current);
+            this._max = Math.max(this._max, this._current);
+            this._average = this._totalAccumulated / this._totalValueCount;
+            // Reset last sec?
+            if ((this._startMonitoringTime - this._lastSecTime) > 1000) {
+                this._lastSecAverage = this._lastSecAccumulated / this._lastSecValueCount;
+                this._lastSecTime = this._startMonitoringTime;
+                this._lastSecAccumulated = 0;
+                this._lastSecValueCount = 0;
+            }
+        };
+        return PerfCounter;
+    })();
+    BABYLON.PerfCounter = PerfCounter;
+    /**
      * Use this className as a decorator on a given class definition to add it a name.
      * Use this className as a decorator on a given class definition to add it a name.
      * You can then use the Tools.getClassName(obj) on an instance to retrieve its class name.
      * You can then use the Tools.getClassName(obj) on an instance to retrieve its class name.
      * This method is the only way to get it done in all cases, even if the .js file declaring the class is minified
      * This method is the only way to get it done in all cases, even if the .js file declaring the class is minified

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

@@ -1020,6 +1020,139 @@
     }
     }
 
 
     /**
     /**
+     * This class is used to track a performance counter which is number based.
+     * The user has access to many properties which give statistics of different nature
+     * 
+     * The implementer can track two kinds of Performance Counter: time and count
+     * For time you can optionally call fetchNewFrame() to notify the start of a new frame to monitor, then call beginMonitoring() to start and endMonitoring() to record the lapsed time. endMonitoring takes a newFrame parameter for you to specify if the monitored time should be set for a new frame or accumulated to the current frame being monitored.
+     * For count you first have to call fetchNewFrame() to notify the start of a new frame to monitor, then call addCount() how many time required to increment the count value you monitor.
+     */
+    export class PerfCounter {
+        /**
+         * Returns the smallest value ever
+         */
+        public get min(): number {
+            return this._min;
+        }
+
+        /**
+         * Returns the biggest value ever
+         */
+        public get max(): number {
+            return this._max;
+        }
+
+        /**
+         * Returns the average value since the performance counter is running
+         */
+        public get average(): number {
+            return this._average;
+        }
+
+        /**
+         * Returns the average value of the last second the counter was monitored
+         */
+        public get lastSecAverage(): number {
+            return this._lastSecAverage;
+        }
+
+        /**
+         * Returns the current value
+         */
+        public get current(): number {
+            return this._current;
+        }
+
+        constructor() {
+            this._startMonitoringTime = 0;
+            this._min                 = 0;
+            this._max                 = 0;
+            this._average             = 0;
+            this._lastSecAverage      = 0;
+            this._current             = 0;
+            this._totalValueCount     = 0;
+            this._totalAccumulated    = 0;
+            this._lastSecAccumulated  = 0;
+            this._lastSecTime         = 0;
+            this._lastSecValueCount   = 0;
+        }
+
+        /**
+         * Call this method to start monitoring a new frame.
+         * This scenario is typically used when you accumulate monitoring time many times for a single frame, you call this method at the start of the frame, then beginMonitoring to start recording and endMonitoring(false) to accumulated the recorded time to the PerfCounter or addCount() to accumulate a monitored count.
+         */
+        public fetchNewFrame() {
+            this._totalValueCount++;
+            this._current = 0;
+        }
+
+        /**
+         * Call this method to monitor a count of something (e.g. mesh drawn in viewport count)
+         * @param newCount the count value to add to the monitored count
+         * @param fetchResult true when it's the last time in the frame you add to the counter and you wish to update the statistics properties (min/max/average), false if you only want to update statistics.
+         */
+        public addCount(newCount: number, fetchResult: boolean) {
+            this._current += newCount;
+            if (fetchResult) {
+                this._fetchResult();
+            }
+        }
+
+        /**
+         * Start monitoring this performance counter
+         */
+        public beginMonitoring() {
+            this._startMonitoringTime = Tools.Now;
+        }
+
+        /**
+         * Compute the time lapsed since the previous beginMonitoring() call.
+         * @param newFrame true by default to fetch the result and monitor a new frame, if false the time monitored will be added to the current frame counter
+         */
+        public endMonitoring(newFrame: boolean = true) {
+            if (newFrame) {
+                this.fetchNewFrame();
+            }
+
+            let currentTime = Tools.Now;
+            this._current = currentTime - this._startMonitoringTime;
+
+            if (newFrame) {
+                this._fetchResult();
+            }
+        }
+
+        private _fetchResult() {
+            this._totalAccumulated += this._current;
+
+            // Min/Max update
+            this._min = Math.min(this._min, this._current);
+            this._max = Math.max(this._max, this._current);
+            this._average = this._totalAccumulated / this._totalValueCount;
+
+            // Reset last sec?
+            if ((this._startMonitoringTime - this._lastSecTime) > 1000) {
+                this._lastSecAverage = this._lastSecAccumulated / this._lastSecValueCount;
+                this._lastSecTime = this._startMonitoringTime;
+                this._lastSecAccumulated = 0;
+                this._lastSecValueCount = 0;
+            }
+        }
+
+        private _startMonitoringTime: number;
+        private _min: number;
+        private _max: number;
+        private _average: number;
+        private _current: number;
+        private _totalValueCount: number;
+        private _totalAccumulated: number;
+        private _lastSecAverage: number;
+        private _lastSecAccumulated: number;
+        private _lastSecTime: number;
+        private _lastSecValueCount: number;
+    }
+
+    /**
      * Use this className as a decorator on a given class definition to add it a name.
      * Use this className as a decorator on a given class definition to add it a name.
      * You can then use the Tools.getClassName(obj) on an instance to retrieve its class name.
      * You can then use the Tools.getClassName(obj) on an instance to retrieve its class name.
      * This method is the only way to get it done in all cases, even if the .js file declaring the class is minified
      * This method is the only way to get it done in all cases, even if the .js file declaring the class is minified

+ 11 - 8
src/babylon.engine.js

@@ -134,7 +134,7 @@ var BABYLON;
             this.scenes = new Array();
             this.scenes = new Array();
             this._windowIsBackground = false;
             this._windowIsBackground = false;
             this._webGLVersion = "1.0";
             this._webGLVersion = "1.0";
-            this._drawCalls = 0;
+            this._drawCalls = new BABYLON.PerfCounter();
             this._renderingQueueLaunched = false;
             this._renderingQueueLaunched = false;
             this._activeRenderLoops = [];
             this._activeRenderLoops = [];
             // FPS
             // FPS
@@ -481,15 +481,18 @@ var BABYLON;
         };
         };
         Object.defineProperty(Engine.prototype, "drawCalls", {
         Object.defineProperty(Engine.prototype, "drawCalls", {
             get: function () {
             get: function () {
+                return this._drawCalls.current;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Engine.prototype, "drawCallsPerfCounter", {
+            get: function () {
                 return this._drawCalls;
                 return this._drawCalls;
             },
             },
             enumerable: true,
             enumerable: true,
             configurable: true
             configurable: true
         });
         });
-        // Methods
-        Engine.prototype.resetDrawCalls = function () {
-            this._drawCalls = 0;
-        };
         Engine.prototype.getDepthFunction = function () {
         Engine.prototype.getDepthFunction = function () {
             return this._depthCullingState.depthFunc;
             return this._depthCullingState.depthFunc;
         };
         };
@@ -978,7 +981,7 @@ var BABYLON;
         Engine.prototype.draw = function (useTriangles, indexStart, indexCount, instancesCount) {
         Engine.prototype.draw = function (useTriangles, indexStart, indexCount, instancesCount) {
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             // Render
             // Render
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
@@ -991,7 +994,7 @@ var BABYLON;
         Engine.prototype.drawPointClouds = function (verticesStart, verticesCount, instancesCount) {
         Engine.prototype.drawPointClouds = function (verticesStart, verticesCount, instancesCount) {
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             if (instancesCount) {
             if (instancesCount) {
                 this._caps.instancedArrays.drawArraysInstancedANGLE(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
                 this._caps.instancedArrays.drawArraysInstancedANGLE(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
                 return;
                 return;
@@ -1001,7 +1004,7 @@ var BABYLON;
         Engine.prototype.drawUnIndexed = function (useTriangles, verticesStart, verticesCount, instancesCount) {
         Engine.prototype.drawUnIndexed = function (useTriangles, verticesStart, verticesCount, instancesCount) {
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             if (instancesCount) {
             if (instancesCount) {
                 this._caps.instancedArrays.drawArraysInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, verticesStart, verticesCount, instancesCount);
                 this._caps.instancedArrays.drawArraysInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, verticesStart, verticesCount, instancesCount);
                 return;
                 return;

+ 7 - 8
src/babylon.engine.ts

@@ -312,7 +312,7 @@
 
 
         private _loadingScreen: ILoadingScreen;
         private _loadingScreen: ILoadingScreen;
 
 
-        private _drawCalls = 0;
+        public  _drawCalls = new PerfCounter();
 
 
         private _glVersion: string;
         private _glVersion: string;
         private _glRenderer: string;
         private _glRenderer: string;
@@ -595,12 +595,11 @@
         }
         }
 
 
         public get drawCalls(): number {
         public get drawCalls(): number {
-            return this._drawCalls;
+            return this._drawCalls.current;
         }
         }
 
 
-        // Methods
-        public resetDrawCalls(): void {
-            this._drawCalls = 0;
+        public get drawCallsPerfCounter(): PerfCounter {
+            return this._drawCalls;
         }
         }
 
 
         public getDepthFunction(): number {
         public getDepthFunction(): number {
@@ -1165,7 +1164,7 @@
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
 
 
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             // Render
             // Render
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
@@ -1180,7 +1179,7 @@
         public drawPointClouds(verticesStart: number, verticesCount: number, instancesCount?: number): void {
         public drawPointClouds(verticesStart: number, verticesCount: number, instancesCount?: number): void {
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
 
 
             if (instancesCount) {
             if (instancesCount) {
                 this._caps.instancedArrays.drawArraysInstancedANGLE(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
                 this._caps.instancedArrays.drawArraysInstancedANGLE(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
@@ -1193,7 +1192,7 @@
         public drawUnIndexed(useTriangles: boolean, verticesStart: number, verticesCount: number, instancesCount?: number): void {
         public drawUnIndexed(useTriangles: boolean, verticesStart: number, verticesCount: number, instancesCount?: number): void {
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
 
 
             if (instancesCount) {
             if (instancesCount) {
                 this._caps.instancedArrays.drawArraysInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, verticesStart, verticesCount, instancesCount);
                 this._caps.instancedArrays.drawArraysInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, verticesStart, verticesCount, instancesCount);

+ 132 - 49
src/babylon.scene.js

@@ -277,16 +277,22 @@ var BABYLON;
             this.soundTracks = new Array();
             this.soundTracks = new Array();
             this._audioEnabled = true;
             this._audioEnabled = true;
             this._headphone = false;
             this._headphone = false;
-            this._totalVertices = 0;
-            this._activeIndices = 0;
-            this._activeParticles = 0;
-            this._lastFrameDuration = 0;
-            this._evaluateActiveMeshesDuration = 0;
-            this._renderTargetsDuration = 0;
-            this._particlesDuration = 0;
-            this._renderDuration = 0;
-            this._spritesDuration = 0;
-            this._animationRatio = 0;
+            // Performance counters
+            this._totalMeshesCounter = new BABYLON.PerfCounter();
+            this._totalLightsCounter = new BABYLON.PerfCounter();
+            this._totalMaterialsCounter = new BABYLON.PerfCounter();
+            this._totalTexturesCounter = new BABYLON.PerfCounter();
+            this._totalVertices = new BABYLON.PerfCounter();
+            this._activeIndices = new BABYLON.PerfCounter();
+            this._activeParticles = new BABYLON.PerfCounter();
+            this._lastFrameDuration = new BABYLON.PerfCounter();
+            this._evaluateActiveMeshesDuration = new BABYLON.PerfCounter();
+            this._renderTargetsDuration = new BABYLON.PerfCounter();
+            this._particlesDuration = new BABYLON.PerfCounter();
+            this._renderDuration = new BABYLON.PerfCounter();
+            this._spritesDuration = new BABYLON.PerfCounter();
+            this._animationRatio = new BABYLON.PerfCounter();
+            this._activeBones = new BABYLON.PerfCounter();
             this._renderId = 0;
             this._renderId = 0;
             this._executeWhenReadyTimeoutId = -1;
             this._executeWhenReadyTimeoutId = -1;
             this._intermediateRendering = false;
             this._intermediateRendering = false;
@@ -298,7 +304,6 @@ var BABYLON;
             this._activeParticleSystems = new BABYLON.SmartArray(256);
             this._activeParticleSystems = new BABYLON.SmartArray(256);
             this._activeSkeletons = new BABYLON.SmartArray(32);
             this._activeSkeletons = new BABYLON.SmartArray(32);
             this._softwareSkinnedMeshes = new BABYLON.SmartArray(32);
             this._softwareSkinnedMeshes = new BABYLON.SmartArray(32);
-            this._activeBones = 0;
             this._activeAnimatables = new Array();
             this._activeAnimatables = new Array();
             this._transformMatrix = BABYLON.Matrix.Zero();
             this._transformMatrix = BABYLON.Matrix.Zero();
             this._edgesRenderers = new BABYLON.SmartArray(16);
             this._edgesRenderers = new BABYLON.SmartArray(16);
@@ -488,40 +493,106 @@ var BABYLON;
             return this._engine;
             return this._engine;
         };
         };
         Scene.prototype.getTotalVertices = function () {
         Scene.prototype.getTotalVertices = function () {
-            return this._totalVertices;
+            return this._totalVertices.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "totalVerticesPerfCounter", {
+            get: function () {
+                return this._totalVertices;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getActiveIndices = function () {
         Scene.prototype.getActiveIndices = function () {
-            return this._activeIndices;
+            return this._activeIndices.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "totalActiveIndicesPerfCounter", {
+            get: function () {
+                return this._activeIndices;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getActiveParticles = function () {
         Scene.prototype.getActiveParticles = function () {
-            return this._activeParticles;
+            return this._activeParticles.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "activeParticlesPerfCounter", {
+            get: function () {
+                return this._activeParticles;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getActiveBones = function () {
         Scene.prototype.getActiveBones = function () {
-            return this._activeBones;
+            return this._activeBones.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "activeBonesPerfCounter", {
+            get: function () {
+                return this._activeBones;
+            },
+            enumerable: true,
+            configurable: true
+        });
         // Stats
         // Stats
         Scene.prototype.getLastFrameDuration = function () {
         Scene.prototype.getLastFrameDuration = function () {
-            return this._lastFrameDuration;
+            return this._lastFrameDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "lastFramePerfCounter", {
+            get: function () {
+                return this._lastFrameDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getEvaluateActiveMeshesDuration = function () {
         Scene.prototype.getEvaluateActiveMeshesDuration = function () {
-            return this._evaluateActiveMeshesDuration;
+            return this._evaluateActiveMeshesDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "evaluateActiveMeshesDurationPerfCounter", {
+            get: function () {
+                return this._evaluateActiveMeshesDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getActiveMeshes = function () {
         Scene.prototype.getActiveMeshes = function () {
             return this._activeMeshes;
             return this._activeMeshes;
         };
         };
         Scene.prototype.getRenderTargetsDuration = function () {
         Scene.prototype.getRenderTargetsDuration = function () {
-            return this._renderTargetsDuration;
+            return this._renderTargetsDuration.current;
         };
         };
         Scene.prototype.getRenderDuration = function () {
         Scene.prototype.getRenderDuration = function () {
-            return this._renderDuration;
+            return this._renderDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "renderDurationPerfCounter", {
+            get: function () {
+                return this._renderDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getParticlesDuration = function () {
         Scene.prototype.getParticlesDuration = function () {
-            return this._particlesDuration;
+            return this._particlesDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "particlesDurationPerfCounter", {
+            get: function () {
+                return this._particlesDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getSpritesDuration = function () {
         Scene.prototype.getSpritesDuration = function () {
-            return this._spritesDuration;
+            return this._spritesDuration.current;
         };
         };
+        Object.defineProperty(Scene.prototype, "spriteDuractionPerfCounter", {
+            get: function () {
+                return this._spritesDuration;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Scene.prototype.getAnimationRatio = function () {
         Scene.prototype.getAnimationRatio = function () {
+            return this._animationRatio.current;
+        };
+        Scene.prototype.animationRatioPerfCounter = function () {
             return this._animationRatio;
             return this._animationRatio;
         };
         };
         Scene.prototype.getRenderId = function () {
         Scene.prototype.getRenderId = function () {
@@ -983,6 +1054,13 @@ var BABYLON;
             this._viewMatrix = view;
             this._viewMatrix = view;
             this._projectionMatrix = projection;
             this._projectionMatrix = projection;
             this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
             this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
+            // Update frustum
+            if (!this._frustumPlanes) {
+                this._frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
+            }
+            else {
+                BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
+            }
         };
         };
         // Methods
         // Methods
         Scene.prototype.addMesh = function (newMesh) {
         Scene.prototype.addMesh = function (newMesh) {
@@ -1467,7 +1545,7 @@ var BABYLON;
                         }
                         }
                     }
                     }
                     // Dispatch
                     // Dispatch
-                    this._activeIndices += subMesh.indexCount;
+                    this._activeIndices.addCount(subMesh.indexCount, false);
                     this._renderingManager.dispatch(subMesh);
                     this._renderingManager.dispatch(subMesh);
                 }
                 }
             }
             }
@@ -1485,12 +1563,6 @@ var BABYLON;
             this._softwareSkinnedMeshes.reset();
             this._softwareSkinnedMeshes.reset();
             this._boundingBoxRenderer.reset();
             this._boundingBoxRenderer.reset();
             this._edgesRenderers.reset();
             this._edgesRenderers.reset();
-            if (!this._frustumPlanes) {
-                this._frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
-            }
-            else {
-                BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
-            }
             // Meshes
             // Meshes
             var meshes;
             var meshes;
             var len;
             var len;
@@ -1508,7 +1580,7 @@ var BABYLON;
                 if (mesh.isBlocked) {
                 if (mesh.isBlocked) {
                     continue;
                     continue;
                 }
                 }
-                this._totalVertices += mesh.getTotalVertices();
+                this._totalVertices.addCount(mesh.getTotalVertices(), false);
                 if (!mesh.isReady() || !mesh.isEnabled()) {
                 if (!mesh.isReady() || !mesh.isEnabled()) {
                     continue;
                     continue;
                 }
                 }
@@ -1531,6 +1603,7 @@ var BABYLON;
                 }
                 }
             }
             }
             // Particle systems
             // Particle systems
+            this._particlesDuration.beginMonitoring();
             var beforeParticlesDate = BABYLON.Tools.Now;
             var beforeParticlesDate = BABYLON.Tools.Now;
             if (this.particlesEnabled) {
             if (this.particlesEnabled) {
                 BABYLON.Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
                 BABYLON.Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
@@ -1546,7 +1619,7 @@ var BABYLON;
                 }
                 }
                 BABYLON.Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
                 BABYLON.Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
             }
             }
-            this._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
+            this._particlesDuration.endMonitoring(false);
         };
         };
         Scene.prototype._activeMesh = function (mesh) {
         Scene.prototype._activeMesh = function (mesh) {
             if (mesh.skeleton && this.skeletonsEnabled) {
             if (mesh.skeleton && this.skeletonsEnabled) {
@@ -1587,6 +1660,7 @@ var BABYLON;
         };
         };
         Scene.prototype._renderForCamera = function (camera) {
         Scene.prototype._renderForCamera = function (camera) {
             var engine = this._engine;
             var engine = this._engine;
+            var startTime = BABYLON.Tools.Now;
             this.activeCamera = camera;
             this.activeCamera = camera;
             if (!this.activeCamera)
             if (!this.activeCamera)
                 throw new Error("Active camera not set");
                 throw new Error("Active camera not set");
@@ -1599,10 +1673,10 @@ var BABYLON;
             this.updateTransformMatrix();
             this.updateTransformMatrix();
             this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
             this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
             // Meshes
             // Meshes
-            var beforeEvaluateActiveMeshesDate = BABYLON.Tools.Now;
+            this._evaluateActiveMeshesDuration.beginMonitoring();
             BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
             BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
             this._evaluateActiveMeshes();
             this._evaluateActiveMeshes();
-            this._evaluateActiveMeshesDuration += BABYLON.Tools.Now - beforeEvaluateActiveMeshesDate;
+            this._evaluateActiveMeshesDuration.endMonitoring(false);
             BABYLON.Tools.EndPerformanceCounter("Active meshes evaluation");
             BABYLON.Tools.EndPerformanceCounter("Active meshes evaluation");
             // Software skinning
             // Software skinning
             for (var softwareSkinnedMeshIndex = 0; softwareSkinnedMeshIndex < this._softwareSkinnedMeshes.length; softwareSkinnedMeshIndex++) {
             for (var softwareSkinnedMeshIndex = 0; softwareSkinnedMeshIndex < this._softwareSkinnedMeshes.length; softwareSkinnedMeshIndex++) {
@@ -1610,6 +1684,7 @@ var BABYLON;
                 mesh.applySkeleton(mesh.skeleton);
                 mesh.applySkeleton(mesh.skeleton);
             }
             }
             // Render targets
             // Render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
                 this._intermediateRendering = true;
                 this._intermediateRendering = true;
@@ -1627,10 +1702,10 @@ var BABYLON;
                 this._renderId++;
                 this._renderId++;
                 engine.restoreDefaultFramebuffer(); // Restore back buffer
                 engine.restoreDefaultFramebuffer(); // Restore back buffer
             }
             }
-            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring(false);
             // Prepare Frame
             // Prepare Frame
             this.postProcessManager._prepareFrame();
             this.postProcessManager._prepareFrame();
-            var beforeRenderDate = BABYLON.Tools.Now;
+            this._renderDuration.beginMonitoring();
             // Backgrounds
             // Backgrounds
             var layerIndex;
             var layerIndex;
             var layer;
             var layer;
@@ -1676,7 +1751,7 @@ var BABYLON;
                 }
                 }
                 engine.setDepthBuffer(true);
                 engine.setDepthBuffer(true);
             }
             }
-            this._renderDuration += BABYLON.Tools.Now - beforeRenderDate;
+            this._renderDuration.endMonitoring(false);
             // Finalize frame
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
             // Update camera
             // Update camera
@@ -1735,17 +1810,17 @@ var BABYLON;
             }
             }
         };
         };
         Scene.prototype.render = function () {
         Scene.prototype.render = function () {
-            var startDate = BABYLON.Tools.Now;
-            this._particlesDuration = 0;
-            this._spritesDuration = 0;
-            this._activeParticles = 0;
-            this._renderDuration = 0;
-            this._renderTargetsDuration = 0;
-            this._evaluateActiveMeshesDuration = 0;
-            this._totalVertices = 0;
-            this._activeIndices = 0;
-            this._activeBones = 0;
-            this.getEngine().resetDrawCalls();
+            this._lastFrameDuration.beginMonitoring();
+            this._particlesDuration.fetchNewFrame();
+            this._spritesDuration.fetchNewFrame();
+            this._activeParticles.fetchNewFrame();
+            this._renderDuration.fetchNewFrame();
+            this._renderTargetsDuration.fetchNewFrame();
+            this._evaluateActiveMeshesDuration.fetchNewFrame();
+            this._totalVertices.fetchNewFrame();
+            this._activeIndices.fetchNewFrame();
+            this._activeBones.fetchNewFrame();
+            this.getEngine().drawCallsPerfCounter.fetchNewFrame();
             this._meshesForIntersections.reset();
             this._meshesForIntersections.reset();
             this.resetCachedMaterial();
             this.resetCachedMaterial();
             BABYLON.Tools.StartPerformanceCounter("Scene rendering");
             BABYLON.Tools.StartPerformanceCounter("Scene rendering");
@@ -1759,7 +1834,7 @@ var BABYLON;
             }
             }
             // Animations
             // Animations
             var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
             var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
-            this._animationRatio = deltaTime * (60.0 / 1000.0);
+            this._animationRatio.addCount(deltaTime * (60.0 / 1000.0), true);
             this._animate();
             this._animate();
             // Physics
             // Physics
             if (this._physicsEngine) {
             if (this._physicsEngine) {
@@ -1770,6 +1845,7 @@ var BABYLON;
             // Before render
             // Before render
             this.onBeforeRenderObservable.notifyObservers(this);
             this.onBeforeRenderObservable.notifyObservers(this);
             // Customs render targets
             // Customs render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var engine = this.getEngine();
             var engine = this.getEngine();
             var currentActiveCamera = this.activeCamera;
             var currentActiveCamera = this.activeCamera;
@@ -1795,7 +1871,7 @@ var BABYLON;
             if (this.customRenderTargets.length > 0) {
             if (this.customRenderTargets.length > 0) {
                 engine.restoreDefaultFramebuffer();
                 engine.restoreDefaultFramebuffer();
             }
             }
-            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring();
             this.activeCamera = currentActiveCamera;
             this.activeCamera = currentActiveCamera;
             // Procedural textures
             // Procedural textures
             if (this.proceduralTexturesEnabled) {
             if (this.proceduralTexturesEnabled) {
@@ -1864,7 +1940,14 @@ var BABYLON;
                 this.dumpNextRenderTargets = false;
                 this.dumpNextRenderTargets = false;
             }
             }
             BABYLON.Tools.EndPerformanceCounter("Scene rendering");
             BABYLON.Tools.EndPerformanceCounter("Scene rendering");
-            this._lastFrameDuration = BABYLON.Tools.Now - startDate;
+            this._lastFrameDuration.endMonitoring();
+            this._totalMeshesCounter.addCount(this.meshes.length, true);
+            this._totalLightsCounter.addCount(this.lights.length, true);
+            this._totalMaterialsCounter.addCount(this.materials.length, true);
+            this._totalTexturesCounter.addCount(this.textures.length, true);
+            this._activeBones.addCount(0, true);
+            this._activeIndices.addCount(0, true);
+            this._activeParticles.addCount(0, true);
         };
         };
         Scene.prototype._updateAudioParameters = function () {
         Scene.prototype._updateAudioParameters = function () {
             if (!this.audioEnabled || (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 1)) {
             if (!this.audioEnabled || (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 1)) {

+ 99 - 40
src/babylon.scene.ts

@@ -419,16 +419,24 @@
 
 
         // Private
         // Private
         private _engine: Engine;
         private _engine: Engine;
-        private _totalVertices = 0;
-        public _activeIndices = 0;
-        public _activeParticles = 0;
-        private _lastFrameDuration = 0;
-        private _evaluateActiveMeshesDuration = 0;
-        private _renderTargetsDuration = 0;
-        public _particlesDuration = 0;
-        private _renderDuration = 0;
-        public _spritesDuration = 0;
-        private _animationRatio = 0;
+
+        // Performance counters
+        private _totalMeshesCounter           = new PerfCounter();
+        private _totalLightsCounter           = new PerfCounter();
+        private _totalMaterialsCounter        = new PerfCounter();
+        private _totalTexturesCounter         = new PerfCounter();
+        private _totalVertices                = new PerfCounter();
+        public  _activeIndices                = new PerfCounter();
+        public  _activeParticles              = new PerfCounter();
+        private _lastFrameDuration            = new PerfCounter();
+        private _evaluateActiveMeshesDuration = new PerfCounter();
+        private _renderTargetsDuration        = new PerfCounter();
+        public  _particlesDuration            = new PerfCounter();
+        private _renderDuration               = new PerfCounter();
+        public  _spritesDuration              = new PerfCounter();
+        private _animationRatio               = new PerfCounter();
+        public  _activeBones                  = new PerfCounter();
+
         private _animationStartDate: number;
         private _animationStartDate: number;
         public _cachedMaterial: Material;
         public _cachedMaterial: Material;
 
 
@@ -445,7 +453,6 @@
         public _activeParticleSystems = new SmartArray<ParticleSystem>(256);
         public _activeParticleSystems = new SmartArray<ParticleSystem>(256);
         private _activeSkeletons = new SmartArray<Skeleton>(32);
         private _activeSkeletons = new SmartArray<Skeleton>(32);
         private _softwareSkinnedMeshes = new SmartArray<Mesh>(32);
         private _softwareSkinnedMeshes = new SmartArray<Mesh>(32);
-        public _activeBones = 0;
 
 
         private _renderingManager: RenderingManager;
         private _renderingManager: RenderingManager;
         private _physicsEngine: PhysicsEngine;
         private _physicsEngine: PhysicsEngine;
@@ -584,27 +591,51 @@
         }
         }
 
 
         public getTotalVertices(): number {
         public getTotalVertices(): number {
+            return this._totalVertices.current;
+        }
+
+        public get totalVerticesPerfCounter(): PerfCounter {
             return this._totalVertices;
             return this._totalVertices;
         }
         }
 
 
         public getActiveIndices(): number {
         public getActiveIndices(): number {
+            return this._activeIndices.current;
+        }
+
+        public get totalActiveIndicesPerfCounter(): PerfCounter {
             return this._activeIndices;
             return this._activeIndices;
         }
         }
 
 
         public getActiveParticles(): number {
         public getActiveParticles(): number {
+            return this._activeParticles.current;
+        }
+
+        public get activeParticlesPerfCounter(): PerfCounter {
             return this._activeParticles;
             return this._activeParticles;
         }
         }
 
 
         public getActiveBones(): number {
         public getActiveBones(): number {
+            return this._activeBones.current;
+        }
+
+        public get activeBonesPerfCounter(): PerfCounter {
             return this._activeBones;
             return this._activeBones;
         }
         }
 
 
         // Stats
         // Stats
         public getLastFrameDuration(): number {
         public getLastFrameDuration(): number {
+            return this._lastFrameDuration.current;
+        }
+
+        public get lastFramePerfCounter(): PerfCounter {
             return this._lastFrameDuration;
             return this._lastFrameDuration;
         }
         }
 
 
         public getEvaluateActiveMeshesDuration(): number {
         public getEvaluateActiveMeshesDuration(): number {
+            return this._evaluateActiveMeshesDuration.current;
+        }
+
+        public get evaluateActiveMeshesDurationPerfCounter(): PerfCounter {
             return this._evaluateActiveMeshesDuration;
             return this._evaluateActiveMeshesDuration;
         }
         }
 
 
@@ -613,22 +644,38 @@
         }
         }
 
 
         public getRenderTargetsDuration(): number {
         public getRenderTargetsDuration(): number {
-            return this._renderTargetsDuration;
+            return this._renderTargetsDuration.current;
         }
         }
 
 
         public getRenderDuration(): number {
         public getRenderDuration(): number {
+            return this._renderDuration.current;
+        }
+
+        public get renderDurationPerfCounter(): PerfCounter {
             return this._renderDuration;
             return this._renderDuration;
         }
         }
 
 
         public getParticlesDuration(): number {
         public getParticlesDuration(): number {
+            return this._particlesDuration.current;
+        }
+
+        public get particlesDurationPerfCounter(): PerfCounter {
             return this._particlesDuration;
             return this._particlesDuration;
         }
         }
 
 
         public getSpritesDuration(): number {
         public getSpritesDuration(): number {
+            return this._spritesDuration.current;
+        }
+
+        public get spriteDuractionPerfCounter(): PerfCounter {
             return this._spritesDuration;
             return this._spritesDuration;
         }
         }
 
 
         public getAnimationRatio(): number {
         public getAnimationRatio(): number {
+            return this._animationRatio.current;
+        }
+
+        public animationRatioPerfCounter(): PerfCounter {
             return this._animationRatio;
             return this._animationRatio;
         }
         }
 
 
@@ -1182,6 +1229,13 @@
             this._projectionMatrix = projection;
             this._projectionMatrix = projection;
 
 
             this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
             this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
+
+            // Update frustum
+            if (!this._frustumPlanes) {
+                this._frustumPlanes = Frustum.GetPlanes(this._transformMatrix);
+            } else {
+                Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
+            }
         }
         }
 
 
         // Methods
         // Methods
@@ -1767,7 +1821,7 @@
                     }
                     }
 
 
                     // Dispatch
                     // Dispatch
-                    this._activeIndices += subMesh.indexCount;
+                    this._activeIndices.addCount(subMesh.indexCount, false);
                     this._renderingManager.dispatch(subMesh);
                     this._renderingManager.dispatch(subMesh);
                 }
                 }
             }
             }
@@ -1788,12 +1842,6 @@
             this._boundingBoxRenderer.reset();
             this._boundingBoxRenderer.reset();
             this._edgesRenderers.reset();
             this._edgesRenderers.reset();
 
 
-            if (!this._frustumPlanes) {
-                this._frustumPlanes = Frustum.GetPlanes(this._transformMatrix);
-            } else {
-                Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
-            }
-
             // Meshes
             // Meshes
             var meshes: AbstractMesh[];
             var meshes: AbstractMesh[];
             var len: number;
             var len: number;
@@ -1814,7 +1862,7 @@
                     continue;
                     continue;
                 }
                 }
 
 
-                this._totalVertices += mesh.getTotalVertices();
+                this._totalVertices.addCount(mesh.getTotalVertices(), false);
 
 
                 if (!mesh.isReady() || !mesh.isEnabled()) {
                 if (!mesh.isReady() || !mesh.isEnabled()) {
                     continue;
                     continue;
@@ -1846,6 +1894,7 @@
             }
             }
 
 
             // Particle systems
             // Particle systems
+            this._particlesDuration.beginMonitoring();
             var beforeParticlesDate = Tools.Now;
             var beforeParticlesDate = Tools.Now;
             if (this.particlesEnabled) {
             if (this.particlesEnabled) {
                 Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
                 Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
@@ -1863,7 +1912,7 @@
                 }
                 }
                 Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
                 Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
             }
             }
-            this._particlesDuration += Tools.Now - beforeParticlesDate;
+            this._particlesDuration.endMonitoring(false);
         }
         }
 
 
         private _activeMesh(mesh: AbstractMesh): void {
         private _activeMesh(mesh: AbstractMesh): void {
@@ -1914,6 +1963,7 @@
 
 
         private _renderForCamera(camera: Camera): void {
         private _renderForCamera(camera: Camera): void {
             var engine = this._engine;
             var engine = this._engine;
+            var startTime = Tools.Now;
 
 
             this.activeCamera = camera;
             this.activeCamera = camera;
 
 
@@ -1933,10 +1983,10 @@
             this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
             this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
 
 
             // Meshes
             // Meshes
-            var beforeEvaluateActiveMeshesDate = Tools.Now;
+            this._evaluateActiveMeshesDuration.beginMonitoring();
             Tools.StartPerformanceCounter("Active meshes evaluation");
             Tools.StartPerformanceCounter("Active meshes evaluation");
             this._evaluateActiveMeshes();
             this._evaluateActiveMeshes();
-            this._evaluateActiveMeshesDuration += Tools.Now - beforeEvaluateActiveMeshesDate;
+            this._evaluateActiveMeshesDuration.endMonitoring(false);
             Tools.EndPerformanceCounter("Active meshes evaluation");
             Tools.EndPerformanceCounter("Active meshes evaluation");
 
 
             // Software skinning
             // Software skinning
@@ -1947,6 +1997,7 @@
             }
             }
 
 
             // Render targets
             // Render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = Tools.Now;
             var beforeRenderTargetDate = Tools.Now;
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
                 this._intermediateRendering = true;
                 this._intermediateRendering = true;
@@ -1965,12 +2016,12 @@
                 this._renderId++;
                 this._renderId++;
                 engine.restoreDefaultFramebuffer(); // Restore back buffer
                 engine.restoreDefaultFramebuffer(); // Restore back buffer
             }
             }
-            this._renderTargetsDuration += Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring(false);
 
 
             // Prepare Frame
             // Prepare Frame
             this.postProcessManager._prepareFrame();
             this.postProcessManager._prepareFrame();
 
 
-            var beforeRenderDate = Tools.Now;
+            this._renderDuration.beginMonitoring();
             // Backgrounds
             // Backgrounds
             var layerIndex;
             var layerIndex;
             var layer;
             var layer;
@@ -2023,7 +2074,7 @@
                 engine.setDepthBuffer(true);
                 engine.setDepthBuffer(true);
             }
             }
 
 
-            this._renderDuration += Tools.Now - beforeRenderDate;
+            this._renderDuration.endMonitoring(false);
 
 
             // Finalize frame
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
@@ -2097,17 +2148,17 @@
         }
         }
 
 
         public render(): void {
         public render(): void {
-            var startDate = Tools.Now;
-            this._particlesDuration = 0;
-            this._spritesDuration = 0;
-            this._activeParticles = 0;
-            this._renderDuration = 0;
-            this._renderTargetsDuration = 0;
-            this._evaluateActiveMeshesDuration = 0;
-            this._totalVertices = 0;
-            this._activeIndices = 0;
-            this._activeBones = 0;
-            this.getEngine().resetDrawCalls();
+            this._lastFrameDuration.beginMonitoring();
+            this._particlesDuration.fetchNewFrame();
+            this._spritesDuration.fetchNewFrame();
+            this._activeParticles.fetchNewFrame();
+            this._renderDuration.fetchNewFrame();
+            this._renderTargetsDuration.fetchNewFrame();
+            this._evaluateActiveMeshesDuration.fetchNewFrame();
+            this._totalVertices.fetchNewFrame();
+            this._activeIndices.fetchNewFrame();
+            this._activeBones.fetchNewFrame();
+            this.getEngine().drawCallsPerfCounter.fetchNewFrame();
             this._meshesForIntersections.reset();
             this._meshesForIntersections.reset();
             this.resetCachedMaterial();
             this.resetCachedMaterial();
 
 
@@ -2125,7 +2176,7 @@
 
 
             // Animations
             // Animations
             var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
             var deltaTime = Math.max(Scene.MinDeltaTime, Math.min(this._engine.getDeltaTime(), Scene.MaxDeltaTime));
-            this._animationRatio = deltaTime * (60.0 / 1000.0);
+            this._animationRatio.addCount(deltaTime * (60.0 / 1000.0), true);
             this._animate();
             this._animate();
 
 
             // Physics
             // Physics
@@ -2139,6 +2190,7 @@
             this.onBeforeRenderObservable.notifyObservers(this);
             this.onBeforeRenderObservable.notifyObservers(this);
 
 
             // Customs render targets
             // Customs render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = Tools.Now;
             var beforeRenderTargetDate = Tools.Now;
             var engine = this.getEngine();
             var engine = this.getEngine();
             var currentActiveCamera = this.activeCamera;
             var currentActiveCamera = this.activeCamera;
@@ -2171,7 +2223,7 @@
             if (this.customRenderTargets.length > 0) { // Restore back buffer
             if (this.customRenderTargets.length > 0) { // Restore back buffer
                 engine.restoreDefaultFramebuffer();
                 engine.restoreDefaultFramebuffer();
             }
             }
-            this._renderTargetsDuration += Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring();
             this.activeCamera = currentActiveCamera;
             this.activeCamera = currentActiveCamera;
 
 
             // Procedural textures
             // Procedural textures
@@ -2256,7 +2308,14 @@
             }
             }
 
 
             Tools.EndPerformanceCounter("Scene rendering");
             Tools.EndPerformanceCounter("Scene rendering");
-            this._lastFrameDuration = Tools.Now - startDate;
+            this._lastFrameDuration.endMonitoring();
+            this._totalMeshesCounter.addCount(this.meshes.length, true);
+            this._totalLightsCounter.addCount(this.lights.length, true);
+            this._totalMaterialsCounter.addCount(this.materials.length, true);
+            this._totalTexturesCounter.addCount(this.textures.length, true);
+            this._activeBones.addCount(0, true);
+            this._activeIndices.addCount(0, true);
+            this._activeParticles.addCount(0, true);
         }
         }
 
 
         private _updateAudioParameters() {
         private _updateAudioParameters() {