Przeglądaj źródła

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

# Conflicts:
#	dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
#	dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
David Catuhe 8 lat temu
rodzic
commit
e0f9e61098

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

Plik diff jest za duży
+ 2930 - 2900
dist/preview release/babylon.d.ts


Plik diff jest za duży
+ 36 - 36
dist/preview release/babylon.js


+ 110 - 0
dist/preview release/babylon.max.js

@@ -10983,6 +10983,26 @@ var BABYLON;
             return this._gl.RGBA;
         };
         ;
+        Engine.prototype.createQuery = function () {
+            return this._gl.createQuery();
+        };
+        Engine.prototype.isQueryResultAvailable = function (query) {
+            return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT_AVAILABLE);
+        };
+        Engine.prototype.getQueryResult = function (query) {
+            return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT);
+        };
+        Engine.prototype.beginQuery = function (algorithmType, query) {
+            var glAlgorithm = this.getGlAlgorithmType(algorithmType);
+            this._gl.beginQuery(glAlgorithm, query);
+        };
+        Engine.prototype.endQuery = function (algorithmType) {
+            var glAlgorithm = this.getGlAlgorithmType(algorithmType);
+            this._gl.endQuery(glAlgorithm);
+        };
+        Engine.prototype.getGlAlgorithmType = function (algorithmType) {
+            return algorithmType === BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE ? this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE : this._gl.ANY_SAMPLES_PASSED;
+        };
         // Statics
         Engine.isSupported = function () {
             try {
@@ -11798,6 +11818,14 @@ var BABYLON;
             // Properties
             _this.definedFacingForward = true; // orientation for POV movement & rotation
             _this.position = BABYLON.Vector3.Zero();
+            _this._webGLVersion = _this.getEngine().webGLVersion;
+            _this._occlusionInternalRetryCounter = 0;
+            _this.occlusionType = AbstractMesh.OCCLUSION_TYPE_NONE;
+            _this.occlusionRetryCount = -1;
+            _this._isOccluded = false;
+            _this.occlusionQuery = _this.getEngine().createQuery();
+            _this.isOcclusionQueryInProgress = false;
+            _this.occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
             _this._rotation = BABYLON.Vector3.Zero();
             _this._scaling = BABYLON.Vector3.One();
             _this.billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
@@ -11978,6 +12006,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AbstractMesh.prototype, "isOccluded", {
+            get: function () {
+                return this._isOccluded;
+            },
+            set: function (value) {
+                this._isOccluded = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(AbstractMesh.prototype, "material", {
             get: function () {
                 return this._material;
@@ -13820,12 +13858,53 @@ var BABYLON;
             BABYLON.VertexData.ComputeNormals(positions, indices, normals, { useRightHandedSystem: this.getScene().useRightHandedSystem });
             this.setVerticesData(BABYLON.VertexBuffer.NormalKind, normals, updatable);
         };
+        AbstractMesh.prototype.checkOcclusionQuery = function () {
+            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;
+        };
         // Statics
         AbstractMesh._BILLBOARDMODE_NONE = 0;
         AbstractMesh._BILLBOARDMODE_X = 1;
         AbstractMesh._BILLBOARDMODE_Y = 2;
         AbstractMesh._BILLBOARDMODE_Z = 4;
         AbstractMesh._BILLBOARDMODE_ALL = 7;
+        AbstractMesh.OCCLUSION_TYPE_NONE = 0;
+        AbstractMesh.OCCLUSION_TYPE_OPTIMISITC = 1;
+        AbstractMesh.OCCLUSION_TYPE_STRICT = 2;
+        AbstractMesh.OCCLUSION_ALGORITHM_TYPE_ACCURATE = 0;
+        AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE = 1;
         AbstractMesh._rotationAxisCache = new BABYLON.Quaternion();
         AbstractMesh._lookAtVectorCache = new BABYLON.Vector3(0, 0, 0);
         return AbstractMesh;
@@ -21469,6 +21548,10 @@ var BABYLON;
          * Returns the Mesh.
          */
         Mesh.prototype.render = function (subMesh, enableAlphaMode) {
+            this.checkOcclusionQuery();
+            if (this._isOccluded) {
+                return;
+            }
             var scene = this.getScene();
             // Managing instances
             var batch = this._getInstancesRenderList(subMesh._id);
@@ -63129,6 +63212,33 @@ var BABYLON;
             engine.setDepthFunctionToLessOrEqual();
             engine.setDepthWrite(true);
         };
+        BoundingBoxRenderer.prototype.renderOcclusionBoundingBox = function (mesh) {
+            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 = BABYLON.Matrix.Scaling(diff.x, diff.y, diff.z)
+                .multiply(BABYLON.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);
+        };
         BoundingBoxRenderer.prototype.dispose = function () {
             if (!this._colorShader) {
                 return;

Plik diff jest za duży
+ 2930 - 2900
dist/preview release/babylon.module.d.ts


Plik diff jest za duży
+ 41 - 41
dist/preview release/babylon.worker.js


Plik diff jest za duży
+ 22 - 22
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 110 - 0
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -10983,6 +10983,26 @@ var BABYLON;
             return this._gl.RGBA;
         };
         ;
+        Engine.prototype.createQuery = function () {
+            return this._gl.createQuery();
+        };
+        Engine.prototype.isQueryResultAvailable = function (query) {
+            return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT_AVAILABLE);
+        };
+        Engine.prototype.getQueryResult = function (query) {
+            return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT);
+        };
+        Engine.prototype.beginQuery = function (algorithmType, query) {
+            var glAlgorithm = this.getGlAlgorithmType(algorithmType);
+            this._gl.beginQuery(glAlgorithm, query);
+        };
+        Engine.prototype.endQuery = function (algorithmType) {
+            var glAlgorithm = this.getGlAlgorithmType(algorithmType);
+            this._gl.endQuery(glAlgorithm);
+        };
+        Engine.prototype.getGlAlgorithmType = function (algorithmType) {
+            return algorithmType === BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE ? this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE : this._gl.ANY_SAMPLES_PASSED;
+        };
         // Statics
         Engine.isSupported = function () {
             try {
@@ -11798,6 +11818,14 @@ var BABYLON;
             // Properties
             _this.definedFacingForward = true; // orientation for POV movement & rotation
             _this.position = BABYLON.Vector3.Zero();
+            _this._webGLVersion = _this.getEngine().webGLVersion;
+            _this._occlusionInternalRetryCounter = 0;
+            _this.occlusionType = AbstractMesh.OCCLUSION_TYPE_NONE;
+            _this.occlusionRetryCount = -1;
+            _this._isOccluded = false;
+            _this.occlusionQuery = _this.getEngine().createQuery();
+            _this.isOcclusionQueryInProgress = false;
+            _this.occlusionQueryAlgorithmType = AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE;
             _this._rotation = BABYLON.Vector3.Zero();
             _this._scaling = BABYLON.Vector3.One();
             _this.billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
@@ -11978,6 +12006,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AbstractMesh.prototype, "isOccluded", {
+            get: function () {
+                return this._isOccluded;
+            },
+            set: function (value) {
+                this._isOccluded = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(AbstractMesh.prototype, "material", {
             get: function () {
                 return this._material;
@@ -13820,12 +13858,53 @@ var BABYLON;
             BABYLON.VertexData.ComputeNormals(positions, indices, normals, { useRightHandedSystem: this.getScene().useRightHandedSystem });
             this.setVerticesData(BABYLON.VertexBuffer.NormalKind, normals, updatable);
         };
+        AbstractMesh.prototype.checkOcclusionQuery = function () {
+            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;
+        };
         // Statics
         AbstractMesh._BILLBOARDMODE_NONE = 0;
         AbstractMesh._BILLBOARDMODE_X = 1;
         AbstractMesh._BILLBOARDMODE_Y = 2;
         AbstractMesh._BILLBOARDMODE_Z = 4;
         AbstractMesh._BILLBOARDMODE_ALL = 7;
+        AbstractMesh.OCCLUSION_TYPE_NONE = 0;
+        AbstractMesh.OCCLUSION_TYPE_OPTIMISITC = 1;
+        AbstractMesh.OCCLUSION_TYPE_STRICT = 2;
+        AbstractMesh.OCCLUSION_ALGORITHM_TYPE_ACCURATE = 0;
+        AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE = 1;
         AbstractMesh._rotationAxisCache = new BABYLON.Quaternion();
         AbstractMesh._lookAtVectorCache = new BABYLON.Vector3(0, 0, 0);
         return AbstractMesh;
@@ -21469,6 +21548,10 @@ var BABYLON;
          * Returns the Mesh.
          */
         Mesh.prototype.render = function (subMesh, enableAlphaMode) {
+            this.checkOcclusionQuery();
+            if (this._isOccluded) {
+                return;
+            }
             var scene = this.getScene();
             // Managing instances
             var batch = this._getInstancesRenderList(subMesh._id);
@@ -47392,6 +47475,33 @@ var BABYLON;
             engine.setDepthFunctionToLessOrEqual();
             engine.setDepthWrite(true);
         };
+        BoundingBoxRenderer.prototype.renderOcclusionBoundingBox = function (mesh) {
+            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 = BABYLON.Matrix.Scaling(diff.x, diff.y, diff.z)
+                .multiply(BABYLON.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);
+        };
         BoundingBoxRenderer.prototype.dispose = function () {
             if (!this._colorShader) {
                 return;

Plik diff jest za duży
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


+ 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;
+        }
 
     }
 }

+ 30 - 28
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 {
@@ -1106,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
@@ -1145,7 +1151,7 @@
             if (enableAlphaMode) {
                 engine.setAlphaMode(effectiveMaterial.alphaMode);
             }
-            
+
             // Outline - step 1
             var savedDepthWrite = engine.getDepthWrite();
             if (this.renderOutline) {
@@ -1171,7 +1177,7 @@
             }
 
             var world = this.getWorldMatrix();
-            
+
             if (effectiveMaterial.storeEffectOnSubMeshes) {
                 effectiveMaterial.bindForSubMesh(world, this, subMesh);
             } else {
@@ -1272,8 +1278,7 @@
                     this._delayLoadingFunction(JSON.parse(data), this);
                 }
 
-                this.instances.forEach(instance =>
-                {
+                this.instances.forEach(instance => {
                     instance._syncSubMeshes();
                 });
 
@@ -2006,7 +2011,7 @@
             // Alpha
             serializationObject.alphaIndex = this.alphaIndex;
             serializationObject.hasVertexAlpha = this.hasVertexAlpha;
-            
+
             // Overlay
             serializationObject.overlayAlpha = this.overlayAlpha;
             serializationObject.overlayColor = this.overlayColor.asArray();
@@ -2020,7 +2025,7 @@
                 serializationObject.actions = this.actionManager.serialize(this.name);
             }
         }
-        
+
         public _syncGeometryWithMorphTargetManager() {
             if (!this.geometry) {
                 return;
@@ -2048,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++;
-                }    
+                }
             }
         }
 
@@ -2074,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);
@@ -2139,7 +2141,7 @@
             if (parsedMesh.isBlocker !== undefined) {
                 mesh.isBlocker = parsedMesh.isBlocker;
             }
-            
+
             mesh._shouldGenerateFlatShading = parsedMesh.useFlatShading;
 
             // freezeWorldMatrix
@@ -2549,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,
@@ -2564,7 +2566,7 @@
                 sideOrientation: sideOrientation
             }
             return MeshBuilder.ExtrudePolygon(name, options, scene);
-		}
+        }
 
         /**
          * Creates an extruded shape mesh.    
@@ -3092,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;

+ 37 - 11
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) => {
 
@@ -1000,11 +1000,11 @@
         }
 
         public isDeterministicLockStep(): boolean {
-          return this._deterministicLockstep;
+            return this._deterministicLockstep;
         }
 
         public getLockstepMaxSteps(): number {
-          return this._lockstepMaxSteps;
+            return this._lockstepMaxSteps;
         }
 
         public getGlInfo() {
@@ -2635,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);
@@ -2661,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,
@@ -2675,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);
                 }
@@ -2687,7 +2687,7 @@
 
                 this.unBindFramebuffer(rtt);
                 this._releaseTexture(rtt);
-                
+
                 if (onComplete) {
                     onComplete();
                 }
@@ -3714,7 +3714,7 @@
         }
 
         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;
 
@@ -3737,7 +3737,7 @@
             })) {
                 // Returning as texture needs extra async steps
                 return;
-            }         
+            }
 
             this._prepareWebGLTextureContinuation(texture, scene, noMipmap, isCompressed, samplingMode);
         }
@@ -4391,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 {