فهرست منبع

update from upstream

nockawa 9 سال پیش
والد
کامیت
e9f596dc98

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 24 - 24
dist/preview release/babylon.core.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3945 - 3855
dist/preview release/babylon.d.ts


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 38 - 38
dist/preview release/babylon.js


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

@@ -5355,6 +5355,135 @@ var BABYLON;
     })();
     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.
      * 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
@@ -5843,7 +5972,7 @@ var BABYLON;
             this.scenes = new Array();
             this._windowIsBackground = false;
             this._webGLVersion = "1.0";
-            this._drawCalls = 0;
+            this._drawCalls = new BABYLON.PerfCounter();
             this._renderingQueueLaunched = false;
             this._activeRenderLoops = [];
             // FPS
@@ -6190,15 +6319,18 @@ var BABYLON;
         };
         Object.defineProperty(Engine.prototype, "drawCalls", {
             get: function () {
+                return this._drawCalls.current;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Engine.prototype, "drawCallsPerfCounter", {
+            get: function () {
                 return this._drawCalls;
             },
             enumerable: true,
             configurable: true
         });
-        // Methods
-        Engine.prototype.resetDrawCalls = function () {
-            this._drawCalls = 0;
-        };
         Engine.prototype.getDepthFunction = function () {
             return this._depthCullingState.depthFunc;
         };
@@ -6687,7 +6819,7 @@ var BABYLON;
         Engine.prototype.draw = function (useTriangles, indexStart, indexCount, instancesCount) {
             // Apply states
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             // Render
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
@@ -6700,7 +6832,7 @@ var BABYLON;
         Engine.prototype.drawPointClouds = function (verticesStart, verticesCount, instancesCount) {
             // Apply states
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             if (instancesCount) {
                 this._caps.instancedArrays.drawArraysInstancedANGLE(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
                 return;
@@ -6710,7 +6842,7 @@ var BABYLON;
         Engine.prototype.drawUnIndexed = function (useTriangles, verticesStart, verticesCount, instancesCount) {
             // Apply states
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             if (instancesCount) {
                 this._caps.instancedArrays.drawArraysInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, verticesStart, verticesCount, instancesCount);
                 return;
@@ -9671,18 +9803,20 @@ var BABYLON;
             this.computeWorldMatrix(true);
             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>
             /// <param name="targetPoint" type="Vector3">The position (must be in same space as current mesh) to look at</param>
             /// <param name="yawCor" type="Number">optional yaw (y-axis) correction in radians</param>
             /// <param name="pitchCor" type="Number">optional pitch (x-axis) correction in radians</param>
             /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param>
             /// <returns>Mesh oriented towards targetMesh</returns>
-            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;
-            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 len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
             var pitch = Math.atan2(dv.y, len);
@@ -9706,15 +9840,9 @@ var BABYLON;
         AbstractMesh.prototype.isInFrustum = function (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) {
             if (!this._boundingInfo || !mesh._boundingInfo) {
@@ -11589,8 +11717,10 @@ var BABYLON;
             this._computedViewMatrix = BABYLON.Matrix.Identity();
             this._projectionMatrix = new BABYLON.Matrix();
             this._postProcesses = new Array();
+            this._transformMatrix = BABYLON.Matrix.Zero();
             this._activeMeshes = new BABYLON.SmartArray(256);
             this._globalPosition = BABYLON.Vector3.Zero();
+            this._refreshFrustumPlanes = true;
             scene.addCamera(this);
             if (!scene.activeCamera) {
                 scene.activeCamera = this;
@@ -11861,6 +11991,7 @@ var BABYLON;
             if (!force && this._isSynchronizedViewMatrix()) {
                 return this._computedViewMatrix;
             }
+            this._refreshFrustumPlanes = true;
             if (!this.parent || !this.parent.getWorldMatrix) {
                 this._globalPosition.copyFrom(this.position);
             }
@@ -11889,6 +12020,7 @@ var BABYLON;
             if (!force && this._isSynchronizedProjectionMatrix()) {
                 return this._projectionMatrix;
             }
+            this._refreshFrustumPlanes = true;
             var engine = this.getEngine();
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
                 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);
             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 () {
             // Animations
             this.getScene().stopAnimation(this);
@@ -14555,7 +14712,7 @@ var BABYLON;
             }
             // Particles
             var activeCamera = this._scene.activeCamera;
-            var beforeParticlesDate = BABYLON.Tools.Now;
+            this._scene._particlesDuration.beginMonitoring();
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
                 if (particleSystem.renderingGroupId !== index) {
@@ -14566,10 +14723,10 @@ var BABYLON;
                 }
                 this._clearDepthBuffer();
                 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) {
             if (!this._scene.spritesEnabled || this._scene.spriteManagers.length === 0) {
@@ -14577,7 +14734,7 @@ var BABYLON;
             }
             // Sprites       
             var activeCamera = this._scene.activeCamera;
-            var beforeSpritessDate = BABYLON.Tools.Now;
+            this._scene._spritesDuration.beginMonitoring();
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
                 var spriteManager = this._scene.spriteManagers[id];
                 if (spriteManager.renderingGroupId === index && ((activeCamera.layerMask & spriteManager.layerMask) !== 0)) {
@@ -14585,7 +14742,7 @@ var BABYLON;
                     spriteManager.render();
                 }
             }
-            this._scene._spritesDuration += BABYLON.Tools.Now - beforeSpritessDate;
+            this._scene._spritesDuration.endMonitoring(false);
         };
         RenderingManager.prototype._clearDepthBuffer = function () {
             if (this._depthBufferAlreadyCleaned) {
@@ -15028,16 +15185,22 @@ var BABYLON;
             this.soundTracks = new Array();
             this._audioEnabled = true;
             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._executeWhenReadyTimeoutId = -1;
             this._intermediateRendering = false;
@@ -15049,7 +15212,6 @@ var BABYLON;
             this._activeParticleSystems = new BABYLON.SmartArray(256);
             this._activeSkeletons = new BABYLON.SmartArray(32);
             this._softwareSkinnedMeshes = new BABYLON.SmartArray(32);
-            this._activeBones = 0;
             this._activeAnimatables = new Array();
             this._transformMatrix = BABYLON.Matrix.Zero();
             this._edgesRenderers = new BABYLON.SmartArray(16);
@@ -15239,40 +15401,106 @@ var BABYLON;
             return this._engine;
         };
         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 () {
-            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 () {
-            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 () {
-            return this._activeBones;
+            return this._activeBones.current;
         };
+        Object.defineProperty(Scene.prototype, "activeBonesPerfCounter", {
+            get: function () {
+                return this._activeBones;
+            },
+            enumerable: true,
+            configurable: true
+        });
         // Stats
         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 () {
-            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 () {
             return this._activeMeshes;
         };
         Scene.prototype.getRenderTargetsDuration = function () {
-            return this._renderTargetsDuration;
+            return this._renderTargetsDuration.current;
         };
         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 () {
-            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 () {
-            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 () {
+            return this._animationRatio.current;
+        };
+        Scene.prototype.animationRatioPerfCounter = function () {
             return this._animationRatio;
         };
         Scene.prototype.getRenderId = function () {
@@ -15734,6 +15962,13 @@ var BABYLON;
             this._viewMatrix = view;
             this._projectionMatrix = projection;
             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
         Scene.prototype.addMesh = function (newMesh) {
@@ -16218,7 +16453,7 @@ var BABYLON;
                         }
                     }
                     // Dispatch
-                    this._activeIndices += subMesh.indexCount;
+                    this._activeIndices.addCount(subMesh.indexCount, false);
                     this._renderingManager.dispatch(subMesh);
                 }
             }
@@ -16236,12 +16471,6 @@ var BABYLON;
             this._softwareSkinnedMeshes.reset();
             this._boundingBoxRenderer.reset();
             this._edgesRenderers.reset();
-            if (!this._frustumPlanes) {
-                this._frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
-            }
-            else {
-                BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
-            }
             // Meshes
             var meshes;
             var len;
@@ -16259,7 +16488,7 @@ var BABYLON;
                 if (mesh.isBlocked) {
                     continue;
                 }
-                this._totalVertices += mesh.getTotalVertices();
+                this._totalVertices.addCount(mesh.getTotalVertices(), false);
                 if (!mesh.isReady() || !mesh.isEnabled()) {
                     continue;
                 }
@@ -16282,6 +16511,7 @@ var BABYLON;
                 }
             }
             // Particle systems
+            this._particlesDuration.beginMonitoring();
             var beforeParticlesDate = BABYLON.Tools.Now;
             if (this.particlesEnabled) {
                 BABYLON.Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
@@ -16297,7 +16527,7 @@ var BABYLON;
                 }
                 BABYLON.Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
             }
-            this._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
+            this._particlesDuration.endMonitoring(false);
         };
         Scene.prototype._activeMesh = function (mesh) {
             if (mesh.skeleton && this.skeletonsEnabled) {
@@ -16338,6 +16568,7 @@ var BABYLON;
         };
         Scene.prototype._renderForCamera = function (camera) {
             var engine = this._engine;
+            var startTime = BABYLON.Tools.Now;
             this.activeCamera = camera;
             if (!this.activeCamera)
                 throw new Error("Active camera not set");
@@ -16350,10 +16581,10 @@ var BABYLON;
             this.updateTransformMatrix();
             this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
             // Meshes
-            var beforeEvaluateActiveMeshesDate = BABYLON.Tools.Now;
+            this._evaluateActiveMeshesDuration.beginMonitoring();
             BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
             this._evaluateActiveMeshes();
-            this._evaluateActiveMeshesDuration += BABYLON.Tools.Now - beforeEvaluateActiveMeshesDate;
+            this._evaluateActiveMeshesDuration.endMonitoring(false);
             BABYLON.Tools.EndPerformanceCounter("Active meshes evaluation");
             // Software skinning
             for (var softwareSkinnedMeshIndex = 0; softwareSkinnedMeshIndex < this._softwareSkinnedMeshes.length; softwareSkinnedMeshIndex++) {
@@ -16361,6 +16592,7 @@ var BABYLON;
                 mesh.applySkeleton(mesh.skeleton);
             }
             // Render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
                 this._intermediateRendering = true;
@@ -16378,10 +16610,10 @@ var BABYLON;
                 this._renderId++;
                 engine.restoreDefaultFramebuffer(); // Restore back buffer
             }
-            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring(false);
             // Prepare Frame
             this.postProcessManager._prepareFrame();
-            var beforeRenderDate = BABYLON.Tools.Now;
+            this._renderDuration.beginMonitoring();
             // Backgrounds
             var layerIndex;
             var layer;
@@ -16427,7 +16659,7 @@ var BABYLON;
                 }
                 engine.setDepthBuffer(true);
             }
-            this._renderDuration += BABYLON.Tools.Now - beforeRenderDate;
+            this._renderDuration.endMonitoring(false);
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
             // Update camera
@@ -16486,17 +16718,17 @@ var BABYLON;
             }
         };
         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.resetCachedMaterial();
             BABYLON.Tools.StartPerformanceCounter("Scene rendering");
@@ -16510,7 +16742,7 @@ var BABYLON;
             }
             // Animations
             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();
             // Physics
             if (this._physicsEngine) {
@@ -16521,6 +16753,7 @@ var BABYLON;
             // Before render
             this.onBeforeRenderObservable.notifyObservers(this);
             // Customs render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var engine = this.getEngine();
             var currentActiveCamera = this.activeCamera;
@@ -16546,7 +16779,7 @@ var BABYLON;
             if (this.customRenderTargets.length > 0) {
                 engine.restoreDefaultFramebuffer();
             }
-            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring();
             this.activeCamera = currentActiveCamera;
             // Procedural textures
             if (this.proceduralTexturesEnabled) {
@@ -16615,7 +16848,14 @@ var BABYLON;
                 this.dumpNextRenderTargets = false;
             }
             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 () {
             if (!this.audioEnabled || (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 1)) {
@@ -18290,7 +18530,7 @@ var BABYLON;
         /**
          * Sets the mesh indices.
          * 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.
          */
         Mesh.prototype.setIndices = function (indices, totalVertices) {
@@ -18742,7 +18982,7 @@ var BABYLON;
          * Returns a new Mesh object generated from the current mesh properties.
          * This method must not get confused with createInstance().
          * 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 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.
          * 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.
-         *
          * 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.
          * The parameter `radius` (positive float, default 1) is the radius value of the lathe.
@@ -19650,7 +19889,6 @@ var BABYLON;
         /**
          * 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.
-         *
          * 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 `radius` (positive float, default 1) sets the tube radius size.
@@ -19685,7 +19923,6 @@ var BABYLON;
         };
         /**
          * Creates a polyhedron mesh.
-         *
          * 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
          *  to choose the wanted type.
@@ -19722,7 +19959,7 @@ var BABYLON;
          * 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.
          * 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 `angle` (float in radian, default 0) sets the angle to rotate the decal.
          */
@@ -19849,7 +20086,7 @@ var BABYLON;
         };
         // 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
          */
         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) {
             var minMaxVector = meshesOrMinMaxVector.min !== undefined ? meshesOrMinMaxVector : Mesh.MinMax(meshesOrMinMaxVector);
@@ -20036,6 +20273,9 @@ var BABYLON;
         SubMesh.prototype.isInFrustum = function (frustumPlanes) {
             return this.getBoundingInfo().isInFrustum(frustumPlanes);
         };
+        SubMesh.prototype.isCompletelyInFrustum = function (frustumPlanes) {
+            return this.getBoundingInfo().isCompletelyInFrustum(frustumPlanes);
+        };
         SubMesh.prototype.render = function (enableAlphaMode) {
             this._renderingMesh.render(this, enableAlphaMode);
         };
@@ -21932,7 +22172,7 @@ var BABYLON;
                         mesh._activate(sceneRenderId);
                         for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
                             var subMesh = mesh.subMeshes[subIndex];
-                            scene._activeIndices += subMesh.indexCount;
+                            scene._activeIndices.addCount(subMesh.indexCount, false);
                             this._renderingManager.dispatch(subMesh);
                         }
                     }
@@ -27975,7 +28215,7 @@ var BABYLON;
                 this._computeTransformMatrices(this._transformMatrices, null);
             }
             this._isDirty = false;
-            this._scene._activeBones += this.bones.length;
+            this._scene._activeBones.addCount(this.bones.length, false);
         };
         Skeleton.prototype.getAnimatables = function () {
             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...
                 var newCount = Math.min(this.totalElementCount, count * 2);
                 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...
             this.pack();
@@ -41671,12 +41913,14 @@ var BABYLON;
             }
             else {
                 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;
         };
@@ -45367,9 +45611,15 @@ var BABYLON;
                 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
-            if (this.scene.getRenderId() === this._updateRenderId) {
+            if (!forceRecompute && this.scene.getRenderId() === this._updateRenderId) {
                 return;
             }
             // Detect a change of rendering size
@@ -45404,7 +45654,7 @@ var BABYLON;
          */
         Canvas2D.prototype._render = function () {
             this._updateTrackedNodes();
-            this._updateCanvasState();
+            this._updateCanvasState(false);
             if (this._primPointerInfo.canvasPointerPos) {
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
                 this._updateOverStatus(); // TODO this._primPointerInfo may not be up to date!

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 40 - 40
dist/preview release/babylon.noworker.js


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

@@ -3,11 +3,13 @@
 ### Major 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 GPU state change reduction ([benaadams](https://github.com/benaadams)) 
 - 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
 - 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
     

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

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

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

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

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

@@ -36,8 +36,10 @@ var BABYLON;
             this._computedViewMatrix = BABYLON.Matrix.Identity();
             this._projectionMatrix = new BABYLON.Matrix();
             this._postProcesses = new Array();
+            this._transformMatrix = BABYLON.Matrix.Zero();
             this._activeMeshes = new BABYLON.SmartArray(256);
             this._globalPosition = BABYLON.Vector3.Zero();
+            this._refreshFrustumPlanes = true;
             scene.addCamera(this);
             if (!scene.activeCamera) {
                 scene.activeCamera = this;
@@ -308,6 +310,7 @@ var BABYLON;
             if (!force && this._isSynchronizedViewMatrix()) {
                 return this._computedViewMatrix;
             }
+            this._refreshFrustumPlanes = true;
             if (!this.parent || !this.parent.getWorldMatrix) {
                 this._globalPosition.copyFrom(this.position);
             }
@@ -336,6 +339,7 @@ var BABYLON;
             if (!force && this._isSynchronizedProjectionMatrix()) {
                 return this._projectionMatrix;
             }
+            this._refreshFrustumPlanes = true;
             var engine = this.getEngine();
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
                 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);
             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 () {
             // Animations
             this.getScene().stopAnimation(this);

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

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

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

@@ -751,9 +751,15 @@ var BABYLON;
                 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
-            if (this.scene.getRenderId() === this._updateRenderId) {
+            if (!forceRecompute && this.scene.getRenderId() === this._updateRenderId) {
                 return;
             }
             // Detect a change of rendering size
@@ -788,7 +794,7 @@ var BABYLON;
          */
         Canvas2D.prototype._render = function () {
             this._updateTrackedNodes();
-            this._updateCanvasState();
+            this._updateCanvasState(false);
             if (this._primPointerInfo.canvasPointerPos) {
                 this._updateIntersectionList(this._primPointerInfo.canvasPointerPos, false);
                 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?: {
 
-            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,
-            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;

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

@@ -556,12 +556,14 @@ var BABYLON;
             }
             else {
                 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;
         };

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

@@ -1,5 +1,5 @@
 module BABYLON {
-    export class BoundingBox {
+    export class BoundingBox implements ICullable {
         public vectors: Vector3[] = new Array<Vector3>();
         public center: 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);
     }
 
-    export class BoundingInfo {
+    export interface ICullable {
+        isInFrustum(frustumPlanes: Plane[]): boolean;
+        isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
+    }
+
+    export class BoundingInfo implements ICullable {
         public boundingBox: BoundingBox;
         public boundingSphere: BoundingSphere;
 

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

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

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1050 - 1055
src/Mesh/babylon.abstractMesh.js


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

@@ -1,5 +1,5 @@
 module BABYLON {
-    export class AbstractMesh extends Node implements IDisposable {
+    export class AbstractMesh extends Node implements IDisposable, ICullable {
         // Statics
         private static _BILLBOARDMODE_NONE = 0;
         private static _BILLBOARDMODE_X = 1;
@@ -746,7 +746,7 @@
         }
 
         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>
             /// <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>
@@ -754,12 +754,9 @@
             /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param>
             /// <returns>Mesh oriented towards targetMesh</returns>
 
-            yawCor = yawCor || 0; // default to zero if undefined
-            pitchCor = pitchCor || 0;
-            rollCor = rollCor || 0;
-
             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 len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
             var pitch = Math.atan2(dv.y, len);
@@ -789,18 +786,8 @@
             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 {

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

@@ -718,7 +718,7 @@ var BABYLON;
         /**
          * Sets the mesh indices.
          * 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.
          */
         Mesh.prototype.setIndices = function (indices, totalVertices) {
@@ -1170,7 +1170,7 @@ var BABYLON;
          * Returns a new Mesh object generated from the current mesh properties.
          * This method must not get confused with createInstance().
          * 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 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.
          * 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.
-         *
          * 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.
          * The parameter `radius` (positive float, default 1) is the radius value of the lathe.
@@ -2078,7 +2077,6 @@ var BABYLON;
         /**
          * 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.
-         *
          * 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 `radius` (positive float, default 1) sets the tube radius size.
@@ -2113,7 +2111,6 @@ var BABYLON;
         };
         /**
          * Creates a polyhedron mesh.
-         *
          * 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
          *  to choose the wanted type.
@@ -2150,7 +2147,7 @@ var BABYLON;
          * 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.
          * 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 `angle` (float in radian, default 0) sets the angle to rotate the decal.
          */
@@ -2277,7 +2274,7 @@ var BABYLON;
         };
         // 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
          */
         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) {
             var minMaxVector = meshesOrMinMaxVector.min !== undefined ? meshesOrMinMaxVector : Mesh.MinMax(meshesOrMinMaxVector);

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

@@ -762,7 +762,7 @@
         /**
          * Sets the mesh indices.  
          * 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.
          */
         public setIndices(indices: number[] | Int32Array, totalVertices?: number): void {
@@ -1309,7 +1309,7 @@
          * Returns a new Mesh object generated from the current mesh properties.
          * This method must not get confused with createInstance().  
          * 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 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.  
          * 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.    
-         *
          * 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.    
          * The parameter `radius` (positive float, default 1) is the radius value of the lathe.        
@@ -2337,7 +2336,6 @@
         /**
          * 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.    
-         *
          * 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 `radius` (positive float, default 1) sets the tube radius size.    
@@ -2372,7 +2370,6 @@
         }
         /**
          * Creates a polyhedron mesh.  
-         * 
          * 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
          *  to choose the wanted type.  
@@ -2410,7 +2407,7 @@
          * 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.  
          * 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 `angle` (float in radian, default 0) sets the angle to rotate the decal.  
          */
@@ -2561,7 +2558,7 @@
 
         // 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
          */
         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 {
             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) {
             return this.getBoundingInfo().isInFrustum(frustumPlanes);
         };
+        SubMesh.prototype.isCompletelyInFrustum = function (frustumPlanes) {
+            return this.getBoundingInfo().isCompletelyInFrustum(frustumPlanes);
+        };
         SubMesh.prototype.render = function (enableAlphaMode) {
             this._renderingMesh.render(this, enableAlphaMode);
         };

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

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

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

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

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

@@ -22,7 +22,7 @@
 
             // Particles
             var activeCamera = this._scene.activeCamera;
-            var beforeParticlesDate = Tools.Now;
+            this._scene._particlesDuration.beginMonitoring();
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
 
@@ -37,10 +37,10 @@
                 this._clearDepthBuffer();
 
                 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 {
@@ -50,7 +50,7 @@
 
             // Sprites       
             var activeCamera = this._scene.activeCamera;
-            var beforeSpritessDate = Tools.Now;
+            this._scene._spritesDuration.beginMonitoring();
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
                 var spriteManager = this._scene.spriteManagers[id];
 
@@ -59,7 +59,7 @@
                     spriteManager.render();
                 }
             }
-            this._scene._spritesDuration += Tools.Now - beforeSpritessDate;
+            this._scene._spritesDuration.endMonitoring(false);
         }
 
         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...
                 var newCount = Math.min(this.totalElementCount, count * 2);
                 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...
             this.pack();

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

@@ -914,6 +914,135 @@ var BABYLON;
     })();
     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.
      * 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

+ 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.
      * 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

+ 11 - 8
src/babylon.engine.js

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

+ 7 - 8
src/babylon.engine.ts

@@ -312,7 +312,7 @@
 
         private _loadingScreen: ILoadingScreen;
 
-        private _drawCalls = 0;
+        public  _drawCalls = new PerfCounter();
 
         private _glVersion: string;
         private _glRenderer: string;
@@ -595,12 +595,11 @@
         }
 
         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 {
@@ -1165,7 +1164,7 @@
             // Apply states
             this.applyStates();
 
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
             // Render
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
@@ -1180,7 +1179,7 @@
         public drawPointClouds(verticesStart: number, verticesCount: number, instancesCount?: number): void {
             // Apply states
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
 
             if (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 {
             // Apply states
             this.applyStates();
-            this._drawCalls++;
+            this._drawCalls.addCount(1, false);
 
             if (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._audioEnabled = true;
             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._executeWhenReadyTimeoutId = -1;
             this._intermediateRendering = false;
@@ -298,7 +304,6 @@ var BABYLON;
             this._activeParticleSystems = new BABYLON.SmartArray(256);
             this._activeSkeletons = new BABYLON.SmartArray(32);
             this._softwareSkinnedMeshes = new BABYLON.SmartArray(32);
-            this._activeBones = 0;
             this._activeAnimatables = new Array();
             this._transformMatrix = BABYLON.Matrix.Zero();
             this._edgesRenderers = new BABYLON.SmartArray(16);
@@ -488,40 +493,106 @@ var BABYLON;
             return this._engine;
         };
         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 () {
-            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 () {
-            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 () {
-            return this._activeBones;
+            return this._activeBones.current;
         };
+        Object.defineProperty(Scene.prototype, "activeBonesPerfCounter", {
+            get: function () {
+                return this._activeBones;
+            },
+            enumerable: true,
+            configurable: true
+        });
         // Stats
         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 () {
-            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 () {
             return this._activeMeshes;
         };
         Scene.prototype.getRenderTargetsDuration = function () {
-            return this._renderTargetsDuration;
+            return this._renderTargetsDuration.current;
         };
         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 () {
-            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 () {
-            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 () {
+            return this._animationRatio.current;
+        };
+        Scene.prototype.animationRatioPerfCounter = function () {
             return this._animationRatio;
         };
         Scene.prototype.getRenderId = function () {
@@ -983,6 +1054,13 @@ var BABYLON;
             this._viewMatrix = view;
             this._projectionMatrix = projection;
             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
         Scene.prototype.addMesh = function (newMesh) {
@@ -1467,7 +1545,7 @@ var BABYLON;
                         }
                     }
                     // Dispatch
-                    this._activeIndices += subMesh.indexCount;
+                    this._activeIndices.addCount(subMesh.indexCount, false);
                     this._renderingManager.dispatch(subMesh);
                 }
             }
@@ -1485,12 +1563,6 @@ var BABYLON;
             this._softwareSkinnedMeshes.reset();
             this._boundingBoxRenderer.reset();
             this._edgesRenderers.reset();
-            if (!this._frustumPlanes) {
-                this._frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
-            }
-            else {
-                BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
-            }
             // Meshes
             var meshes;
             var len;
@@ -1508,7 +1580,7 @@ var BABYLON;
                 if (mesh.isBlocked) {
                     continue;
                 }
-                this._totalVertices += mesh.getTotalVertices();
+                this._totalVertices.addCount(mesh.getTotalVertices(), false);
                 if (!mesh.isReady() || !mesh.isEnabled()) {
                     continue;
                 }
@@ -1531,6 +1603,7 @@ var BABYLON;
                 }
             }
             // Particle systems
+            this._particlesDuration.beginMonitoring();
             var beforeParticlesDate = BABYLON.Tools.Now;
             if (this.particlesEnabled) {
                 BABYLON.Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
@@ -1546,7 +1619,7 @@ var BABYLON;
                 }
                 BABYLON.Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
             }
-            this._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
+            this._particlesDuration.endMonitoring(false);
         };
         Scene.prototype._activeMesh = function (mesh) {
             if (mesh.skeleton && this.skeletonsEnabled) {
@@ -1587,6 +1660,7 @@ var BABYLON;
         };
         Scene.prototype._renderForCamera = function (camera) {
             var engine = this._engine;
+            var startTime = BABYLON.Tools.Now;
             this.activeCamera = camera;
             if (!this.activeCamera)
                 throw new Error("Active camera not set");
@@ -1599,10 +1673,10 @@ var BABYLON;
             this.updateTransformMatrix();
             this.onBeforeCameraRenderObservable.notifyObservers(this.activeCamera);
             // Meshes
-            var beforeEvaluateActiveMeshesDate = BABYLON.Tools.Now;
+            this._evaluateActiveMeshesDuration.beginMonitoring();
             BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
             this._evaluateActiveMeshes();
-            this._evaluateActiveMeshesDuration += BABYLON.Tools.Now - beforeEvaluateActiveMeshesDate;
+            this._evaluateActiveMeshesDuration.endMonitoring(false);
             BABYLON.Tools.EndPerformanceCounter("Active meshes evaluation");
             // Software skinning
             for (var softwareSkinnedMeshIndex = 0; softwareSkinnedMeshIndex < this._softwareSkinnedMeshes.length; softwareSkinnedMeshIndex++) {
@@ -1610,6 +1684,7 @@ var BABYLON;
                 mesh.applySkeleton(mesh.skeleton);
             }
             // Render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             if (this.renderTargetsEnabled && this._renderTargets.length > 0) {
                 this._intermediateRendering = true;
@@ -1627,10 +1702,10 @@ var BABYLON;
                 this._renderId++;
                 engine.restoreDefaultFramebuffer(); // Restore back buffer
             }
-            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring(false);
             // Prepare Frame
             this.postProcessManager._prepareFrame();
-            var beforeRenderDate = BABYLON.Tools.Now;
+            this._renderDuration.beginMonitoring();
             // Backgrounds
             var layerIndex;
             var layer;
@@ -1676,7 +1751,7 @@ var BABYLON;
                 }
                 engine.setDepthBuffer(true);
             }
-            this._renderDuration += BABYLON.Tools.Now - beforeRenderDate;
+            this._renderDuration.endMonitoring(false);
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
             // Update camera
@@ -1735,17 +1810,17 @@ var BABYLON;
             }
         };
         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.resetCachedMaterial();
             BABYLON.Tools.StartPerformanceCounter("Scene rendering");
@@ -1759,7 +1834,7 @@ var BABYLON;
             }
             // Animations
             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();
             // Physics
             if (this._physicsEngine) {
@@ -1770,6 +1845,7 @@ var BABYLON;
             // Before render
             this.onBeforeRenderObservable.notifyObservers(this);
             // Customs render targets
+            this._renderTargetsDuration.beginMonitoring();
             var beforeRenderTargetDate = BABYLON.Tools.Now;
             var engine = this.getEngine();
             var currentActiveCamera = this.activeCamera;
@@ -1795,7 +1871,7 @@ var BABYLON;
             if (this.customRenderTargets.length > 0) {
                 engine.restoreDefaultFramebuffer();
             }
-            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.endMonitoring();
             this.activeCamera = currentActiveCamera;
             // Procedural textures
             if (this.proceduralTexturesEnabled) {
@@ -1864,7 +1940,14 @@ var BABYLON;
                 this.dumpNextRenderTargets = false;
             }
             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 () {
             if (!this.audioEnabled || (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 1)) {

+ 99 - 40
src/babylon.scene.ts

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