Browse Source

SceneOptimizer v0.1
High performance timer
performance log
new isCompletelyInFrustum API

David Catuhe 10 năm trước cách đây
mục cha
commit
0d77dde14d
30 tập tin đã thay đổi với 1708 bổ sung179 xóa
  1. 15 0
      Babylon/Culling/babylon.BoundingBox.ts
  2. 15 0
      Babylon/Culling/babylon.boundingBox.js
  3. 4 0
      Babylon/Culling/babylon.boundingInfo.js
  4. 4 0
      Babylon/Culling/babylon.boundingInfo.ts
  5. 28 24
      Babylon/Materials/babylon.standardMaterial.js
  6. 27 23
      Babylon/Materials/babylon.standardMaterial.ts
  7. 23 0
      Babylon/Materials/textures/babylon.baseTexture.js
  8. 19 0
      Babylon/Materials/textures/babylon.baseTexture.ts
  9. 22 0
      Babylon/Materials/textures/babylon.dynamicTexture.js
  10. 18 0
      Babylon/Materials/textures/babylon.dynamicTexture.ts
  11. 14 0
      Babylon/Materials/textures/babylon.renderTargetTexture.js
  12. 11 1
      Babylon/Materials/textures/babylon.renderTargetTexture.ts
  13. 1 1
      Babylon/Materials/textures/babylon.texture.ts
  14. 2 2
      Babylon/Materials/textures/babylon.videoTexture.js
  15. 2 2
      Babylon/Materials/textures/babylon.videoTexture.ts
  16. 14 0
      Babylon/Mesh/babylon.abstractMesh.js
  17. 16 1
      Babylon/Mesh/babylon.abstractMesh.ts
  18. 4 4
      Babylon/Rendering/babylon.renderingManager.js
  19. 4 4
      Babylon/Rendering/babylon.renderingManager.ts
  20. 271 0
      Babylon/Tools/babylon.sceneOptimizer.js
  21. 218 0
      Babylon/Tools/babylon.sceneOptimizer.ts
  22. 116 1
      Babylon/Tools/babylon.tools.js
  23. 93 1
      Babylon/Tools/babylon.tools.ts
  24. 1 1
      Babylon/babylon.engine.js
  25. 1 1
      Babylon/babylon.engine.ts
  26. 53 21
      Babylon/babylon.scene.js
  27. 51 21
      Babylon/babylon.scene.ts
  28. 567 53
      babylon.2.0-alpha.debug.js
  29. 16 16
      babylon.2.0-alpha.js
  30. 78 2
      babylon.2.0.d.ts

+ 15 - 0
Babylon/Culling/babylon.BoundingBox.ts

@@ -91,6 +91,10 @@
             return BoundingBox.IsInFrustum(this.vectorsWorld, frustumPlanes);
         }
 
+        public isCompletelyInFrustum(frustumPlanes: Plane[]): boolean {
+            return BoundingBox.IsCompletelyInFrustum(this.vectorsWorld, frustumPlanes);
+        }
+
         public intersectsPoint(point: Vector3): boolean {
             var delta = Engine.Epsilon;
 
@@ -143,6 +147,17 @@
             return (num <= (sphereRadius * sphereRadius));
         }
 
