= 9 лет назад
Родитель
Сommit
505db09ae6

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

@@ -157,7 +157,7 @@
 
             if (this._isScreenSpace) {
                 this._afterRenderObserver = this._scene.onAfterRenderObservable.add((d, s) => {
-                    this._engine.clear(null, false, true);
+                    this._engine.clear(null, false, true, true);
                     this._render();
                 });
             } else {

+ 2 - 2
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -266,9 +266,9 @@
 
             this._shadowMap.onClearObservable.add((engine: Engine) => {
                 if (this.useBlurVarianceShadowMap || this.useVarianceShadowMap) {
-                    engine.clear(new Color4(0, 0, 0, 0), true, true);
+                    engine.clear(new Color4(0, 0, 0, 0), true, true, true);
                 } else {
-                    engine.clear(new Color4(1.0, 1.0, 1.0, 1.0), true, true);
+                    engine.clear(new Color4(1.0, 1.0, 1.0, 1.0), true, true, true);
                 }
             });
         }

+ 2 - 2
src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts

@@ -299,7 +299,7 @@
                     this._effect.setFloat("face", face);
 
                     // Clear
-                    engine.clear(scene.clearColor, true, true);
+                    engine.clear(scene.clearColor, true, true, true);
 
                     // Draw order
                     engine.draw(true, 0, 6);
@@ -313,7 +313,7 @@
                 engine.bindFramebuffer(this._texture);
 
                 // Clear
-                engine.clear(scene.clearColor, true, true);
+                engine.clear(scene.clearColor, true, true, true);
 
                 // Draw order
                 engine.draw(true, 0, 6);

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

@@ -300,7 +300,7 @@
             if (this.onClearObservable.hasObservers()) {
                 this.onClearObservable.notifyObservers(engine);
             } else {
-                engine.clear(scene.clearColor, true, true);
+                engine.clear(scene.clearColor, true, true, true);
             }
 
             if (!this._doNotChangeAspectRatio) {
@@ -338,6 +338,21 @@
             }
         }
 
+        public setRenderingOrder(renderingGroupId: number,
+            opaqueSortCompareFn: (a: SubMesh, b: SubMesh) => number = null,
+            alphaTestSortCompareFn: (a: SubMesh, b: SubMesh) => number = null,
+            transparentSortCompareFn: (a: SubMesh, b: SubMesh) => number = null) {
+            
+            this._renderingManager.setRenderingOrder(renderingGroupId,
+                opaqueSortCompareFn,
+                alphaTestSortCompareFn,
+                transparentSortCompareFn);
+        }
+
+        public setRenderingAutoClear(renderingGroupId: number, autoClear: boolean) {            
+            this._renderingManager.setRenderingAutoClear(renderingGroupId, autoClear);
+        }
+
         public clone(): RenderTargetTexture {
             var textureSize = this.getSize();
             var newTexture = new RenderTargetTexture(this.name, textureSize.width, this.getScene(), this._generateMipMaps);

+ 9 - 1
src/Materials/babylon.material.ts

@@ -135,7 +135,7 @@
         }
 
         /**
-        * An event triggered when the material is compiled.
+        * An event triggered when the material is bound.
         * @type {BABYLON.Observable}
         */
         public onBindObservable = new Observable<AbstractMesh>();
@@ -148,6 +148,11 @@
             this._onBindObserver = this.onBindObservable.add(callback);
         }
 
+        /**
+        * An event triggered when the material is unbound.
+        * @type {BABYLON.Observable}
+        */
+        public onUnBindObservable = new Observable<Material>();
 
 
         @serialize()
