Ver código fonte

Merge remote-tracking branch 'refs/remotes/BabylonJS/master'

DESKTOP-QJU4N0L\mityh 8 anos atrás
pai
commit
5fa21ea66e
38 arquivos alterados com 15572 adições e 14932 exclusões
  1. 1 1
      Tools/Gulp/config.json
  2. 6260 6226
      dist/preview release/babylon.d.ts
  3. 42 42
      dist/preview release/babylon.js
  4. 151 20
      dist/preview release/babylon.max.js
  5. 6260 6226
      dist/preview release/babylon.module.d.ts
  6. 44 44
      dist/preview release/babylon.worker.js
  7. 995 961
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  8. 32 32
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  9. 143 16
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  10. 995 961
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  11. 1 1
      dist/preview release/gui/babylon.gui.min.js
  12. 263 263
      dist/preview release/inspector/babylon.inspector.bundle.js
  13. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  14. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  15. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  16. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  17. 8 0
      dist/preview release/loaders/babylon.objFileLoader.js
  18. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  19. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  20. 2 0
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.d.ts
  21. 27 0
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js
  22. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  23. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  24. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  25. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  26. 8 0
      loaders/src/OBJ/babylon.objFileLoader.ts
  27. 28 0
      materialsLibrary/src/shadowOnly/babylon.shadowOnlyMaterial.ts
  28. 4 9
      src/Behaviors/Cameras/babylon.framingBehavior.ts
  29. 8 6
      src/Layer/babylon.highlightlayer.ts
  30. 2 0
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  31. 2 1
      src/Materials/Textures/babylon.renderTargetTexture.ts
  32. 127 59
      src/Mesh/babylon.abstractMesh.ts
  33. 36 32
      src/Mesh/babylon.mesh.ts
  34. 38 1
      src/Rendering/babylon.boundingBoxRenderer.ts
  35. 5 3
      src/Rendering/babylon.outlineRenderer.ts
  36. 8 0
      src/Tools/babylon.tools.ts
  37. 55 13
      src/babylon.engine.ts
  38. 13 1
      src/babylon.mixins.ts

+ 1 - 1
Tools/Gulp/config.json

@@ -80,7 +80,7 @@
                 "../../src/Mesh/babylon.mesh.vertexData.js",
                 "../../src/Mesh/babylon.geometry.js",
                 "../../src/PostProcess/babylon.postProcessManager.js",
-                "../../src/Tools/babylon.performanceMonitor.js" 
+                "../../src/Tools/babylon.performanceMonitor.js"
             ]
         },         
         "particles" : 

Diferenças do arquivo suprimidas por serem muito extensas
+ 6260 - 6226
dist/preview release/babylon.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 42 - 42
dist/preview release/babylon.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 151 - 20
dist/preview release/babylon.max.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 6260 - 6226
dist/preview release/babylon.module.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 44 - 44
dist/preview release/babylon.worker.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 995 - 961
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 32 - 32
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 143 - 16
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 995 - 961
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 263 - 263
dist/preview release/inspector/babylon.inspector.bundle.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 8 - 0
dist/preview release/loaders/babylon.objFileLoader.js

@@ -76,6 +76,11 @@ var BABYLON;
                         //Set the color into the material
                         material.specularColor = BABYLON.Color3.FromArray(color);
                     }
