Browse Source

Update for reflection probes

David Catuhe 10 years ago
parent
commit
17a874df67

File diff suppressed because it is too large
+ 537 - 481
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 23 - 23
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 324 - 100
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 23 - 23
dist/preview release/babylon.noworker.js


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

@@ -1,6 +1,7 @@
 - 2.3.0:
   - **Major updates**
     - New `StandardMaterial.lightmapTexture` which can be controlled with `StandardMaterial.lightmapThreshold`. [Demo here](#NEEDDEMO) ([deltakosh](https://github.com/deltakosh))
+    - Support for reflection probes. [See documentation here](http://doc.babylonjs.com/tutorials/How_to_use_Reflection_probes) ([deltakosh](https://github.com/deltakosh))
   - **Updates**
     - New `Material.sideOrientation` property to define clockwise or counter-clockwise faces selection. [Demo here](http://www.babylonjs-playground.com/#1TZJQY) ([deltakosh](https://github.com/deltakosh))
     - It is now possible to create a custom loading screen [PR](https://github.com/BabylonJS/Babylon.js/pull/700) ([RaananW](https://github.com/RaananW))

+ 9 - 5
src/Cameras/babylon.camera.js

@@ -290,9 +290,11 @@ var BABYLON;
                 return this._postProcesses.length - 1;
             }
             var add = 0;
+            var i;
+            var start;
             if (this._postProcesses[insertAt]) {
-                var start = this._postProcesses.length - 1;
-                for (var i = start; i >= insertAt + 1; --i) {
+                start = this._postProcesses.length - 1;
+                for (i = start; i >= insertAt + 1; --i) {
                     this._postProcesses[i + 1] = this._postProcesses[i];
                 }
                 add = 1;
@@ -308,7 +310,7 @@ var BABYLON;
                 this._postProcessesTakenIndices[i] = insertAt;
                 break;
             }
-            if (!add && this._postProcessesTakenIndices.indexOf(insertAt) == -1) {
+            if (!add && this._postProcessesTakenIndices.indexOf(insertAt) === -1) {
                 this._postProcessesTakenIndices.push(insertAt);
             }
             var result = insertAt + add;
@@ -318,14 +320,16 @@ var BABYLON;
         Camera.prototype.detachPostProcess = function (postProcess, atIndices) {
             if (atIndices === void 0) { atIndices = null; }
             var result = [];
+            var i;
+            var index;
             if (!atIndices) {
                 var length = this._postProcesses.length;
-                for (var i = 0; i < length; i++) {
+                for (i = 0; i < length; i++) {
                     if (this._postProcesses[i] !== postProcess) {
                         continue;
                     }
                     delete this._postProcesses[i];
-                    var index = this._postProcessesTakenIndices.indexOf(i);
+                    index = this._postProcessesTakenIndices.indexOf(i);
                     this._postProcessesTakenIndices.splice(index, 1);
                 }
             }

+ 9 - 11
src/Cameras/babylon.camera.ts

@@ -298,13 +298,11 @@
             }
 
             var add = 0;
-
+            var i: number;
+            var start: number;
             if (this._postProcesses[insertAt]) {
-
-                var start = this._postProcesses.length - 1;
-
-
-                for (var i = start; i >= insertAt + 1; --i) {
+                start = this._postProcesses.length - 1;
+                for (i = start; i >= insertAt + 1; --i) {
                     this._postProcesses[i + 1] = this._postProcesses[i];
                 }
 
@@ -324,7 +322,7 @@
                 break;
             }
 
-            if (!add && this._postProcessesTakenIndices.indexOf(insertAt) == -1) {
+            if (!add && this._postProcessesTakenIndices.indexOf(insertAt) === -1) {
                 this._postProcessesTakenIndices.push(insertAt);
             }
 
@@ -337,20 +335,20 @@
 
         public detachPostProcess(postProcess: PostProcess, atIndices: any = null): number[] {
             var result = [];
-
+            var i: number;
+            var index: number;
             if (!atIndices) {
 
                 var length = this._postProcesses.length;
 
-                for (var i = 0; i < length; i++) {
+                for (i = 0; i < length; i++) {
 
                     if (this._postProcesses[i] !== postProcess) {
                         continue;
                     }
 
                     delete this._postProcesses[i];
-
-                    var index = this._postProcessesTakenIndices.indexOf(i);
+                    index = this._postProcessesTakenIndices.indexOf(i);
                     this._postProcessesTakenIndices.splice(index, 1);
                 }
 

+ 8 - 0
src/Loading/Plugins/babylon.babylonFileLoader.js

@@ -814,6 +814,14 @@ var BABYLON;
                 }
                 // Action or condition(s) and not CombineAction
                 var newAction = instanciate(parsedAction.name, parameters);
+                if (newAction instanceof BABYLON.Condition && condition !== null) {
+                    var nothing = new BABYLON.DoNothingAction(trigger, condition);
+                    if (action)
+                        action.then(nothing);
+                    else
+                        actionManager.registerAction(nothing);
+                    action = nothing;
+                }
                 if (combineArray === null) {
                     if (newAction instanceof BABYLON.Condition) {
                         condition = newAction;

+ 12 - 0
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -1012,6 +1012,18 @@
 
             // Action or condition(s) and not CombineAction
             var newAction = instanciate(parsedAction.name, parameters);
+
+            if (newAction instanceof BABYLON.Condition && condition !== null) {
+                var nothing = new DoNothingAction(trigger, condition);
+
+                if (action)
+                    action.then(nothing);
+                else
+                    actionManager.registerAction(nothing);
+
+                action = nothing;
+            }
+
             if (combineArray === null) {
                 if (newAction instanceof BABYLON.Condition) {
                     condition = newAction;

+ 2 - 0
src/Materials/Textures/babylon.mirrorTexture.js

@@ -20,10 +20,12 @@ var BABYLON;
                 scene.setTransformMatrix(_this._transformMatrix, scene.getProjectionMatrix());
                 scene.clipPlane = _this.mirrorPlane;
                 scene.getEngine().cullBackFaces = false;
+                scene._mirroredCameraPosition = BABYLON.Vector3.TransformCoordinates(scene.activeCamera.position, _this._mirrorMatrix);
             };
             this.onAfterRender = function () {
                 scene.setTransformMatrix(_this._savedViewMatrix, scene.getProjectionMatrix());
                 scene.getEngine().cullBackFaces = true;
+                scene._mirroredCameraPosition = null;
                 delete scene.clipPlane;
             };
         }

+ 3 - 0
src/Materials/Textures/babylon.mirrorTexture.ts

@@ -20,11 +20,14 @@
                 scene.clipPlane = this.mirrorPlane;
 
                 scene.getEngine().cullBackFaces = false;
+
+                scene._mirroredCameraPosition = Vector3.TransformCoordinates(scene.activeCamera.position, this._mirrorMatrix);
             }
 
             this.onAfterRender = () => {
                 scene.setTransformMatrix(this._savedViewMatrix, scene.getProjectionMatrix());
                 scene.getEngine().cullBackFaces = true;
+                scene._mirroredCameraPosition = null;
 
                 delete scene.clipPlane;
             }

+ 56 - 11
src/Materials/Textures/babylon.renderTargetTexture.js

@@ -25,7 +25,9 @@ var BABYLON;
             this._generateMipMaps = generateMipMaps;
             this._doNotChangeAspectRatio = doNotChangeAspectRatio;
             if (isCube) {
-                this._texture = scene.getEngine().createRenderTargetCubeTexture(size);
+                this._texture = scene.getEngine().createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps });
+                this.coordinatesMode = BABYLON.Texture.INVCUBIC_MODE;
+                this._textureMatrix = BABYLON.Matrix.Identity();
             }
             else {
                 this._texture = scene.getEngine().createRenderTargetTexture(size, { generateMipMaps: generateMipMaps, type: type });
@@ -33,6 +35,27 @@ var BABYLON;
             // Rendering groups
             this._renderingManager = new BABYLON.RenderingManager(scene);
         }
+        Object.defineProperty(RenderTargetTexture, "REFRESHRATE_RENDER_ONCE", {
+            get: function () {
+                return RenderTargetTexture._REFRESHRATE_RENDER_ONCE;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RenderTargetTexture, "REFRESHRATE_RENDER_ONEVERYFRAME", {
+            get: function () {
+                return RenderTargetTexture._REFRESHRATE_RENDER_ONEVERYFRAME;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RenderTargetTexture, "REFRESHRATE_RENDER_ONEVERYTWOFRAMES", {
+            get: function () {
+                return RenderTargetTexture._REFRESHRATE_RENDER_ONEVERYTWOFRAMES;
+            },
+            enumerable: true,
+            configurable: true
+        });
         RenderTargetTexture.prototype.resetRefreshCounter = function () {
             this._currentRefreshId = -1;
         };
@@ -80,6 +103,12 @@ var BABYLON;
             var newSize = this._size * ratio;
             this.resize(newSize, this._generateMipMaps);
         };
+        RenderTargetTexture.prototype.getReflectionTextureMatrix = function () {
+            if (this.isCube) {
+                return this._textureMatrix;
+            }
+            return _super.prototype.getReflectionTextureMatrix.call(this);
+        };
         RenderTargetTexture.prototype.resize = function (size, generateMipMaps) {
             this.releaseInternalTexture();
             if (this.isCube) {
@@ -125,25 +154,31 @@ var BABYLON;
             }
             if (this.isCube) {
                 for (var face = 0; face < 6; face++) {
-                    this.renderToTarget(this._texture._cubeFaces[face], currentRenderList, useCameraPostProcess, dumpForDebug);
+                    this.renderToTarget(face, currentRenderList, useCameraPostProcess, dumpForDebug);
                 }
             }
             else {
-                this.renderToTarget(this._texture, currentRenderList, useCameraPostProcess, dumpForDebug);
+                this.renderToTarget(0, currentRenderList, useCameraPostProcess, dumpForDebug);
             }
             if (this.onAfterUnbind) {
                 this.onAfterUnbind();
             }
+            scene.resetCachedMaterial();
         };
-        RenderTargetTexture.prototype.renderToTarget = function (targetTexture, currentRenderList, useCameraPostProcess, dumpForDebug) {
+        RenderTargetTexture.prototype.renderToTarget = function (faceIndex, currentRenderList, useCameraPostProcess, dumpForDebug) {
             var scene = this.getScene();
             var engine = scene.getEngine();
             // Bind
-            if (!useCameraPostProcess || !scene.postProcessManager._prepareFrame(targetTexture)) {
-                engine.bindFramebuffer(targetTexture);
+            if (!useCameraPostProcess || !scene.postProcessManager._prepareFrame(this._texture)) {
+                if (this.isCube) {
+                    engine.bindFramebuffer(this._texture, faceIndex);
+                }
+                else {
+                    engine.bindFramebuffer(this._texture);
+                }
             }
             if (this.onBeforeRender) {
-                this.onBeforeRender();
+                this.onBeforeRender(faceIndex);
             }
             // Clear
             if (this.onClear) {
@@ -158,20 +193,27 @@ var BABYLON;
             // Render
             this._renderingManager.render(this.customRenderFunction, currentRenderList, this.renderParticles, this.renderSprites);
             if (useCameraPostProcess) {
-                scene.postProcessManager._finalizeFrame(false, targetTexture);
+                scene.postProcessManager._finalizeFrame(false, this._texture, faceIndex);
             }
             if (!this._doNotChangeAspectRatio) {
                 scene.updateTransformMatrix(true);
             }
             if (this.onAfterRender) {
-                this.onAfterRender();
+                this.onAfterRender(faceIndex);
             }
             // Dump ?
-            if (!this.isCube && dumpForDebug) {
+            if (dumpForDebug) {
                 BABYLON.Tools.DumpFramebuffer(this._size, this._size, engine);
             }
             // Unbind
-            engine.unBindFramebuffer(targetTexture);
+            if (!this.isCube || faceIndex === 5) {
+                if (this.isCube) {
+                    if (faceIndex === 5) {
+                        engine.generateMipMapsForCubemap(this._texture);
+                    }
+                }
+                engine.unBindFramebuffer(this._texture, true);
+            }
         };
         RenderTargetTexture.prototype.clone = function () {
             var textureSize = this.getSize();
@@ -184,6 +226,9 @@ var BABYLON;
             newTexture.renderList = this.renderList.slice(0);
             return newTexture;
         };
+        RenderTargetTexture._REFRESHRATE_RENDER_ONCE = 0;
+        RenderTargetTexture._REFRESHRATE_RENDER_ONEVERYFRAME = 1;
+        RenderTargetTexture._REFRESHRATE_RENDER_ONEVERYTWOFRAMES = 2;
         return RenderTargetTexture;
     })(BABYLON.Texture);
     BABYLON.RenderTargetTexture = RenderTargetTexture;

+ 55 - 13
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -1,11 +1,27 @@
 module BABYLON {
     export class RenderTargetTexture extends Texture {
+        public static _REFRESHRATE_RENDER_ONCE: number = 0;
+        public static _REFRESHRATE_RENDER_ONEVERYFRAME: number = 1;
+        public static _REFRESHRATE_RENDER_ONEVERYTWOFRAMES: number = 2;
+
+        public static get REFRESHRATE_RENDER_ONCE(): number {
+            return RenderTargetTexture._REFRESHRATE_RENDER_ONCE;
+        }
+
+        public static get REFRESHRATE_RENDER_ONEVERYFRAME(): number {
+            return RenderTargetTexture._REFRESHRATE_RENDER_ONEVERYFRAME;
+        }
+
+        public static get REFRESHRATE_RENDER_ONEVERYTWOFRAMES(): number {
+            return RenderTargetTexture._REFRESHRATE_RENDER_ONEVERYTWOFRAMES;
+        }
+
         public renderList = new Array<AbstractMesh>();
         public renderParticles = true;
         public renderSprites = false;
         public coordinatesMode = Texture.PROJECTION_MODE;
-        public onBeforeRender: () => void;
-        public onAfterRender: () => void;
+        public onBeforeRender: (faceIndex: number) => void;
+        public onAfterRender: (faceIndex: number) => void;
         public onAfterUnbind: () => void;
         public onClear: (engine: Engine) => void;
         public activeCamera: Camera;
@@ -18,6 +34,7 @@
         private _doNotChangeAspectRatio: boolean;
         private _currentRefreshId = -1;
         private _refreshRate = 1;
+        private _textureMatrix: Matrix;
 
         constructor(name: string, size: any, scene: Scene, generateMipMaps?: boolean, doNotChangeAspectRatio: boolean = true, type: number = Engine.TEXTURETYPE_UNSIGNED_INT, public isCube = false) {
             super(null, scene, !generateMipMaps);
@@ -29,7 +46,9 @@
             this._doNotChangeAspectRatio = doNotChangeAspectRatio;
 
             if (isCube) {
-                this._texture = scene.getEngine().createRenderTargetCubeTexture(size);
+                this._texture = scene.getEngine().createRenderTargetCubeTexture(size, { generateMipMaps: generateMipMaps });
+                this.coordinatesMode = Texture.INVCUBIC_MODE;
+                this._textureMatrix = Matrix.Identity();
             } else {
                 this._texture = scene.getEngine().createRenderTargetTexture(size, { generateMipMaps: generateMipMaps, type: type });
             }
@@ -88,6 +107,14 @@
             this.resize(newSize, this._generateMipMaps);
         }
 
+        public getReflectionTextureMatrix(): Matrix {
+            if (this.isCube) {
+                return this._textureMatrix;
+            }
+
+            return super.getReflectionTextureMatrix();
+        }
+
         public resize(size: any, generateMipMaps?: boolean) {
             this.releaseInternalTexture();
             if (this.isCube) {
@@ -143,28 +170,34 @@
             
             if (this.isCube) {
                 for (var face = 0; face < 6; face++) {
-                    this.renderToTarget(this._texture._cubeFaces[face], currentRenderList, useCameraPostProcess, dumpForDebug);
+                    this.renderToTarget(face, currentRenderList, useCameraPostProcess, dumpForDebug);
                 }
             } else {
-                this.renderToTarget(this._texture, currentRenderList, useCameraPostProcess, dumpForDebug);
+                this.renderToTarget(0, currentRenderList, useCameraPostProcess, dumpForDebug);
             }
 
             if (this.onAfterUnbind) {
                 this.onAfterUnbind();
             }
+
+            scene.resetCachedMaterial();
         }
 
-        renderToTarget(targetTexture: WebGLTexture, currentRenderList: AbstractMesh[], useCameraPostProcess: boolean, dumpForDebug: boolean): void {
+        renderToTarget(faceIndex: number, currentRenderList: AbstractMesh[], useCameraPostProcess: boolean, dumpForDebug: boolean): void {
             var scene = this.getScene();
             var engine = scene.getEngine();
 
             // Bind
-            if (!useCameraPostProcess || !scene.postProcessManager._prepareFrame(targetTexture)) {
-                engine.bindFramebuffer(targetTexture);
+            if (!useCameraPostProcess || !scene.postProcessManager._prepareFrame(this._texture)) {
+                if (this.isCube) {
+                    engine.bindFramebuffer(this._texture, faceIndex);
+                } else {
+                    engine.bindFramebuffer(this._texture);
+                }
             }
 
             if (this.onBeforeRender) {
-                this.onBeforeRender();
+                this.onBeforeRender(faceIndex);
             }
 
             // Clear
@@ -182,7 +215,7 @@
             this._renderingManager.render(this.customRenderFunction, currentRenderList, this.renderParticles, this.renderSprites);
 
             if (useCameraPostProcess) {
-                scene.postProcessManager._finalizeFrame(false, targetTexture);
+                scene.postProcessManager._finalizeFrame(false, this._texture, faceIndex);
             }
 
             if (!this._doNotChangeAspectRatio) {
@@ -190,16 +223,25 @@
             }
 
             if (this.onAfterRender) {
-                this.onAfterRender();
+                this.onAfterRender(faceIndex);
             }
 
             // Dump ?
-            if (!this.isCube && dumpForDebug) {
+            if (dumpForDebug) {
                 Tools.DumpFramebuffer(this._size, this._size, engine);
             }
 
             // Unbind
-            engine.unBindFramebuffer(targetTexture);
+            if (!this.isCube || faceIndex === 5) {
+                if (this.isCube) {
+
+                    if (faceIndex === 5) {
+                        engine.generateMipMapsForCubemap(this._texture);
+                    }
+                }
+
+                engine.unBindFramebuffer(this._texture, true);
+            }
         }
 
         public clone(): RenderTargetTexture {

+ 1 - 0
src/Materials/Textures/babylon.texture.js

@@ -204,6 +204,7 @@ var BABYLON;
         Texture.CUBIC_MODE = 3;
         Texture.PROJECTION_MODE = 4;
         Texture.SKYBOX_MODE = 5;
+        Texture.INVCUBIC_MODE = 6;
         Texture.CLAMP_ADDRESSMODE = 0;
         Texture.WRAP_ADDRESSMODE = 1;
         Texture.MIRROR_ADDRESSMODE = 2;

+ 1 - 0
src/Materials/Textures/babylon.texture.ts

@@ -11,6 +11,7 @@
         public static CUBIC_MODE = 3;
         public static PROJECTION_MODE = 4;
         public static SKYBOX_MODE = 5;
+        public static INVCUBIC_MODE = 6;
 
         public static CLAMP_ADDRESSMODE = 0;
         public static WRAP_ADDRESSMODE = 1;

+ 7 - 3
src/Materials/babylon.standardMaterial.js

@@ -87,6 +87,7 @@ var BABYLON;
             this.EMISSIVEASILLUMINATION = false;
             this.REFLECTIONFRESNELFROMSPECULAR = false;
             this.LIGHTMAP = false;
+            this.INVERTCUBICMAP = false;
             this._keys = Object.keys(this);
         }
         StandardMaterialDefines.prototype.isEqual = function (other) {
@@ -235,6 +236,9 @@ var BABYLON;
                         if (this.roughness > 0) {
                             this._defines.ROUGHNESS = true;
                         }
+                        if (this.reflectionTexture.coordinatesMode === BABYLON.Texture.INVCUBIC_MODE) {
+                            this._defines.INVERTCUBICMAP = true;
+                        }
                     }
                 }
                 if (this.emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
@@ -440,7 +444,7 @@ var BABYLON;
                 if (this._defines.FOG) {
                     fallbacks.addFallback(1, "FOG");
                 }
-                for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                for (lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
                     if (!this._defines["LIGHT" + lightIndex]) {
                         continue;
                     }
@@ -595,7 +599,7 @@ var BABYLON;
                         this._effect.setTexture("reflection2DSampler", this.reflectionTexture);
                     }
                     this._effect.setMatrix("reflectionMatrix", this.reflectionTexture.getReflectionTextureMatrix());
-                    this._effect.setFloat3("vReflectionInfos", this.reflectionTexture.coordinatesMode, this.reflectionTexture.level, this.reflectionTexture.isCube ? 1 : 0);
+                    this._effect.setFloat3("vReflectionInfos", this.reflectionTexture.coordinatesMode === BABYLON.Texture.INVCUBIC_MODE ? BABYLON.Texture.CUBIC_MODE : this.reflectionTexture.coordinatesMode, this.reflectionTexture.level, this.reflectionTexture.isCube ? 1 : 0);
                 }
                 if (this.emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
                     this._effect.setTexture("emissiveSampler", this.emissiveTexture);
@@ -632,7 +636,7 @@ var BABYLON;
                 this._scaledSpecular.r = this.specularColor.r * BABYLON.Tools.Clamp(1.0 - this.emissiveColor.r);
                 this._scaledSpecular.g = this.specularColor.g * BABYLON.Tools.Clamp(1.0 - this.emissiveColor.g);
                 this._scaledSpecular.b = this.specularColor.b * BABYLON.Tools.Clamp(1.0 - this.emissiveColor.b);
-                this._effect.setVector3("vEyePosition", scene.activeCamera.position);
+                this._effect.setVector3("vEyePosition", scene._mirroredCameraPosition ? scene._mirroredCameraPosition : scene.activeCamera.position);
                 this._effect.setColor3("vAmbientColor", this._globalAmbientColor);
                 if (this._defines.SPECULARTERM) {
                     this._effect.setColor4("vSpecularColor", this._scaledSpecular, this.specularPower);

+ 9 - 4
src/Materials/babylon.standardMaterial.ts

@@ -81,6 +81,7 @@
         public EMISSIVEASILLUMINATION = false;
         public REFLECTIONFRESNELFROMSPECULAR = false;
         public LIGHTMAP = false;
+        public INVERTCUBICMAP = false;
 
         _keys: string[];
 
@@ -279,6 +280,10 @@
                         if (this.roughness > 0) {
                             this._defines.ROUGHNESS = true;
                         }
+
+                        if (this.reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE) {
+                            this._defines.INVERTCUBICMAP = true;
+                        }
                     }
                 }
 
@@ -481,7 +486,7 @@
                     this._defines.VERTEXCOLOR = true;
 
                     if (mesh.hasVertexAlpha) {
-                        this._defines.VERTEXALPHA = true
+                        this._defines.VERTEXALPHA = true;
                     }
                 }
                 if (mesh.useBones && mesh.computeBonesUsingShaders) {
@@ -524,7 +529,7 @@
                     fallbacks.addFallback(1, "FOG");
                 }
 
-                for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                for (lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
                     if (!this._defines["LIGHT" + lightIndex]) {
                         continue;
                     }
@@ -722,7 +727,7 @@
                     }
 
                     this._effect.setMatrix("reflectionMatrix", this.reflectionTexture.getReflectionTextureMatrix());
-                    this._effect.setFloat3("vReflectionInfos", this.reflectionTexture.coordinatesMode, this.reflectionTexture.level, this.reflectionTexture.isCube ? 1 : 0);
+                    this._effect.setFloat3("vReflectionInfos", this.reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE ? Texture.CUBIC_MODE : this.reflectionTexture.coordinatesMode, this.reflectionTexture.level, this.reflectionTexture.isCube ? 1 : 0);
                 }
 
                 if (this.emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
@@ -772,7 +777,7 @@
                 this._scaledSpecular.g = this.specularColor.g * Tools.Clamp(1.0 - this.emissiveColor.g);
                 this._scaledSpecular.b = this.specularColor.b * Tools.Clamp(1.0 - this.emissiveColor.b);
 
-                this._effect.setVector3("vEyePosition", scene.activeCamera.position);
+                this._effect.setVector3("vEyePosition", scene._mirroredCameraPosition ? scene._mirroredCameraPosition : scene.activeCamera.position);
                 this._effect.setColor3("vAmbientColor", this._globalAmbientColor);
 
                 if (this._defines.SPECULARTERM) {

+ 89 - 45
src/Mesh/babylon.mesh.js

@@ -1068,28 +1068,30 @@ var BABYLON;
             if (instance === void 0) { instance = null; }
             var pathArray;
             var closeArray;
-            if (closeArrayOrScene instanceof BABYLON.Scene) {
-                scene = closeArrayOrScene;
-                updatable = options.updatable;
-                if (options.instance) {
-                    pathArray = options.pathArray;
-                    instance = options.instance;
-                    closePath = options.closePath;
-                    closeArray = options.closeArray;
-                }
-            }
-            else {
+            if (Array.isArray(options)) {
                 pathArray = options;
+                closeArray = closeArrayOrScene;
                 if (!instance) {
                     options = {
                         pathArray: pathArray,
-                        closeArray: closeArrayOrScene,
+                        closeArray: closeArray,
                         closePath: closePath,
                         offset: offset,
+                        updatable: updatable,
                         sideOrientation: sideOrientation
                     };
                 }
             }
+            else {
+                scene = closeArrayOrScene;
+                pathArray = options.pathArray;
+                closeArray = options.closeArray;
+                closePath = options.closePath;
+                offset = options.offset;
+                sideOrientation = options.sideOrientation;
+                instance = options.instance;
+                updatable = options.updatable;
+            }
             if (instance) {
                 // positionFunction : ribbon case
                 // only pathArray and sideOrientation parameters are taken into account for positions update
@@ -1397,19 +1399,58 @@ var BABYLON;
             dashedLines.gapSize = gapSize;
             return dashedLines;
         };
-        // Extrusion
-        Mesh.ExtrudeShape = function (name, shape, path, scale, rotation, cap, scene, updatable, sideOrientation, extrudedInstance) {
+        Mesh.ExtrudeShape = function (name, options, pathOrScene, scale, rotation, cap, scene, updatable, sideOrientation, instance) {
             if (sideOrientation === void 0) { sideOrientation = Mesh.DEFAULTSIDE; }
-            if (extrudedInstance === void 0) { extrudedInstance = null; }
-            scale = scale || 1;
-            rotation = rotation || 0;
-            var extruded = Mesh._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, cap, false, scene, updatable, sideOrientation, extrudedInstance);
+            if (instance === void 0) { instance = null; }
+            var path;
+            var shape;
+            if (Array.isArray(options)) {
+                shape = options;
+                path = pathOrScene;
+                scale = scale || 1;
+                rotation = rotation || 0;
+                cap = (cap === 0) ? 0 : cap || Mesh.NO_CAP;
+            }
+            else {
+                scene = pathOrScene;
+                path = options.path;
+                shape = options.shape;
+                scale = options.scale || 1;
+                rotation = options.rotation || 0;
+                cap = (options.cap === 0) ? 0 : options.cap || Mesh.NO_CAP;
+                updatable = options.updatable;
+                sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || Mesh.DEFAULTSIDE;
+                instance = options.instance;
+            }
+            var extruded = Mesh._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, cap, false, scene, updatable, sideOrientation, instance);
             return extruded;
         };
-        Mesh.ExtrudeShapeCustom = function (name, shape, path, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, scene, updatable, sideOrientation, extrudedInstance) {
+        Mesh.ExtrudeShapeCustom = function (name, options, pathOrScene, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, scene, updatable, sideOrientation, instance) {
             if (sideOrientation === void 0) { sideOrientation = Mesh.DEFAULTSIDE; }
-            if (extrudedInstance === void 0) { extrudedInstance = null; }
-            var extrudedCustom = Mesh._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, extrudedInstance);
+            if (instance === void 0) { instance = null; }
+            var path;
+            var shape;
+            if (Array.isArray(options)) {
+                shape = options;
+                path = pathOrScene;
+                ribbonCloseArray = ribbonCloseArray || false;
+                ribbonClosePath = ribbonClosePath || false;
+                cap = (cap === 0) ? 0 : cap || Mesh.NO_CAP;
+            }
+            else {
+                scene = pathOrScene;
+                path = options.path;
+                shape = options.shape;
+                scaleFunction = options.scaleFunction || (function (i, distance) { return 1; });
+                rotationFunction = options.rotationFunction || (function (i, distance) { return 0; });
+                ribbonCloseArray = options.ribbonCloseArray || false;
+                ribbonClosePath = options.ribbonClosePath || false;
+                cap = (options.cap === 0) ? 0 : options.cap || Mesh.NO_CAP;
+                updatable = options.updatable;
+                sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || Mesh.DEFAULTSIDE;
+                instance = options.instance;
+            }
+            var extrudedCustom = Mesh._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, instance);
             return extrudedCustom;
         };
         Mesh._ExtrudeShapeGeneric = function (name, shape, curve, scale, rotation, scaleFunction, rotateFunction, rbCA, rbCP, cap, custom, scene, updtbl, side, instance) {
@@ -1424,7 +1465,7 @@ var BABYLON;
                 var returnRotation = function (i, distance) { return rotation; };
                 var rotate = custom ? rotateFunction : returnRotation;
                 var scl = custom ? scaleFunction : returnScale;
-                var index = 0;
+                var index = (cap === Mesh.NO_CAP || cap === Mesh.CAP_END) ? 0 : 1;
                 for (var i = 0; i < curve.length; i++) {
                     var shapePath = new Array();
                     var angleStep = rotate(i, distances[i]);
@@ -1457,14 +1498,14 @@ var BABYLON;
                     case Mesh.NO_CAP:
                         break;
                     case Mesh.CAP_START:
-                        shapePaths.unshift(capPath(shapePaths[0]));
+                        shapePaths[0] = capPath(shapePaths[1]);
                         break;
                     case Mesh.CAP_END:
-                        shapePaths.push(capPath(shapePaths[shapePaths.length - 1]));
+                        shapePaths[index] = capPath(shapePaths[index - 1]);
                         break;
                     case Mesh.CAP_ALL:
-                        shapePaths.unshift(capPath(shapePaths[0]));
-                        shapePaths.push(capPath(shapePaths[shapePaths.length - 1]));
+                        shapePaths[0] = capPath(shapePaths[1]);
+                        shapePaths[index] = capPath(shapePaths[index - 1]);
                         break;
                     default:
                         break;
@@ -1593,24 +1634,25 @@ var BABYLON;
             return ground;
         };
         Mesh.CreateTube = function (name, options, radiusOrScene, tessellation, radiusFunction, cap, scene, updatable, sideOrientation, instance) {
+            if (sideOrientation === void 0) { sideOrientation = Mesh.DEFAULTSIDE; }
+            if (instance === void 0) { instance = null; }
             var path;
             var radius;
-            if (radiusOrScene instanceof BABYLON.Scene) {
-                scene = radiusOrScene;
-                path = options.path;
-                radius = options.radius;
-            }
-            else {
+            if (Array.isArray(options)) {
                 path = options;
                 radius = radiusOrScene;
             }
-            radius = radius || 1;
-            tessellation = tessellation || options.tessellation || 60;
-            radiusFunction = radiusFunction || options.radiusFunction;
-            cap = cap || options.cap || Mesh.NO_CAP;
-            updatable = updatable || options.updatable;
-            instance = instance || options.instance;
-            sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || Mesh.DEFAULTSIDE;
+            else {
+                scene = radiusOrScene;
+                path = options.path;
+                radius = options.radius || 1;
+                tessellation = options.tessellation || 64;
+                radiusFunction = options.radiusFunction;
+                cap = options.cap || Mesh.NO_CAP,
+                    updatable = options.updatable;
+                sideOrientation = options.sideOrientation || Mesh.DEFAULTSIDE,
+                    instance = options.instance;
+            }
             // tube geometry
             var tubePathArray = function (path, path3D, circlePaths, radius, tessellation, radiusFunction, cap) {
                 var tangents = path3D.getTangents();
@@ -1625,7 +1667,7 @@ var BABYLON;
                 var normal;
                 var rotated;
                 var rotationMatrix;
-                var index = 0;
+                var index = (cap === Mesh._NO_CAP || cap === Mesh.CAP_END) ? 0 : 1;
                 for (var i = 0; i < path.length; i++) {
                     rad = radiusFunctionFinal(i, distances[i]); // current radius
                     circlePath = Array(); // current circle array
@@ -1650,14 +1692,14 @@ var BABYLON;
                     case Mesh.NO_CAP:
                         break;
                     case Mesh.CAP_START:
-                        circlePaths.unshift(capPath(tessellation + 1, 0));
+                        circlePaths[0] = capPath(tessellation, 0);
                         break;
                     case Mesh.CAP_END:
-                        circlePaths.push(capPath(tessellation + 1, path.length - 1));
+                        circlePaths[index] = capPath(tessellation, path.length - 1);
                         break;
                     case Mesh.CAP_ALL:
-                        circlePaths.unshift(capPath(tessellation + 1, 0));
-                        circlePaths.push(capPath(tessellation + 1, path.length - 1));
+                        circlePaths[0] = capPath(tessellation, 0);
+                        circlePaths[index] = capPath(tessellation, path.length - 1);
                         break;
                     default:
                         break;
@@ -1669,7 +1711,9 @@ var BABYLON;
             if (instance) {
                 path3D = (instance.path3D).update(path);
                 pathArray = tubePathArray(path, path3D, instance.pathArray, radius, instance.tessellation, radiusFunction, instance.cap);
-                instance = Mesh.CreateRibbon(null, pathArray, null, null, null, null, null, null, instance);
+                instance = Mesh.CreateRibbon(null, { pathArray: pathArray, instance: instance });
+                instance.path3D = path3D;
+                instance.pathArray = pathArray;
                 return instance;
             }
             // tube creation

+ 19 - 18
src/Mesh/babylon.mesh.ts

@@ -1629,7 +1629,7 @@
 
         // Extrusion
         public static ExtrudeShape(name: string, shape: Vector3[], path: Vector3[], scale: number, rotation: number, cap: number, scene: Scene, updatable?: boolean, sideOrientation?: number, instance?: Mesh): Mesh;
-        public static ExtrudeShape(name: string, options: {shape: Vector3[], path: Vector3[], scale?: number, rotation?: number, cap?: number, updatable?: boolean, sideOrientation?: number, instance?: Mesh}, scene: Scene): Mesh;
+        public static ExtrudeShape(name: string, options: { shape: Vector3[], path: Vector3[], scale?: number, rotation?: number, cap?: number, updatable?: boolean, sideOrientation?: number, instance?: Mesh }, scene: Scene): Mesh;
         public static ExtrudeShape(name: string, options: any, pathOrScene?: any, scale?: number, rotation?: number, cap?: number, scene?: Scene, updatable?: boolean, sideOrientation: number = Mesh.DEFAULTSIDE, instance: Mesh = null): Mesh {
             var path: Vector3[];
             var shape: Vector3[];
@@ -1648,7 +1648,7 @@
                 cap = (options.cap === 0) ? 0 : options.cap || Mesh.NO_CAP;
                 updatable = options.updatable;
                 sideOrientation = (options.sideOrientation === 0) ? 0 : options.sideOrientation || Mesh.DEFAULTSIDE;
-                instance = options.instance
+                instance = options.instance;
             }
 
 
@@ -1657,7 +1657,7 @@
         }
 
         public static ExtrudeShapeCustom(name: string, shape: Vector3[], path: Vector3[], scaleFunction, rotationFunction, ribbonCloseArray: boolean, ribbonClosePath: boolean, cap: number, scene: Scene, updatable?: boolean, sideOrientation?: number, instance?: Mesh): Mesh;
-        public static ExtrudeShapeCustom(name: string, options: {shape: Vector3[], path: Vector3[], scaleFunction?, rotationFunction?, ribbonCloseArray?: boolean, ribbonClosePath?: boolean, cap?: number, updatable?: boolean, sideOrientation?: number, instance?: Mesh}, scene: Scene): Mesh;
+        public static ExtrudeShapeCustom(name: string, options: { shape: Vector3[], path: Vector3[], scaleFunction?, rotationFunction?, ribbonCloseArray?: boolean, ribbonClosePath?: boolean, cap?: number, updatable?: boolean, sideOrientation?: number, instance?: Mesh }, scene: Scene): Mesh;
         public static ExtrudeShapeCustom(name: string, options: any, pathOrScene?: any, scaleFunction?, rotationFunction?, ribbonCloseArray?: boolean, ribbonClosePath?: boolean, cap?: number, scene?: Scene, updatable?: boolean, sideOrientation: number = Mesh.DEFAULTSIDE, instance: Mesh = null): Mesh {
             var path: Vector3[];
             var shape: Vector3[];
@@ -1906,19 +1906,19 @@
             var path: Vector3[];
             var radius: number;
             if (Array.isArray(options)) {
-                    path = options;
-                    radius = radiusOrScene;
-                } else {
-                    scene = radiusOrScene;
-                    path = options.path;
-                    radius = options.radius || 1;
-                    tessellation = options.tessellation || 64;
-                    radiusFunction = options.radiusFunction;
-                    cap = options.cap || Mesh.NO_CAP,
-                    updatable = options.updatable;
-                    sideOrientation = options.sideOrientation || Mesh.DEFAULTSIDE,
-                    instance = options.instance
-                }
+                path = options;
+                radius = radiusOrScene;
+            } else {
+                scene = radiusOrScene;
+                path = options.path;
+                radius = options.radius || 1;
+                tessellation = options.tessellation || 64;
+                radiusFunction = options.radiusFunction;
+                cap = options.cap || Mesh.NO_CAP,
+                updatable = options.updatable;
+                sideOrientation = options.sideOrientation || Mesh.DEFAULTSIDE,
+                instance = options.instance;
+            }
             // tube geometry
             var tubePathArray = (path, path3D, circlePaths, radius, tessellation, radiusFunction, cap) => {
                 var tangents = path3D.getTangents();
@@ -1962,7 +1962,7 @@
                         circlePaths[0] = capPath(tessellation, 0);
                         break;
                     case Mesh.CAP_END:
-                        circlePaths[index]= capPath(tessellation, path.length - 1);
+                        circlePaths[index] = capPath(tessellation, path.length - 1);
                         break;
                     case Mesh.CAP_ALL:
                         circlePaths[0] = capPath(tessellation, 0);
@@ -1990,7 +1990,7 @@
             var newPathArray = new Array<Array<Vector3>>();
             cap = (cap < 0 || cap > 3) ? 0 : cap;
             pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction, cap);
-            var tube = Mesh.CreateRibbon(name, {pathArray: pathArray, closePath: true, closeArray: false, updatable: updatable, sideOrientation: sideOrientation}, scene);
+            var tube = Mesh.CreateRibbon(name, { pathArray: pathArray, closePath: true, closeArray: false, updatable: updatable, sideOrientation: sideOrientation }, scene);
             (<any>tube).pathArray = pathArray;
             (<any>tube).path3D = path3D;
             (<any>tube).tessellation = tessellation;
@@ -2391,3 +2391,4 @@
 
 
 
+

+ 4 - 1
src/Mesh/babylon.mesh.vertexData.js

@@ -393,7 +393,10 @@ var BABYLON;
                 if (closeArray) {
                     path1 = pathArray[p];
                     path2 = pathArray[0];
-                    vectlg = path2[i].subtract(path1[i]).length();
+                    if (i === minlg) {
+                        vertex2 = path2[0];
+                    }
+                    vectlg = vertex2.subtract(vertex1).length();
                     dist = vectlg + vTotalDistance[i];
                     vTotalDistance[i] = dist;
                 }

+ 1 - 0
src/Mesh/babylon.mesh.vertexData.ts

@@ -1503,3 +1503,4 @@
 
 
 
+

+ 2 - 2
src/PostProcess/babylon.postProcessManager.js

@@ -71,7 +71,7 @@ var BABYLON;
             engine.setDepthBuffer(true);
             engine.setDepthWrite(true);
         };
-        PostProcessManager.prototype._finalizeFrame = function (doNotPresent, targetTexture, postProcesses) {
+        PostProcessManager.prototype._finalizeFrame = function (doNotPresent, targetTexture, faceIndex, postProcesses) {
             postProcesses = postProcesses || this._scene.activeCamera._postProcesses;
             var postProcessesTakenIndices = this._scene.activeCamera._postProcessesTakenIndices;
             if (postProcessesTakenIndices.length === 0 || !this._scene.postProcessesEnabled) {
@@ -84,7 +84,7 @@ var BABYLON;
                 }
                 else {
                     if (targetTexture) {
-                        engine.bindFramebuffer(targetTexture);
+                        engine.bindFramebuffer(targetTexture, faceIndex);
                     }
                     else {
                         engine.restoreDefaultFramebuffer();

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

@@ -90,7 +90,7 @@
             engine.setDepthWrite(true);
         }
 
-        public _finalizeFrame(doNotPresent?: boolean, targetTexture?: WebGLTexture, postProcesses?: PostProcess[]): void {
+        public _finalizeFrame(doNotPresent?: boolean, targetTexture?: WebGLTexture, faceIndex?: number, postProcesses?: PostProcess[]): void {
             postProcesses = postProcesses || this._scene.activeCamera._postProcesses;
             var postProcessesTakenIndices = this._scene.activeCamera._postProcessesTakenIndices;
             if (postProcessesTakenIndices.length === 0 || !this._scene.postProcessesEnabled) {
@@ -103,7 +103,7 @@
                     postProcesses[postProcessesTakenIndices[index + 1]].activate(this._scene.activeCamera);
                 } else {
                     if (targetTexture) {
-                        engine.bindFramebuffer(targetTexture);
+                        engine.bindFramebuffer(targetTexture, faceIndex);
                     } else {
                         engine.restoreDefaultFramebuffer();
                     }

+ 68 - 1
src/Probes/babylon.reflectionProbe.js

@@ -1,13 +1,80 @@
 var BABYLON;
 (function (BABYLON) {
     var ReflectionProbe = (function () {
-        function ReflectionProbe(size, scene) {
+        function ReflectionProbe(name, size, scene, generateMipMaps) {
+            var _this = this;
+            if (generateMipMaps === void 0) { generateMipMaps = true; }
+            this.name = name;
+            this._viewMatrix = BABYLON.Matrix.Identity();
+            this._target = BABYLON.Vector3.Zero();
+            this._add = BABYLON.Vector3.Zero();
+            this.position = BABYLON.Vector3.Zero();
             this._scene = scene;
             this._scene.reflectionProbes.push(this);
+            this._renderTargetTexture = new BABYLON.RenderTargetTexture(name, size, scene, generateMipMaps, true, BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT, true);
+            this._renderTargetTexture.onBeforeRender = function (faceIndex) {
+                switch (faceIndex) {
+                    case 0:
+                        _this._add.copyFromFloats(1, 0, 0);
+                        break;
+                    case 1:
+                        _this._add.copyFromFloats(-1, 0, 0);
+                        break;
+                    case 2:
+                        _this._add.copyFromFloats(0, -1, 0);
+                        break;
+                    case 3:
+                        _this._add.copyFromFloats(0, 1, 0);
+                        break;
+                    case 4:
+                        _this._add.copyFromFloats(0, 0, 1);
+                        break;
+                    case 5:
+                        _this._add.copyFromFloats(0, 0, -1);
+                        break;
+                }
+                if (_this._attachedMesh) {
+                    _this.position.copyFrom(_this._attachedMesh.getAbsolutePosition());
+                }
+                _this.position.addToRef(_this._add, _this._target);
+                BABYLON.Matrix.LookAtLHToRef(_this.position, _this._target, BABYLON.Vector3.Up(), _this._viewMatrix);
+                scene.setTransformMatrix(_this._viewMatrix, _this._projectionMatrix);
+            };
+            this._renderTargetTexture.onAfterUnbind = function () {
+                scene.updateTransformMatrix(true);
+            };
+            this._projectionMatrix = BABYLON.Matrix.PerspectiveFovLH(Math.PI / 2, 1, scene.activeCamera.minZ, scene.activeCamera.maxZ);
         }
+        Object.defineProperty(ReflectionProbe.prototype, "refreshRate", {
+            get: function () {
+                return this._renderTargetTexture.refreshRate;
+            },
+            set: function (value) {
+                this._renderTargetTexture.refreshRate = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         ReflectionProbe.prototype.getScene = function () {
             return this._scene;
         };
+        Object.defineProperty(ReflectionProbe.prototype, "cubeTexture", {
+            get: function () {
+                return this._renderTargetTexture;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(ReflectionProbe.prototype, "renderList", {
+            get: function () {
+                return this._renderTargetTexture.renderList;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        ReflectionProbe.prototype.attachToMesh = function (mesh) {
+            this._attachedMesh = mesh;
+        };
         ReflectionProbe.prototype.dispose = function () {
             var index = this._scene.reflectionProbes.indexOf(this);
             if (index !== -1) {

+ 71 - 1
src/Probes/babylon.reflectionProbe.ts

@@ -1,16 +1,86 @@
 module BABYLON {
     export class ReflectionProbe{  
         private _scene: Scene;
+        private _renderTargetTexture: RenderTargetTexture;
+        private _projectionMatrix: Matrix;
+        private _viewMatrix = Matrix.Identity();
+        private _target = Vector3.Zero();
+        private _add = Vector3.Zero();
+        private _attachedMesh: AbstractMesh;
+
+        public position = Vector3.Zero();
           
-        constructor(size: number, scene: Scene) {
+        constructor(public name: string, size: number, scene: Scene, generateMipMaps = true) {
             this._scene = scene;
 
             this._scene.reflectionProbes.push(this);
+
+            this._renderTargetTexture = new RenderTargetTexture(name, size, scene, generateMipMaps, true, Engine.TEXTURETYPE_UNSIGNED_INT, true);
+
+            this._renderTargetTexture.onBeforeRender = (faceIndex: number) => {
+                switch (faceIndex) {
+                    case 0:
+                        this._add.copyFromFloats(1, 0, 0);
+                        break;
+                    case 1:
+                        this._add.copyFromFloats(-1, 0, 0);
+                        break;
+                    case 2:
+                        this._add.copyFromFloats(0, -1, 0);
+                        break;
+                    case 3:
+                        this._add.copyFromFloats(0, 1, 0);
+                        break;
+                    case 4:
+                        this._add.copyFromFloats(0, 0, 1);
+                        break;
+                    case 5:
+                        this._add.copyFromFloats(0, 0, -1);
+                        break;
+
+                }
+
+                if (this._attachedMesh) {
+                    this.position.copyFrom(this._attachedMesh.getAbsolutePosition());
+                }
+
+                this.position.addToRef(this._add, this._target);
+
+                Matrix.LookAtLHToRef(this.position, this._target, Vector3.Up(), this._viewMatrix);
+
+                scene.setTransformMatrix(this._viewMatrix, this._projectionMatrix);
+            }
+
+            this._renderTargetTexture.onAfterUnbind = () => {
+                scene.updateTransformMatrix(true);
+            }
+
+            this._projectionMatrix = Matrix.PerspectiveFovLH(Math.PI / 2, 1, scene.activeCamera.minZ, scene.activeCamera.maxZ);
+        }
+
+        public get refreshRate(): number {
+            return this._renderTargetTexture.refreshRate;
+        }
+
+        public set refreshRate(value: number) {
+            this._renderTargetTexture.refreshRate = value;
         }
 
         public getScene(): Scene {
             return this._scene;
         }
+
+        public get cubeTexture(): RenderTargetTexture {
+            return this._renderTargetTexture;
+        }
+
+        public get renderList(): AbstractMesh[] {
+            return this._renderTargetTexture.renderList;
+        }
+
+        public attachToMesh(mesh: AbstractMesh): void {
+            this._attachedMesh = mesh;
+        }
         
         public dispose() {
             var index = this._scene.reflectionProbes.indexOf(this);

+ 3 - 0
src/Shaders/default.fragment.fx

@@ -203,6 +203,9 @@ vec3 computeReflectionCoords(float mode, vec4 worldPos, vec3 worldNormal)
 	{
 		vec3 viewDir = worldPos.xyz - vEyePosition;
 		vec3 coords = reflect(viewDir, worldNormal);
+#ifdef INVERTCUBICMAP
+		coords.y = 1.0 - coords.y;
+#endif
 
 		return vec3(reflectionMatrix * vec4(coords, 0));
 	}

+ 9 - 8
src/Tools/babylon.sceneSerializer.ts

@@ -180,14 +180,14 @@
             key.frame = animationKey.frame;
 
             switch (dataType) {
-            case Animation.ANIMATIONTYPE_FLOAT:
-                key.values = [animationKey.value];
-                break;
-            case Animation.ANIMATIONTYPE_QUATERNION:
-            case Animation.ANIMATIONTYPE_MATRIX:
-            case Animation.ANIMATIONTYPE_VECTOR3:
-                key.values = animationKey.value.asArray();
-                break;
+                case Animation.ANIMATIONTYPE_FLOAT:
+                    key.values = [animationKey.value];
+                    break;
+                case Animation.ANIMATIONTYPE_QUATERNION:
+                case Animation.ANIMATIONTYPE_MATRIX:
+                case Animation.ANIMATIONTYPE_VECTOR3:
+                    key.values = animationKey.value.asArray();
+                    break;
             }
 
             serializationObject.keys.push(key);
@@ -956,3 +956,4 @@
 
 
 
+

+ 63 - 33
src/babylon.engine.js

@@ -834,16 +834,23 @@ var BABYLON;
                 }
             }
         };
-        Engine.prototype.bindFramebuffer = function (texture) {
+        Engine.prototype.bindFramebuffer = function (texture, faceIndex) {
             this._currentRenderTarget = texture;
             var gl = this._gl;
             gl.bindFramebuffer(gl.FRAMEBUFFER, texture._framebuffer);
+            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, texture._width, texture._height);
             this.wipeCaches();
         };
-        Engine.prototype.unBindFramebuffer = function (texture) {
+        Engine.prototype.unBindFramebuffer = function (texture, disableGenerateMipMaps) {
+            if (disableGenerateMipMaps === void 0) { disableGenerateMipMaps = false; }
             this._currentRenderTarget = null;
-            if (texture.generateMipMaps) {
+            if (texture.generateMipMaps && !disableGenerateMipMaps) {
                 var gl = this._gl;
                 gl.bindTexture(gl.TEXTURE_2D, texture);
                 gl.generateMipmap(gl.TEXTURE_2D);
@@ -851,6 +858,14 @@ var BABYLON;
             }
             this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, null);
         };
+        Engine.prototype.generateMipMapsForCubemap = function (texture) {
+            if (texture.generateMipMaps) {
+                var gl = this._gl;
+                gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+                gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+            }
+        };
         Engine.prototype.flushFramebuffer = function () {
             this._gl.flush();
         };
@@ -1210,7 +1225,7 @@ var BABYLON;
             var showSide = reverseSide ? this._gl.FRONT : this._gl.BACK;
             var hideSide = reverseSide ? this._gl.BACK : this._gl.FRONT;
             var cullFace = this.cullBackFaces ? showSide : hideSide;
-            if (this._depthCullingState.cull !== culling || force || this._depthCullingState.cullFace != cullFace) {
+            if (this._depthCullingState.cull !== culling || force || this._depthCullingState.cullFace !== cullFace) {
                 if (culling) {
                     this._depthCullingState.cullFace = cullFace;
                     this._depthCullingState.cull = true;
@@ -1235,7 +1250,7 @@ var BABYLON;
             this._gl.colorMask(enable, enable, enable, enable);
         };
         Engine.prototype.setAlphaMode = function (mode) {
-            if (this._alphaMode == mode) {
+            if (this._alphaMode === mode) {
                 return;
             }
             switch (mode) {
@@ -1347,8 +1362,9 @@ var BABYLON;
                     onError();
                 }
             };
+            var callback;
             if (isTGA) {
-                var callback = function (arrayBuffer) {
+                callback = function (arrayBuffer) {
                     var data = new Uint8Array(arrayBuffer);
                     var header = BABYLON.Internals.TGATools.GetTGAHeader(data);
                     prepareWebGLTexture(texture, _this._gl, scene, header.width, header.height, invertY, noMipmap, false, function () {
@@ -1556,7 +1572,7 @@ var BABYLON;
                 texture._isDisabled = true;
             }
         };
-        Engine.prototype.createRenderTargetTexture = function (size, options, face) {
+        Engine.prototype.createRenderTargetTexture = function (size, options) {
             // old version had a "generateMipMaps" arg instead of options.
             // if options.generateMipMaps is undefined, consider that options itself if the generateMipmaps value
             // in the same way, generateDepthBuffer is defaulted to true
@@ -1565,7 +1581,7 @@ var BABYLON;
             var type = Engine.TEXTURETYPE_UNSIGNED_INT;
             var samplingMode = BABYLON.Texture.TRILINEAR_SAMPLINGMODE;
             if (options !== undefined) {
-                generateMipMaps = options.generateMipMaps === undefined ? options : options.generateMipmaps;
+                generateMipMaps = options.generateMipMaps === undefined ? options : options.generateMipMaps;
                 generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
                 type = options.type === undefined ? type : options.type;
                 if (options.samplingMode !== undefined) {
@@ -1586,14 +1602,11 @@ var BABYLON;
                 type = Engine.TEXTURETYPE_UNSIGNED_INT;
                 BABYLON.Tools.Warn("Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type");
             }
-            if (face === undefined) {
-                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filters.mag);
-                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filters.min);
-                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);
-            }
-            var target = face === undefined ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
-            gl.texImage2D(target, 0, gl.RGBA, width, height, 0, gl.RGBA, getWebGLTextureType(gl, type), null);
+            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filters.mag);
+            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filters.min);
+            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.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, getWebGLTextureType(gl, type), null);
             var depthBuffer;
             // Create the depth buffer
             if (generateDepthBuffer) {
@@ -1604,7 +1617,6 @@ var BABYLON;
             // Create the framebuffer
             var framebuffer = gl.createFramebuffer();
             gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
-            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
             if (generateDepthBuffer) {
                 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
             }
@@ -1629,22 +1641,45 @@ var BABYLON;
             this._loadedTexturesCache.push(texture);
             return texture;
         };
-        Engine.prototype.createRenderTargetCubeTexture = function (size) {
+        Engine.prototype.createRenderTargetCubeTexture = function (size, options) {
             var gl = this._gl;
             var texture = gl.createTexture();
+            var generateMipMaps = true;
+            var samplingMode = BABYLON.Texture.TRILINEAR_SAMPLINGMODE;
+            if (options !== undefined) {
+                generateMipMaps = options.generateMipMaps === undefined ? options : options.generateMipMaps;
+                if (options.samplingMode !== undefined) {
+                    samplingMode = options.samplingMode;
+                }
+            }
             texture.isCube = true;
             texture.references = 1;
-            this._loadedTexturesCache.push(texture);
+            texture.generateMipMaps = generateMipMaps;
+            texture.references = 1;
+            texture.samplingMode = samplingMode;
+            var filters = getSamplingParameters(samplingMode, generateMipMaps, gl);
             gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
-            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
             for (var face = 0; face < 6; face++) {
-                texture._cubeFaces[face] = this.createRenderTargetTexture(size, {}, face);
+                gl.texImage2D((gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
             }
-            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, filters.mag);
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, filters.min);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
             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);
+            // Create the framebuffer
+            var framebuffer = gl.createFramebuffer();
+            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+            // Unbind
             gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+            gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+            texture._framebuffer = framebuffer;
+            texture._depthBuffer = depthBuffer;
             this._activeTexturesCache = [];
             texture._width = size;
             texture._height = size;
@@ -1658,7 +1693,6 @@ var BABYLON;
             texture.isCube = true;
             texture.url = rootUrl;
             texture.references = 1;
-            this._loadedTexturesCache.push(texture);
             var extension = rootUrl.substr(rootUrl.length - 4, 4).toLowerCase();
             var isDDS = this.getCaps().s3tc && (extension === ".dds");
             if (isDDS) {
@@ -1847,18 +1881,14 @@ var BABYLON;
             if (!texture) {
                 return;
             }
-            var index;
-            if (texture._cubeFaces) {
-                for (index = 0; index < 6; index++) {
-                    this.releaseInternalTexture(texture._cubeFaces[index]);
-                }
-            }
-            var texturesCache = this.getLoadedTexturesCache();
             texture.references--;
             // Final reference ?
             if (texture.references === 0) {
-                index = texturesCache.indexOf(texture);
-                texturesCache.splice(index, 1);
+                var texturesCache = this.getLoadedTexturesCache();
+                var index = texturesCache.indexOf(texture);
+                if (index > -1) {
+                    texturesCache.splice(index, 1);
+                }
                 this._releaseTexture(texture);
             }
         };

+ 72 - 40
src/babylon.engine.ts

@@ -951,19 +951,26 @@
             }
         }
 
-        public bindFramebuffer(texture: WebGLTexture): void {
+        public bindFramebuffer(texture: WebGLTexture, faceIndex?: number): void {
             this._currentRenderTarget = texture;
 
             var gl = this._gl;
             gl.bindFramebuffer(gl.FRAMEBUFFER, texture._framebuffer);
+
+            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, texture._width, texture._height);
 
             this.wipeCaches();
         }
 
-        public unBindFramebuffer(texture: WebGLTexture): void {
+        public unBindFramebuffer(texture: WebGLTexture, disableGenerateMipMaps = false): void {
             this._currentRenderTarget = null;
-            if (texture.generateMipMaps) {
+            if (texture.generateMipMaps && !disableGenerateMipMaps) {
                 var gl = this._gl;
                 gl.bindTexture(gl.TEXTURE_2D, texture);
                 gl.generateMipmap(gl.TEXTURE_2D);
@@ -973,6 +980,15 @@
             this._gl.bindFramebuffer(this._gl.FRAMEBUFFER, null);
         }
 
+        public generateMipMapsForCubemap(texture: WebGLTexture) {
+            if (texture.generateMipMaps) {
+                var gl = this._gl;
+                gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+                gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
+                gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+            }
+        }
+
         public flushFramebuffer(): void {
             this._gl.flush();
         }
@@ -1435,7 +1451,7 @@
             var hideSide = reverseSide ? this._gl.BACK : this._gl.FRONT;
             var cullFace = this.cullBackFaces ? showSide : hideSide;
 
-            if (this._depthCullingState.cull !== culling || force || this._depthCullingState.cullFace != cullFace) {
+            if (this._depthCullingState.cull !== culling || force || this._depthCullingState.cullFace !== cullFace) {
                 if (culling) {
                     this._depthCullingState.cullFace = cullFace;
                     this._depthCullingState.cull = true;
@@ -1465,7 +1481,7 @@
         }
 
         public setAlphaMode(mode: number): void {
-            if (this._alphaMode == mode) {
+            if (this._alphaMode === mode) {
                 return;
             }
 
@@ -1593,9 +1609,9 @@
                     onError();
                 }
             };
-
+            var callback: (arrayBuffer: any) => void;
             if (isTGA) {
-                var callback = (arrayBuffer) => {
+                callback = (arrayBuffer) => {
                     var data = new Uint8Array(arrayBuffer);
 
                     var header = Internals.TGATools.GetTGAHeader(data);
@@ -1608,7 +1624,6 @@
                         }
                     }, samplingMode);
                 };
-
                 if (!(fromData instanceof Array))
                     Tools.LoadFile(url, arrayBuffer => {
                         callback(arrayBuffer);
@@ -1843,7 +1858,7 @@
             }
         }
 
-        public createRenderTargetTexture(size: any, options, face?: number): WebGLTexture {
+        public createRenderTargetTexture(size: any, options): WebGLTexture {
             // old version had a "generateMipMaps" arg instead of options.
             // if options.generateMipMaps is undefined, consider that options itself if the generateMipmaps value
             // in the same way, generateDepthBuffer is defaulted to true
@@ -1852,7 +1867,7 @@
             var type = Engine.TEXTURETYPE_UNSIGNED_INT;
             var samplingMode = Texture.TRILINEAR_SAMPLINGMODE;
             if (options !== undefined) {
-                generateMipMaps = options.generateMipMaps === undefined ? options : options.generateMipmaps;
+                generateMipMaps = options.generateMipMaps === undefined ? options : options.generateMipMaps;
                 generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
                 type = options.type === undefined ? type : options.type;
                 if (options.samplingMode !== undefined) {
@@ -1878,17 +1893,12 @@
                 Tools.Warn("Float textures are not supported. Render target forced to TEXTURETYPE_UNSIGNED_BYTE type");
             }
 
+            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filters.mag);
+            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filters.min);
+            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);
 
-            if (face === undefined) {
-                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filters.mag);
-                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filters.min);
-                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);
-            }
-
-            var target = face === undefined ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
-
-            gl.texImage2D(target, 0, gl.RGBA, width, height, 0, gl.RGBA, getWebGLTextureType(gl, type), null);
+            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, getWebGLTextureType(gl, type), null);
 
             var depthBuffer: WebGLRenderbuffer;
             // Create the depth buffer
@@ -1900,7 +1910,6 @@
             // Create the framebuffer
             var framebuffer = gl.createFramebuffer();
             gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
-            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
             if (generateDepthBuffer) {
                 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
             }
@@ -1931,27 +1940,56 @@
             return texture;
         }
 
-        public createRenderTargetCubeTexture(size: number): WebGLTexture {
+        public createRenderTargetCubeTexture(size: number, options?: any): WebGLTexture {
             var gl = this._gl;
 
             var texture = gl.createTexture();
+
+            var generateMipMaps = true;
+            var samplingMode = Texture.TRILINEAR_SAMPLINGMODE;
+            if (options !== undefined) {
+                generateMipMaps = options.generateMipMaps === undefined ? options : options.generateMipMaps;
+                if (options.samplingMode !== undefined) {
+                    samplingMode = options.samplingMode;
+                }
+            }
+
             texture.isCube = true;
             texture.references = 1;
-            this._loadedTexturesCache.push(texture);
+            texture.generateMipMaps = generateMipMaps;
+            texture.references = 1;
+            texture.samplingMode = samplingMode;
 
-            gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
-            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
+            var filters = getSamplingParameters(samplingMode, generateMipMaps, gl);
 
+            gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
+            
             for (var face = 0; face < 6; face++) {
-                texture._cubeFaces[face] = this.createRenderTargetTexture(size, {}, face);
+                gl.texImage2D((gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
             }
 
-            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
-            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, filters.mag);
+            gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, filters.min);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
             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);
+
+            // Create the framebuffer
+            var framebuffer = gl.createFramebuffer();
+            gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
+            gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+
+            // Unbind
             gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
+            gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+            texture._framebuffer = framebuffer;
+            texture._depthBuffer = depthBuffer;
 
             this._activeTexturesCache = [];
 
@@ -1969,7 +2007,6 @@
             texture.isCube = true;
             texture.url = rootUrl;
             texture.references = 1;
-            this._loadedTexturesCache.push(texture);
 
             var extension = rootUrl.substr(rootUrl.length - 4, 4).toLowerCase();
             var isDDS = this.getCaps().s3tc && (extension === ".dds");
@@ -2203,21 +2240,16 @@
                 return;
             }
 
-            var index;
-
-            if (texture._cubeFaces) {
-                for (index = 0; index < 6; index++) {
-                    this.releaseInternalTexture(texture._cubeFaces[index]);
-                }
-            }
-
-            var texturesCache = this.getLoadedTexturesCache();
             texture.references--;
 
             // Final reference ?
             if (texture.references === 0) {
-                index = texturesCache.indexOf(texture);
-                texturesCache.splice(index, 1);
+                var texturesCache = this.getLoadedTexturesCache();
+                var index = texturesCache.indexOf(texture);
+
+                if (index > -1) {
+                    texturesCache.splice(index, 1);
+                }
 
                 this._releaseTexture(texture);
             }

+ 0 - 1
src/babylon.mixins.ts

@@ -72,7 +72,6 @@ interface WebGLTexture {
     _cachedWrapU: number;
     _cachedWrapV: number;
     _isDisabled: boolean;
-    _cubeFaces: WebGLTexture[];
 }
 
 interface WebGLBuffer {

+ 3 - 0
src/babylon.scene.ts

@@ -71,6 +71,9 @@
         private _pointerY: number;
         private _meshUnderPointer: AbstractMesh;
 
+        // Mirror
+        public _mirroredCameraPosition: Vector3;
+
         // Keyboard
         private _onKeyDown: (evt: Event) => void;
         private _onKeyUp: (evt: Event) => void;