@@ -283,6 +288,9 @@
         }
 
         public unbind(): void {
+
+            this.onUnBindObservable.notifyObservers(this);
+
             if (this.disableDepthWrite) {
                 var engine = this._scene.getEngine();
                 engine.setDepthWrite(this._cachedDepthWriteState);

+ 2 - 2
src/PostProcess/babylon.postProcess.ts

@@ -196,9 +196,9 @@
 
             // Clear
             if (this.clearColor) {
-                this._engine.clear(this.clearColor, true, true);
+                this._engine.clear(this.clearColor, true, true, true);
             } else {
-                this._engine.clear(scene.clearColor, scene.autoClear || scene.forceWireframe, true);
+                this._engine.clear(scene.clearColor, scene.autoClear || scene.forceWireframe, true, true);
             }
 
             if (this._reusable) {

+ 1 - 1
src/Rendering/babylon.depthRenderer.ts

@@ -25,7 +25,7 @@
             
             // set default depth value to 1.0 (far away)
             this._depthMap.onClearObservable.add((engine: Engine) => {
-                engine.clear(new Color4(1.0, 1.0, 1.0, 1.0), true, true);
+                engine.clear(new Color4(1.0, 1.0, 1.0, 1.0), true, true, true);
             });
 
             // Custom render function

+ 88 - 27
src/Rendering/babylon.renderingGroup.ts

@@ -6,10 +6,21 @@
         private _alphaTestSubMeshes = new SmartArray<SubMesh>(256);
         private _activeVertices: number;
 
+        public opaqueSortCompareFn: (a: SubMesh, b: SubMesh) => number;
+        public alphaTestSortCompareFn: (a: SubMesh, b: SubMesh) => number;
+        public transparentSortCompareFn: (a: SubMesh, b: SubMesh) => number;
+
         public onBeforeTransparentRendering: () => void;
 
-        constructor(public index: number, scene: Scene) {
+        constructor(public index: number, scene: Scene,
+            opaqueSortCompareFn: (a: SubMesh, b: SubMesh) => number = null,
+            alphaTestSortCompareFn: (a: SubMesh, b: SubMesh) => number = null,
+            transparentSortCompareFn: (a: SubMesh, b: SubMesh) => number = RenderingGroup.defaultTransparentSortCompare) {
             this._scene = scene;
+
+            this.opaqueSortCompareFn = opaqueSortCompareFn;
+            this.alphaTestSortCompareFn = alphaTestSortCompareFn;
+            this.transparentSortCompareFn = transparentSortCompareFn;            
         }
 
         public render(customRenderFunction: (opaqueSubMeshes: SmartArray<SubMesh>, transparentSubMeshes: SmartArray<SubMesh>, alphaTestSubMeshes: SmartArray<SubMesh>) => void): boolean {
@@ -28,19 +39,52 @@
             // Opaque
             var subIndex: number;
             var submesh: SubMesh;
+            var sortedArray: SubMesh[];
 
-            for (subIndex = 0; subIndex < this._opaqueSubMeshes.length; subIndex++) {
-                submesh = this._opaqueSubMeshes.data[subIndex];
+            if (this.opaqueSortCompareFn) {
+                for (subIndex = 0; subIndex < this._opaqueSubMeshes.length; subIndex++) {
+                    submesh = this._opaqueSubMeshes.data[subIndex];
+                    submesh._alphaIndex = submesh.getMesh().alphaIndex;
+                    submesh._distanceToCamera = submesh.getBoundingInfo().boundingSphere.centerWorld.subtract(this._scene.activeCamera.globalPosition).length();
+                }
 
-                submesh.render(false);
+                sortedArray = this._opaqueSubMeshes.data.slice(0, this._opaqueSubMeshes.length);
+                sortedArray.sort(this.opaqueSortCompareFn);
+
+                for (subIndex = 0; subIndex < sortedArray.length; subIndex++) {
+                    submesh = sortedArray[subIndex];
+                    submesh.render(false);
+                }
+            }
+            else {
+                for (subIndex = 0; subIndex < this._opaqueSubMeshes.length; subIndex++) {
+                    submesh = this._opaqueSubMeshes.data[subIndex];
+                    submesh.render(false);
+                }
             }
 
             // Alpha test
             engine.setAlphaTesting(true);
-            for (subIndex = 0; subIndex < this._alphaTestSubMeshes.length; subIndex++) {
-                submesh = this._alphaTestSubMeshes.data[subIndex];
+            if (this.alphaTestSortCompareFn) {
+                for (subIndex = 0; subIndex < this._alphaTestSubMeshes.length; subIndex++) {
+                    submesh = this._alphaTestSubMeshes.data[subIndex];
+                    submesh._alphaIndex = submesh.getMesh().alphaIndex;
+                    submesh._distanceToCamera = submesh.getBoundingInfo().boundingSphere.centerWorld.subtract(this._scene.activeCamera.globalPosition).length();
+                }
 
-                submesh.render(false);
+                sortedArray = this._alphaTestSubMeshes.data.slice(0, this._alphaTestSubMeshes.length);
+                sortedArray.sort(this.alphaTestSortCompareFn);
+
+                for (subIndex = 0; subIndex < sortedArray.length; subIndex++) {
+                    submesh = sortedArray[subIndex];
+                    submesh.render(false);
+                }
+            }
+            else {
+                for (subIndex = 0; subIndex < this._alphaTestSubMeshes.length; subIndex++) {
+                    submesh = this._alphaTestSubMeshes.data[subIndex];
+                    submesh.render(false);
+                }
             }
             engine.setAlphaTesting(false);
 
@@ -58,26 +102,7 @@
                 }
 
                 var sortedArray = this._transparentSubMeshes.data.slice(0, this._transparentSubMeshes.length);
-
-                sortedArray.sort((a, b) => {
-                    // Alpha index first
-                    if (a._alphaIndex > b._alphaIndex) {
-                        return 1;
-                    }
-                    if (a._alphaIndex < b._alphaIndex) {
-                        return -1;
-                    }
-
-                    // Then distance to camera
-                    if (a._distanceToCamera < b._distanceToCamera) {
-                        return 1;
-                    }
-                    if (a._distanceToCamera > b._distanceToCamera) {
-                        return -1;
-                    }
-
-                    return 0;
-                });
+                sortedArray.sort(this.transparentSortCompareFn);
 
                 // Rendering                
                 for (subIndex = 0; subIndex < sortedArray.length; subIndex++) {
@@ -90,6 +115,42 @@
             return true;
         }
 
+        public static defaultTransparentSortCompare(a: SubMesh, b:SubMesh) : number {
+            // Alpha index first
+            if (a._alphaIndex > b._alphaIndex) {
+                return 1;
+            }
+            if (a._alphaIndex < b._alphaIndex) {
+                return -1;
+            }
+
+            // Then distance to camera
+            return RenderingGroup.backToFrontSortCompare(a, b);
+        }
+
+        public static backToFrontSortCompare(a: SubMesh, b:SubMesh) : number {
+            // Then distance to camera
+            if (a._distanceToCamera < b._distanceToCamera) {
+                return 1;
+            }
+            if (a._distanceToCamera > b._distanceToCamera) {
+                return -1;
+            }
+
+            return 0;
+        }
+
+        public static frontToBackSortCompare(a: SubMesh, b:SubMesh) : number {
+            // Then distance to camera
+            if (a._distanceToCamera < b._distanceToCamera) {
+                return -1;
+            }
+            if (a._distanceToCamera > b._distanceToCamera) {
+                return 1;
+            }
+
+            return 0;
+        }
 
         public prepare(): void {
             this._opaqueSubMeshes.reset();

+ 47 - 11
src/Rendering/babylon.renderingManager.ts

@@ -1,18 +1,28 @@
 module BABYLON {
     export class RenderingManager {
         public static MAX_RENDERINGGROUPS = 4;
+        public static MIN_RENDERINGGROUPS = 0;
 
         private _scene: Scene;
         private _renderingGroups = new Array<RenderingGroup>();
-        private _depthBufferAlreadyCleaned: boolean;
+        private _depthStencilBufferAlreadyCleaned: boolean;
 
         private _currentIndex: number;
         private _currentActiveMeshes: AbstractMesh[];
         private _currentRenderParticles: boolean;
         private _currentRenderSprites: boolean;
 
+        private _autoClear: { [id:number]: boolean } = {};
+        private _customOpaqueSortCompareFn: { [id:number]: (a: SubMesh, b: SubMesh) => number } = {};
+        private _customAlphaTestSortCompareFn: { [id:number]: (a: SubMesh, b: SubMesh) => number } = {};
+        private _customTransparentSortCompareFn: { [id:number]: (a: SubMesh, b: SubMesh) => number } = {};
+
         constructor(scene: Scene) {
             this._scene = scene;
+
+            for (let i = RenderingManager.MIN_RENDERINGGROUPS; i < RenderingManager.MAX_RENDERINGGROUPS; i++) {
+                this._autoClear[i] = true;
+            }
         }
 
         private _renderParticles(index: number, activeMeshes: AbstractMesh[]): void {
@@ -34,7 +44,7 @@
                     continue;
                 }
 
-                this._clearDepthBuffer();
+                this._clearDepthStencilBuffer();
 
                 if (!particleSystem.emitter.position || !activeMeshes || activeMeshes.indexOf(particleSystem.emitter) !== -1) {
                     this._scene._activeParticles.addCount(particleSystem.render(), false);
@@ -55,20 +65,20 @@
                 var spriteManager = this._scene.spriteManagers[id];
 
                 if (spriteManager.renderingGroupId === index && ((activeCamera.layerMask & spriteManager.layerMask) !== 0)) {
-                    this._clearDepthBuffer();
+                    this._clearDepthStencilBuffer();
                     spriteManager.render();
                 }
             }
             this._scene._spritesDuration.endMonitoring(false);
         }
 
-        private _clearDepthBuffer(): void {
-            if (this._depthBufferAlreadyCleaned) {
+        private _clearDepthStencilBuffer(): void {
+            if (this._depthStencilBufferAlreadyCleaned) {
                 return;
             }
 
-            this._scene.getEngine().clear(0, false, true);
-            this._depthBufferAlreadyCleaned = true;
+            this._scene.getEngine().clear(0, false, true, true);
+            this._depthStencilBufferAlreadyCleaned = true;
         }
 
         private _renderSpritesAndParticles() {
@@ -88,15 +98,17 @@
             this._currentRenderParticles = renderParticles;
             this._currentRenderSprites = renderSprites;
 
-            for (var index = 0; index < RenderingManager.MAX_RENDERINGGROUPS; index++) {
-                this._depthBufferAlreadyCleaned = index === 0;
+            for (var index = RenderingManager.MIN_RENDERINGGROUPS; index < RenderingManager.MAX_RENDERINGGROUPS; index++) {
+                this._depthStencilBufferAlreadyCleaned = index === RenderingManager.MIN_RENDERINGGROUPS;
                 var renderingGroup = this._renderingGroups[index];
                 var needToStepBack = false;
 
                 this._currentIndex = index;
 
                 if (renderingGroup) {
-                    this._clearDepthBuffer();
+                    if (this._autoClear[index]) {
+                        this._clearDepthStencilBuffer();
+                    }
 
                     if (!renderingGroup.onBeforeTransparentRendering) {
                         renderingGroup.onBeforeTransparentRendering = this._renderSpritesAndParticles.bind(this);
@@ -130,11 +142,35 @@
             var renderingGroupId = mesh.renderingGroupId || 0;
 
             if (!this._renderingGroups[renderingGroupId]) {
-                this._renderingGroups[renderingGroupId] = new RenderingGroup(renderingGroupId, this._scene);
+                this._renderingGroups[renderingGroupId] = new RenderingGroup(renderingGroupId, this._scene,
+                    this._customOpaqueSortCompareFn[renderingGroupId],
+                    this._customAlphaTestSortCompareFn[renderingGroupId],
+                    this._customTransparentSortCompareFn[renderingGroupId]
+                );
             }
 
             this._renderingGroups[renderingGroupId].dispatch(subMesh);
         }
 
+        public setRenderingOrder(renderingGroupId: number,
+            opaqueSortCompareFn: (a: SubMesh, b: SubMesh) => number = null,
+            alphaTestSortCompareFn: (a: SubMesh, b: SubMesh) => number = null,
+            transparentSortCompareFn: (a: SubMesh, b: SubMesh) => number = null) {
+            
+            if (this._renderingGroups[renderingGroupId]) {
+                var group = this._renderingGroups[renderingGroupId];
+                group.opaqueSortCompareFn = this._customOpaqueSortCompareFn[renderingGroupId];
+                group.alphaTestSortCompareFn = this._customAlphaTestSortCompareFn[renderingGroupId];
+                group.transparentSortCompareFn = this._customTransparentSortCompareFn[renderingGroupId];
+            }
+
+            this._customOpaqueSortCompareFn[renderingGroupId] = opaqueSortCompareFn;
+            this._customAlphaTestSortCompareFn[renderingGroupId] = alphaTestSortCompareFn;
+            this._customTransparentSortCompareFn[renderingGroupId] = transparentSortCompareFn;
+        }
+
+        public setRenderingAutoClear(renderingGroupId: number, autoClear: boolean) {            
+            this._autoClear[renderingGroupId] = autoClear;
+        }
     }
 } 

+ 180 - 0
src/States/babylon.stencilState.ts

@@ -0,0 +1,180 @@
+module BABYLON.Internals {
+    export class _StencilState {
+        private _isStencilTestDirty = false;
+        private _isStencilMaskDirty = false;
+        private _isStencilFuncDirty = false;
+        private _isStencilOpDirty = false;
+
+        private _stencilTest: boolean;
+
+        private _stencilMask: number;
+
+        private _stencilFunc: number;
+        private _stencilFuncRef: number;
+        private _stencilFuncMask: number;
+
+        private _stencilOpStencilFail: number;
+        private _stencilOpDepthFail: number;
+        private _stencilOpStencilDepthPass: number;
+
+        public get isDirty(): boolean {
+            return this._isStencilTestDirty || this._isStencilMaskDirty || this._isStencilFuncDirty;
+        }
+
+        public get stencilFunc(): number {
+            return this._stencilFunc;
+        }
+
+        public set stencilFunc(value: number) {
+            if (this._stencilFunc === value) {
+                return;
+            }
+
+            this._stencilFunc = value;
+            this._isStencilFuncDirty = true;
+        }
+
+        public get stencilFuncRef(): number {
+            return this._stencilFuncRef;
+        }
+
+        public set stencilFuncRef(value: number) {
+            if (this._stencilFuncRef === value) {
+                return;
+            }
+
+            this._stencilFuncRef = value;
+            this._isStencilFuncDirty = true;
+        }
+
+        public get stencilFuncMask(): number {
+            return this._stencilFuncMask;
+        }
+
+        public set stencilFuncMask(value: number) {
+            if (this._stencilFuncMask === value) {
+                return;
+            }
+
+            this._stencilFuncMask = value;
+            this._isStencilFuncDirty = true;
+        }
+
+        public get stencilOpStencilFail(): number {
+            return this._stencilOpStencilFail;
+        }
+
+        public set stencilOpStencilFail(value: number) {
+            if (this._stencilOpStencilFail === value) {
+                return;
+            }
+
+            this._stencilOpStencilFail = value;
+            this._isStencilOpDirty = true;
+        }
+
+        public get stencilOpDepthFail(): number {
+            return this._stencilOpDepthFail;
+        }
+
+        public set stencilOpDepthFail(value: number) {
+            if (this._stencilOpDepthFail === value) {
+                return;
+            }
+
+            this._stencilOpDepthFail = value;
+            this._isStencilOpDirty = true;
+        }
+
+        public get stencilOpStencilDepthPass(): number {
+            return this._stencilOpStencilDepthPass;
+        }
+
+        public set stencilOpStencilDepthPass(value: number) {
+            if (this._stencilOpStencilDepthPass === value) {
+                return;
+            }
+
+            this._stencilOpStencilDepthPass = value;
+            this._isStencilOpDirty = true;
+        }
+
+        public get stencilMask(): number {
+            return this._stencilMask;
+        }
+
+        public set stencilMask(value: number) {
+            if (this._stencilMask === value) {
+                return;
+            }
+
+            this._stencilMask = value;
+            this._isStencilMaskDirty = true;
+        }
+
+        public get stencilTest(): boolean {
+            return this._stencilTest;
+        }
+
+        public set stencilTest(value: boolean) {
+            if (this._stencilTest === value) {
+                return;
+            }
+
+            this._stencilTest = value;
+            this._isStencilTestDirty = true;
+        }
+
+        public reset() {
+            this._stencilMask = 0xFF;
+            this._stencilTest = false;
+
+            this._stencilFunc = null;
+            this._stencilFuncRef = 1;
+            this._stencilFuncMask = 0xFF;
+
+            this._stencilOpStencilFail = 0x1E00; // KEEP
+            this._stencilOpDepthFail = 0x1E00; // KEEP
+            this._stencilOpStencilDepthPass = 0x1E01; // REPLACE
+
+            this._isStencilTestDirty = true;
+            this._isStencilMaskDirty = true;
+            this._isStencilFuncDirty = false;
+            this._isStencilOpDirty = false;
+        }
+
+        public apply(gl: WebGLRenderingContext) {
+            if (!this.isDirty) {
+                return;
+            }
+
+            // Stencil test
+            if (this._isStencilTestDirty) {
+                if (this.stencilTest) {
+                    gl.enable(gl.STENCIL_TEST);
+                } else {
+                    gl.disable(gl.STENCIL_TEST);
+                }
+                this._isStencilTestDirty = false;
+            }
+
+            // Stencil mask
+            if (this._isStencilMaskDirty) {
+                gl.stencilMask(this.stencilMask);
+                this._isStencilMaskDirty = false;
+            }
+
+            // Stencil func
+            if (this._isStencilFuncDirty) {
+                gl.stencilFunc(this.stencilFunc, this.stencilFuncRef, this.stencilFuncMask);
+                this._isStencilFuncDirty = false;
+            }
+
+            // Stencil op
+            if (this._isStencilOpDirty) {
+                gl.stencilOp(this.stencilOpStencilFail, this.stencilOpDepthFail, this.stencilOpStencilDepthPass);
+                this._isStencilOpDirty = false;
+            }
+        }
+    }
+}

+ 140 - 36
src/babylon.engine.ts

@@ -349,6 +349,7 @@
 
         // States
         private _depthCullingState = new Internals._DepthCullingState();
+        private _stencilState = new Internals._StencilState();
         private _alphaState = new Internals._AlphaState();
         private _alphaMode = Engine.ALPHA_DISABLE;
 
@@ -656,6 +657,70 @@
             this._depthCullingState.depthFunc = this._gl.LEQUAL;
         }
 
+        public getStencilBuffer(): boolean {
+            return this._stencilState.stencilTest;
+        }
+
+        public setStencilBuffer(enable: boolean): void {
+            this._stencilState.stencilTest = enable;
+        }
+
+        public getStencilMask(): number {
+            return this._stencilState.stencilMask;
+        }
+
+        public setStencilMask(mask: number): void {
+            this._stencilState.stencilMask = mask;
+        }
+
+        public getStencilFunction(): number {
+            return this._stencilState.stencilFunc;
+        }
+
+        public getStencilFunctionReference(): number {
+            return this._stencilState.stencilFuncRef;
+        }
+
+        public getStencilFunctionMask(): number {
+            return this._stencilState.stencilFuncMask;
+        }
+
+        public setStencilFunction(stencilFunc: number) {
+            this._stencilState.stencilFunc = stencilFunc;
+        }
+        
+        public setStencilFunctionReference(reference: number) {
+            this._stencilState.stencilFuncRef = reference;
+        }
+        
+        public setStencilFunctionMask(mask: number) {
+            this._stencilState.stencilFuncMask = mask;
+        }
+
+        public getStencilOperationFail(): number {
+            return this._stencilState.stencilOpStencilFail;
+        }
+
+        public getStencilOperationDepthFail(): number {
+            return this._stencilState.stencilOpDepthFail;
+        }
+
+        public getStencilOperationPass(): number {
+            return this._stencilState.stencilOpStencilDepthPass;
+        }
+
+        public setStencilOperationFail(operation: number): void {
+            this._stencilState.stencilOpStencilFail = operation;
+        }
+
+        public setStencilOperationDepthFail(operation: number): void {
+            this._stencilState.stencilOpDepthFail = operation;
+        }
+
+        public setStencilOperationPass(operation: number): void {
+            this._stencilState.stencilOpStencilDepthPass = operation;
+        }
+
         /**
          * stop executing a render loop function and remove it from the execution array
          * @param {Function} [renderFunction] the function to be removed. If not provided all functions will be removed.
@@ -737,26 +802,22 @@
             }
         }
 
-        public clear(color: any, backBuffer: boolean, depthStencil: boolean): void {
+        public clear(color: any, backBuffer: boolean, depth: boolean, stencil:boolean = false): void {
             this.applyStates();
 
+            var mode = 0;            
             if (backBuffer) {
                 this._gl.clearColor(color.r, color.g, color.b, color.a !== undefined ? color.a : 1.0);
-            }
-
-            if (depthStencil && this._depthCullingState.depthMask) {
-                this._gl.clearDepth(1.0);
-            }
-            var mode = 0;
-
-            if (backBuffer) {
                 mode |= this._gl.COLOR_BUFFER_BIT;
             }
-
-            if (depthStencil && this._depthCullingState.depthMask) {
+            if (depth) {
+                this._gl.clearDepth(1.0);
                 mode |= this._gl.DEPTH_BUFFER_BIT;
             }
-
+            if (stencil) {
+                this._gl.clearStencil(0);
+                mode |= this._gl.STENCIL_BUFFER_BIT;
+            }
             this._gl.clear(mode);
         }
 
@@ -772,7 +833,7 @@
             gl.scissor(x, y, width, height);
 
             // Clear
-            this.clear(clearColor, true, true);
+            this.clear(clearColor, true, true, true);
 
             // Restore state
             gl.scissor(curScissorBox[0], curScissorBox[1], curScissorBox[2], curScissorBox[3]);
@@ -866,17 +927,14 @@
 
         public bindFramebuffer(texture: WebGLTexture, faceIndex?: number, requiredWidth?: number, requiredHeight?: number): void {
             this._currentRenderTarget = texture;
-
-            var gl = this._gl;
             this.bindUnboundFramebuffer(texture._framebuffer);
-
+            var gl = this._gl;
             if (texture.isCube) {
+                
                 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture, 0);
-            } else {
-                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
-            }
+            } 
 
-            this._gl.viewport(0, 0, requiredWidth || texture._width, requiredHeight || texture._height);
+            gl.viewport(0, 0, requiredWidth || texture._width, requiredHeight || texture._height);
 
             this.wipeCaches();
         }
@@ -1187,6 +1245,7 @@
 
         public applyStates() {
             this._depthCullingState.apply(this._gl);
+            this._stencilState.apply(this._gl);
             this._alphaState.apply(this._gl);
         }
 
@@ -1630,6 +1689,7 @@
             this.resetTextureCache();
             this._currentEffect = null;
 
+            this._stencilState.reset();
             this._depthCullingState.reset();
             this.setDepthFunctionToLessOrEqual();
             this._alphaState.reset();
@@ -1962,11 +2022,15 @@
             // in the same way, generateDepthBuffer is defaulted to true
             var generateMipMaps = false;
             var generateDepthBuffer = true;
+            var generateStencilBuffer = false;
+
             var type = Engine.TEXTURETYPE_UNSIGNED_INT;
             var samplingMode = Texture.TRILINEAR_SAMPLINGMODE;
             if (options !== undefined) {
                 generateMipMaps = options.generateMipMaps === undefined ? options : options.generateMipMaps;
                 generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
+                generateStencilBuffer = generateDepthBuffer && options.generateStencilBuffer;
+
                 type = options.type === undefined ? type : options.type;
                 if (options.samplingMode !== undefined) {
                     samplingMode = options.samplingMode;
@@ -2002,19 +2066,32 @@
 
             gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, getWebGLTextureType(gl, type), null);
 
-            var depthBuffer: WebGLRenderbuffer;
-            // Create the depth buffer
-            if (generateDepthBuffer) {
-                depthBuffer = gl.createRenderbuffer();
-                gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
+            var depthStencilBuffer: WebGLRenderbuffer;
+
+            // Create the depth/stencil buffer
+            if (generateStencilBuffer) {
+                depthStencilBuffer = gl.createRenderbuffer();
+                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
+                gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
+            }
+            else if (generateDepthBuffer) {
+                depthStencilBuffer = gl.createRenderbuffer();
+                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
                 gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
             }
+
             // Create the framebuffer
             var framebuffer = gl.createFramebuffer();
             this.bindUnboundFramebuffer(framebuffer);
-            if (generateDepthBuffer) {
-                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+
+            // Manage attachments
+            if (generateStencilBuffer) {                
+                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
             }
+            else if (generateDepthBuffer) {
+                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+            }
+            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
 
             if (generateMipMaps) {
                 this._gl.generateMipmap(this._gl.TEXTURE_2D);
@@ -2027,7 +2104,7 @@
 
             texture._framebuffer = framebuffer;
             if (generateDepthBuffer) {
-                texture._depthBuffer = depthBuffer;
+                texture._depthBuffer = depthStencilBuffer;
             }
             texture._baseWidth = width;
             texture._baseHeight = height;
@@ -2038,6 +2115,7 @@
             texture.references = 1;
             texture.samplingMode = samplingMode;
             texture.type = type;
+            
             this.resetTextureCache();
 
             this._loadedTexturesCache.push(texture);
@@ -2051,9 +2129,15 @@
             var texture = gl.createTexture();
 
             var generateMipMaps = true;
+            var generateDepthBuffer = true;
+            var generateStencilBuffer = false;
+
             var samplingMode = Texture.TRILINEAR_SAMPLINGMODE;
             if (options !== undefined) {
                 generateMipMaps = options.generateMipMaps === undefined ? options : options.generateMipMaps;
+                generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
+                generateStencilBuffer = generateDepthBuffer && options.generateStencilBuffer;
+
                 if (options.samplingMode !== undefined) {
                     samplingMode = options.samplingMode;
                 }
@@ -2079,14 +2163,31 @@
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
             // Create the depth buffer
-            var depthBuffer = gl.createRenderbuffer();
-            gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
-            gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
+            var depthStencilBuffer: WebGLRenderbuffer;
+
+            // Create the depth/stencil buffer
+            if (generateStencilBuffer) {
+                depthStencilBuffer = gl.createRenderbuffer();
+                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
+                gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, size, size);
+            }
+            else if (generateDepthBuffer) {
+                depthStencilBuffer = gl.createRenderbuffer();
+                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
+                gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
+            }
 
             // Create the framebuffer
             var framebuffer = gl.createFramebuffer();
             this.bindUnboundFramebuffer(framebuffer);
-            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+
+            // Manage attachments
+            if (generateStencilBuffer) {                
+                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+            }
+            else if (generateDepthBuffer) {
+                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+            }
 
             // Mipmaps
             if (texture.generateMipMaps) {
@@ -2100,14 +2201,17 @@
             this.bindUnboundFramebuffer(null);
 
             texture._framebuffer = framebuffer;
-            texture._depthBuffer = depthBuffer;
-
-            this.resetTextureCache();
-
+            if (generateDepthBuffer) {
+                texture._depthBuffer = depthStencilBuffer;
+            }
             texture._width = size;
             texture._height = size;
             texture.isReady = true;
 
+            this.resetTextureCache();
+
+            this._loadedTexturesCache.push(texture);
+
             return texture;
         }
 

+ 17 - 2
src/babylon.scene.ts

@@ -2238,7 +2238,7 @@
             }
 
             // Clear
-            this._engine.clear(this.clearColor, this.autoClear || this.forceWireframe || this.forcePointsCloud, true);
+            this._engine.clear(this.clearColor, this.autoClear || this.forceWireframe || this.forcePointsCloud, true, true);
 
             // Shadows
             if (this.shadowsEnabled) {
@@ -2266,7 +2266,7 @@
                 for (var cameraIndex = 0; cameraIndex < this.activeCameras.length; cameraIndex++) {
                     this._renderId = currentRenderId;
                     if (cameraIndex > 0) {
-                        this._engine.clear(0, false, true);
+                        this._engine.clear(0, false, true, true);
                     }
 
                     this._processSubCameras(this.activeCameras[cameraIndex]);
@@ -2921,5 +2921,20 @@
         public getMaterialByTags(tagsQuery: string, forEach?: (material: Material) => void): Material[] {
             return this._getByTags(this.materials, tagsQuery, forEach).concat(this._getByTags(this.multiMaterials, tagsQuery, forEach));
         }
+
+        public setRenderingOrder(renderingGroupId: number,
+            opaqueSortCompareFn: (a: SubMesh, b: SubMesh) => number = null,
+            alphaTestSortCompareFn: (a: SubMesh, b: SubMesh) => number = null,
+            transparentSortCompareFn: (a: SubMesh, b: SubMesh) => number = null) {
+            
+            this._renderingManager.setRenderingOrder(renderingGroupId,
+                opaqueSortCompareFn,
+                alphaTestSortCompareFn,
+                transparentSortCompareFn);
+        }
+
+        public setRenderingAutoClear(renderingGroupId: number, autoClear: boolean) {            
+            this._renderingManager.setRenderingAutoClear(renderingGroupId, autoClear);
+        }
     }
 }