+                    else if (key === "ke") {
+                        // Emissive color using RGB values
+                        color = value.split(delimiter_pattern, 3);
+                        material.emissiveColor = BABYLON.Color3.FromArray(color);
+                    }
                     else if (key === "ns") {
                         //value = "Integer"
                         material.specularPower = value;
@@ -173,6 +178,9 @@ var BABYLON;
          * @return The Texture
          */
         MTLFileLoader._getTexture = function (rootUrl, value, scene) {
+            if (!value) {
+                return null;
+            }
             var url = rootUrl;
             // Load from input file.
             if (rootUrl === "file:") {

Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


+ 2 - 0
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.d.ts

@@ -4,10 +4,12 @@ declare module BABYLON {
         private _worldViewProjectionMatrix;
         private _scaledDiffuse;
         private _renderId;
+        private _activeLight;
         constructor(name: string, scene: Scene);
         needAlphaBlending(): boolean;
         needAlphaTesting(): boolean;
         getAlphaTestTexture(): BaseTexture;
+        activeLight: IShadowLight;
         isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
         clone(name: string): ShadowOnlyMaterial;

+ 27 - 0
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js

@@ -51,6 +51,16 @@ var BABYLON;
         ShadowOnlyMaterial.prototype.getAlphaTestTexture = function () {
             return null;
         };
+        Object.defineProperty(ShadowOnlyMaterial.prototype, "activeLight", {
+            get: function () {
+                return this._activeLight;
+            },
+            set: function (light) {
+                this._activeLight = light;
+            },
+            enumerable: true,
+            configurable: true
+        });
         // Methods   
         ShadowOnlyMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
             if (this.isFrozen) {
@@ -69,6 +79,23 @@ var BABYLON;
                 }
             }
             var engine = scene.getEngine();
+            // Ensure that active light is the first shadow light
+            if (this._activeLight) {
+                for (var _i = 0, _a = mesh._lightSources; _i < _a.length; _i++) {
+                    var light = _a[_i];
+                    if (light.shadowEnabled) {
+                        if (this._activeLight === light) {
+                            break; // We are good
+                        }
+                        var lightPosition = mesh._lightSources.indexOf(this._activeLight);
+                        if (lightPosition !== -1) {
+                            mesh._lightSources.splice(lightPosition, 1);
+                            mesh._lightSources.splice(0, 0, this._activeLight);
+                        }
+                        break;
+                    }
+                }
+            }
             BABYLON.MaterialHelper.PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances);
             BABYLON.MaterialHelper.PrepareDefinesForMisc(mesh, scene, false, this.pointsCloud, this.fogEnabled, defines);
             defines._needNormals = BABYLON.MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, false, 1);

Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


+ 8 - 0
loaders/src/OBJ/babylon.objFileLoader.ts

@@ -82,6 +82,10 @@ module BABYLON {
                     //color = [r,g,b]
                     //Set the color into the material
                     material.specularColor = BABYLON.Color3.FromArray(color);
+                } else if (key === "ke") {
+                    // Emissive color using RGB values
+                    color = value.split(delimiter_pattern, 3);
+                    material.emissiveColor = BABYLON.Color3.FromArray(color);
                 } else if (key === "ns") {
 
                     //value = "Integer"
@@ -164,6 +168,10 @@ module BABYLON {
          * @return The Texture
          */
         private static _getTexture(rootUrl: string, value: string, scene: Scene): Texture {
+            if (!value) {
+                return null;
+            }
+
             var url = rootUrl;
             // Load from input file.
             if (rootUrl === "file:") {

+ 28 - 0
materialsLibrary/src/shadowOnly/babylon.shadowOnlyMaterial.ts

@@ -23,6 +23,7 @@ module BABYLON {
         private _worldViewProjectionMatrix = Matrix.Zero();
         private _scaledDiffuse = new Color3();
         private _renderId: number;
+        private _activeLight: IShadowLight;
 
         constructor(name: string, scene: Scene) {
             super(name, scene);
@@ -40,6 +41,14 @@ module BABYLON {
             return null;
         }
 
+        public get activeLight(): IShadowLight {
+            return this._activeLight;
+        }
+
+        public set activeLight(light: IShadowLight) {
+            this._activeLight = light;
+        }        
+
         // Methods   
         public isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean {   
             if (this.isFrozen) {
@@ -63,6 +72,25 @@ module BABYLON {
 
             var engine = scene.getEngine();
 
+            // Ensure that active light is the first shadow light
+            if (this._activeLight) {
+                for (var light of mesh._lightSources) {
+                    if (light.shadowEnabled) {
+                        if (this._activeLight === light) {
+                            break; // We are good
+                        }
+
+                        var lightPosition = mesh._lightSources.indexOf(this._activeLight);
+
+                        if (lightPosition !== -1) {
+                            mesh._lightSources.splice(lightPosition, 1);
+                            mesh._lightSources.splice(0, 0, this._activeLight);
+                        }
+                        break;
+                    }
+                }
+            }
+
             MaterialHelper.PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances);
 
             MaterialHelper.PrepareDefinesForMisc(mesh, scene, false, this.pointsCloud, this.fogEnabled, defines);

+ 4 - 9
src/Behaviors/Cameras/babylon.framingBehavior.ts

@@ -4,7 +4,7 @@ module BABYLON {
             return "Framing";
         }
 
-        private _mode = FramingBehavior.IgnoreBoundsSizeMode;
+        private _mode = FramingBehavior.FitFrustumSidesMode;
         private _radiusScale = 1.0;
         private _positionY = 0;
         private _defaultElevation = 0.3;
@@ -201,12 +201,10 @@ module BABYLON {
 		 * Targets the given mesh and updates zoom level accordingly.
 		 * @param mesh  The mesh to target.
 		 * @param radius Optional. If a cached radius position already exists, overrides default.
-		 * @param applyToLowerLimit Optional. Indicates if the calculated target radius should be applied to the
-		 *		camera's lower radius limit too.
 		 * @param framingPositionY Position on mesh to center camera focus where 0 corresponds bottom of its bounding box and 1, the top
 		 * @param focusOnOriginXZ Determines if the camera should focus on 0 in the X and Z axis instead of the mesh
 		 */
-		public zoomOnMesh(mesh: AbstractMesh, radius?: number, applyToLowerLimit: boolean = true, framingPositionY?: number, focusOnOriginXZ: boolean = false): void {
+		public zoomOnMesh(mesh: AbstractMesh, radius?: number, framingPositionY?: number, focusOnOriginXZ: boolean = false): void {
 			if (framingPositionY == null) {
 				framingPositionY = this._positionY;
 			}
@@ -236,17 +234,14 @@ module BABYLON {
 				let delta = 0.1;
 				if (this._mode === FramingBehavior.FitFrustumSidesMode) {
 					let position = this._calculateLowerRadiusFromModelBoundingSphere(mesh);
-					this._attachedCamera.lowerRadiusLimit = position - delta;
+					this._attachedCamera.lowerRadiusLimit = mesh.getBoundingInfo().boundingSphere.radiusWorld + this._attachedCamera.minZ;
 					radius = position;
 				} else if (this._mode === FramingBehavior.IgnoreBoundsSizeMode) {
 					radius = this._calculateLowerRadiusFromModelBoundingSphere(mesh);
+					this._attachedCamera.lowerRadiusLimit = this._attachedCamera.minZ;
 				}
 			}
 
-			if (applyToLowerLimit) {
-				this._attachedCamera.lowerRadiusLimit = mesh.getBoundingInfo().boundingSphere.radiusWorld;;
-			}
-
 			// transition to new radius
 			if (!this._radiusTransition) {
 				this._radiusTransition = Animation.CreateAnimation("radius", Animation.ANIMATIONTYPE_FLOAT, 60, FramingBehavior.EasingFunction);

+ 8 - 6
src/Layer/babylon.highlightlayer.ts

@@ -9,7 +9,7 @@ module BABYLON {
     class GlowBlurPostProcess extends PostProcess {
         constructor(name: string, public direction: Vector2, public kernel: number, options: number | PostProcessOptions, camera: Camera, samplingMode: number = Texture.BILINEAR_SAMPLINGMODE, engine?: Engine, reusable?: boolean) {
             super(name, "glowBlurPostProcess", ["screenSize", "direction", "blurWidth"], null, options, camera, samplingMode, engine, reusable);
-           
+
             this.onApplyObservable.add((effect: Effect) => {
                 effect.setFloat2("screenSize", this.width, this.height);
                 effect.setVector2("direction", this.direction);
@@ -371,7 +371,7 @@ module BABYLON {
             }
             else {
                 this._horizontalBlurPostprocess = new BlurPostProcess("HighlightLayerHBP", new BABYLON.Vector2(1.0, 0), this._options.blurHorizontalSize, 1,
-                null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
+                    null, Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine());
                 this._horizontalBlurPostprocess.onApplyObservable.add(effect => {
                     effect.setFloat2("screenSize", blurTextureWidth, blurTextureHeight);
                 });
@@ -445,8 +445,10 @@ module BABYLON {
                     // Alpha test
                     if (material && material.needAlphaTesting()) {
                         var alphaTexture = material.getAlphaTestTexture();
-                        this._glowMapGenerationEffect.setTexture("diffuseSampler", alphaTexture);
-                        this._glowMapGenerationEffect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
+                        if (alphaTexture) {
+                            this._glowMapGenerationEffect.setTexture("diffuseSampler", alphaTexture);
+                            this._glowMapGenerationEffect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
+                        }
                     }
 
                     // Glow emissive only
@@ -613,7 +615,7 @@ module BABYLON {
             var previousStencilMask = engine.getStencilMask();
             var previousStencilOperationPass = engine.getStencilOperationPass();
             var previousStencilOperationFail = engine.getStencilOperationFail();
-            var previousStencilOperationDepthFail = engine.getStencilOperationDepthFail();            
+            var previousStencilOperationDepthFail = engine.getStencilOperationDepthFail();
             var previousAlphaMode = engine.getAlphaMode();
 
             // Texture
@@ -651,7 +653,7 @@ module BABYLON {
             engine.setStencilBuffer(previousStencilBuffer);
             engine.setStencilOperationPass(previousStencilOperationPass);
             engine.setStencilOperationFail(previousStencilOperationFail);
-            engine.setStencilOperationDepthFail(previousStencilOperationDepthFail);            
+            engine.setStencilOperationDepthFail(previousStencilOperationDepthFail);
 
             (<any>engine)._stencilState.reset();
 

+ 2 - 0
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -701,10 +701,12 @@
                                 return false;
                             }
 
+                            defines.METALLICWORKFLOW = false;
                             MaterialHelper.PrepareDefinesForMergedUV(this._reflectivityTexture, defines, "REFLECTIVITY");
                             defines.MICROSURFACEFROMREFLECTIVITYMAP = this._useMicroSurfaceFromReflectivityMapAlpha;
                             defines.MICROSURFACEAUTOMATIC = this._useAutoMicroSurfaceFromReflectivityMap;
                         } else {
+                            defines.METALLICWORKFLOW = false;
                             defines.REFLECTIVITY = false;
                         }
 

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

@@ -109,6 +109,7 @@
             this._onClearObserver = this.onClearObservable.add(callback);
         }
 
+        public clearColor: Color4;
         protected _size: number;
         public _generateMipMaps: boolean;
         protected _renderingManager: RenderingManager;
@@ -427,7 +428,7 @@
             if (this.onClearObservable.hasObservers()) {
                 this.onClearObservable.notifyObservers(engine);
             } else {
-                engine.clear(scene.clearColor, true, true, true);
+                engine.clear(this.clearColor || scene.clearColor, true, true, true);
             }
 
             if (!this._doNotChangeAspectRatio) {

+ 127 - 59
src/Mesh/babylon.abstractMesh.ts

@@ -7,6 +7,12 @@
         private static _BILLBOARDMODE_Z = 4;
         private static _BILLBOARDMODE_ALL = 7;
 
+        public static OCCLUSION_TYPE_NONE = 0;
+        public static OCCLUSION_TYPE_OPTIMISITC = 1;
+        public static OCCLUSION_TYPE_STRICT = 2;
+        public static OCCLUSION_ALGORITHM_TYPE_ACCURATE = 0;
+        public static OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE = 1;
+
         public static get BILLBOARDMODE_NONE(): number {
             return AbstractMesh._BILLBOARDMODE_NONE;
         }
@@ -57,7 +63,7 @@
         }
         public set partitioningSubdivisions(nb: number) {
             this._partitioningSubdivisions = nb;
-        } 
+        }
         /**
          * The ratio (float) to apply to the bouding box size to set to the partioning space.  
          * Ex : 1.01 (default) the partioning space is 1% bigger than the bounding box.
@@ -121,6 +127,24 @@
         // Properties
         public definedFacingForward = true; // orientation for POV movement & rotation
         public position = Vector3.Zero();
+
+        private _webGLVersion = this.getEngine().webGLVersion;
+        private _occlusionInternalRetryCounter = 0;
+        public occlusionType = AbstractMesh.OCCLUSION_TYPE_NONE;
+        public occlusionRetryCount = -1;
+        protected _isOccluded = false;
+        get isOccluded(): boolean {
+            return this._isOccluded;
+        }
+        set isOccluded(value: boolean) {
+            this._isOccluded = value;
+        }
+
+        public occlusionQuery = this.getEngine().createQuery();
+        public isOcclusionQueryInProgress = false;
+
+        public occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
+
         private _rotation = Vector3.Zero();
         private _rotationQuaternion: Quaternion;
         private _scaling = Vector3.One();
@@ -153,7 +177,7 @@
             if (!this.subMeshes) {
                 return;
             }
-            
+
             for (var subMesh of this.subMeshes) {
                 subMesh.setEffect(null);
             }
@@ -189,7 +213,7 @@
 
             this._hasVertexAlpha = value;
             this._markSubMeshesAsAttributesDirty();
-        }        
+        }
 
         private _useVertexColors = true;
         public get useVertexColors(): boolean {
@@ -202,7 +226,7 @@
 
             this._useVertexColors = value;
             this._markSubMeshesAsAttributesDirty();
-        }         
+        }
 
         private _computeBonesUsingShaders = true;
         public get computeBonesUsingShaders(): boolean {
@@ -215,7 +239,7 @@
 
             this._computeBonesUsingShaders = value;
             this._markSubMeshesAsAttributesDirty();
-        }                
+        }
 
         private _numBoneInfluencers = 4;
         public get numBoneInfluencers(): number {
@@ -228,7 +252,7 @@
 
             this._numBoneInfluencers = value;
             this._markSubMeshesAsAttributesDirty();
-        }           
+        }
 
         private _applyFog = true;
         public get applyFog(): boolean {
@@ -241,7 +265,7 @@
 
             this._applyFog = value;
             this._markSubMeshesAsMiscDirty();
-        }  
+        }
 
         public scalingDeterminant = 1;
 
@@ -288,24 +312,24 @@
         private _oldPositionForCollisions = new Vector3(0, 0, 0);
         private _diffPositionForCollisions = new Vector3(0, 0, 0);
         private _newPositionForCollisions = new Vector3(0, 0, 0);
-        
-        
+
+
         public get collisionMask(): number {
             return this._collisionMask;
         }
-        
+
         public set collisionMask(mask: number) {
             this._collisionMask = !isNaN(mask) ? mask : -1;
         }
-        
+
         public get collisionGroup(): number {
             return this._collisionGroup;
         }
-        
+
         public set collisionGroup(mask: number) {
             this._collisionGroup = !isNaN(mask) ? mask : -1;
         }
-        
+
         // Attach to bone
         private _meshToBoneReferal: AbstractMesh;
 
@@ -443,7 +467,7 @@
                 if (isIn) {
                     return;
                 }
-                this._lightSources.splice(index, 1);            
+                this._lightSources.splice(index, 1);
             }
 
             this._markSubMeshesAsLightDirty();
@@ -455,14 +479,14 @@
             if (index === -1) {
                 return;
             }
-            this._lightSources.splice(index, 1);       
+            this._lightSources.splice(index, 1);
         }
 
         private _markSubMeshesAsDirty(func: (defines: MaterialDefines) => void) {
             if (!this.subMeshes) {
                 return;
             }
-            
+
             for (var subMesh of this.subMeshes) {
                 if (subMesh._materialDefines) {
                     func(subMesh._materialDefines);
@@ -482,7 +506,7 @@
             if (!this.subMeshes) {
                 return;
             }
-            
+
             for (var subMesh of this.subMeshes) {
                 var material = subMesh.getMaterial();
                 if (material) {
@@ -727,7 +751,7 @@
         public _activate(renderId: number): void {
             this._renderId = renderId;
         }
-   
+
         /**
          * Returns the last update of the World matrix
          * Returns a Matrix.  
@@ -736,7 +760,7 @@
             if (this._masterMesh) {
                 return this._masterMesh.getWorldMatrix();
             }
-            
+
             if (this._currentRenderId !== this.getScene().getRenderId() || !this.isSynchronized()) {
                 this.computeWorldMatrix();
             }
@@ -817,7 +841,7 @@
             }
             return this;
         }
-        
+
         /**
          * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in world space.  
          * Note that the property `rotationQuaternion` is then automatically updated and the property `rotation` is set to (0,0,0) and no longer used.  
@@ -845,7 +869,7 @@
 
             return this;
         }
-        
+
         /**
          * Translates the mesh along the axis vector for the passed distance in the given space.  
          * space (default LOCAL) can be either BABYLON.Space.LOCAL, either BABYLON.Space.WORLD.
@@ -1170,7 +1194,7 @@
             // Composing transformations
             this._pivotMatrix.multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[4]);
             Tmp.Matrix[4].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
-            
+
             // Billboarding (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
             if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE && this.getScene().activeCamera) {
                 if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL) {
@@ -1191,21 +1215,18 @@
                     currentPosition.subtractInPlace(this.getScene().activeCamera.globalPosition);
 
                     var finalEuler = Tmp.Vector3[4].copyFromFloats(0, 0, 0);
-                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_X) === AbstractMesh.BILLBOARDMODE_X)
-                    {
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_X) === AbstractMesh.BILLBOARDMODE_X) {
                         finalEuler.x = Math.atan2(-currentPosition.y, currentPosition.z);
                     }
-                    
-                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Y) === AbstractMesh.BILLBOARDMODE_Y)
-                    {
+
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Y) === AbstractMesh.BILLBOARDMODE_Y) {
                         finalEuler.y = Math.atan2(currentPosition.x, currentPosition.z);
                     }
-                    
-                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Z) === AbstractMesh.BILLBOARDMODE_Z)
-                    {
+
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Z) === AbstractMesh.BILLBOARDMODE_Z) {
                         finalEuler.z = Math.atan2(currentPosition.y, currentPosition.x);
                     }
- 
+
                     Matrix.RotationYawPitchRollToRef(finalEuler.y, finalEuler.x, finalEuler.z, Tmp.Matrix[0]);
                 } else {
                     Tmp.Matrix[1].copyFrom(this.getScene().activeCamera.getViewMatrix());
@@ -1230,12 +1251,12 @@
                     } else {
                         Tmp.Matrix[5].copyFrom(this.parent.getWorldMatrix());
                     }
-                    
+
                     this._localWorld.getTranslationToRef(Tmp.Vector3[5]);
                     Vector3.TransformCoordinatesToRef(Tmp.Vector3[5], Tmp.Matrix[5], Tmp.Vector3[5]);
                     this._worldMatrix.copyFrom(this._localWorld);
                     this._worldMatrix.setTranslation(Tmp.Vector3[5]);
-                    
+
                 } else {
                     if (this._meshToBoneReferal) {
                         this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
@@ -1753,7 +1774,7 @@
             }
 
             // SubMeshes
-            if (this.getClassName() !== "InstancedMesh"){
+            if (this.getClassName() !== "InstancedMesh") {
                 this.releaseSubMeshes();
             }
 
@@ -1814,11 +1835,11 @@
          * Returns a new Vector3 what is the localAxis, expressed in the mesh local space, rotated like the mesh.  
          * This Vector3 is expressed in the World space.  
          */
-        public getDirection(localAxis:Vector3): Vector3 {
+        public getDirection(localAxis: Vector3): Vector3 {
             var result = Vector3.Zero();
 
             this.getDirectionToRef(localAxis, result);
-            
+
             return result;
         }
 
@@ -1828,19 +1849,19 @@
          * result is computed in the Wordl space from the mesh World matrix.  
          * Returns the AbstractMesh.  
          */
-        public getDirectionToRef(localAxis:Vector3, result:Vector3): AbstractMesh {
+        public getDirectionToRef(localAxis: Vector3, result: Vector3): AbstractMesh {
             Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
             return this;
         }
 
-        public setPivotPoint(point:Vector3, space:Space = Space.LOCAL): AbstractMesh {
+        public setPivotPoint(point: Vector3, space: Space = Space.LOCAL): AbstractMesh {
 
-            if(this.getScene().getRenderId() == 0){
+            if (this.getScene().getRenderId() == 0) {
                 this.computeWorldMatrix(true);
             }
 
             var wm = this.getWorldMatrix();
-            
+
             if (space == Space.WORLD) {
                 var tmat = Tmp.Matrix[0];
                 wm.invertToRef(tmat);
@@ -1868,7 +1889,7 @@
          * Sets the passed Vector3 "result" with the coordinates of the mesh pivot point in the local space.   
          * Returns the AbstractMesh.   
          */
-        public getPivotPointToRef(result:Vector3): AbstractMesh{
+        public getPivotPointToRef(result: Vector3): AbstractMesh {
             result.x = -this._pivotMatrix.m[12];
             result.y = -this._pivotMatrix.m[13];
             result.z = -this._pivotMatrix.m[14];
@@ -1888,12 +1909,12 @@
          * Defines the passed mesh as the parent of the current mesh.  
          * Returns the AbstractMesh.  
          */
-        public setParent(mesh:AbstractMesh): AbstractMesh {
-            
+        public setParent(mesh: AbstractMesh): AbstractMesh {
+
             var child = this;
             var parent = mesh;
 
-            if(mesh == null){
+            if (mesh == null) {
 
                 var rotation = Tmp.Quaternion[0];
                 var position = Tmp.Vector3[0];
@@ -1912,7 +1933,7 @@
                 child.position.z = position.z;
 
             } else {
-  
+
                 var rotation = Tmp.Quaternion[0];
                 var position = Tmp.Vector3[0];
                 var scale = Tmp.Vector3[1];
@@ -1957,7 +1978,7 @@
          * Adds the passed mesh as a child to the current mesh.  
          * Returns the AbstractMesh.  
          */
-        public addChild(mesh:AbstractMesh): AbstractMesh {
+        public addChild(mesh: AbstractMesh): AbstractMesh {
             mesh.setParent(this);
             return this;
         }
@@ -1966,7 +1987,7 @@
          * Removes the passed mesh from the current mesh children list.  
          * Returns the AbstractMesh.  
          */
-        public removeChild(mesh:AbstractMesh): AbstractMesh {
+        public removeChild(mesh: AbstractMesh): AbstractMesh {
             mesh.setParent(null);
             return this;
         }
@@ -1975,7 +1996,7 @@
          * Sets the Vector3 "result" coordinates with the mesh pivot point World coordinates.  
          * Returns the AbstractMesh.  
          */
-        public getAbsolutePivotPointToRef(result:Vector3): AbstractMesh {
+        public getAbsolutePivotPointToRef(result: Vector3): AbstractMesh {
             result.x = this._pivotMatrix.m[12];
             result.y = this._pivotMatrix.m[13];
             result.z = this._pivotMatrix.m[14];
@@ -1984,7 +2005,7 @@
             return this;
         }
 
-       // Facet data
+        // Facet data
         /** 
          *  Initialize the facet data arrays : facetNormals, facetPositions and facetPartitioning.   
          * Returns the AbstractMesh.  
@@ -2006,7 +2027,7 @@
                 this._facetNormals[f] = Vector3.Zero();
                 this._facetPositions[f] = Vector3.Zero();
             }
-            this._facetDataEnabled = true;           
+            this._facetDataEnabled = true;
             return this;
         }
 
@@ -2037,7 +2058,7 @@
             this._subDiv.Y = this._subDiv.Y < 1 ? 1 : this._subDiv.Y;
             this._subDiv.Z = this._subDiv.Z < 1 ? 1 : this._subDiv.Z;
             // set the parameters for ComputeNormals()
-            this._facetParameters.facetNormals = this.getFacetLocalNormals(); 
+            this._facetParameters.facetNormals = this.getFacetLocalNormals();
             this._facetParameters.facetPositions = this.getFacetLocalPositions();
             this._facetParameters.facetPartitioning = this.getFacetLocalPartitioning();
             this._facetParameters.bInfo = bInfo;
@@ -2065,7 +2086,7 @@
             if (!this._facetPositions) {
                 this.updateFacetData();
             }
-            return this._facetPositions;           
+            return this._facetPositions;
         }
         /**
          * Returns the facetLocalPartioning array.
@@ -2156,7 +2177,7 @@
          */
         public getClosestFacetAtLocalCoordinates(x: number, y: number, z: number, projected?: Vector3, checkFace: boolean = false, facing: boolean = true): number {
             var closest = null;
-            var tmpx = 0.0;         
+            var tmpx = 0.0;
             var tmpy = 0.0;
             var tmpz = 0.0;
             var d = 0.0;            // tmp dot facet normal * facet position
@@ -2179,14 +2200,14 @@
             var p0;                                     // current facet barycenter position
             // loop on all the facets in the current partitioning block
             for (var idx = 0; idx < facetsInBlock.length; idx++) {
-                fib = facetsInBlock[idx];           
+                fib = facetsInBlock[idx];
                 norm = facetNormals[fib];
                 p0 = facetPositions[fib];
 
                 d = (x - p0.x) * norm.x + (y - p0.y) * norm.y + (z - p0.z) * norm.z;
-                if ( !checkFace || (checkFace && facing && d >= 0.0) || (checkFace && !facing && d <= 0.0) ) {
+                if (!checkFace || (checkFace && facing && d >= 0.0) || (checkFace && !facing && d <= 0.0)) {
                     // compute (x,y,z) projection on the facet = (projx, projy, projz)
-                    d = norm.x * p0.x + norm.y * p0.y + norm.z * p0.z; 
+                    d = norm.x * p0.x + norm.y * p0.y + norm.z * p0.z;
                     t0 = -(norm.x * x + norm.y * y + norm.z * z - d) / (norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);
                     projx = x + norm.x * t0;
                     projy = y + norm.y * t0;
@@ -2198,7 +2219,7 @@
                     tmpDistance = tmpx * tmpx + tmpy * tmpy + tmpz * tmpz;             // compute length between (x, y, z) and its projection on the facet
                     if (tmpDistance < shortest) {                                      // just keep the closest facet to (x, y, z)
                         shortest = tmpDistance;
-                        closest = fib; 
+                        closest = fib;
                         if (projected) {
                             projected.x = projx;
                             projected.y = projy;
@@ -2238,7 +2259,7 @@
             var positions = this.getVerticesData(VertexBuffer.PositionKind);
             var indices = this.getIndices();
             var normals: number[] | Float32Array;
-            
+
             if (this.isVerticesDataPresent(VertexBuffer.NormalKind)) {
                 normals = this.getVerticesData(VertexBuffer.NormalKind);
             } else {
@@ -2247,7 +2268,54 @@
 
             VertexData.ComputeNormals(positions, indices, normals, { useRightHandedSystem: this.getScene().useRightHandedSystem });
             this.setVerticesData(VertexBuffer.NormalKind, normals, updatable);
-        } 
+        }
+
+        protected checkOcclusionQuery() {
+            if (this._webGLVersion < 2 || this.occlusionType === AbstractMesh.OCCLUSION_TYPE_NONE) {
+                this._isOccluded = false;
+                return;
+            }
+
+            var engine = this.getEngine();
+
+            if (this.isOcclusionQueryInProgress) {
+                
+                var isOcclusionQueryAvailable = engine.isQueryResultAvailable(this.occlusionQuery);
+                if (isOcclusionQueryAvailable) {
+                    var occlusionQueryResult = engine.getQueryResult(this.occlusionQuery);
+
+                    this.isOcclusionQueryInProgress = false;
+                    this._occlusionInternalRetryCounter = 0;
+                    this._isOccluded = occlusionQueryResult === 1 ? false : true;
+                }
+                else {
+
+                    this._occlusionInternalRetryCounter++;
+
+                    if (this.occlusionRetryCount !== -1 && this._occlusionInternalRetryCounter > this.occlusionRetryCount) {
+                        // break;
+                        this.isOcclusionQueryInProgress = false;
+                        this._occlusionInternalRetryCounter = 0;
+
+                        // if optimistic set isOccluded to false regardless of the status of isOccluded. (Render in the current render loop)
+                        // if strict continue the last state of the object.
+                        this._isOccluded = this.occlusionType === AbstractMesh.OCCLUSION_TYPE_OPTIMISITC ? false : this._isOccluded;
+                    }
+                    else {
+                        return;
+                    }
+
+                }
+            }
+
+
+            var scene = this.getScene();
+            var occlusionBoundingBoxRenderer = scene.getBoundingBoxRenderer();
+            engine.beginQuery(this.occlusionQueryAlgorithmType, this.occlusionQuery);
+            occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
+            engine.endQuery(this.occlusionQueryAlgorithmType);
+            this.isOcclusionQueryInProgress = true;
+        }
 
     }
 }

+ 36 - 32
src/Mesh/babylon.mesh.ts

@@ -137,7 +137,7 @@
         private _sourceNormals: Float32Array;   // Will be used to save original normals when using software skinning
 
         // Will be used to save a source mesh reference, If any
-        private _source: BABYLON.Mesh = null; 
+        private _source: BABYLON.Mesh = null;
         public get source(): BABYLON.Mesh {
             return this._source;
         }
@@ -224,7 +224,7 @@
          */
         public getClassName(): string {
             return "Mesh";
-        }   
+        }
 
         /**
          * Returns a string.  
@@ -648,7 +648,7 @@
             if (this.subMeshes && this.subMeshes.length > 0) {
                 var totalIndices = this.getIndices().length;
                 let needToRecreate = false;
-    
+
                 if (force) {
                     needToRecreate = true;
                 } else {
@@ -918,7 +918,7 @@
                     if (this._unIndexed) {
                         engine.drawUnIndexed(false, subMesh.verticesStart, subMesh.verticesCount, instancesCount);
                     } else {
-                        engine.draw(false, 0, instancesCount > 0 ? subMesh.linesIndexCount / 2 : subMesh.linesIndexCount, instancesCount);
+                        engine.draw(false, 0, subMesh.linesIndexCount, instancesCount);
                     }
                     break;
 
@@ -1058,8 +1058,7 @@
                 instancesBuffer.updateDirectly(this._instancesData, 0, instancesCount);
             }
 
-            this.geometry._bind(effect);
-
+            this._bind(subMesh, effect, fillMode);
             this._draw(subMesh, fillMode, instancesCount);
 
             engine.unbindInstanceAttributes();
@@ -1107,6 +1106,12 @@
          * Returns the Mesh.   
          */
         public render(subMesh: SubMesh, enableAlphaMode: boolean): Mesh {
+
+            this.checkOcclusionQuery();
+            if (this._isOccluded) {
+                return;
+            }
+
             var scene = this.getScene();
 
             // Managing instances
@@ -1146,7 +1151,7 @@
             if (enableAlphaMode) {
                 engine.setAlphaMode(effectiveMaterial.alphaMode);
             }
-            
+
             // Outline - step 1
             var savedDepthWrite = engine.getDepthWrite();
             if (this.renderOutline) {
@@ -1166,10 +1171,13 @@
 
             // Bind
             var fillMode = scene.forcePointsCloud ? Material.PointFillMode : (scene.forceWireframe ? Material.WireFrameFillMode : effectiveMaterial.fillMode);
-            this._bind(subMesh, effect, fillMode);
+
+            if (!hardwareInstancedRendering) { // Binding will be done later because we need to add more info to the VB
+                this._bind(subMesh, effect, fillMode);
+            }
 
             var world = this.getWorldMatrix();
-            
+
             if (effectiveMaterial.storeEffectOnSubMeshes) {
                 effectiveMaterial.bindForSubMesh(world, this, subMesh);
             } else {
@@ -1270,8 +1278,7 @@
                     this._delayLoadingFunction(JSON.parse(data), this);
                 }
 
-                this.instances.forEach(instance =>
-                {
+                this.instances.forEach(instance => {
                     instance._syncSubMeshes();
                 });
 
@@ -2004,7 +2011,7 @@
             // Alpha
             serializationObject.alphaIndex = this.alphaIndex;
             serializationObject.hasVertexAlpha = this.hasVertexAlpha;
-            
+
             // Overlay
             serializationObject.overlayAlpha = this.overlayAlpha;
             serializationObject.overlayColor = this.overlayColor.asArray();
@@ -2018,7 +2025,7 @@
                 serializationObject.actions = this.actionManager.serialize(this.name);
             }
         }
-        
+
         public _syncGeometryWithMorphTargetManager() {
             if (!this.geometry) {
                 return;
@@ -2046,22 +2053,19 @@
                 }
             } else {
                 var index = 0;
-                
+
                 // Positions
-                while (this.geometry.isVerticesDataPresent(VertexBuffer.PositionKind + index))
-                {
+                while (this.geometry.isVerticesDataPresent(VertexBuffer.PositionKind + index)) {
                     this.geometry.removeVerticesData(VertexBuffer.PositionKind + index);
-                    
-                    if (this.geometry.isVerticesDataPresent(VertexBuffer.NormalKind + index))
-                    {
+
+                    if (this.geometry.isVerticesDataPresent(VertexBuffer.NormalKind + index)) {
                         this.geometry.removeVerticesData(VertexBuffer.NormalKind + index);
                     }
-                    if (this.geometry.isVerticesDataPresent(VertexBuffer.TangentKind + index))
-                    {
+                    if (this.geometry.isVerticesDataPresent(VertexBuffer.TangentKind + index)) {
                         this.geometry.removeVerticesData(VertexBuffer.TangentKind + index);
                     }
                     index++;
-                }    
+                }
             }
         }
 
@@ -2072,7 +2076,7 @@
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          */
         public static Parse(parsedMesh: any, scene: Scene, rootUrl: string): Mesh {
-            var mesh : Mesh;
+            var mesh: Mesh;
 
             if (parsedMesh.type && parsedMesh.type === "GroundMesh") {
                 mesh = GroundMesh.Parse(parsedMesh, scene);
@@ -2137,7 +2141,7 @@
             if (parsedMesh.isBlocker !== undefined) {
                 mesh.isBlocker = parsedMesh.isBlocker;
             }
-            
+
             mesh._shouldGenerateFlatShading = parsedMesh.useFlatShading;
 
             // freezeWorldMatrix
@@ -2547,12 +2551,12 @@
                 sideOrientation: sideOrientation
             }
             return MeshBuilder.CreatePolygon(name, options, scene);
-		}
+        }
 
-       /**
-         * Creates an extruded polygon mesh, with depth in the Y direction. 
-         * Please consider using the same method from the MeshBuilder class instead. 
-		*/
+        /**
+          * Creates an extruded polygon mesh, with depth in the Y direction. 
+          * Please consider using the same method from the MeshBuilder class instead. 
+         */
         public static ExtrudePolygon(name: string, shape: Vector3[], depth: number, scene: Scene, holes?: Vector3[][], updatable?: boolean, sideOrientation?: number): Mesh {
             var options = {
                 shape: shape,
@@ -2562,7 +2566,7 @@
                 sideOrientation: sideOrientation
             }
             return MeshBuilder.ExtrudePolygon(name, options, scene);
-		}
+        }
 
         /**
          * Creates an extruded shape mesh.    
@@ -3090,12 +3094,12 @@
 
             // Subdivide
             if (subdivideWithSubMeshes) {
-                
+
                 //-- Suppresions du submesh global
                 meshSubclass.releaseSubMeshes();
                 index = 0;
                 var offset = 0;
-                
+
                 //-- aplique la subdivision en fonction du tableau d'indices
                 while (index < indiceArray.length) {
                     BABYLON.SubMesh.CreateFromIndices(0, offset, indiceArray[index], meshSubclass);

+ 38 - 1
src/Rendering/babylon.boundingBoxRenderer.ts

@@ -27,7 +27,7 @@
 
 
             var engine = this._scene.getEngine();
-            var boxdata = VertexData.CreateBox({size: 1.0});
+            var boxdata = VertexData.CreateBox({ size: 1.0 });
             this._vertexBuffers[VertexBuffer.PositionKind] = new VertexBuffer(engine, boxdata.positions, VertexBuffer.PositionKind, false);
             this._indexBuffer = engine.createIndexBuffer([0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 7, 1, 6, 2, 5, 3, 4]);
         }
@@ -89,6 +89,43 @@
             engine.setDepthWrite(true);
         }
 
+        public renderOcclusionBoundingBox(mesh: AbstractMesh): void {
+
+            this._prepareRessources();
+
+            if (!this._colorShader.isReady()) {
+                return;
+            }
+
+            var engine = this._scene.getEngine();
+            engine.setDepthWrite(false);
+            engine.setColorWrite(false);
+            this._colorShader._preBind();
+
+            var boundingBox = mesh._boundingInfo.boundingBox;
+            var min = boundingBox.minimum;
+            var max = boundingBox.maximum;
+            var diff = max.subtract(min);
+            var median = min.add(diff.scale(0.5));
+
+            var worldMatrix = Matrix.Scaling(diff.x, diff.y, diff.z)
+                .multiply(Matrix.Translation(median.x, median.y, median.z))
+                .multiply(boundingBox.getWorldMatrix());
+
+            engine.bindBuffers(this._vertexBuffers, this._indexBuffer, this._colorShader.getEffect());
+
+            engine.setDepthFunctionToLess();
+            this._scene.resetCachedMaterial();
+            this._colorShader.bind(worldMatrix);
+
+            engine.draw(false, 0, 24);
+
+            this._colorShader.unbind();
+            engine.setDepthFunctionToLessOrEqual();
+            engine.setDepthWrite(true);
+            engine.setColorWrite(true);
+        }
+
         public dispose(): void {
             if (!this._colorShader) {
                 return;

+ 5 - 3
src/Rendering/babylon.outlineRenderer.ts

@@ -38,14 +38,16 @@
             // Alpha test
             if (material && material.needAlphaTesting()) {
                 var alphaTexture = material.getAlphaTestTexture();
-                this._effect.setTexture("diffuseSampler", alphaTexture);
-                this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
+                if (alphaTexture) {
+                    this._effect.setTexture("diffuseSampler", alphaTexture);
+                    this._effect.setMatrix("diffuseMatrix", alphaTexture.getTextureMatrix());
+                }
             }
 
             engine.setZOffset(-this.zOffset);
 
             mesh._processRendering(subMesh, this._effect, Material.TriangleFillMode, batch, hardwareInstancedRendering,
-                (isInstance, world) => { this._effect.setMatrix("world", world)});
+                (isInstance, world) => { this._effect.setMatrix("world", world) });
 
             engine.setZOffset(0);
         }

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

@@ -378,6 +378,10 @@
             return url;
         }
 
+        public static PreprocessUrl = (url: string) => {
+            return url;
+        }
+
         public static LoadImage(url: any, onload, onerror, database): HTMLImageElement {
             if (url instanceof ArrayBuffer) {
                 url = Tools.EncodeArrayBufferTobase64(url);
@@ -385,6 +389,8 @@
 
             url = Tools.CleanUrl(url);
 
+            url = Tools.PreprocessUrl(url);
+
             var img = new Image();
 
             if (url.substr(0, 5) !== "data:") {
@@ -455,6 +461,8 @@
         public static LoadFile(url: string, callback: (data: any) => void, progressCallBack?: (data: any) => void, database?, useArrayBuffer?: boolean, onError?: (request: XMLHttpRequest) => void): void {
             url = Tools.CleanUrl(url);
 
+            url = Tools.PreprocessUrl(url);
+
             var noIndexedDB = () => {
                 var request = new XMLHttpRequest();
                 var loadUrl = Tools.BaseUrl + url;

+ 55 - 13
src/babylon.engine.ts

@@ -103,7 +103,7 @@
             mag: magFilter
         }
     }
-  
+
     var partialLoad = (url: string, index: number, loadedImages: any, scene,
         onfinish: (images: HTMLImageElement[]) => void, onErrorCallBack: () => void = null) => {
 
@@ -591,6 +591,10 @@
         private _performanceMonitor = new PerformanceMonitor();
         private _fps = 60;
         private _deltaTime = 0;
+        /**
+         * Turn this value on if you want to pause FPS computation when in background
+         */
+        public disablePerformanceMonitorInBackground = false;
 
         public get performanceMonitor(): PerformanceMonitor {
             return this._performanceMonitor;
@@ -740,12 +744,16 @@
                 }
 
                 this._onBlur = () => {
-                    this._performanceMonitor.disable();
+                    if (this.disablePerformanceMonitorInBackground) {
+                        this._performanceMonitor.disable();
+                    }
                     this._windowIsBackground = true;
                 };
 
                 this._onFocus = () => {
-                    this._performanceMonitor.enable();
+                    if (this.disablePerformanceMonitorInBackground) {
+                        this._performanceMonitor.enable();
+                    }
                     this._windowIsBackground = false;
                 };
 
@@ -992,11 +1000,11 @@
         }
 
         public isDeterministicLockStep(): boolean {
-          return this._deterministicLockstep;
+            return this._deterministicLockstep;
         }
 
         public getLockstepMaxSteps(): number {
-          return this._lockstepMaxSteps;
+            return this._lockstepMaxSteps;
         }
 
         public getGlInfo() {
@@ -2627,7 +2635,7 @@
                         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
                         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
-                        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);                        
+                        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
                         this._rescaleTexture(source, texture, scene, internalFormat, () => {
                             this._releaseTexture(source);
@@ -2653,9 +2661,9 @@
 
         private _rescaleTexture(source: WebGLTexture, destination: WebGLTexture, scene: Scene, internalFormat: number, onComplete: () => void): void {
             let rtt = this.createRenderTargetTexture({
-                    width: destination._width,
-                    height: destination._height,
-                }, {
+                width: destination._width,
+                height: destination._height,
+            }, {
                     generateMipMaps: false,
                     type: Engine.TEXTURETYPE_UNSIGNED_INT,
                     samplingMode: Texture.BILINEAR_SAMPLINGMODE,
@@ -2667,7 +2675,7 @@
             if (!this._rescalePostProcess) {
                 this._rescalePostProcess = new BABYLON.PassPostProcess("rescale", 1, null, Texture.BILINEAR_SAMPLINGMODE, this, false, Engine.TEXTURETYPE_UNSIGNED_INT);
             }
-			this._rescalePostProcess.getEffect().executeWhenCompiled(() => {
+            this._rescalePostProcess.getEffect().executeWhenCompiled(() => {
                 this._rescalePostProcess.onApply = function (effect) {
                     effect._bindTexture("textureSampler", source);
                 }
@@ -2679,7 +2687,7 @@
 
                 this.unBindFramebuffer(rtt);
                 this._releaseTexture(rtt);
-                
+
                 if (onComplete) {
                     onComplete();
                 }
@@ -3681,6 +3689,10 @@
 
         private _prepareWebGLTextureContinuation(texture: WebGLTexture, scene: Scene, noMipmap: boolean, isCompressed: boolean, samplingMode: number): void {
             var gl = this._gl;
+            if (!gl) {
+                return;
+            }
+
             var filters = getSamplingParameters(samplingMode, !noMipmap, gl);
 
             gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filters.mag);
@@ -3702,11 +3714,15 @@
         }
 
         private _prepareWebGLTexture(texture: WebGLTexture, scene: Scene, width: number, height: number, invertY: boolean, noMipmap: boolean, isCompressed: boolean,
-                                        processFunction: (width: number, height: number, continuationCallback: () => void) => boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE): void {
+            processFunction: (width: number, height: number, continuationCallback: () => void) => boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE): void {
             var potWidth = this.needPOTTextures ? Tools.GetExponentOfTwo(width, this.getCaps().maxTextureSize) : width;
             var potHeight = this.needPOTTextures ? Tools.GetExponentOfTwo(height, this.getCaps().maxTextureSize) : height;
 
             var gl = this._gl;
+            if (!gl) {
+                return;
+            }
+
             this._bindTextureDirectly(gl.TEXTURE_2D, texture);
             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
 
@@ -3721,7 +3737,7 @@
             })) {
                 // Returning as texture needs extra async steps
                 return;
-            }         
+            }
 
             this._prepareWebGLTextureContinuation(texture, scene, noMipmap, isCompressed, samplingMode);
         }
@@ -4375,6 +4391,32 @@
             return this._gl.RGBA;
         };
 
+        public createQuery(): WebGLQuery {
+            return this._gl.createQuery();
+        }
+
+        public isQueryResultAvailable(query: WebGLQuery) {
+            return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT_AVAILABLE) as boolean;
+        }
+
+        public getQueryResult(query: WebGLQuery) {
+            return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT) as number;
+        }
+
+        public beginQuery(algorithmType: number, query: WebGLQuery) {
+            var glAlgorithm = this.getGlAlgorithmType(algorithmType);
+            this._gl.beginQuery(glAlgorithm, query);
+        }
+
+        public endQuery(algorithmType: number) {
+            var glAlgorithm = this.getGlAlgorithmType(algorithmType);
+            this._gl.endQuery(glAlgorithm);
+        }
+
+        private getGlAlgorithmType(algorithmType: number): number {
+            return algorithmType === AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE ? this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE : this._gl.ANY_SAMPLES_PASSED;
+        }
+
         // Statics
         public static isSupported(): boolean {
             try {

+ 13 - 1
src/babylon.mixins.ts

@@ -44,6 +44,12 @@ interface WebGLRenderingContext {
     getUniformBlockIndex(program: WebGLProgram, uniformBlockName: string): number;
     uniformBlockBinding(program: WebGLProgram, uniformBlockIndex: number, uniformBlockBinding: number): void;
 
+    // Occlusion Query
+    createQuery(): WebGLQuery;
+    beginQuery(target: number, query: WebGLQuery);
+    endQuery(target: number): void;
+    getQueryParameter(query: WebGLQuery, pname: number): any;
+
     MAX_SAMPLES: number;
     RGBA8: number;
     READ_FRAMEBUFFER: number;
@@ -55,7 +61,7 @@ interface WebGLRenderingContext {
     RGBA32F: number;
 
     DEPTH24_STENCIL8: number;
-    
+
     /* Multiple Render Targets */
     drawBuffers(buffers: number[]): void;
     readBuffer(src: number): void;
@@ -65,6 +71,12 @@ interface WebGLRenderingContext {
     readonly COLOR_ATTACHMENT1: number;                             // 0x8CE2
     readonly COLOR_ATTACHMENT2: number;                             // 0x8CE3
     readonly COLOR_ATTACHMENT3: number;                             // 0x8CE4
+
+    // Occlusion Query
+    ANY_SAMPLES_PASSED_CONSERVATIVE: number;
+    ANY_SAMPLES_PASSED: number;
+    QUERY_RESULT_AVAILABLE: number;
+    QUERY_RESULT: number;
 }
 
 interface HTMLURL {