+        public static IsCompletelyInFrustum(boundingVectors: Vector3[], frustumPlanes: Plane[]): boolean {
+            for (var p = 0; p < 6; p++) {
+                for (var i = 0; i < 8; i++) {
+                    if (frustumPlanes[p].dotCoordinate(boundingVectors[i]) < 0) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
         public static IsInFrustum(boundingVectors: Vector3[], frustumPlanes: Plane[]): boolean {
             for (var p = 0; p < 6; p++) {
                 var inCount = 8;

+ 15 - 0
Babylon/Culling/babylon.boundingBox.js

@@ -84,6 +84,10 @@
             return BoundingBox.IsInFrustum(this.vectorsWorld, frustumPlanes);
         };
 
+        BoundingBox.prototype.isCompletelyInFrustum = function (frustumPlanes) {
+            return BoundingBox.IsCompletelyInFrustum(this.vectorsWorld, frustumPlanes);
+        };
+
         BoundingBox.prototype.intersectsPoint = function (point) {
             var delta = BABYLON.Engine.Epsilon;
 
@@ -136,6 +140,17 @@
             return (num <= (sphereRadius * sphereRadius));
         };
 
+        BoundingBox.IsCompletelyInFrustum = function (boundingVectors, frustumPlanes) {
+            for (var p = 0; p < 6; p++) {
+                for (var i = 0; i < 8; i++) {
+                    if (frustumPlanes[p].dotCoordinate(boundingVectors[i]) < 0) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        };
+
         BoundingBox.IsInFrustum = function (boundingVectors, frustumPlanes) {
             for (var p = 0; p < 6; p++) {
                 var inCount = 8;

+ 4 - 0
Babylon/Culling/babylon.boundingInfo.js

@@ -45,6 +45,10 @@
             return this.boundingBox.isInFrustum(frustumPlanes);
         };
 
+        BoundingInfo.prototype.isCompletelyInFrustum = function (frustumPlanes) {
+            return this.boundingBox.isCompletelyInFrustum(frustumPlanes);
+        };
+
         BoundingInfo.prototype._checkCollision = function (collider) {
             return collider._canDoCollision(this.boundingSphere.centerWorld, this.boundingSphere.radiusWorld, this.boundingBox.minimumWorld, this.boundingBox.maximumWorld);
         };

+ 4 - 0
Babylon/Culling/babylon.boundingInfo.ts

@@ -44,6 +44,10 @@
             return this.boundingBox.isInFrustum(frustumPlanes);
         }
 
+        public isCompletelyInFrustum(frustumPlanes: Plane[]): boolean {
+            return this.boundingBox.isCompletelyInFrustum(frustumPlanes);
+        }
+       
         public _checkCollision(collider: Collider): boolean {
             return collider._canDoCollision(this.boundingSphere.centerWorld, this.boundingSphere.radiusWorld, this.boundingBox.minimumWorld, this.boundingBox.maximumWorld);
         }

+ 28 - 24
Babylon/Materials/babylon.standardMaterial.js

@@ -32,7 +32,7 @@ var BABYLON;
             this.emissiveColor = new BABYLON.Color3(0, 0, 0);
             this.useAlphaFromDiffuseTexture = false;
             this.useSpecularOverAlpha = true;
-			this.fogEnabled = true;
+            this.fogEnabled = true;
             this._cachedDefines = null;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
@@ -237,27 +237,29 @@ var BABYLON;
                     }
 
                     // Shadows
-                    var shadowGenerator = light.getShadowGenerator();
-                    if (mesh && mesh.receiveShadows && shadowGenerator) {
-                        defines.push("#define SHADOW" + lightIndex);
-                        fallbacks.addFallback(0, "SHADOW" + lightIndex);
-
-                        if (!shadowsActivated) {
-                            defines.push("#define SHADOWS");
-                            shadowsActivated = true;
-                        }
+                    if (scene.shadowsEnabled) {
+                        var shadowGenerator = light.getShadowGenerator();
+                        if (mesh && mesh.receiveShadows && shadowGenerator) {
+                            defines.push("#define SHADOW" + lightIndex);
+                            fallbacks.addFallback(0, "SHADOW" + lightIndex);
+
+                            if (!shadowsActivated) {
+                                defines.push("#define SHADOWS");
+                                shadowsActivated = true;
+                            }
 
-                        if (shadowGenerator.useVarianceShadowMap) {
-                            defines.push("#define SHADOWVSM" + lightIndex);
-                            if (lightIndex > 0) {
-                                fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                            if (shadowGenerator.useVarianceShadowMap) {
+                                defines.push("#define SHADOWVSM" + lightIndex);
+                                if (lightIndex > 0) {
+                                    fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                                }
                             }
-                        }
 
-                        if (shadowGenerator.usePoissonSampling) {
-                            defines.push("#define SHADOWPCF" + lightIndex);
-                            if (lightIndex > 0) {
-                                fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                            if (shadowGenerator.usePoissonSampling) {
+                                defines.push("#define SHADOWPCF" + lightIndex);
+                                if (lightIndex > 0) {
+                                    fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                                }
                             }
                         }
                     }
@@ -509,11 +511,13 @@ var BABYLON;
                     this._effect.setColor3("vLightSpecular" + lightIndex, this._scaledSpecular);
 
                     // Shadows
-                    var shadowGenerator = light.getShadowGenerator();
-                    if (mesh.receiveShadows && shadowGenerator) {
-                        this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
-                        this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMap());
-                        this._effect.setFloat("darkness" + lightIndex, shadowGenerator.getDarkness());
+                    if (scene.shadowsEnabled) {
+                        var shadowGenerator = light.getShadowGenerator();
+                        if (mesh.receiveShadows && shadowGenerator) {
+                            this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
+                            this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMap());
+                            this._effect.setFloat("darkness" + lightIndex, shadowGenerator.getDarkness());
+                        }
                     }
 
                     lightIndex++;

+ 27 - 23
Babylon/Materials/babylon.standardMaterial.ts

@@ -241,27 +241,29 @@
                     }
 
                     // Shadows
-                    var shadowGenerator = light.getShadowGenerator();
-                    if (mesh && mesh.receiveShadows && shadowGenerator) {
-                        defines.push("#define SHADOW" + lightIndex);
-                        fallbacks.addFallback(0, "SHADOW" + lightIndex);
-
-                        if (!shadowsActivated) {
-                            defines.push("#define SHADOWS");
-                            shadowsActivated = true;
-                        }
+                    if (scene.shadowsEnabled) {
+                        var shadowGenerator = light.getShadowGenerator();
+                        if (mesh && mesh.receiveShadows && shadowGenerator) {
+                            defines.push("#define SHADOW" + lightIndex);
+                            fallbacks.addFallback(0, "SHADOW" + lightIndex);
+
+                            if (!shadowsActivated) {
+                                defines.push("#define SHADOWS");
+                                shadowsActivated = true;
+                            }
 
-                        if (shadowGenerator.useVarianceShadowMap) {
-                            defines.push("#define SHADOWVSM" + lightIndex);
-                            if (lightIndex > 0) {
-                                fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                            if (shadowGenerator.useVarianceShadowMap) {
+                                defines.push("#define SHADOWVSM" + lightIndex);
+                                if (lightIndex > 0) {
+                                    fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                                }
                             }
-                        }
 
-                        if (shadowGenerator.usePoissonSampling) {
-                            defines.push("#define SHADOWPCF" + lightIndex);
-                            if (lightIndex > 0) {
-                                fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                            if (shadowGenerator.usePoissonSampling) {
+                                defines.push("#define SHADOWPCF" + lightIndex);
+                                if (lightIndex > 0) {
+                                    fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                                }
                             }
                         }
                     }
@@ -521,11 +523,13 @@
                     this._effect.setColor3("vLightSpecular" + lightIndex, this._scaledSpecular);
 
                     // Shadows
-                    var shadowGenerator = light.getShadowGenerator();
-                    if (mesh.receiveShadows && shadowGenerator) {
-                        this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
-                        this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMap());
-                        this._effect.setFloat("darkness" + lightIndex, shadowGenerator.getDarkness());
+                    if (scene.shadowsEnabled) {
+                        var shadowGenerator = light.getShadowGenerator();
+                        if (mesh.receiveShadows && shadowGenerator) {
+                            this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
+                            this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMap());
+                            this._effect.setFloat("darkness" + lightIndex, shadowGenerator.getDarkness());
+                        }
                     }
 
                     lightIndex++;

+ 23 - 0
Babylon/Materials/textures/babylon.baseTexture.js

@@ -68,6 +68,29 @@
             return { width: this._texture._baseWidth, height: this._texture._baseHeight };
         };
 
+        BaseTexture.prototype.scale = function (ratio) {
+        };
+
+        Object.defineProperty(BaseTexture.prototype, "canRescale", {
+            get: function () {
+                return false;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        BaseTexture.prototype._removeFromCache = function (url, noMipmap) {
+            var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
+            for (var index = 0; index < texturesCache.length; index++) {
+                var texturesCacheEntry = texturesCache[index];
+
+                if (texturesCacheEntry.url === url && texturesCacheEntry.noMipmap === noMipmap) {
+                    texturesCache.splice(index, 1);
+                    return;
+                }
+            }
+        };
+
         BaseTexture.prototype._getFromCache = function (url, noMipmap) {
             var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
             for (var index = 0; index < texturesCache.length; index++) {

+ 19 - 0
Babylon/Materials/textures/babylon.baseTexture.ts

@@ -75,6 +75,25 @@
             return { width: this._texture._baseWidth, height: this._texture._baseHeight };
         }
 
+        public scale(ratio: number): void {
+        }
+
+        public get canRescale(): boolean {
+            return false;
+        }
+
+        public _removeFromCache(url: string, noMipmap: boolean): void {
+            var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
+            for (var index = 0; index < texturesCache.length; index++) {
+                var texturesCacheEntry = texturesCache[index];
+
+                if (texturesCacheEntry.url === url && texturesCacheEntry.noMipmap === noMipmap) {
+                    texturesCache.splice(index, 1);
+                    return;
+                }
+            }
+        }
+
         public _getFromCache(url: string, noMipmap: boolean): WebGLTexture {
             var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
             for (var index = 0; index < texturesCache.length; index++) {

+ 22 - 0
Babylon/Materials/textures/babylon.dynamicTexture.js

@@ -38,6 +38,28 @@ var BABYLON;
             this._canvas.height = textureSize.height;
             this._context = this._canvas.getContext("2d");
         }
+        Object.defineProperty(DynamicTexture.prototype, "canRescale", {
+            get: function () {
+                return true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        DynamicTexture.prototype.scale = function (ratio) {
+            var textureSize = this.getSize();
+
+            textureSize.width *= ratio;
+            textureSize.height *= ratio;
+
+            this._canvas.width = textureSize.width;
+            this._canvas.height = textureSize.height;
+
+            this.releaseInternalTexture();
+
+            this._texture = this.getScene().getEngine().createDynamicTexture(textureSize.width, textureSize.height, this._generateMipMaps, this._samplingMode);
+        };
+
         DynamicTexture.prototype.getContext = function () {
             return this._context;
         };

+ 18 - 0
Babylon/Materials/textures/babylon.dynamicTexture.ts

@@ -34,6 +34,24 @@
             this._context = this._canvas.getContext("2d");
         }
 
+        public get canRescale(): boolean {
+            return true;
+        }
+
+        public scale(ratio: number): void {
+            var textureSize = this.getSize();
+
+            textureSize.width *= ratio;
+            textureSize.height *= ratio;
+
+            this._canvas.width = textureSize.width;
+            this._canvas.height = textureSize.height;
+
+            this.releaseInternalTexture();
+
+            this._texture = this.getScene().getEngine().createDynamicTexture(textureSize.width, textureSize.height, this._generateMipMaps, this._samplingMode);
+        }
+
         public getContext(): CanvasRenderingContext2D {
             return this._context;
         }

+ 14 - 0
Babylon/Materials/textures/babylon.renderTargetTexture.js

@@ -66,6 +66,20 @@ var BABYLON;
             return this._size;
         };
 
+        Object.defineProperty(RenderTargetTexture.prototype, "canRescale", {
+            get: function () {
+                return true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        RenderTargetTexture.prototype.scale = function (ratio) {
+            var newSize = this._size * ratio;
+
+            this.resize(newSize, this._generateMipMaps);
+        };
+
         RenderTargetTexture.prototype.resize = function (size, generateMipMaps) {
             this.releaseInternalTexture();
             this._texture = this.getScene().getEngine().createRenderTargetTexture(size, generateMipMaps);

+ 11 - 1
Babylon/Materials/textures/babylon.renderTargetTexture.ts

@@ -65,7 +65,17 @@
             return this._size;
         }
 
-        public resize(size, generateMipMaps) {
+        public get canRescale(): boolean {
+            return true;
+        }
+
+        public scale(ratio: number): void {
+            var newSize = this._size * ratio;
+
+            this.resize(newSize, this._generateMipMaps);
+        }
+
+        public resize(size:any, generateMipMaps?: boolean) {
             this.releaseInternalTexture();
             this._texture = this.getScene().getEngine().createRenderTargetTexture(size, generateMipMaps);
         }

+ 1 - 1
Babylon/Materials/textures/babylon.texture.ts

@@ -43,7 +43,7 @@
         private _cachedVAng: number;
         private _cachedWAng: number;
         private _cachedCoordinatesMode: number;
-        private _samplingMode: number;
+        public _samplingMode: number;
         private _buffer: any;
         private _deleteBuffer: boolean;
 

+ 2 - 2
Babylon/Materials/textures/babylon.videoTexture.js

@@ -43,7 +43,7 @@ var BABYLON;
                 _this.video.appendChild(source);
             });
 
-            this._lastUpdate = new Date().getTime();
+            this._lastUpdate = BABYLON.Tools.Now;
         }
         VideoTexture.prototype.update = function () {
             if (this._autoLaunch) {
@@ -51,7 +51,7 @@ var BABYLON;
                 this.video.play();
             }
 
-            var now = new Date().getTime();
+            var now = BABYLON.Tools.Now;
 
             if (now - this._lastUpdate < 15) {
                 return false;

+ 2 - 2
Babylon/Materials/textures/babylon.videoTexture.ts

@@ -37,7 +37,7 @@
                 this.video.appendChild(source);
             });
 
-            this._lastUpdate = new Date().getTime();
+            this._lastUpdate = Tools.Now;
         }
 
         public update(): boolean {
@@ -46,7 +46,7 @@
                 this.video.play();
             }
 
-            var now = new Date().getTime();
+            var now = Tools.Now;
 
             if (now - this._lastUpdate < 15) {
                 return false;

+ 14 - 0
Babylon/Mesh/babylon.abstractMesh.js

@@ -440,6 +440,20 @@ var BABYLON;
             return true;
         };
 
+        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.intersectsMesh = function (mesh, precise) {
             if (!this._boundingInfo || !mesh._boundingInfo) {
                 return false;

+ 16 - 1
Babylon/Mesh/babylon.abstractMesh.ts

@@ -330,7 +330,8 @@
 
                 var cameraGlobalPosition = new BABYLON.Vector3(cameraWorldMatrix.m[12], cameraWorldMatrix.m[13], cameraWorldMatrix.m[14]);
 
-                BABYLON.Matrix.TranslationToRef(this.position.x + cameraGlobalPosition.x, this.position.y + cameraGlobalPosition.y, this.position.z + cameraGlobalPosition.z, this._localTranslation);
+                BABYLON.Matrix.TranslationToRef(this.position.x + cameraGlobalPosition.x, this.position.y + cameraGlobalPosition.y,
+                                                this.position.z + cameraGlobalPosition.z, this._localTranslation);
             } else {
                 BABYLON.Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, this._localTranslation);
             }
@@ -435,6 +436,20 @@
             return true;
         }
 
+        public isCompletelyInFrustum(camera?: Camera): boolean {
+            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;
+        }
+
         public intersectsMesh(mesh: AbstractMesh, precise?: boolean): boolean {
             if (!this._boundingInfo || !mesh._boundingInfo) {
                 return false;

+ 4 - 4
Babylon/Rendering/babylon.renderingManager.js

@@ -11,7 +11,7 @@
             }
 
             // Particles
-            var beforeParticlesDate = new Date().getTime();
+            var beforeParticlesDate = BABYLON.Tools.Now;
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
 
@@ -25,7 +25,7 @@
                     this._scene._activeParticles += particleSystem.render();
                 }
             }
-            this._scene._particlesDuration += new Date().getTime() - beforeParticlesDate;
+            this._scene._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
         };
 
         RenderingManager.prototype._renderSprites = function (index) {
@@ -34,7 +34,7 @@
             }
 
             // Sprites
-            var beforeSpritessDate = new Date().getTime();
+            var beforeSpritessDate = BABYLON.Tools.Now;
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
                 var spriteManager = this._scene.spriteManagers[id];
 
@@ -43,7 +43,7 @@
                     spriteManager.render();
                 }
             }
-            this._scene._spritesDuration += new Date().getTime() - beforeSpritessDate;
+            this._scene._spritesDuration += BABYLON.Tools.Now - beforeSpritessDate;
         };
 
         RenderingManager.prototype._clearDepthBuffer = function () {

+ 4 - 4
Babylon/Rendering/babylon.renderingManager.ts

@@ -16,7 +16,7 @@
             }
 
             // Particles
-            var beforeParticlesDate = new Date().getTime();
+            var beforeParticlesDate = Tools.Now;
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
 
@@ -30,7 +30,7 @@
                     this._scene._activeParticles += particleSystem.render();
                 }
             }
-            this._scene._particlesDuration += new Date().getTime() - beforeParticlesDate;
+            this._scene._particlesDuration += Tools.Now - beforeParticlesDate;
         }
 
         private _renderSprites(index: number): void {
@@ -39,7 +39,7 @@
             }
 
             // Sprites       
-            var beforeSpritessDate = new Date().getTime();
+            var beforeSpritessDate = Tools.Now;
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
                 var spriteManager = this._scene.spriteManagers[id];
 
@@ -48,7 +48,7 @@
                     spriteManager.render();
                 }
             }
-            this._scene._spritesDuration += new Date().getTime() - beforeSpritessDate;
+            this._scene._spritesDuration += Tools.Now - beforeSpritessDate;
         }
 
         private _clearDepthBuffer(): void {

+ 271 - 0
Babylon/Tools/babylon.sceneOptimizer.js

@@ -0,0 +1,271 @@
+var __extends = this.__extends || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    __.prototype = b.prototype;
+    d.prototype = new __();
+};
+var BABYLON;
+(function (BABYLON) {
+    // Standard optimizations
+    var SceneOptimization = (function () {
+        function SceneOptimization(priority) {
+            if (typeof priority === "undefined") { priority = 0; }
+            this.priority = priority;
+            this.apply = function (scene) {
+                return true;
+            };
+        }
+        return SceneOptimization;
+    })();
+    BABYLON.SceneOptimization = SceneOptimization;
+
+    var TextureSceneOptimization = (function (_super) {
+        __extends(TextureSceneOptimization, _super);
+        function TextureSceneOptimization(maximumSize, priority) {
+            if (typeof maximumSize === "undefined") { maximumSize = 1024; }
+            if (typeof priority === "undefined") { priority = 0; }
+            var _this = this;
+            _super.call(this, priority);
+            this.maximumSize = maximumSize;
+            this.priority = priority;
+            this.apply = function (scene) {
+                var allDone = true;
+                for (var index = 0; index < scene.textures.length; index++) {
+                    var texture = scene.textures[index];
+
+                    if (!texture.canRescale) {
+                        continue;
+                    }
+
+                    var currentSize = texture.getSize();
+                    var maxDimension = Math.max(currentSize.width, currentSize.height);
+
+                    if (maxDimension > _this.maximumSize) {
+                        texture.scale(0.5);
+                        allDone = false;
+                    }
+                }
+
+                return allDone;
+            };
+        }
+        return TextureSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.TextureSceneOptimization = TextureSceneOptimization;
+
+    var HardwareScalingSceneOptimization = (function (_super) {
+        __extends(HardwareScalingSceneOptimization, _super);
+        function HardwareScalingSceneOptimization(maximumScale, priority) {
+            if (typeof maximumScale === "undefined") { maximumScale = 2; }
+            if (typeof priority === "undefined") { priority = 0; }
+            var _this = this;
+            _super.call(this, priority);
+            this.maximumScale = maximumScale;
+            this.priority = priority;
+            this._currentScale = 1;
+            this.apply = function (scene) {
+                _this._currentScale++;
+
+                scene.getEngine().setHardwareScalingLevel(_this._currentScale);
+
+                return _this._currentScale >= _this.maximumScale;
+            };
+        }
+        return HardwareScalingSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.HardwareScalingSceneOptimization = HardwareScalingSceneOptimization;
+
+    var ShadowsSceneOptimization = (function (_super) {
+        __extends(ShadowsSceneOptimization, _super);
+        function ShadowsSceneOptimization() {
+            _super.apply(this, arguments);
+            this.apply = function (scene) {
+                scene.shadowsEnabled = false;
+                return true;
+            };
+        }
+        return ShadowsSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.ShadowsSceneOptimization = ShadowsSceneOptimization;
+
+    var PostProcessesSceneOptimization = (function (_super) {
+        __extends(PostProcessesSceneOptimization, _super);
+        function PostProcessesSceneOptimization() {
+            _super.apply(this, arguments);
+            this.apply = function (scene) {
+                scene.postProcessesEnabled = false;
+                return true;
+            };
+        }
+        return PostProcessesSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.PostProcessesSceneOptimization = PostProcessesSceneOptimization;
+
+    var LensFlaresSceneOptimization = (function (_super) {
+        __extends(LensFlaresSceneOptimization, _super);
+        function LensFlaresSceneOptimization() {
+            _super.apply(this, arguments);
+            this.apply = function (scene) {
+                scene.lensFlaresEnabled = false;
+                return true;
+            };
+        }
+        return LensFlaresSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.LensFlaresSceneOptimization = LensFlaresSceneOptimization;
+
+    var ParticlesSceneOptimization = (function (_super) {
+        __extends(ParticlesSceneOptimization, _super);
+        function ParticlesSceneOptimization() {
+            _super.apply(this, arguments);
+            this.apply = function (scene) {
+                scene.particlesEnabled = false;
+                return true;
+            };
+        }
+        return ParticlesSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.ParticlesSceneOptimization = ParticlesSceneOptimization;
+
+    // Options
+    var SceneOptimizerOptions = (function () {
+        function SceneOptimizerOptions(targetFrameRate, trackerDuration) {
+            if (typeof targetFrameRate === "undefined") { targetFrameRate = 60; }
+            if (typeof trackerDuration === "undefined") { trackerDuration = 2000; }
+            this.targetFrameRate = targetFrameRate;
+            this.trackerDuration = trackerDuration;
+            this.optimizations = new Array();
+        }
+        SceneOptimizerOptions.LowDegradationAllowed = function (targetFrameRate) {
+            var result = new SceneOptimizerOptions(targetFrameRate);
+
+            var priority = 0;
+            result.optimizations.push(new ShadowsSceneOptimization(priority));
+            result.optimizations.push(new LensFlaresSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new PostProcessesSceneOptimization(priority));
+            result.optimizations.push(new ParticlesSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new TextureSceneOptimization(priority, 1024));
+
+            return result;
+        };
+
+        SceneOptimizerOptions.ModerateDegradationAllowed = function (targetFrameRate) {
+            var result = new SceneOptimizerOptions(targetFrameRate);
+
+            var priority = 0;
+            result.optimizations.push(new ShadowsSceneOptimization(priority));
+            result.optimizations.push(new LensFlaresSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new PostProcessesSceneOptimization(priority));
+            result.optimizations.push(new ParticlesSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new TextureSceneOptimization(priority, 512));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new HardwareScalingSceneOptimization(priority, 2));
+
+            return result;
+        };
+
+        SceneOptimizerOptions.HighDegradationAllowed = function (targetFrameRate) {
+            var result = new SceneOptimizerOptions(targetFrameRate);
+
+            var priority = 0;
+            result.optimizations.push(new ShadowsSceneOptimization(priority));
+            result.optimizations.push(new LensFlaresSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new PostProcessesSceneOptimization(priority));
+            result.optimizations.push(new ParticlesSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new TextureSceneOptimization(priority, 256));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new HardwareScalingSceneOptimization(priority, 4));
+
+            return result;
+        };
+        return SceneOptimizerOptions;
+    })();
+    BABYLON.SceneOptimizerOptions = SceneOptimizerOptions;
+
+    // Scene optimizer tool
+    var SceneOptimizer = (function () {
+        function SceneOptimizer() {
+        }
+        SceneOptimizer._CheckCurrentState = function (scene, options, currentPriorityLevel, onSuccess, onFailure) {
+            // TODO: add an epsilon
+            if (BABYLON.Tools.GetFps() >= options.targetFrameRate) {
+                if (onSuccess) {
+                    onSuccess();
+                }
+
+                return;
+            }
+
+            // Apply current level of optimizations
+            var allDone = true;
+            var noOptimizationApplied = true;
+            for (var index = 0; index < options.optimizations.length; index++) {
+                var optimization = options.optimizations[index];
+
+                if (optimization.priority === currentPriorityLevel) {
+                    noOptimizationApplied = false;
+                    allDone = allDone && optimization.apply(scene);
+                }
+            }
+
+            // If no optimization was applied, this is a failure :(
+            if (noOptimizationApplied) {
+                if (onFailure) {
+                    onFailure();
+                }
+
+                return;
+            }
+
+            // If all optimizations were done, move to next level
+            if (allDone) {
+                currentPriorityLevel++;
+            }
+
+            // Let's the system running for a specific amount of time before checking FPS
+            scene.executeWhenReady(function () {
+                setTimeout(function () {
+                    SceneOptimizer._CheckCurrentState(scene, options, currentPriorityLevel, onSuccess, onFailure);
+                }, options.trackerDuration);
+            });
+        };
+
+        SceneOptimizer.OptimizeAsync = function (scene, options, onSuccess, onFailure) {
+            if (!options) {
+                options = SceneOptimizerOptions.ModerateDegradationAllowed();
+            }
+
+            // Let's the system running for a specific amount of time before checking FPS
+            scene.executeWhenReady(function () {
+                setTimeout(function () {
+                    SceneOptimizer._CheckCurrentState(scene, options, 0, onSuccess, onFailure);
+                }, options.trackerDuration);
+            });
+        };
+        return SceneOptimizer;
+    })();
+    BABYLON.SceneOptimizer = SceneOptimizer;
+})(BABYLON || (BABYLON = {}));
+//# sourceMappingURL=babylon.sceneOptimizer.js.map

+ 218 - 0
Babylon/Tools/babylon.sceneOptimizer.ts

@@ -0,0 +1,218 @@
+module BABYLON {
+    // Standard optimizations
+    export class SceneOptimization {
+        public apply = (scene: Scene): boolean => {
+            return true; // Return true if everything that can be done was applied
+        };
+
+        constructor(public priority: number = 0) {
+        }
+    }
+
+    export class TextureSceneOptimization extends SceneOptimization {
+        constructor(public maximumSize: number = 1024, public priority: number = 0) {
+            super(priority);
+        }
+
+        public apply = (scene: Scene): boolean => {
+
+            var allDone = true;
+            for (var index = 0; index < scene.textures.length; index++) {
+                var texture = scene.textures[index];
+
+                if (!texture.canRescale) {
+                    continue;
+                }
+
+                var currentSize = texture.getSize();
+                var maxDimension = Math.max(currentSize.width, currentSize.height);
+
+                if (maxDimension > this.maximumSize) {
+                    texture.scale(0.5);
+                    allDone = false;
+                }
+            }
+
+            return allDone;
+        }
+    }
+
+    export class HardwareScalingSceneOptimization extends SceneOptimization {
+        private _currentScale = 1;
+
+        constructor(public maximumScale: number = 2, public priority: number = 0) {
+            super(priority);
+        }
+
+        public apply = (scene: Scene): boolean => {
+            this._currentScale++;
+
+            scene.getEngine().setHardwareScalingLevel(this._currentScale);
+
+            return this._currentScale >= this.maximumScale;
+        };
+    }
+
+    export class ShadowsSceneOptimization extends SceneOptimization {
+        public apply = (scene: Scene): boolean => {
+            scene.shadowsEnabled = false;
+            return true;
+        };
+    }
+
+    export class PostProcessesSceneOptimization extends SceneOptimization {
+        public apply = (scene: Scene): boolean => {
+            scene.postProcessesEnabled = false;
+            return true;
+        };
+    }
+
+    export class LensFlaresSceneOptimization extends SceneOptimization {
+        public apply = (scene: Scene): boolean => {
+            scene.lensFlaresEnabled = false;
+            return true;
+        };
+    }
+
+    export class ParticlesSceneOptimization extends SceneOptimization {
+        public apply = (scene: Scene): boolean => {
+            scene.particlesEnabled = false;
+            return true;
+        };
+    }
+
+    // Options
+    export class SceneOptimizerOptions {
+        public optimizations = new Array<SceneOptimization>();
+
+        constructor(public targetFrameRate: number = 60, public trackerDuration: number = 2000) {
+        }
+
+        public static LowDegradationAllowed(targetFrameRate?: number): SceneOptimizerOptions {
+            var result = new SceneOptimizerOptions(targetFrameRate);
+
+            var priority = 0;
+            result.optimizations.push(new ShadowsSceneOptimization(priority));
+            result.optimizations.push(new LensFlaresSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new PostProcessesSceneOptimization(priority));
+            result.optimizations.push(new ParticlesSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new TextureSceneOptimization(priority, 1024));
+
+            return result;
+        }
+
+        public static ModerateDegradationAllowed(targetFrameRate?: number): SceneOptimizerOptions {
+            var result = new SceneOptimizerOptions(targetFrameRate);
+
+            var priority = 0;
+            result.optimizations.push(new ShadowsSceneOptimization(priority));
+            result.optimizations.push(new LensFlaresSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new PostProcessesSceneOptimization(priority));
+            result.optimizations.push(new ParticlesSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new TextureSceneOptimization(priority, 512));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new HardwareScalingSceneOptimization(priority, 2));
+
+            return result;
+        }
+
+        public static HighDegradationAllowed(targetFrameRate?: number): SceneOptimizerOptions {
+            var result = new SceneOptimizerOptions(targetFrameRate);
+
+            var priority = 0;
+            result.optimizations.push(new ShadowsSceneOptimization(priority));
+            result.optimizations.push(new LensFlaresSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new PostProcessesSceneOptimization(priority));
+            result.optimizations.push(new ParticlesSceneOptimization(priority));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new TextureSceneOptimization(priority, 256));
+
+            // Next priority
+            priority++;
+            result.optimizations.push(new HardwareScalingSceneOptimization(priority, 4));
+
+            return result;
+        }
+    }
+
+
+    // Scene optimizer tool
+    export class SceneOptimizer {
+
+        static _CheckCurrentState(scene: Scene, options: SceneOptimizerOptions, currentPriorityLevel: number, onSuccess?: () => void, onFailure?: () => void) {
+            // TODO: add an epsilon
+            if (Tools.GetFps() >= options.targetFrameRate) {
+                if (onSuccess) {
+                    onSuccess();
+                }
+
+                return;
+            }
+
+            // Apply current level of optimizations
+            var allDone = true;
+            var noOptimizationApplied = true;
+            for (var index = 0; index < options.optimizations.length; index++) {
+                var optimization = options.optimizations[index];
+
+                if (optimization.priority === currentPriorityLevel) {
+                    noOptimizationApplied = false;
+                    allDone = allDone && optimization.apply(scene);
+                }
+            }
+
+            // If no optimization was applied, this is a failure :(
+            if (noOptimizationApplied) {
+                if (onFailure) {
+                    onFailure();
+                }
+
+                return;
+            }
+
+            // If all optimizations were done, move to next level
+            if (allDone) {
+                currentPriorityLevel++;
+            }
+
+            // Let's the system running for a specific amount of time before checking FPS
+            scene.executeWhenReady(() => {
+                setTimeout(() => {
+                    SceneOptimizer._CheckCurrentState(scene, options, currentPriorityLevel, onSuccess, onFailure);
+                }, options.trackerDuration);
+            });
+        }
+
+        public static OptimizeAsync(scene: Scene, options?: SceneOptimizerOptions, onSuccess?: () => void, onFailure?: () => void): void {
+            if (!options) {
+                options = SceneOptimizerOptions.ModerateDegradationAllowed();
+            }
+
+            // Let's the system running for a specific amount of time before checking FPS
+            scene.executeWhenReady(() => {
+                setTimeout(() => {
+                    SceneOptimizer._CheckCurrentState(scene, options, 0, onSuccess, onFailure);
+                }, options.trackerDuration);
+            });
+        }
+    }
+} 

+ 116 - 1
Babylon/Tools/babylon.tools.js

@@ -389,7 +389,7 @@
         };
 
         Tools._MeasureFps = function () {
-            previousFramesDuration.push((new Date).getTime());
+            previousFramesDuration.push(Tools.Now);
             var length = previousFramesDuration.length;
 
             if (length >= 2) {
@@ -650,6 +650,112 @@
             enumerable: true,
             configurable: true
         });
+
+        Object.defineProperty(Tools, "PerformanceNoneLogLevel", {
+            get: function () {
+                return Tools._PerformanceNoneLogLevel;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        Object.defineProperty(Tools, "PerformanceUserMarkLogLevel", {
+            get: function () {
+                return Tools._PerformanceUserMarkLogLevel;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        Object.defineProperty(Tools, "PerformanceConsoleLogLevel", {
+            get: function () {
+                return Tools._PerformanceConsoleLogLevel;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        Object.defineProperty(Tools, "PerformanceLogLevel", {
+            set: function (level) {
+                if ((level & Tools.PerformanceUserMarkLogLevel) === Tools.PerformanceUserMarkLogLevel) {
+                    Tools.StartPerformanceCounter = Tools._StartUserMark;
+                    Tools.EndPerformanceCounter = Tools._EndUserMark;
+                    return;
+                }
+
+                if ((level & Tools.PerformanceConsoleLogLevel) === Tools.PerformanceConsoleLogLevel) {
+                    Tools.StartPerformanceCounter = Tools._StartPerformanceConsole;
+                    Tools.EndPerformanceCounter = Tools._EndPerformanceConsole;
+                    return;
+                }
+
+                Tools.StartPerformanceCounter = Tools._StartPerformanceCounterDisabled;
+                Tools.EndPerformanceCounter = Tools._EndPerformanceCounterDisabled;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        Tools._StartPerformanceCounterDisabled = function (counterName, condition) {
+        };
+
+        Tools._EndPerformanceCounterDisabled = function (counterName, condition) {
+        };
+
+        Tools._StartUserMark = function (counterName, condition) {
+            if (typeof condition === "undefined") { condition = true; }
+            if (!condition || !Tools._performance.mark) {
+                return;
+            }
+            Tools._performance.mark(counterName + "-Begin");
+        };
+
+        Tools._EndUserMark = function (counterName, condition) {
+            if (typeof condition === "undefined") { condition = true; }
+            if (!condition || !Tools._performance.mark) {
+                return;
+            }
+            Tools._performance.mark(counterName + "-End");
+            Tools._performance.measure(counterName, counterName + "-Begin", counterName + "-End");
+        };
+
+        Tools._StartPerformanceConsole = function (counterName, condition) {
+            if (typeof condition === "undefined") { condition = true; }
+            if (!condition) {
+                return;
+            }
+
+            Tools._StartUserMark(counterName, condition);
+
+            if (console.time) {
+                console.time(counterName);
+            }
+        };
+
+        Tools._EndPerformanceConsole = function (counterName, condition) {
+            if (typeof condition === "undefined") { condition = true; }
+            if (!condition) {
+                return;
+            }
+
+            Tools._EndUserMark(counterName, condition);
+
+            if (console.time) {
+                console.timeEnd(counterName);
+            }
+        };
+
+        Object.defineProperty(Tools, "Now", {
+            get: function () {
+                if (window.performance.now) {
+                    return window.performance.now();
+                }
+
+                return new Date().getTime();
+            },
+            enumerable: true,
+            configurable: true
+        });
         Tools.BaseUrl = "";
 
         Tools.GetExponantOfTwo = function (value, max) {
@@ -675,6 +781,15 @@
         Tools.Warn = Tools._WarnEnabled;
 
         Tools.Error = Tools._ErrorEnabled;
+
+        Tools._PerformanceNoneLogLevel = 0;
+        Tools._PerformanceUserMarkLogLevel = 1;
+        Tools._PerformanceConsoleLogLevel = 2;
+
+        Tools._performance = window.performance;
+
+        Tools.StartPerformanceCounter = Tools._StartPerformanceCounterDisabled;
+        Tools.EndPerformanceCounter = Tools._EndPerformanceCounterDisabled;
         return Tools;
     })();
     BABYLON.Tools = Tools;

+ 93 - 1
Babylon/Tools/babylon.tools.ts

@@ -424,7 +424,7 @@
         }
 
         public static _MeasureFps(): void {
-            previousFramesDuration.push((new Date).getTime());
+            previousFramesDuration.push(Tools.Now);
             var length = previousFramesDuration.length;
 
             if (length >= 2) {
@@ -689,5 +689,97 @@
                 Tools.Error = Tools._ErrorDisabled;
             }
         }
+
+        // Performances
+        private static _PerformanceNoneLogLevel = 0;
+        private static _PerformanceUserMarkLogLevel = 1;
+        private static _PerformanceConsoleLogLevel = 2;
+
+        private static _performance: Performance = window.performance;
+
+        static get PerformanceNoneLogLevel(): number {
+            return Tools._PerformanceNoneLogLevel;
+        }
+
+        static get PerformanceUserMarkLogLevel(): number {
+            return Tools._PerformanceUserMarkLogLevel;
+        }
+
+        static get PerformanceConsoleLogLevel(): number {
+            return Tools._PerformanceConsoleLogLevel;
+        }
+
+        public static set PerformanceLogLevel(level: number) {
+            if ((level & Tools.PerformanceUserMarkLogLevel) === Tools.PerformanceUserMarkLogLevel) {
+                Tools.StartPerformanceCounter = Tools._StartUserMark;
+                Tools.EndPerformanceCounter = Tools._EndUserMark;
+                return;
+            }
+
+            if ((level & Tools.PerformanceConsoleLogLevel) === Tools.PerformanceConsoleLogLevel) {
+                Tools.StartPerformanceCounter = Tools._StartPerformanceConsole;
+                Tools.EndPerformanceCounter = Tools._EndPerformanceConsole;
+                return;
+            }
+
+            Tools.StartPerformanceCounter = Tools._StartPerformanceCounterDisabled;
+            Tools.EndPerformanceCounter = Tools._EndPerformanceCounterDisabled;
+        }
+
+        static _StartPerformanceCounterDisabled(counterName: string, condition?: boolean): void {
+        }
+
+        static _EndPerformanceCounterDisabled(counterName: string, condition?: boolean): void {
+        }
+
+        static _StartUserMark(counterName: string, condition = true): void {
+            if (!condition || !Tools._performance.mark) {
+                return;
+            }
+            Tools._performance.mark(counterName + "-Begin");
+        }
+
+        static _EndUserMark(counterName: string, condition = true): void {
+            if (!condition || !Tools._performance.mark) {
+                return;
+            }
+            Tools._performance.mark(counterName + "-End");
+            Tools._performance.measure(counterName, counterName + "-Begin", counterName + "-End");
+        }
+
+        static _StartPerformanceConsole(counterName: string, condition = true): void {
+            if (!condition) {
+                return;
+            }
+
+            Tools._StartUserMark(counterName, condition);
+
+            if (console.time) {
+                console.time(counterName);
+            }
+        }
+
+        static _EndPerformanceConsole(counterName: string, condition = true): void {
+            if (!condition) {
+                return;
+            }
+
+            Tools._EndUserMark(counterName, condition);
+
+            if (console.time) {
+                console.timeEnd(counterName);
+            }
+        }
+
+        public static StartPerformanceCounter: (counterName: string, condition?: boolean) => void = Tools._StartPerformanceCounterDisabled;
+        public static EndPerformanceCounter: (counterName: string, condition?: boolean) => void = Tools._EndPerformanceCounterDisabled;
+
+        public static get Now(): number {
+            if (window.performance.now) {
+                return window.performance.now();
+            }
+
+            return new Date().getTime();
+        }
     }
 } 

+ 1 - 1
Babylon/babylon.engine.js

@@ -1242,7 +1242,7 @@
                 else
                     callback(buffer);
             } else if (isDDS) {
-                var callback = function (data) {
+                callback = function (data) {
                     var info = BABYLON.Internals.DDSTools.GetDDSInfo(data);
 
                     var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap && ((info.width >> (info.mipmapCount - 1)) == 1);

+ 1 - 1
Babylon/babylon.engine.ts

@@ -1254,7 +1254,7 @@
                     callback(buffer);
 
             } else if (isDDS) {
-                var callback = (data) => {
+                callback = (data) => {
                     var info = BABYLON.Internals.DDSTools.GetDDSInfo(data);
 
                     var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap && ((info.width >> (info.mipmapCount - 1)) == 1);

+ 53 - 21
Babylon/babylon.scene.js

@@ -16,6 +16,7 @@
             this.fogStart = 0;
             this.fogEnd = 1000.0;
             // Lights
+            this.shadowsEnabled = true;
             this.lightsEnabled = true;
             this.lights = new Array();
             // Cameras
@@ -41,6 +42,7 @@
             // Skeletons
             this.skeletons = new Array();
             // Lens flares
+            this.lensFlaresEnabled = true;
             this.lensFlareSystems = new Array();
             // Collisions
             this.collisionsEnabled = true;
@@ -431,11 +433,11 @@
 
         Scene.prototype._animate = function () {
             if (!this._animationStartDate) {
-                this._animationStartDate = new Date().getTime();
+                this._animationStartDate = BABYLON.Tools.Now;
             }
 
             // Getting time
-            var now = new Date().getTime();
+            var now = BABYLON.Tools.Now;
             var delay = now - this._animationStartDate;
 
             for (var index = 0; index < this._activeAnimatables.length; index++) {
@@ -737,8 +739,9 @@
             }
 
             // Particle systems
-            var beforeParticlesDate = new Date().getTime();
+            var beforeParticlesDate = BABYLON.Tools.Now;
             if (this.particlesEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
                 for (var particleIndex = 0; particleIndex < this.particleSystems.length; particleIndex++) {
                     var particleSystem = this.particleSystems[particleIndex];
 
@@ -751,8 +754,9 @@
                         particleSystem.animate();
                     }
                 }
+                BABYLON.Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
             }
-            this._particlesDuration += new Date().getTime() - beforeParticlesDate;
+            this._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
         };
 
         Scene.prototype._activeMesh = function (mesh) {
@@ -799,6 +803,8 @@
             if (!this.activeCamera)
                 throw new Error("Active camera not set");
 
+            BABYLON.Tools.StartPerformanceCounter("Rendering camera " + this.activeCamera.name);
+
             // Viewport
             engine.setViewport(this.activeCamera.viewport);
 
@@ -811,9 +817,11 @@
             }
 
             // Meshes
-            var beforeEvaluateActiveMeshesDate = new Date().getTime();
+            var beforeEvaluateActiveMeshesDate = BABYLON.Tools.Now;
+            BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
             this._evaluateActiveMeshes();
-            this._evaluateActiveMeshesDuration += new Date().getTime() - beforeEvaluateActiveMeshesDate;
+            this._evaluateActiveMeshesDuration += BABYLON.Tools.Now - beforeEvaluateActiveMeshesDate;
+            BABYLON.Tools.EndPerformanceCounter("Active meshes evaluation");
 
             for (var skeletonIndex = 0; skeletonIndex < this._activeSkeletons.length; skeletonIndex++) {
                 var skeleton = this._activeSkeletons.data[skeletonIndex];
@@ -822,8 +830,9 @@
             }
 
             // Render targets
-            var beforeRenderTargetDate = new Date().getTime();
+            var beforeRenderTargetDate = BABYLON.Tools.Now;
             if (this.renderTargetsEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Render targets", this._renderTargets.length > 0);
                 for (var renderIndex = 0; renderIndex < this._renderTargets.length; renderIndex++) {
                     var renderTarget = this._renderTargets.data[renderIndex];
                     if (renderTarget._shouldRender()) {
@@ -831,18 +840,19 @@
                         renderTarget.render();
                     }
                 }
+                BABYLON.Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
                 this._renderId++;
             }
 
             if (this._renderTargets.length > 0) {
                 engine.restoreDefaultFramebuffer();
             }
-            this._renderTargetsDuration += new Date().getTime() - beforeRenderTargetDate;
+            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
 
             // Prepare Frame
             this.postProcessManager._prepareFrame();
 
-            var beforeRenderDate = new Date().getTime();
+            var beforeRenderDate = BABYLON.Tools.Now;
 
             // Backgrounds
             if (this.layers.length) {
@@ -859,13 +869,20 @@
             }
 
             // Render
+            BABYLON.Tools.StartPerformanceCounter("Main render");
             this._renderingManager.render(null, null, true, true);
+            BABYLON.Tools.EndPerformanceCounter("Main render");
 
             // Bounding boxes
             this._boundingBoxRenderer.render();
 
-            for (var lensFlareSystemIndex = 0; lensFlareSystemIndex < this.lensFlareSystems.length; lensFlareSystemIndex++) {
-                this.lensFlareSystems[lensFlareSystemIndex].render();
+            // Lens flares
+            if (this.lensFlaresEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Lens flares", this.lensFlareSystems.length > 0);
+                for (var lensFlareSystemIndex = 0; lensFlareSystemIndex < this.lensFlareSystems.length; lensFlareSystemIndex++) {
+                    this.lensFlareSystems[lensFlareSystemIndex].render();
+                }
+                BABYLON.Tools.EndPerformanceCounter("Lens flares", this.lensFlareSystems.length > 0);
             }
 
             // Foregrounds
@@ -880,7 +897,7 @@
                 engine.setDepthBuffer(true);
             }
 
-            this._renderDuration += new Date().getTime() - beforeRenderDate;
+            this._renderDuration += BABYLON.Tools.Now - beforeRenderDate;
 
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
@@ -894,6 +911,8 @@
             if (this.afterCameraRender) {
                 this.afterCameraRender(this.activeCamera);
             }
+
+            BABYLON.Tools.EndPerformanceCounter("Rendering camera " + this.activeCamera.name);
         };
 
         Scene.prototype._processSubCameras = function (camera) {
@@ -944,7 +963,7 @@
         };
 
         Scene.prototype.render = function () {
-            var startDate = new Date().getTime();
+            var startDate = BABYLON.Tools.Now;
             this._particlesDuration = 0;
             this._spritesDuration = 0;
             this._activeParticles = 0;
@@ -954,6 +973,8 @@
             this._activeVertices = 0;
             this._meshesForIntersections.reset();
 
+            BABYLON.Tools.StartPerformanceCounter("Scene rendering");
+
             // Actions
             if (this.actionManager) {
                 this.actionManager.processTrigger(BABYLON.ActionManager.OnEveryFrameTrigger, null);
@@ -975,13 +996,16 @@
 
             // Physics
             if (this._physicsEngine) {
+                BABYLON.Tools.StartPerformanceCounter("Physics");
                 this._physicsEngine._runOneStep(deltaTime / 1000.0);
+                BABYLON.Tools.EndPerformanceCounter("Physics");
             }
 
             // Customs render targets
-            var beforeRenderTargetDate = new Date().getTime();
+            var beforeRenderTargetDate = BABYLON.Tools.Now;
             var engine = this.getEngine();
             if (this.renderTargetsEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);
                 for (var customIndex = 0; customIndex < this.customRenderTargets.length; customIndex++) {
                     var renderTarget = this.customRenderTargets[customIndex];
                     if (renderTarget._shouldRender()) {
@@ -1001,33 +1025,40 @@
                         renderTarget.render();
                     }
                 }
+                BABYLON.Tools.EndPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);
+
                 this._renderId++;
             }
 
             if (this.customRenderTargets.length > 0) {
                 engine.restoreDefaultFramebuffer();
             }
-            this._renderTargetsDuration += new Date().getTime() - beforeRenderTargetDate;
+            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
 
             // Procedural textures
             if (this.proceduralTexturesEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Procedural textures", this._proceduralTextures.length > 0);
                 for (var proceduralIndex = 0; proceduralIndex < this._proceduralTextures.length; proceduralIndex++) {
                     var proceduralTexture = this._proceduralTextures[proceduralIndex];
                     if (proceduralTexture._shouldRender()) {
                         proceduralTexture.render();
                     }
                 }
+                BABYLON.Tools.EndPerformanceCounter("Procedural textures", this._proceduralTextures.length > 0);
             }
 
             // Clear
             this._engine.clear(this.clearColor, this.autoClear || this.forceWireframe, true);
 
-            for (var lightIndex = 0; lightIndex < this.lights.length; lightIndex++) {
-                var light = this.lights[lightIndex];
-                var shadowGenerator = light.getShadowGenerator();
+            // Shadows
+            if (this.shadowsEnabled) {
+                for (var lightIndex = 0; lightIndex < this.lights.length; lightIndex++) {
+                    var light = this.lights[lightIndex];
+                    var shadowGenerator = light.getShadowGenerator();
 
-                if (light.isEnabled() && shadowGenerator && shadowGenerator.getShadowMap().getScene().textures.indexOf(shadowGenerator.getShadowMap()) !== -1) {
-                    this._renderTargets.push(shadowGenerator.getShadowMap());
+                    if (light.isEnabled() && shadowGenerator && shadowGenerator.getShadowMap().getScene().textures.indexOf(shadowGenerator.getShadowMap()) !== -1) {
+                        this._renderTargets.push(shadowGenerator.getShadowMap());
+                    }
                 }
             }
 
@@ -1064,7 +1095,8 @@
 
             this._toBeDisposed.reset();
 
-            this._lastFrameDuration = new Date().getTime() - startDate;
+            BABYLON.Tools.EndPerformanceCounter("Scene rendering");
+            this._lastFrameDuration = BABYLON.Tools.Now - startDate;
         };
 
         Scene.prototype.dispose = function () {

+ 51 - 21
Babylon/babylon.scene.ts

@@ -46,6 +46,7 @@
         public fogEnd = 1000.0;
 
         // Lights
+        public shadowsEnabled = true;
         public lightsEnabled = true;
         public lights = new Array<Light>();
 
@@ -82,6 +83,7 @@
         public skeletons = new Array<Skeleton>();
 
         // Lens flares
+        public lensFlaresEnabled = true;
         public lensFlareSystems = new Array<LensFlareSystem>();
 
         // Collisions
@@ -508,10 +510,10 @@
 
         private _animate(): void {
             if (!this._animationStartDate) {
-                this._animationStartDate = new Date().getTime();
+                this._animationStartDate = Tools.Now;
             }
             // Getting time
-            var now = new Date().getTime();
+            var now = Tools.Now;
             var delay = now - this._animationStartDate;
 
             for (var index = 0; index < this._activeAnimatables.length; index++) {
@@ -813,8 +815,9 @@
             }
 
             // Particle systems
-            var beforeParticlesDate = new Date().getTime();
+            var beforeParticlesDate = Tools.Now;
             if (this.particlesEnabled) {
+                Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
                 for (var particleIndex = 0; particleIndex < this.particleSystems.length; particleIndex++) {
                     var particleSystem = this.particleSystems[particleIndex];
 
@@ -827,8 +830,9 @@
                         particleSystem.animate();
                     }
                 }
+                Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
             }
-            this._particlesDuration += new Date().getTime() - beforeParticlesDate;
+            this._particlesDuration += Tools.Now - beforeParticlesDate;
         }
 
         private _activeMesh(mesh: AbstractMesh): void {
@@ -875,6 +879,8 @@
             if (!this.activeCamera)
                 throw new Error("Active camera not set");
 
+            Tools.StartPerformanceCounter("Rendering camera " + this.activeCamera.name);
+
             // Viewport
             engine.setViewport(this.activeCamera.viewport);
 
@@ -887,9 +893,11 @@
             }
 
             // Meshes
-            var beforeEvaluateActiveMeshesDate = new Date().getTime();
+            var beforeEvaluateActiveMeshesDate = Tools.Now;
+            Tools.StartPerformanceCounter("Active meshes evaluation");
             this._evaluateActiveMeshes();
-            this._evaluateActiveMeshesDuration += new Date().getTime() - beforeEvaluateActiveMeshesDate;
+            this._evaluateActiveMeshesDuration += Tools.Now - beforeEvaluateActiveMeshesDate;
+            Tools.EndPerformanceCounter("Active meshes evaluation");
 
             // Skeletons
             for (var skeletonIndex = 0; skeletonIndex < this._activeSkeletons.length; skeletonIndex++) {
@@ -899,8 +907,9 @@
             }
 
             // Render targets
-            var beforeRenderTargetDate = new Date().getTime();
+            var beforeRenderTargetDate = Tools.Now;
             if (this.renderTargetsEnabled) {
+                Tools.StartPerformanceCounter("Render targets", this._renderTargets.length > 0);
                 for (var renderIndex = 0; renderIndex < this._renderTargets.length; renderIndex++) {
                     var renderTarget = this._renderTargets.data[renderIndex];
                     if (renderTarget._shouldRender()) {
@@ -908,18 +917,19 @@
                         renderTarget.render();
                     }
                 }
+                Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
                 this._renderId++;
             }
 
             if (this._renderTargets.length > 0) { // Restore back buffer
                 engine.restoreDefaultFramebuffer();
             }
-            this._renderTargetsDuration += new Date().getTime() - beforeRenderTargetDate;
+            this._renderTargetsDuration += Tools.Now - beforeRenderTargetDate;
 
             // Prepare Frame
             this.postProcessManager._prepareFrame();
 
-            var beforeRenderDate = new Date().getTime();
+            var beforeRenderDate = Tools.Now;
             // Backgrounds
             if (this.layers.length) {
                 engine.setDepthBuffer(false);
@@ -935,14 +945,20 @@
             }
 
             // Render
+            Tools.StartPerformanceCounter("Main render");
             this._renderingManager.render(null, null, true, true);
+            Tools.EndPerformanceCounter("Main render");
 
             // Bounding boxes
             this._boundingBoxRenderer.render();
 
             // Lens flares
-            for (var lensFlareSystemIndex = 0; lensFlareSystemIndex < this.lensFlareSystems.length; lensFlareSystemIndex++) {
-                this.lensFlareSystems[lensFlareSystemIndex].render();
+            if (this.lensFlaresEnabled) {
+                Tools.StartPerformanceCounter("Lens flares", this.lensFlareSystems.length > 0);
+                for (var lensFlareSystemIndex = 0; lensFlareSystemIndex < this.lensFlareSystems.length; lensFlareSystemIndex++) {
+                    this.lensFlareSystems[lensFlareSystemIndex].render();
+                }
+                Tools.EndPerformanceCounter("Lens flares", this.lensFlareSystems.length > 0);
             }
 
             // Foregrounds
@@ -957,7 +973,7 @@
                 engine.setDepthBuffer(true);
             }
 
-            this._renderDuration += new Date().getTime() - beforeRenderDate;
+            this._renderDuration += Tools.Now - beforeRenderDate;
 
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
@@ -972,6 +988,7 @@
                 this.afterCameraRender(this.activeCamera);
             }
 
+            Tools.EndPerformanceCounter("Rendering camera " + this.activeCamera.name);
         }
 
         private _processSubCameras(camera: Camera): void {
@@ -1024,7 +1041,7 @@
         }
 
         public render(): void {
-            var startDate = new Date().getTime();
+            var startDate = Tools.Now;
             this._particlesDuration = 0;
             this._spritesDuration = 0;
             this._activeParticles = 0;
@@ -1034,6 +1051,8 @@
             this._activeVertices = 0;
             this._meshesForIntersections.reset();
 
+            Tools.StartPerformanceCounter("Scene rendering");
+
             // Actions
             if (this.actionManager) {
                 this.actionManager.processTrigger(ActionManager.OnEveryFrameTrigger, null);
@@ -1055,13 +1074,16 @@
 
             // Physics
             if (this._physicsEngine) {
+                Tools.StartPerformanceCounter("Physics");
                 this._physicsEngine._runOneStep(deltaTime / 1000.0);
+                Tools.EndPerformanceCounter("Physics");
             }
 
             // Customs render targets
-            var beforeRenderTargetDate = new Date().getTime();
+            var beforeRenderTargetDate = Tools.Now;
             var engine = this.getEngine();
             if (this.renderTargetsEnabled) {
+                Tools.StartPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);
                 for (var customIndex = 0; customIndex < this.customRenderTargets.length; customIndex++) {
                     var renderTarget = this.customRenderTargets[customIndex];
                     if (renderTarget._shouldRender()) {
@@ -1081,34 +1103,40 @@
                         renderTarget.render();
                     }
                 }
+                Tools.EndPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);
+
                 this._renderId++;
             }
 
             if (this.customRenderTargets.length > 0) { // Restore back buffer
                 engine.restoreDefaultFramebuffer();
             }
-            this._renderTargetsDuration += new Date().getTime() - beforeRenderTargetDate;
+            this._renderTargetsDuration += Tools.Now - beforeRenderTargetDate;
 
             // Procedural textures
             if (this.proceduralTexturesEnabled) {
+                Tools.StartPerformanceCounter("Procedural textures", this._proceduralTextures.length > 0);
                 for (var proceduralIndex = 0; proceduralIndex < this._proceduralTextures.length; proceduralIndex++) {
                     var proceduralTexture = this._proceduralTextures[proceduralIndex];
                     if (proceduralTexture._shouldRender()) {
                         proceduralTexture.render();
                     }
                 }
+                Tools.EndPerformanceCounter("Procedural textures", this._proceduralTextures.length > 0);
             }
 
             // Clear
             this._engine.clear(this.clearColor, this.autoClear || this.forceWireframe, true);
 
             // Shadows
-            for (var lightIndex = 0; lightIndex < this.lights.length; lightIndex++) {
-                var light = this.lights[lightIndex];
-                var shadowGenerator = light.getShadowGenerator();
+            if (this.shadowsEnabled) {
+                for (var lightIndex = 0; lightIndex < this.lights.length; lightIndex++) {
+                    var light = this.lights[lightIndex];
+                    var shadowGenerator = light.getShadowGenerator();
 
-                if (light.isEnabled() && shadowGenerator && shadowGenerator.getShadowMap().getScene().textures.indexOf(shadowGenerator.getShadowMap()) !== -1) {
-                    this._renderTargets.push(shadowGenerator.getShadowMap());
+                    if (light.isEnabled() && shadowGenerator && shadowGenerator.getShadowMap().getScene().textures.indexOf(shadowGenerator.getShadowMap()) !== -1) {
+                        this._renderTargets.push(shadowGenerator.getShadowMap());
+                    }
                 }
             }
 
@@ -1146,7 +1174,9 @@
 
             this._toBeDisposed.reset();
 
-            this._lastFrameDuration = new Date().getTime() - startDate;
+
+            Tools.EndPerformanceCounter("Scene rendering");
+            this._lastFrameDuration = Tools.Now - startDate;
         }
 
         public dispose(): void {

+ 567 - 53
babylon.2.0-alpha.debug.js

@@ -2556,7 +2556,7 @@ var BABYLON;
         };
 
         Tools._MeasureFps = function () {
-            previousFramesDuration.push((new Date).getTime());
+            previousFramesDuration.push(Tools.Now);
             var length = previousFramesDuration.length;
 
             if (length >= 2) {
@@ -2817,6 +2817,112 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+
+        Object.defineProperty(Tools, "PerformanceNoneLogLevel", {
+            get: function () {
+                return Tools._PerformanceNoneLogLevel;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        Object.defineProperty(Tools, "PerformanceUserMarkLogLevel", {
+            get: function () {
+                return Tools._PerformanceUserMarkLogLevel;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        Object.defineProperty(Tools, "PerformanceConsoleLogLevel", {
+            get: function () {
+                return Tools._PerformanceConsoleLogLevel;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        Object.defineProperty(Tools, "PerformanceLogLevel", {
+            set: function (level) {
+                if ((level & Tools.PerformanceUserMarkLogLevel) === Tools.PerformanceUserMarkLogLevel) {
+                    Tools.StartPerformanceCounter = Tools._StartUserMark;
+                    Tools.EndPerformanceCounter = Tools._EndUserMark;
+                    return;
+                }
+
+                if ((level & Tools.PerformanceConsoleLogLevel) === Tools.PerformanceConsoleLogLevel) {
+                    Tools.StartPerformanceCounter = Tools._StartPerformanceConsole;
+                    Tools.EndPerformanceCounter = Tools._EndPerformanceConsole;
+                    return;
+                }
+
+                Tools.StartPerformanceCounter = Tools._StartPerformanceCounterDisabled;
+                Tools.EndPerformanceCounter = Tools._EndPerformanceCounterDisabled;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        Tools._StartPerformanceCounterDisabled = function (counterName, condition) {
+        };
+
+        Tools._EndPerformanceCounterDisabled = function (counterName, condition) {
+        };
+
+        Tools._StartUserMark = function (counterName, condition) {
+            if (typeof condition === "undefined") { condition = true; }
+            if (!condition || !Tools._performance.mark) {
+                return;
+            }
+            Tools._performance.mark(counterName + "-Begin");
+        };
+
+        Tools._EndUserMark = function (counterName, condition) {
+            if (typeof condition === "undefined") { condition = true; }
+            if (!condition || !Tools._performance.mark) {
+                return;
+            }
+            Tools._performance.mark(counterName + "-End");
+            Tools._performance.measure(counterName, counterName + "-Begin", counterName + "-End");
+        };
+
+        Tools._StartPerformanceConsole = function (counterName, condition) {
+            if (typeof condition === "undefined") { condition = true; }
+            if (!condition) {
+                return;
+            }
+
+            Tools._StartUserMark(counterName, condition);
+
+            if (console.time) {
+                console.time(counterName);
+            }
+        };
+
+        Tools._EndPerformanceConsole = function (counterName, condition) {
+            if (typeof condition === "undefined") { condition = true; }
+            if (!condition) {
+                return;
+            }
+
+            Tools._EndUserMark(counterName, condition);
+
+            if (console.time) {
+                console.timeEnd(counterName);
+            }
+        };
+
+        Object.defineProperty(Tools, "Now", {
+            get: function () {
+                if (window.performance.now) {
+                    return window.performance.now();
+                }
+
+                return new Date().getTime();
+            },
+            enumerable: true,
+            configurable: true
+        });
         Tools.BaseUrl = "";
 
         Tools.GetExponantOfTwo = function (value, max) {
@@ -2842,6 +2948,15 @@ var BABYLON;
         Tools.Warn = Tools._WarnEnabled;
 
         Tools.Error = Tools._ErrorEnabled;
+
+        Tools._PerformanceNoneLogLevel = 0;
+        Tools._PerformanceUserMarkLogLevel = 1;
+        Tools._PerformanceConsoleLogLevel = 2;
+
+        Tools._performance = window.performance;
+
+        Tools.StartPerformanceCounter = Tools._StartPerformanceCounterDisabled;
+        Tools.EndPerformanceCounter = Tools._EndPerformanceCounterDisabled;
         return Tools;
     })();
     BABYLON.Tools = Tools;
@@ -4090,7 +4205,7 @@ var BABYLON;
                 else
                     callback(buffer);
             } else if (isDDS) {
-                var callback = function (data) {
+                callback = function (data) {
                     var info = BABYLON.Internals.DDSTools.GetDDSInfo(data);
 
                     var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap && ((info.width >> (info.mipmapCount - 1)) == 1);
@@ -4996,6 +5111,10 @@ var BABYLON;
             return BoundingBox.IsInFrustum(this.vectorsWorld, frustumPlanes);
         };
 
+        BoundingBox.prototype.isCompletelyInFrustum = function (frustumPlanes) {
+            return BoundingBox.IsCompletelyInFrustum(this.vectorsWorld, frustumPlanes);
+        };
+
         BoundingBox.prototype.intersectsPoint = function (point) {
             var delta = BABYLON.Engine.Epsilon;
 
@@ -5048,6 +5167,17 @@ var BABYLON;
             return (num <= (sphereRadius * sphereRadius));
         };
 
+        BoundingBox.IsCompletelyInFrustum = function (boundingVectors, frustumPlanes) {
+            for (var p = 0; p < 6; p++) {
+                for (var i = 0; i < 8; i++) {
+                    if (frustumPlanes[p].dotCoordinate(boundingVectors[i]) < 0) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        };
+
         BoundingBox.IsInFrustum = function (boundingVectors, frustumPlanes) {
             for (var p = 0; p < 6; p++) {
                 var inCount = 8;
@@ -5115,6 +5245,10 @@ var BABYLON;
             return this.boundingBox.isInFrustum(frustumPlanes);
         };
 
+        BoundingInfo.prototype.isCompletelyInFrustum = function (frustumPlanes) {
+            return this.boundingBox.isCompletelyInFrustum(frustumPlanes);
+        };
+
         BoundingInfo.prototype._checkCollision = function (collider) {
             return collider._canDoCollision(this.boundingSphere.centerWorld, this.boundingSphere.radiusWorld, this.boundingBox.minimumWorld, this.boundingBox.maximumWorld);
         };
@@ -7667,6 +7801,7 @@ var BABYLON;
             this.fogStart = 0;
             this.fogEnd = 1000.0;
            
+            this.shadowsEnabled = true;
             this.lightsEnabled = true;
             this.lights = new Array();
            
@@ -7692,6 +7827,7 @@ var BABYLON;
            
             this.skeletons = new Array();
            
+            this.lensFlaresEnabled = true;
             this.lensFlareSystems = new Array();
            
             this.collisionsEnabled = true;
@@ -8082,11 +8218,11 @@ var BABYLON;
 
         Scene.prototype._animate = function () {
             if (!this._animationStartDate) {
-                this._animationStartDate = new Date().getTime();
+                this._animationStartDate = BABYLON.Tools.Now;
             }
 
            
-            var now = new Date().getTime();
+            var now = BABYLON.Tools.Now;
             var delay = now - this._animationStartDate;
 
             for (var index = 0; index < this._activeAnimatables.length; index++) {
@@ -8388,8 +8524,9 @@ var BABYLON;
             }
 
            
-            var beforeParticlesDate = new Date().getTime();
+            var beforeParticlesDate = BABYLON.Tools.Now;
             if (this.particlesEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Particles", this.particleSystems.length > 0);
                 for (var particleIndex = 0; particleIndex < this.particleSystems.length; particleIndex++) {
                     var particleSystem = this.particleSystems[particleIndex];
 
@@ -8402,8 +8539,9 @@ var BABYLON;
                         particleSystem.animate();
                     }
                 }
+                BABYLON.Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
             }
-            this._particlesDuration += new Date().getTime() - beforeParticlesDate;
+            this._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
         };
 
         Scene.prototype._activeMesh = function (mesh) {
@@ -8450,6 +8588,8 @@ var BABYLON;
             if (!this.activeCamera)
                 throw new Error("Active camera not set");
 
+            BABYLON.Tools.StartPerformanceCounter("Rendering camera " + this.activeCamera.name);
+
            
             engine.setViewport(this.activeCamera.viewport);
 
@@ -8462,9 +8602,11 @@ var BABYLON;
             }
 
            
-            var beforeEvaluateActiveMeshesDate = new Date().getTime();
+            var beforeEvaluateActiveMeshesDate = BABYLON.Tools.Now;
+            BABYLON.Tools.StartPerformanceCounter("Active meshes evaluation");
             this._evaluateActiveMeshes();
-            this._evaluateActiveMeshesDuration += new Date().getTime() - beforeEvaluateActiveMeshesDate;
+            this._evaluateActiveMeshesDuration += BABYLON.Tools.Now - beforeEvaluateActiveMeshesDate;
+            BABYLON.Tools.EndPerformanceCounter("Active meshes evaluation");
 
             for (var skeletonIndex = 0; skeletonIndex < this._activeSkeletons.length; skeletonIndex++) {
                 var skeleton = this._activeSkeletons.data[skeletonIndex];
@@ -8473,8 +8615,9 @@ var BABYLON;
             }
 
            
-            var beforeRenderTargetDate = new Date().getTime();
+            var beforeRenderTargetDate = BABYLON.Tools.Now;
             if (this.renderTargetsEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Render targets", this._renderTargets.length > 0);
                 for (var renderIndex = 0; renderIndex < this._renderTargets.length; renderIndex++) {
                     var renderTarget = this._renderTargets.data[renderIndex];
                     if (renderTarget._shouldRender()) {
@@ -8482,18 +8625,19 @@ var BABYLON;
                         renderTarget.render();
                     }
                 }
+                BABYLON.Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
                 this._renderId++;
             }
 
             if (this._renderTargets.length > 0) {
                 engine.restoreDefaultFramebuffer();
             }
-            this._renderTargetsDuration += new Date().getTime() - beforeRenderTargetDate;
+            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
 
            
             this.postProcessManager._prepareFrame();
 
-            var beforeRenderDate = new Date().getTime();
+            var beforeRenderDate = BABYLON.Tools.Now;
 
            
             if (this.layers.length) {
@@ -8510,13 +8654,20 @@ var BABYLON;
             }
 
            
+            BABYLON.Tools.StartPerformanceCounter("Main render");
             this._renderingManager.render(null, null, true, true);
+            BABYLON.Tools.EndPerformanceCounter("Main render");
 
            
             this._boundingBoxRenderer.render();
 
-            for (var lensFlareSystemIndex = 0; lensFlareSystemIndex < this.lensFlareSystems.length; lensFlareSystemIndex++) {
-                this.lensFlareSystems[lensFlareSystemIndex].render();
+           
+            if (this.lensFlaresEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Lens flares", this.lensFlareSystems.length > 0);
+                for (var lensFlareSystemIndex = 0; lensFlareSystemIndex < this.lensFlareSystems.length; lensFlareSystemIndex++) {
+                    this.lensFlareSystems[lensFlareSystemIndex].render();
+                }
+                BABYLON.Tools.EndPerformanceCounter("Lens flares", this.lensFlareSystems.length > 0);
             }
 
            
@@ -8531,7 +8682,7 @@ var BABYLON;
                 engine.setDepthBuffer(true);
             }
 
-            this._renderDuration += new Date().getTime() - beforeRenderDate;
+            this._renderDuration += BABYLON.Tools.Now - beforeRenderDate;
 
            
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
@@ -8545,6 +8696,8 @@ var BABYLON;
             if (this.afterCameraRender) {
                 this.afterCameraRender(this.activeCamera);
             }
+
+            BABYLON.Tools.EndPerformanceCounter("Rendering camera " + this.activeCamera.name);
         };
 
         Scene.prototype._processSubCameras = function (camera) {
@@ -8595,7 +8748,7 @@ var BABYLON;
         };
 
         Scene.prototype.render = function () {
-            var startDate = new Date().getTime();
+            var startDate = BABYLON.Tools.Now;
             this._particlesDuration = 0;
             this._spritesDuration = 0;
             this._activeParticles = 0;
@@ -8605,6 +8758,8 @@ var BABYLON;
             this._activeVertices = 0;
             this._meshesForIntersections.reset();
 
+            BABYLON.Tools.StartPerformanceCounter("Scene rendering");
+
            
             if (this.actionManager) {
                 this.actionManager.processTrigger(BABYLON.ActionManager.OnEveryFrameTrigger, null);
@@ -8626,13 +8781,16 @@ var BABYLON;
 
            
             if (this._physicsEngine) {
+                BABYLON.Tools.StartPerformanceCounter("Physics");
                 this._physicsEngine._runOneStep(deltaTime / 1000.0);
+                BABYLON.Tools.EndPerformanceCounter("Physics");
             }
 
            
-            var beforeRenderTargetDate = new Date().getTime();
+            var beforeRenderTargetDate = BABYLON.Tools.Now;
             var engine = this.getEngine();
             if (this.renderTargetsEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);
                 for (var customIndex = 0; customIndex < this.customRenderTargets.length; customIndex++) {
                     var renderTarget = this.customRenderTargets[customIndex];
                     if (renderTarget._shouldRender()) {
@@ -8652,33 +8810,40 @@ var BABYLON;
                         renderTarget.render();
                     }
                 }
+                BABYLON.Tools.EndPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);
+
                 this._renderId++;
             }
 
             if (this.customRenderTargets.length > 0) {
                 engine.restoreDefaultFramebuffer();
             }
-            this._renderTargetsDuration += new Date().getTime() - beforeRenderTargetDate;
+            this._renderTargetsDuration += BABYLON.Tools.Now - beforeRenderTargetDate;
 
            
             if (this.proceduralTexturesEnabled) {
+                BABYLON.Tools.StartPerformanceCounter("Procedural textures", this._proceduralTextures.length > 0);
                 for (var proceduralIndex = 0; proceduralIndex < this._proceduralTextures.length; proceduralIndex++) {
                     var proceduralTexture = this._proceduralTextures[proceduralIndex];
                     if (proceduralTexture._shouldRender()) {
                         proceduralTexture.render();
                     }
                 }
+                BABYLON.Tools.EndPerformanceCounter("Procedural textures", this._proceduralTextures.length > 0);
             }
 
            
             this._engine.clear(this.clearColor, this.autoClear || this.forceWireframe, true);
 
-            for (var lightIndex = 0; lightIndex < this.lights.length; lightIndex++) {
-                var light = this.lights[lightIndex];
-                var shadowGenerator = light.getShadowGenerator();
+           
+            if (this.shadowsEnabled) {
+                for (var lightIndex = 0; lightIndex < this.lights.length; lightIndex++) {
+                    var light = this.lights[lightIndex];
+                    var shadowGenerator = light.getShadowGenerator();
 
-                if (light.isEnabled() && shadowGenerator && shadowGenerator.getShadowMap().getScene().textures.indexOf(shadowGenerator.getShadowMap()) !== -1) {
-                    this._renderTargets.push(shadowGenerator.getShadowMap());
+                    if (light.isEnabled() && shadowGenerator && shadowGenerator.getShadowMap().getScene().textures.indexOf(shadowGenerator.getShadowMap()) !== -1) {
+                        this._renderTargets.push(shadowGenerator.getShadowMap());
+                    }
                 }
             }
 
@@ -8715,7 +8880,8 @@ var BABYLON;
 
             this._toBeDisposed.reset();
 
-            this._lastFrameDuration = new Date().getTime() - startDate;
+            BABYLON.Tools.EndPerformanceCounter("Scene rendering");
+            this._lastFrameDuration = BABYLON.Tools.Now - startDate;
         };
 
         Scene.prototype.dispose = function () {
@@ -9699,6 +9865,20 @@ var BABYLON;
             return true;
         };
 
+        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.intersectsMesh = function (mesh, precise) {
             if (!this._boundingInfo || !mesh._boundingInfo) {
                 return false;
@@ -11551,6 +11731,29 @@ var BABYLON;
             return { width: this._texture._baseWidth, height: this._texture._baseHeight };
         };
 
+        BaseTexture.prototype.scale = function (ratio) {
+        };
+
+        Object.defineProperty(BaseTexture.prototype, "canRescale", {
+            get: function () {
+                return false;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        BaseTexture.prototype._removeFromCache = function (url, noMipmap) {
+            var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
+            for (var index = 0; index < texturesCache.length; index++) {
+                var texturesCacheEntry = texturesCache[index];
+
+                if (texturesCacheEntry.url === url && texturesCacheEntry.noMipmap === noMipmap) {
+                    texturesCache.splice(index, 1);
+                    return;
+                }
+            }
+        };
+
         BaseTexture.prototype._getFromCache = function (url, noMipmap) {
             var texturesCache = this._scene.getEngine().getLoadedTexturesCache();
             for (var index = 0; index < texturesCache.length; index++) {
@@ -11729,7 +11932,7 @@ var BABYLON;
             }
 
            
-            var beforeParticlesDate = new Date().getTime();
+            var beforeParticlesDate = BABYLON.Tools.Now;
             for (var particleIndex = 0; particleIndex < this._scene._activeParticleSystems.length; particleIndex++) {
                 var particleSystem = this._scene._activeParticleSystems.data[particleIndex];
 
@@ -11743,7 +11946,7 @@ var BABYLON;
                     this._scene._activeParticles += particleSystem.render();
                 }
             }
-            this._scene._particlesDuration += new Date().getTime() - beforeParticlesDate;
+            this._scene._particlesDuration += BABYLON.Tools.Now - beforeParticlesDate;
         };
 
         RenderingManager.prototype._renderSprites = function (index) {
@@ -11752,7 +11955,7 @@ var BABYLON;
             }
 
            
-            var beforeSpritessDate = new Date().getTime();
+            var beforeSpritessDate = BABYLON.Tools.Now;
             for (var id = 0; id < this._scene.spriteManagers.length; id++) {
                 var spriteManager = this._scene.spriteManagers[id];
 
@@ -11761,7 +11964,7 @@ var BABYLON;
                     spriteManager.render();
                 }
             }
-            this._scene._spritesDuration += new Date().getTime() - beforeSpritessDate;
+            this._scene._spritesDuration += BABYLON.Tools.Now - beforeSpritessDate;
         };
 
         RenderingManager.prototype._clearDepthBuffer = function () {
@@ -12171,6 +12374,20 @@ var BABYLON;
             return this._size;
         };
 
+        Object.defineProperty(RenderTargetTexture.prototype, "canRescale", {
+            get: function () {
+                return true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        RenderTargetTexture.prototype.scale = function (ratio) {
+            var newSize = this._size * ratio;
+
+            this.resize(newSize, this._generateMipMaps);
+        };
+
         RenderTargetTexture.prototype.resize = function (size, generateMipMaps) {
             this.releaseInternalTexture();
             this._texture = this.getScene().getEngine().createRenderTargetTexture(size, generateMipMaps);
@@ -12884,6 +13101,28 @@ var BABYLON;
             this._canvas.height = textureSize.height;
             this._context = this._canvas.getContext("2d");
         }
+        Object.defineProperty(DynamicTexture.prototype, "canRescale", {
+            get: function () {
+                return true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+
+        DynamicTexture.prototype.scale = function (ratio) {
+            var textureSize = this.getSize();
+
+            textureSize.width *= ratio;
+            textureSize.height *= ratio;
+
+            this._canvas.width = textureSize.width;
+            this._canvas.height = textureSize.height;
+
+            this.releaseInternalTexture();
+
+            this._texture = this.getScene().getEngine().createDynamicTexture(textureSize.width, textureSize.height, this._generateMipMaps, this._samplingMode);
+        };
+
         DynamicTexture.prototype.getContext = function () {
             return this._context;
         };
@@ -12974,7 +13213,7 @@ var BABYLON;
                 _this.video.appendChild(source);
             });
 
-            this._lastUpdate = new Date().getTime();
+            this._lastUpdate = BABYLON.Tools.Now;
         }
         VideoTexture.prototype.update = function () {
             if (this._autoLaunch) {
@@ -12982,7 +13221,7 @@ var BABYLON;
                 this.video.play();
             }
 
-            var now = new Date().getTime();
+            var now = BABYLON.Tools.Now;
 
             if (now - this._lastUpdate < 15) {
                 return false;
@@ -13550,6 +13789,7 @@ var BABYLON;
             this.emissiveColor = new BABYLON.Color3(0, 0, 0);
             this.useAlphaFromDiffuseTexture = false;
             this.useSpecularOverAlpha = true;
+            this.fogEnabled = true;
             this._cachedDefines = null;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
@@ -13688,7 +13928,7 @@ var BABYLON;
             }
 
            
-            if (scene.fogMode !== BABYLON.Scene.FOGMODE_NONE) {
+            if (scene.fogMode !== BABYLON.Scene.FOGMODE_NONE && this.fogEnabled) {
                 defines.push("#define FOG");
                 fallbacks.addFallback(1, "FOG");
             }
@@ -13754,27 +13994,29 @@ var BABYLON;
                     }
 
                    
-                    var shadowGenerator = light.getShadowGenerator();
-                    if (mesh && mesh.receiveShadows && shadowGenerator) {
-                        defines.push("#define SHADOW" + lightIndex);
-                        fallbacks.addFallback(0, "SHADOW" + lightIndex);
-
-                        if (!shadowsActivated) {
-                            defines.push("#define SHADOWS");
-                            shadowsActivated = true;
-                        }
+                    if (scene.shadowsEnabled) {
+                        var shadowGenerator = light.getShadowGenerator();
+                        if (mesh && mesh.receiveShadows && shadowGenerator) {
+                            defines.push("#define SHADOW" + lightIndex);
+                            fallbacks.addFallback(0, "SHADOW" + lightIndex);
+
+                            if (!shadowsActivated) {
+                                defines.push("#define SHADOWS");
+                                shadowsActivated = true;
+                            }
 
-                        if (shadowGenerator.useVarianceShadowMap) {
-                            defines.push("#define SHADOWVSM" + lightIndex);
-                            if (lightIndex > 0) {
-                                fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                            if (shadowGenerator.useVarianceShadowMap) {
+                                defines.push("#define SHADOWVSM" + lightIndex);
+                                if (lightIndex > 0) {
+                                    fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                                }
                             }
-                        }
 
-                        if (shadowGenerator.usePoissonSampling) {
-                            defines.push("#define SHADOWPCF" + lightIndex);
-                            if (lightIndex > 0) {
-                                fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                            if (shadowGenerator.usePoissonSampling) {
+                                defines.push("#define SHADOWPCF" + lightIndex);
+                                if (lightIndex > 0) {
+                                    fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                                }
                             }
                         }
                     }
@@ -14026,11 +14268,13 @@ var BABYLON;
                     this._effect.setColor3("vLightSpecular" + lightIndex, this._scaledSpecular);
 
                    
-                    var shadowGenerator = light.getShadowGenerator();
-                    if (mesh.receiveShadows && shadowGenerator) {
-                        this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
-                        this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMap());
-                        this._effect.setFloat("darkness" + lightIndex, shadowGenerator.getDarkness());
+                    if (scene.shadowsEnabled) {
+                        var shadowGenerator = light.getShadowGenerator();
+                        if (mesh.receiveShadows && shadowGenerator) {
+                            this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
+                            this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMap());
+                            this._effect.setFloat("darkness" + lightIndex, shadowGenerator.getDarkness());
+                        }
                     }
 
                     lightIndex++;
@@ -26663,3 +26907,273 @@ var BABYLON;
     })(BABYLON.OculusCamera);
     BABYLON.WebVRCamera = WebVRCamera;
 })(BABYLON || (BABYLON = {}));
+var __extends = this.__extends || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    __.prototype = b.prototype;
+    d.prototype = new __();
+};
+var BABYLON;
+(function (BABYLON) {
+   
+    var SceneOptimization = (function () {
+        function SceneOptimization(priority) {
+            if (typeof priority === "undefined") { priority = 0; }
+            this.priority = priority;
+            this.apply = function (scene) {
+                return true;
+            };
+        }
+        return SceneOptimization;
+    })();
+    BABYLON.SceneOptimization = SceneOptimization;
+
+    var TextureSceneOptimization = (function (_super) {
+        __extends(TextureSceneOptimization, _super);
+        function TextureSceneOptimization(maximumSize, priority) {
+            if (typeof maximumSize === "undefined") { maximumSize = 1024; }
+            if (typeof priority === "undefined") { priority = 0; }
+            var _this = this;
+            _super.call(this, priority);
+            this.maximumSize = maximumSize;
+            this.priority = priority;
+            this.apply = function (scene) {
+                var allDone = true;
+                for (var index = 0; index < scene.textures.length; index++) {
+                    var texture = scene.textures[index];
+
+                    if (!texture.canRescale) {
+                        continue;
+                    }
+
+                    var currentSize = texture.getSize();
+                    var maxDimension = Math.max(currentSize.width, currentSize.height);
+
+                    if (maxDimension > _this.maximumSize) {
+                        texture.scale(0.5);
+                        allDone = false;
+                    }
+                }
+
+                return allDone;
+            };
+        }
+        return TextureSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.TextureSceneOptimization = TextureSceneOptimization;
+
+    var HardwareScalingSceneOptimization = (function (_super) {
+        __extends(HardwareScalingSceneOptimization, _super);
+        function HardwareScalingSceneOptimization(maximumScale, priority) {
+            if (typeof maximumScale === "undefined") { maximumScale = 2; }
+            if (typeof priority === "undefined") { priority = 0; }
+            var _this = this;
+            _super.call(this, priority);
+            this.maximumScale = maximumScale;
+            this.priority = priority;
+            this._currentScale = 1;
+            this.apply = function (scene) {
+                _this._currentScale++;
+
+                scene.getEngine().setHardwareScalingLevel(_this._currentScale);
+
+                return _this._currentScale >= _this.maximumScale;
+            };
+        }
+        return HardwareScalingSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.HardwareScalingSceneOptimization = HardwareScalingSceneOptimization;
+
+    var ShadowsSceneOptimization = (function (_super) {
+        __extends(ShadowsSceneOptimization, _super);
+        function ShadowsSceneOptimization() {
+            _super.apply(this, arguments);
+            this.apply = function (scene) {
+                scene.shadowsEnabled = false;
+                return true;
+            };
+        }
+        return ShadowsSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.ShadowsSceneOptimization = ShadowsSceneOptimization;
+
+    var PostProcessesSceneOptimization = (function (_super) {
+        __extends(PostProcessesSceneOptimization, _super);
+        function PostProcessesSceneOptimization() {
+            _super.apply(this, arguments);
+            this.apply = function (scene) {
+                scene.postProcessesEnabled = false;
+                return true;
+            };
+        }
+        return PostProcessesSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.PostProcessesSceneOptimization = PostProcessesSceneOptimization;
+
+    var LensFlaresSceneOptimization = (function (_super) {
+        __extends(LensFlaresSceneOptimization, _super);
+        function LensFlaresSceneOptimization() {
+            _super.apply(this, arguments);
+            this.apply = function (scene) {
+                scene.lensFlaresEnabled = false;
+                return true;
+            };
+        }
+        return LensFlaresSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.LensFlaresSceneOptimization = LensFlaresSceneOptimization;
+
+    var ParticlesSceneOptimization = (function (_super) {
+        __extends(ParticlesSceneOptimization, _super);
+        function ParticlesSceneOptimization() {
+            _super.apply(this, arguments);
+            this.apply = function (scene) {
+                scene.particlesEnabled = false;
+                return true;
+            };
+        }
+        return ParticlesSceneOptimization;
+    })(SceneOptimization);
+    BABYLON.ParticlesSceneOptimization = ParticlesSceneOptimization;
+
+   
+    var SceneOptimizerOptions = (function () {
+        function SceneOptimizerOptions(targetFrameRate, trackerDuration) {
+            if (typeof targetFrameRate === "undefined") { targetFrameRate = 60; }
+            if (typeof trackerDuration === "undefined") { trackerDuration = 2000; }
+            this.targetFrameRate = targetFrameRate;
+            this.trackerDuration = trackerDuration;
+            this.optimizations = new Array();
+        }
+        SceneOptimizerOptions.LowDegradationAllowed = function (targetFrameRate) {
+            var result = new SceneOptimizerOptions(targetFrameRate);
+
+            var priority = 0;
+            result.optimizations.push(new ShadowsSceneOptimization(priority));
+            result.optimizations.push(new LensFlaresSceneOptimization(priority));
+
+           
+            priority++;
+            result.optimizations.push(new PostProcessesSceneOptimization(priority));
+            result.optimizations.push(new ParticlesSceneOptimization(priority));
+
+           
+            priority++;
+            result.optimizations.push(new TextureSceneOptimization(priority, 1024));
+
+            return result;
+        };
+
+        SceneOptimizerOptions.ModerateDegradationAllowed = function (targetFrameRate) {
+            var result = new SceneOptimizerOptions(targetFrameRate);
+
+            var priority = 0;
+            result.optimizations.push(new ShadowsSceneOptimization(priority));
+            result.optimizations.push(new LensFlaresSceneOptimization(priority));
+
+           
+            priority++;
+            result.optimizations.push(new PostProcessesSceneOptimization(priority));
+            result.optimizations.push(new ParticlesSceneOptimization(priority));
+
+           
+            priority++;
+            result.optimizations.push(new TextureSceneOptimization(priority, 512));
+
+           
+            priority++;
+            result.optimizations.push(new HardwareScalingSceneOptimization(priority, 2));
+
+            return result;
+        };
+
+        SceneOptimizerOptions.HighDegradationAllowed = function (targetFrameRate) {
+            var result = new SceneOptimizerOptions(targetFrameRate);
+
+            var priority = 0;
+            result.optimizations.push(new ShadowsSceneOptimization(priority));
+            result.optimizations.push(new LensFlaresSceneOptimization(priority));
+
+           
+            priority++;
+            result.optimizations.push(new PostProcessesSceneOptimization(priority));
+            result.optimizations.push(new ParticlesSceneOptimization(priority));
+
+           
+            priority++;
+            result.optimizations.push(new TextureSceneOptimization(priority, 256));
+
+           
+            priority++;
+            result.optimizations.push(new HardwareScalingSceneOptimization(priority, 4));
+
+            return result;
+        };
+        return SceneOptimizerOptions;
+    })();
+    BABYLON.SceneOptimizerOptions = SceneOptimizerOptions;
+
+   
+    var SceneOptimizer = (function () {
+        function SceneOptimizer() {
+        }
+        SceneOptimizer._CheckCurrentState = function (scene, options, currentPriorityLevel, onSuccess, onFailure) {
+           
+            if (BABYLON.Tools.GetFps() >= options.targetFrameRate) {
+                if (onSuccess) {
+                    onSuccess();
+                }
+
+                return;
+            }
+
+           
+            var allDone = true;
+            var noOptimizationApplied = true;
+            for (var index = 0; index < options.optimizations.length; index++) {
+                var optimization = options.optimizations[index];
+
+                if (optimization.priority === currentPriorityLevel) {
+                    noOptimizationApplied = false;
+                    allDone = allDone && optimization.apply(scene);
+                }
+            }
+
+           
+            if (noOptimizationApplied) {
+                if (onFailure) {
+                    onFailure();
+                }
+
+                return;
+            }
+
+           
+            if (allDone) {
+                currentPriorityLevel++;
+            }
+
+           
+            scene.executeWhenReady(function () {
+                setTimeout(function () {
+                    SceneOptimizer._CheckCurrentState(scene, options, currentPriorityLevel, onSuccess, onFailure);
+                }, options.trackerDuration);
+            });
+        };
+
+        SceneOptimizer.OptimizeAsync = function (scene, options, onSuccess, onFailure) {
+            if (!options) {
+                options = SceneOptimizerOptions.ModerateDegradationAllowed();
+            }
+
+           
+            scene.executeWhenReady(function () {
+                setTimeout(function () {
+                    SceneOptimizer._CheckCurrentState(scene, options, 0, onSuccess, onFailure);
+                }, options.trackerDuration);
+            });
+        };
+        return SceneOptimizer;
+    })();
+    BABYLON.SceneOptimizer = SceneOptimizer;
+})(BABYLON || (BABYLON = {}));

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 16 - 16
babylon.2.0-alpha.js


+ 78 - 2
babylon.2.0.d.ts

@@ -342,6 +342,7 @@ declare module BABYLON {
         public fogDensity: number;
         public fogStart: number;
         public fogEnd: number;
+        public shadowsEnabled: boolean;
         public lightsEnabled: boolean;
         public lights: Light[];
         public cameras: Camera[];
@@ -359,6 +360,7 @@ declare module BABYLON {
         public spriteManagers: SpriteManager[];
         public layers: Layer[];
         public skeletons: Skeleton[];
+        public lensFlaresEnabled: boolean;
         public lensFlareSystems: LensFlareSystem[];
         public collisionsEnabled: boolean;
         public gravity: Vector3;
@@ -1215,11 +1217,13 @@ declare module BABYLON {
         public getWorldMatrix(): Matrix;
         public _update(world: Matrix): void;
         public isInFrustum(frustumPlanes: Plane[]): boolean;
+        public isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
         public intersectsPoint(point: Vector3): boolean;
         public intersectsSphere(sphere: BoundingSphere): boolean;
         public intersectsMinMax(min: Vector3, max: Vector3): boolean;
         static Intersects(box0: BoundingBox, box1: BoundingBox): boolean;
         static IntersectsSphere(minPoint: Vector3, maxPoint: Vector3, sphereCenter: Vector3, sphereRadius: number): boolean;
+        static IsCompletelyInFrustum(boundingVectors: Vector3[], frustumPlanes: Plane[]): boolean;
         static IsInFrustum(boundingVectors: Vector3[], frustumPlanes: Plane[]): boolean;
     }
 }
@@ -1232,6 +1236,7 @@ declare module BABYLON {
         constructor(minimum: Vector3, maximum: Vector3);
         public _update(world: Matrix): void;
         public isInFrustum(frustumPlanes: Plane[]): boolean;
+        public isCompletelyInFrustum(frustumPlanes: Plane[]): boolean;
         public _checkCollision(collider: Collider): boolean;
         public intersectsPoint(point: Vector3): boolean;
         public intersects(boundingInfo: BoundingInfo, precise: boolean): boolean;
@@ -1648,6 +1653,7 @@ declare module BABYLON {
         public emissiveColor: Color3;
         public useAlphaFromDiffuseTexture: boolean;
         public useSpecularOverAlpha: boolean;
+        public fogEnabled: boolean;
         public diffuseFresnelParameters: FresnelParameters;
         public opacityFresnelParameters: FresnelParameters;
         public reflectionFresnelParameters: FresnelParameters;
@@ -1707,6 +1713,9 @@ declare module BABYLON {
         public isReady(): boolean;
         public getSize(): ISize;
         public getBaseSize(): ISize;
+        public scale(ratio: number): void;
+        public canRescale : boolean;
+        public _removeFromCache(url: string, noMipmap: boolean): void;
         public _getFromCache(url: string, noMipmap: boolean): WebGLTexture;
         public delayLoad(): void;
         public releaseInternalTexture(): void;
@@ -1733,6 +1742,8 @@ declare module BABYLON {
         private _canvas;
         private _context;
         constructor(name: string, options: any, scene: Scene, generateMipMaps: boolean, samplingMode?: number);
+        public canRescale : boolean;
+        public scale(ratio: number): void;
         public getContext(): CanvasRenderingContext2D;
         public update(invertY?: boolean): void;
         public drawText(text: string, x: number, y: number, font: string, color: string, clearColor: string, invertY?: boolean): void;
@@ -1815,7 +1826,9 @@ declare module BABYLON {
         public refreshRate : number;
         public _shouldRender(): boolean;
         public getRenderSize(): number;
-        public resize(size: any, generateMipMaps: any): void;
+        public canRescale : boolean;
+        public scale(ratio: number): void;
+        public resize(size: any, generateMipMaps?: boolean): void;
         public render(useCameraPostProcess?: boolean): void;
         public clone(): RenderTargetTexture;
     }
@@ -1858,7 +1871,7 @@ declare module BABYLON {
         private _cachedVAng;
         private _cachedWAng;
         private _cachedCoordinatesMode;
-        private _samplingMode;
+        public _samplingMode: number;
         private _buffer;
         private _deleteBuffer;
         constructor(url: string, scene: Scene, noMipmap?: boolean, invertY?: boolean, samplingMode?: number, onLoad?: () => void, onError?: () => void, buffer?: any, deleteBuffer?: boolean);
@@ -2356,6 +2369,7 @@ declare module BABYLON {
         public locallyTranslate(vector3: Vector3): void;
         public lookAt(targetPoint: Vector3, yawCor: number, pitchCor: number, rollCor: number): void;
         public isInFrustum(frustumPlanes: Plane[]): boolean;
+        public isCompletelyInFrustum(camera?: Camera): boolean;
         public intersectsMesh(mesh: AbstractMesh, precise?: boolean): boolean;
         public intersectsPoint(point: Vector3): boolean;
         public setPhysicsState(impostor?: any, options?: PhysicsBodyCreationOptions): void;
@@ -3581,6 +3595,51 @@ interface Navigator {
     webkitGamepads(func?: any): any;
 }
 declare module BABYLON {
+    class SceneOptimization {
+        public priority: number;
+        public apply: (scene: Scene) => boolean;
+        constructor(priority?: number);
+    }
+    class TextureSceneOptimization extends SceneOptimization {
+        public maximumSize: number;
+        public priority: number;
+        constructor(maximumSize?: number, priority?: number);
+        public apply: (scene: Scene) => boolean;
+    }
+    class HardwareScalingSceneOptimization extends SceneOptimization {
+        public maximumScale: number;
+        public priority: number;
+        private _currentScale;
+        constructor(maximumScale?: number, priority?: number);
+        public apply: (scene: Scene) => boolean;
+    }
+    class ShadowsSceneOptimization extends SceneOptimization {
+        public apply: (scene: Scene) => boolean;
+    }
+    class PostProcessesSceneOptimization extends SceneOptimization {
+        public apply: (scene: Scene) => boolean;
+    }
+    class LensFlaresSceneOptimization extends SceneOptimization {
+        public apply: (scene: Scene) => boolean;
+    }
+    class ParticlesSceneOptimization extends SceneOptimization {
+        public apply: (scene: Scene) => boolean;
+    }
+    class SceneOptimizerOptions {
+        public targetFrameRate: number;
+        public trackerDuration: number;
+        public optimizations: SceneOptimization[];
+        constructor(targetFrameRate?: number, trackerDuration?: number);
+        static LowDegradationAllowed(targetFrameRate?: number): SceneOptimizerOptions;
+        static ModerateDegradationAllowed(targetFrameRate?: number): SceneOptimizerOptions;
+        static HighDegradationAllowed(targetFrameRate?: number): SceneOptimizerOptions;
+    }
+    class SceneOptimizer {
+        static _CheckCurrentState(scene: Scene, options: SceneOptimizerOptions, currentPriorityLevel: number, onSuccess?: () => void, onFailure?: () => void): void;
+        static OptimizeAsync(scene: Scene, options?: SceneOptimizerOptions, onSuccess?: () => void, onFailure?: () => void): void;
+    }
+}
+declare module BABYLON {
     class SceneSerializer {
         static Serialize(scene: Scene): any;
     }
@@ -3685,6 +3744,23 @@ declare module BABYLON {
         private static _ErrorDisabled(message);
         private static _ErrorEnabled(message);
         static LogLevels : number;
+        private static _PerformanceNoneLogLevel;
+        private static _PerformanceUserMarkLogLevel;
+        private static _PerformanceConsoleLogLevel;
+        private static _performance;
+        static PerformanceNoneLogLevel : number;
+        static PerformanceUserMarkLogLevel : number;
+        static PerformanceConsoleLogLevel : number;
+        static PerformanceLogLevel : number;
+        static _StartPerformanceCounterDisabled(counterName: string, condition?: boolean): void;
+        static _EndPerformanceCounterDisabled(counterName: string, condition?: boolean): void;
+        static _StartUserMark(counterName: string, condition?: boolean): void;
+        static _EndUserMark(counterName: string, condition?: boolean): void;
+        static _StartPerformanceConsole(counterName: string, condition?: boolean): void;
+        static _EndPerformanceConsole(counterName: string, condition?: boolean): void;
+        static StartPerformanceCounter: (counterName: string, condition?: boolean) => void;
+        static EndPerformanceCounter: (counterName: string, condition?: boolean) => void;
+        static Now : number;
     }
 }
 declare module BABYLON.Internals {