浏览代码

Merge remote-tracking branch 'upstream/master' into scale9

nockawa 8 年之前
父节点
当前提交
7ce10ffd00

+ 36 - 6
.vscode/launch.json

@@ -8,7 +8,22 @@
             "url": "http://localhost:1338/Playground/index-local.html",
             "webRoot": "${workspaceRoot}/",
             "sourceMaps": true,
-            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug"
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
+        },        
+        {
+            "name": "Launch playground (Chrome+WebGL 1.0 forced)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/Playground/index-local.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--disable-es3-apis" 
+            ]
         },
         {
             "name": "Launch Materials Library (Chrome)",
@@ -17,7 +32,10 @@
             "url": "http://localhost:1338/materialsLibrary/index.html",
             "webRoot": "${workspaceRoot}/",
             "sourceMaps": true,
-            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug"
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
         },
         {
             "name": "Launch Post Processes Library (Chrome)",
@@ -26,7 +44,10 @@
             "url": "http://localhost:1338/postProcessLibrary/index.html",
             "webRoot": "${workspaceRoot}/",
             "sourceMaps": true,
-            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug"
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
         },
         {
             "name": "Launch Procedural Textures Library (Chrome)",
@@ -35,7 +56,10 @@
             "url": "http://localhost:1338/proceduralTexturesLibrary/index.html",
             "webRoot": "${workspaceRoot}/",
             "sourceMaps": true,
-            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug"
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
         },
         {
             "name": "Launch Inspector (Chrome)",
@@ -44,7 +68,10 @@
             "url": "http://localhost:1338/inspector/index.html",
             "webRoot": "${workspaceRoot}/",
             "sourceMaps": true,
-            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug"
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
         },
         {
             "name": "Launch Local Dev (Chrome)",
@@ -53,7 +80,10 @@
             "url": "http://localhost:1338/localDev/index.html",
             "webRoot": "${workspaceRoot}/",
             "sourceMaps": true,
-            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug"
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
         }
     ]
 }

+ 59 - 0
Playground/scripts/instanced bones.js

@@ -0,0 +1,59 @@
+var createScene = function () {
+    var scene = new BABYLON.Scene(engine);
+    var light = new BABYLON.DirectionalLight("dir01", new BABYLON.Vector3(0, -0.5, -1.0), scene);
+    var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 10, new BABYLON.Vector3(0, 30, 0), scene);
+    
+    camera.attachControl(canvas, false);
+    camera.setPosition(new BABYLON.Vector3(20, 70, 120));
+    light.position = new BABYLON.Vector3(50, 250, 200);
+	light.shadowOrthoScale = 2.0;
+    camera.minZ = 1.0;
+
+    scene.ambientColor = new BABYLON.Color3(0.3, 0.3, 0.3);
+
+    // Ground
+    var ground = BABYLON.Mesh.CreateGround("ground", 1000, 1000, 1, scene, false);
+    var groundMaterial = new BABYLON.StandardMaterial("ground", scene);
+    groundMaterial.diffuseColor = new BABYLON.Color3(0.2, 0.2, 0.2);
+    groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
+    ground.material = groundMaterial;
+    ground.receiveShadows = true;
+
+    // Shadows
+    var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
+    shadowGenerator.useBlurVarianceShadowMap = true;
+
+    // Dude
+    BABYLON.SceneLoader.ImportMesh("him", "scenes/Dude/", "Dude.babylon", scene, function (newMeshes2, particleSystems2, skeletons2) {
+        var dude = newMeshes2[0];
+
+        for (var index = 1; index < newMeshes2.length; index++) {
+            shadowGenerator.getShadowMap().renderList.push(newMeshes2[index]);
+        }
+
+        for (var count = 0; count < 50; count++) {
+            var offsetX = 200 * Math.random() - 100;
+            var offsetZ = 200 * Math.random() - 100;
+            for (index = 1; index < newMeshes2.length; index++) {
+                var instance = newMeshes2[index].createInstance("instance" + count);
+
+                shadowGenerator.getShadowMap().renderList.push(instance);
+
+                instance.parent = newMeshes2[index].parent;
+                instance.position = newMeshes2[index].position.clone();
+
+                if (!instance.parent.subMeshes) {
+                    instance.position.x += offsetX;
+                    instance.position.z -= offsetZ;
+                }
+            }
+        }
+
+        dude.rotation.y = Math.PI;
+        dude.position = new BABYLON.Vector3(0, 0, -80);
+
+        scene.beginAnimation(skeletons2[0], 0, 100, true, 1.0);
+    });
+
+    return scene;
+};

+ 2 - 1
Playground/scripts/scripts.txt

@@ -25,4 +25,5 @@ SSAO rendering pipeline
 Volumetric Light Scattering
 HDR Rendering Pipeline
 Refraction and Reflection
-PBR
+PBR
+Instanced bones

+ 1 - 0
Tools/Gulp/config.json

@@ -163,6 +163,7 @@
       "../../src/Materials/Textures/babylon.mapTexture.js",
       "../../src/Materials/babylon.shaderMaterial.js",
       "../../src/Tools/babylon.tools.dds.js",
+      "../../src/Tools/babylon.khronosTextureContainer.js",
       "../../src/Physics/Plugins/babylon.cannonJSPlugin.js",
       "../../src/Physics/Plugins/babylon.oimoJSPlugin.js",
       "../../src/PostProcess/babylon.displayPassPostProcess.js",

文件差异内容过多而无法显示
+ 23 - 23
dist/preview release/babylon.core.js


文件差异内容过多而无法显示
+ 1430 - 1359
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 37 - 37
dist/preview release/babylon.js


文件差异内容过多而无法显示
+ 406 - 180
dist/preview release/babylon.max.js


文件差异内容过多而无法显示
+ 34 - 34
dist/preview release/babylon.noworker.js


+ 1 - 1
dist/preview release/canvas2D/babylon.canvas2d.js

@@ -7372,7 +7372,7 @@ var BABYLON;
                 t.m[1] = ty.x, t.m[5] = ty.y, t.m[13] = ty.w;
                 var las = this.actualScale;
                 t.decompose(RenderablePrim2D_1._s, RenderablePrim2D_1._r, RenderablePrim2D_1._t);
-                var scale = new BABYLON.Vector3(RenderablePrim2D_1._s.y / las.x, RenderablePrim2D_1._s.y / las.y, 1);
+                var scale = new BABYLON.Vector3(RenderablePrim2D_1._s.x / las.x, RenderablePrim2D_1._s.y / las.y, 1);
                 t = BABYLON.Matrix.Compose(scale, RenderablePrim2D_1._r, RenderablePrim2D_1._t);
                 tx = new BABYLON.Vector4(t.m[0], t.m[4], 0, t.m[12]);
                 ty = new BABYLON.Vector4(t.m[1], t.m[5], 0, t.m[13]);

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/canvas2D/babylon.canvas2d.min.js


文件差异内容过多而无法显示
+ 2 - 2
dist/preview release/inspector/babylon.inspector.bundle.js


+ 7 - 1
dist/preview release/inspector/babylon.inspector.js

@@ -2866,6 +2866,12 @@ var INSPECTOR;
                     elem: elemValue,
                     updateFct: function () { return (_this._engine.getCaps().drawBuffersExtension ? "Yes" : "No"); }
                 });
+                elemLabel = _this._createStatLabel("Vertex array object", _this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', _this._panel);
+                _this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().vertexArrayObject ? "Yes" : "No"); }
+                });
             }
             title = INSPECTOR.Helpers.CreateDiv('stat-title2', _this._panel);
             title.textContent = "Caps.";
@@ -2901,7 +2907,7 @@ var INSPECTOR;
                 var elemValue = INSPECTOR.Helpers.CreateDiv('stat-infos', _this._panel);
                 _this._updatableProperties.push({
                     elem: elemValue,
-                    updateFct: function () { return _this._engine.webGLVersion + " - " + _this._glInfo.version + " - " + _this._glInfo.renderer; }
+                    updateFct: function () { return "WebGL v" + _this._engine.webGLVersion + " - " + _this._glInfo.version + " - " + _this._glInfo.renderer; }
                 });
             }
             // Register the update loop

文件差异内容过多而无法显示
+ 2 - 2
dist/preview release/inspector/babylon.inspector.min.js


+ 3 - 1
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -326,6 +326,8 @@ var BABYLON;
                     targetNode.animations.push(babylonAnimation);
                 }
                 lastAnimation = babylonAnimation;
+                gltfRuntime.scene.stopAnimation(targetNode);
+                gltfRuntime.scene.beginAnimation(targetNode, 0, bufferInput[bufferInput.length - 1], true, 1.0);
             }
         }
     };
@@ -1293,7 +1295,7 @@ var BABYLON;
                 attributes: attributes,
                 uniforms: uniforms,
                 samplers: samplers,
-                needAlphaBlending: states.functions && states.functions.blendEquationSeparate
+                needAlphaBlending: states.enable && states.enable.indexOf(3042) !== -1
             };
             BABYLON.Effect.ShadersStore[program.vertexShader + id + "VertexShader"] = newVertexShader;
             BABYLON.Effect.ShadersStore[program.fragmentShader + id + "PixelShader"] = newPixelShader;

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.min.js


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

@@ -1,6 +1,7 @@
 # 2.6.0:
 
 ### Major updates
+ - WebGL2 context support.WebGL2 is initialized instead of WebGL 1 when available ([deltakosh](https://github.com/deltakosh))
  - New Unity 5 Editor Toolkit. Complete pipeline integration [Doc](TODO) - ([MackeyK24](https://github.com/MackeyK24))
  - New DebugLayer. [Doc](TODO) - ([temechon](https://github.com/temechon))
  - New `VideoTexture.CreateFromWebCam` to generate video texture using WebRTC. [Demo](https://www.babylonjs-playground.com#1R77YT#2) - (Sebastien Vandenberghe)(https://github.com/sebavanmicrosoft) / ([deltakosh](https://github.com/deltakosh))

+ 7 - 1
inspector/src/tabs/StatsTab.ts

@@ -228,6 +228,12 @@ module INSPECTOR {
                 this._updatableProperties.push({ 
                     elem:elemValue, 
                     updateFct:() => { return (this._engine.getCaps().drawBuffersExtension ? "Yes" : "No")}
+                });                 
+                elemLabel = this._createStatLabel("Vertex array object", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({ 
+                    elem:elemValue, 
+                    updateFct:() => { return (this._engine.getCaps().vertexArrayObject ? "Yes" : "No")}
                 }); 
             }
 
@@ -265,7 +271,7 @@ module INSPECTOR {
                 let elemValue = Helpers.CreateDiv('stat-infos', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return this._engine.webGLVersion + " - " + this._glInfo.version + " - "+this._glInfo.renderer}
+                    updateFct:() => { return "WebGL v" + this._engine.webGLVersion + " - " + this._glInfo.version + " - "+this._glInfo.renderer}
                 });
             }
 

+ 45 - 7
src/Materials/babylon.effect.ts

@@ -116,9 +116,13 @@
 
             this._loadVertexShader(vertexSource, vertexCode => {
                 this._processIncludes(vertexCode, vertexCodeWithIncludes => {
-                    this._loadFragmentShader(fragmentSource, (fragmentCode) => {
-                        this._processIncludes(fragmentCode, fragmentCodeWithIncludes => {
-                            this._prepareEffect(vertexCodeWithIncludes, fragmentCodeWithIncludes, attributesNames, defines, fallbacks);
+                    this._processShaderConversion(vertexCodeWithIncludes, false, migratedVertexCode => {
+                        this._loadFragmentShader(fragmentSource, (fragmentCode) => {
+                            this._processIncludes(fragmentCode, fragmentCodeWithIncludes => {
+                                this._processShaderConversion(fragmentCodeWithIncludes, true, migratedFragmentCode => {
+                                    this._prepareEffect(migratedVertexCode, migratedFragmentCode, attributesNames, defines, fallbacks);
+                                });
+                            });
                         });
                     });
                 });
@@ -260,6 +264,44 @@
                 Tools.Error("Fragment shader:" + this.name);
             }
         }
+        private _processShaderConversion(sourceCode: string, isFragment: boolean, callback: (data: any) => void): void {
+
+            var preparedSourceCode = this._processPrecision(sourceCode);
+
+            if (this._engine.webGLVersion == 1) {
+                callback(preparedSourceCode);
+                return;
+            }
+
+            // Already converted
+            if (preparedSourceCode.indexOf("#version 3") !== -1) {
+                callback(preparedSourceCode);
+                return;
+            }
+            
+            // Remove extensions 
+            // #extension GL_OES_standard_derivatives : enable
+            // #extension GL_EXT_shader_texture_lod : enable
+            // #extension GL_EXT_frag_depth : enable
+            var regex = /#extension.+(GL_OES_standard_derivatives|GL_EXT_shader_texture_lod|GL_EXT_frag_depth).+enable/g;
+            var result = preparedSourceCode.replace(regex, "");
+
+            // Migrate to GLSL v300
+            result = result.replace(/varying\s/g, isFragment ? "in " : "out ");
+            result = result.replace(/attribute[ \t]/g, "in ");
+            result = result.replace(/[ \t]attribute/g, " in");
+            
+            if (isFragment) {
+                result = result.replace(/textureCubeLodEXT\(/g, "textureLod(");
+                result = result.replace(/texture2D\(/g, "texture(");
+                result = result.replace(/textureCube\(/g, "texture(");
+                result = result.replace(/gl_FragDepthEXT/g, "gl_FragDepth");
+                result = result.replace(/gl_FragColor/g, "glFragColor");
+                result = result.replace(/void\s+?main\(/g, "out vec4 glFragColor;\nvoid main(");
+            }
+            
+            callback(result);
+        }
 
         private _processIncludes(sourceCode: string, callback: (data: any) => void): void {
             var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
@@ -344,10 +386,6 @@
             try {
                 var engine = this._engine;
 
-                // Precision
-                vertexSourceCode = this._processPrecision(vertexSourceCode);
-                fragmentSourceCode = this._processPrecision(fragmentSourceCode);
-
                 this._program = engine.createShaderProgram(vertexSourceCode, fragmentSourceCode, defines);
 
                 this._uniforms = engine.getUniforms(this._program, this._uniformsNames);

+ 5 - 0
src/Materials/babylon.pbrMaterial.ts

@@ -809,6 +809,11 @@
                         if (this.invertNormalMapY) {
                             this._defines.INVERTNORMALMAPY = true;
                         }
+
+                        if (scene._mirroredCameraPosition) {
+                            this._defines.INVERTNORMALMAPX = !this._defines.INVERTNORMALMAPX;
+                            this._defines.INVERTNORMALMAPY = !this._defines.INVERTNORMALMAPY;
+                        }
                     }
                 }
 

+ 5 - 0
src/Materials/babylon.standardMaterial.ts

@@ -435,6 +435,11 @@
                         if (this.invertNormalMapY) {
                             this._defines.INVERTNORMALMAPY = true;
                         }
+
+                        if (scene._mirroredCameraPosition) {
+                            this._defines.INVERTNORMALMAPX = !this._defines.INVERTNORMALMAPX;
+                            this._defines.INVERTNORMALMAPY = !this._defines.INVERTNORMALMAPY;
+                        }
                     }
                 }
 

+ 1 - 1
src/Math/babylon.math.ts

@@ -1227,7 +1227,7 @@
             world.multiplyToRef(view, matrix)
             matrix.multiplyToRef(projection, matrix);
             matrix.invert();
-            var screenSource = new Vector3(source.x / viewportWidth * 2 - 1, -(source.y / viewportHeight * 2 - 1), source.z);
+            var screenSource = new Vector3(source.x / viewportWidth * 2 - 1, -(source.y / viewportHeight * 2 - 1), 2 * source.z - 1.0);
             var vector = Vector3.TransformCoordinates(screenSource, matrix);
             var num = screenSource.x * matrix.m[3] + screenSource.y * matrix.m[7] + screenSource.z * matrix.m[11] + matrix.m[15];
 

+ 1 - 0
src/Mesh/babylon.abstractMesh.ts

@@ -1175,6 +1175,7 @@
 
             // Engine
             this.getScene().getEngine().unbindAllAttributes();
+            this.getScene().getEngine().wipeCaches();
 
             // Remove from scene
             this.getScene().removeMesh(this);

+ 0 - 8
src/Mesh/babylon.geometry.ts

@@ -319,14 +319,6 @@
                 return;
             }
 
-            for (var kind in this._vertexBuffers) {
-                this._vertexBuffers[kind].dispose();
-            }
-
-            if (this._indexBuffer && this._engine._releaseBuffer(this._indexBuffer)) {
-                this._indexBuffer = null;
-            }
-
             meshes.splice(index, 1);
 
             mesh._geometry = null;

+ 3 - 3
src/Shaders/volumetricLightScattering.fragment.fx

@@ -20,9 +20,9 @@ void main(void) {
 
     for(int i=0; i < NUM_SAMPLES; i++) {
         tc -= deltaTexCoord;
-		vec4 sample = texture2D(lightScatteringSampler, tc) * 0.4;
-        sample *= illuminationDecay * weight;
-        color += sample;
+		vec4 dataSample = texture2D(lightScatteringSampler, tc) * 0.4;
+        dataSample *= illuminationDecay * weight;
+        color += dataSample;
         illuminationDecay *= decay;
     }
 

+ 142 - 0
src/Tools/babylon.khronosTextureContainer.ts

@@ -0,0 +1,142 @@
+module BABYLON.Internals {
+    /**
+     * for description see https://www.khronos.org/opengles/sdk/tools/KTX/
+     * for file layout see https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+     */
+    export class KhronosTextureContainer {
+        static HEADER_LEN = 12 + (13 * 4); // identifier + header elements (not including key value meta-data pairs)
+        
+        // load types
+        static COMPRESSED_2D = 0; // uses a gl.compressedTexImage2D()
+        static COMPRESSED_3D = 1; // uses a gl.compressedTexImage3D()
+        static TEX_2D        = 2; // uses a gl.texImage2D()
+        static TEX_3D        = 3; // uses a gl.texImage3D()
+        
+        // elements of the header 
+        public glType : number;
+        public glTypeSize : number;
+        public glFormat : number;
+        public glInternalFormat : number;
+        public glBaseInternalFormat : number;
+        public pixelWidth : number;
+        public pixelHeight : number;
+        public pixelDepth : number;
+        public numberOfArrayElements : number;
+        public numberOfFaces : number;
+        public numberOfMipmapLevels : number;
+        public bytesOfKeyValueData : number;
+        
+        public loadType : number;
+        /**
+         * @param {ArrayBuffer} arrayBuffer- contents of the KTX container file
+         * @param {number} facesExpected- should be either 1 or 6, based whether a cube texture or or
+         * @param {boolean} threeDExpected- provision for indicating that data should be a 3D texture, not implemented
+         * @param {boolean} textureArrayExpected- provision for indicating that data should be a texture array, not implemented
+         */
+        public constructor (public arrayBuffer : any, facesExpected : number, threeDExpected? : boolean, textureArrayExpected? : boolean) {
+            // Test that it is a ktx formatted file, based on the first 12 bytes, character representation is:
+            // '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n'
+            // 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
+            var identifier = new Uint8Array(this.arrayBuffer, 0, 12);
+            if (identifier[ 0] !== 0xAB || identifier[ 1] !== 0x4B || identifier[ 2] !== 0x54 || identifier[ 3] !== 0x58 || identifier[ 4] !== 0x20 || identifier[ 5] !== 0x31 || 
+                identifier[ 6] !== 0x31 || identifier[ 7] !== 0xBB || identifier[ 8] !== 0x0D || identifier[ 9] !== 0x0A || identifier[10] !== 0x1A || identifier[11] !== 0x0A) {
+                Tools.Error("texture missing KTX identifier");
+                return;
+            }
+            
+            // load the reset of the header in native 32 bit int
+            var header = new Int32Array(this.arrayBuffer, 12, 13);
+            
+            // determine of the remaining header values are recorded in the opposite endianness & require conversion
+            var oppositeEndianess = header[0] === 0x01020304;
+            
+            // read all the header elements in order they exist in the file, without modification (sans endainness)
+            this.glType                = oppositeEndianess ? this.switchEndainness(header[ 1]) : header[ 1]; // must be 0 for compressed textures
+            this.glTypeSize            = oppositeEndianess ? this.switchEndainness(header[ 2]) : header[ 2]; // must be 1 for compressed textures
+            this.glFormat              = oppositeEndianess ? this.switchEndainness(header[ 3]) : header[ 3]; // must be 0 for compressed textures
+            this.glInternalFormat      = oppositeEndianess ? this.switchEndainness(header[ 4]) : header[ 4]; // the value of arg passed to gl.compressedTexImage2D(,,x,,,,)
+            this.glBaseInternalFormat  = oppositeEndianess ? this.switchEndainness(header[ 5]) : header[ 5]; // specify GL_RGB, GL_RGBA, GL_ALPHA, etc (un-compressed only)
+            this.pixelWidth            = oppositeEndianess ? this.switchEndainness(header[ 6]) : header[ 6]; // level 0 value of arg passed to gl.compressedTexImage2D(,,,x,,,)
+            this.pixelHeight           = oppositeEndianess ? this.switchEndainness(header[ 7]) : header[ 7]; // level 0 value of arg passed to gl.compressedTexImage2D(,,,,x,,)
+            this.pixelDepth            = oppositeEndianess ? this.switchEndainness(header[ 8]) : header[ 8]; // level 0 value of arg passed to gl.compressedTexImage3D(,,,,,x,,)
+            this.numberOfArrayElements = oppositeEndianess ? this.switchEndainness(header[ 9]) : header[ 9]; // used for texture arrays
+            this.numberOfFaces         = oppositeEndianess ? this.switchEndainness(header[10]) : header[10]; // used for cubemap textures, should either be 1 or 6
+            this.numberOfMipmapLevels  = oppositeEndianess ? this.switchEndainness(header[11]) : header[11]; // number of levels; disregard possibility of 0 for compressed textures
+            this.bytesOfKeyValueData   = oppositeEndianess ? this.switchEndainness(header[12]) : header[12]; // the amount of space after the header for meta-data
+            
+            // Make sure we have a compressed type.  Not only reduces work, but probably better to let dev know they are not compressing.
+            if (this.glType !== 0) {
+                Tools.Error("only compressed formats currently supported");
+                return;
+            } else {
+                // value of zero is an indication to generate mipmaps @ runtime.  Not usually allowed for compressed, so disregard.
+                this.numberOfMipmapLevels = Math.max(1, this.numberOfMipmapLevels);
+            }
+            
+            if (this.pixelHeight === 0 || this.pixelDepth !== 0) {
+                Tools.Error("only 2D textures currently supported");
+                return;
+            }
+            
+            if (this.numberOfArrayElements !== 0) {
+                Tools.Error("texture arrays not currently supported");
+                return;
+            }
+            
+            if (this.numberOfFaces !== facesExpected) {
+                Tools.Error("number of faces expected" + facesExpected + ", but found " + this.numberOfFaces);
+                return;
+            }
+              
+            // we now have a completely validated file, so could use existence of loadType as success
+            // would need to make this more elaborate & adjust checks above to support more than one load type
+            this.loadType = KhronosTextureContainer.COMPRESSED_2D;
+        }
+        
+        // not as fast hardware based, but will probably never need to use
+        public switchEndainness(val : number) : number {
+            return ((val & 0xFF) << 24)
+                 | ((val & 0xFF00) << 8)
+                 | ((val >> 8) & 0xFF00)
+                 | ((val >> 24) & 0xFF);            
+        }
+        
+        /**
+         * It is assumed that the texture has already been created & is currently bound
+         */
+        public uploadLevels(gl: WebGLRenderingContext, loadMipmaps: boolean) : void {
+            switch (this.loadType){
+                case KhronosTextureContainer.COMPRESSED_2D:
+                    this._upload2DCompressedLevels(gl, loadMipmaps);
+                    break;
+                    
+                case KhronosTextureContainer.TEX_2D:
+                case KhronosTextureContainer.COMPRESSED_3D:
+                case KhronosTextureContainer.TEX_3D:
+            }
+        }
+        
+        private _upload2DCompressedLevels(gl: WebGLRenderingContext, loadMipmaps: boolean): void {
+            // initialize width & height for level 1
+            var dataOffset = KhronosTextureContainer.HEADER_LEN + this.bytesOfKeyValueData;
+            var width = this.pixelWidth;
+            var height = this.pixelHeight;
+            
+            var mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
+            for (var level = 0; level < mipmapCount; level++) {
+                var imageSize = new Int32Array(this.arrayBuffer, dataOffset, 1)[0]; // size per face, since not supporting array cubemaps
+                
+                for (var face = 0; face < this.numberOfFaces; face++) {
+                    var sampler = this.numberOfFaces === 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
+                    var byteArray = new Uint8Array(this.arrayBuffer, dataOffset + 4, imageSize);
+                    gl.compressedTexImage2D(sampler, level, this.glInternalFormat, width, height, 0, byteArray);
+
+                    dataOffset += imageSize + 4; // size of the image + 4 for the imageSize field
+                    dataOffset += 3 - ((imageSize + 3) % 4); // add padding for odd sized image
+                }
+                width = Math.max(1.0, width * 0.5);
+                height = Math.max(1.0, height * 0.5);
+            }
+        }
+    }
+} 

+ 353 - 241
src/babylon.engine.ts

@@ -1,8 +1,8 @@
 module BABYLON {
-    var compileShader = (gl: WebGLRenderingContext, source: string, type: string, defines: string): WebGLShader => {
+    var compileShader = (gl: WebGLRenderingContext, source: string, type: string, defines: string, shaderVersion: string): WebGLShader => {
         var shader = gl.createShader(type === "vertex" ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
 
-        gl.shaderSource(shader, (defines ? defines + "\n" : "") + source);
+        gl.shaderSource(shader, shaderVersion + (defines ? defines + "\n" : "") + source);
         gl.compileShader(shader);
 
         if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
@@ -181,10 +181,12 @@
         public etc1: any; //WEBGL_compressed_texture_etc1;
         public etc2: any; //WEBGL_compressed_texture_etc;
         public astc: any; //WEBGL_compressed_texture_astc;
+        public atc: any; //WEBGL_compressed_texture_atc;
         public textureFloat: boolean;
+        public vertexArrayObject: boolean;
         public textureAnisotropicFilterExtension: EXT_texture_filter_anisotropic;
         public maxAnisotropy: number;
-        public instancedArrays: ANGLE_instanced_arrays;
+        public instancedArrays: boolean;
         public uintIndices: boolean;
         public highPrecisionShaderSupported: boolean;
         public fragmentDepthSupported: boolean;
@@ -200,6 +202,7 @@
     export interface EngineOptions extends WebGLContextAttributes {
         limitDeviceRatio?: number;
         autoEnableWebVR?: boolean;
+        disableWebGL2Support?: boolean;
     }
 
     /**
@@ -424,7 +427,7 @@
         public _gl: WebGLRenderingContext;
         private _renderingCanvas: HTMLCanvasElement;
         private _windowIsBackground = false;
-        private _webGLVersion = "1.0";
+        private _webGLVersion = 1.0;
 
         private _badOS = false;
 
@@ -476,6 +479,7 @@
         private _compiledEffects = {};
         private _vertexAttribArraysEnabled: boolean[] = [];
         private _cachedViewport: Viewport;
+        private _cachedVertexArrayObject: WebGLVertexArrayObject;
         private _cachedVertexBuffers: any;
         private _cachedIndexBuffer: WebGLBuffer;
         private _cachedEffectForVertexBuffers: Effect;
@@ -527,19 +531,17 @@
                 options.preserveDrawingBuffer = false;
             }
 
-            // Checks if some of the format renders first to allow the use of webgl inspector.
-            var renderToFullFloat = this._canRenderToFloatTexture();
-            var renderToHalfFloat = this._canRenderToHalfFloatTexture();
-
             // GL
-            // try {
-            //    this._gl = <WebGLRenderingContext>(canvas.getContext("webgl2", options) || canvas.getContext("experimental-webgl2", options));
-            //    if (this._gl) {
-            //        this._webGLVersion = "2.0";
-            //    }
-            // } catch (e) {
-            //    // Do nothing
-            // }
+            if (!options.disableWebGL2Support) {
+                try {
+                this._gl = <WebGLRenderingContext>(canvas.getContext("webgl2", options) || canvas.getContext("experimental-webgl2", options));
+                if (this._gl) {
+                    this._webGLVersion = 2.0;
+                }
+                } catch (e) {
+                // Do nothing
+                }  
+            }
 
             if (!this._gl) {
                 if (!canvas) {
@@ -556,6 +558,10 @@
                 throw new Error("WebGL not supported");
             }
 
+            // Checks if some of the format renders first to allow the use of webgl inspector.
+            var renderToFullFloat = this._canRenderToFloatTexture();
+            var renderToHalfFloat = this._canRenderToHalfFloatTexture();
+            
             this._onBlur = () => {
                 this._windowIsBackground = true;
             };
@@ -602,40 +608,74 @@
             }
 
             // Extensions
-            this._caps.standardDerivatives = (this._gl.getExtension('OES_standard_derivatives') !== null);
+            this._caps.standardDerivatives = this._webGLVersion > 1 || (this._gl.getExtension('OES_standard_derivatives') !== null);
             
-            this._caps.astc  = this._gl.getExtension('WEBGL_compressed_texture_astc');
-            this._caps.s3tc  = this._gl.getExtension('WEBGL_compressed_texture_s3tc');
-            this._caps.pvrtc = this._gl.getExtension('WEBGL_compressed_texture_pvrtc') || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc'); // 2nd is what iOS reports
-            this._caps.etc1  = this._gl.getExtension('WEBGL_compressed_texture_etc1');
-            this._caps.etc2  = this._gl.getExtension('WEBGL_compressed_texture_etc') || this._gl.getExtension('WEBGL_compressed_texture_es3_0'); // first is the final name, found hardware using 2nd
+            this._caps.astc  = this._gl.getExtension('WEBGL_compressed_texture_astc' ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_astc' );
+            this._caps.s3tc  = this._gl.getExtension('WEBGL_compressed_texture_s3tc' ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc' );
+            this._caps.pvrtc = this._gl.getExtension('WEBGL_compressed_texture_pvrtc') || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc');
+            this._caps.etc1  = this._gl.getExtension('WEBGL_compressed_texture_etc1' ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_etc1' );
+            this._caps.etc2  = this._gl.getExtension('WEBGL_compressed_texture_etc'  ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_etc'  ) ||
+                               this._gl.getExtension('WEBGL_compressed_texture_es3_0'); // also a requirement of OpenGL ES 3
+            this._caps.atc   = this._gl.getExtension('WEBGL_compressed_texture_atc'  ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_atc'  );
             
-            this._caps.textureFloat = (this._gl.getExtension('OES_texture_float') !== null);
+            this._caps.textureFloat = this._webGLVersion > 1 || (this._gl.getExtension('OES_texture_float') !== null);
             this._caps.textureAnisotropicFilterExtension = this._gl.getExtension('EXT_texture_filter_anisotropic') || this._gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || this._gl.getExtension('MOZ_EXT_texture_filter_anisotropic');
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
-            this._caps.instancedArrays = this._gl.getExtension('ANGLE_instanced_arrays');
-            this._caps.uintIndices = this._gl.getExtension('OES_element_index_uint') !== null;
-            this._caps.fragmentDepthSupported = this._gl.getExtension('EXT_frag_depth') !== null;
+            this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
+            this._caps.fragmentDepthSupported = this._webGLVersion > 1 || this._gl.getExtension('EXT_frag_depth') !== null;
             this._caps.highPrecisionShaderSupported = true;
-            this._caps.drawBuffersExtension = this._gl.getExtension('WEBGL_draw_buffers');
+            this._caps.drawBuffersExtension = this._webGLVersion > 1 || this._gl.getExtension('WEBGL_draw_buffers');
             this._caps.textureFloatLinearFiltering = this._gl.getExtension('OES_texture_float_linear');
-            this._caps.textureLOD = this._gl.getExtension('EXT_shader_texture_lod');
+            this._caps.textureLOD = this._webGLVersion > 1 || this._gl.getExtension('EXT_shader_texture_lod');
             this._caps.textureFloatRender = renderToFullFloat;
 
-            this._caps.textureHalfFloat = (this._gl.getExtension('OES_texture_half_float') !== null);
-            this._caps.textureHalfFloatLinearFiltering = this._gl.getExtension('OES_texture_half_float_linear');
+            this._caps.textureHalfFloat = this._webGLVersion > 1 || (this._gl.getExtension('OES_texture_half_float') !== null);
+            this._caps.textureHalfFloatLinearFiltering = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_half_float_linear');
             this._caps.textureHalfFloatRender = renderToHalfFloat;
+
+            // Vertex array object 
+            if ( this._webGLVersion > 1) {
+                this._caps.vertexArrayObject = true;
+            } else{
+                var vertexArrayObjectExtension = this._gl.getExtension('OES_vertex_array_object');
+
+                if (vertexArrayObjectExtension != null) {
+                    this._caps.vertexArrayObject =  true;
+                    this._gl.createVertexArray = vertexArrayObjectExtension.createVertexArrayOES.bind(vertexArrayObjectExtension);
+                    this._gl.bindVertexArray = vertexArrayObjectExtension.bindVertexArrayOES.bind(vertexArrayObjectExtension);
+                    this._gl.deleteVertexArray = vertexArrayObjectExtension.deleteVertexArrayOES.bind(vertexArrayObjectExtension);
+                } else{
+                    this._caps.vertexArrayObject = false;
+                }
+            }
+            // Instances count            
+            if ( this._webGLVersion > 1) {
+                this._caps.instancedArrays = true;
+            } else{
+                var instanceExtension = <ANGLE_instanced_arrays>this._gl.getExtension('ANGLE_instanced_arrays');
+
+                if (instanceExtension != null) {
+                    this._caps.instancedArrays =  true;
+                    this._gl.drawArraysInstanced = instanceExtension.drawArraysInstancedANGLE.bind(instanceExtension);
+                    this._gl.drawElementsInstanced = instanceExtension.drawElementsInstancedANGLE.bind(instanceExtension);
+                    this._gl.vertexAttribDivisor = instanceExtension.vertexAttribDivisorANGLE.bind(instanceExtension);
+                } else{
+                    this._caps.instancedArrays = false;
+                }
+            }
             
-            // Intelligently add supported commpressed formats in order to check for.
+            // Intelligently add supported compressed formats in order to check for.
             // Check for ASTC support first as it is most powerful and to be very cross platform.
-            // Next PVR & S3, which are probably superior to ETC1/2.  
-            // Likely no hardware which supports both PVR & S3, so order matters little.
-            // ETC2 is newer and handles ETC1, so check for first.
-            if (this._caps.astc ) this.texturesSupported.push('.astc');
-            if (this._caps.s3tc ) this.texturesSupported.push('.dds');
-            if (this._caps.pvrtc) this.texturesSupported.push('.pvr');
-            if (this._caps.etc2 ) this.texturesSupported.push('.etc2');
-            if (this._caps.etc1 ) this.texturesSupported.push('.etc1');
+            // Next PVRTC & DXT, which are probably superior to ETC1/2.  
+            // Likely no hardware which supports both PVR & DXT, so order matters little.
+            // ETC2 is newer and handles ETC1 (no alpha capability), so check for first.
+            // ATC before ETC1, since both old (widely supported), but ATC supports alpha, but ETC1 does not
+            if (this._caps.astc ) this.texturesSupported.push('-astc.ktx');
+            if (this._caps.s3tc ) this.texturesSupported.push('-dxt.ktx');
+            if (this._caps.pvrtc) this.texturesSupported.push('-pvrtc.ktx');
+            if (this._caps.etc2 ) this.texturesSupported.push('-etc2.ktx');
+            if (this._caps.atc  ) this.texturesSupported.push('-atc.ktx');
+            if (this._caps.etc1 ) this.texturesSupported.push('-etc1.ktx');
             
             if (this._gl.getShaderPrecisionFormat) {
                 var highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
@@ -711,7 +751,7 @@
             Tools.Log("Babylon.js engine (v" + Engine.Version + ") launched");
         }
 
-        public get webGLVersion(): string {
+        public get webGLVersion(): number {
             return this._webGLVersion;
         }
 
@@ -1213,6 +1253,7 @@
         private _resetVertexBufferBinding(): void {
             this.bindArrayBuffer(null);
             this._cachedVertexBuffers = null;
+            this._cachedVertexArrayObject = null;
         }
 
         public createVertexBuffer(vertices: number[] | Float32Array): WebGLBuffer {
@@ -1343,6 +1384,68 @@
             }
         }
 
+        private _bindIndexBufferWithCache(indexBuffer: WebGLBuffer): void {            
+            if (this._cachedIndexBuffer !== indexBuffer) {
+                this._cachedIndexBuffer = indexBuffer;
+                this.bindIndexBuffer(indexBuffer);
+                this._uintIndicesCurrentlySet = indexBuffer.is32Bits;
+            }
+        }
+
+        private _bindVertexBuffersAttributes(vertexBuffers: { [key: string]: VertexBuffer; }, effect: Effect, storeInstanceLocationsAndBuffers = false) {
+            var attributes = effect.getAttributesNames();
+
+            this.unbindAllAttributes();
+
+            for (var index = 0; index < attributes.length; index++) {
+                var order = effect.getAttributeLocation(index);
+
+                if (order >= 0) {
+                    var vertexBuffer = vertexBuffers[attributes[index]];
+
+                    if (!vertexBuffer) {
+                        continue;
+                    }
+
+                    this._gl.enableVertexAttribArray(order);
+                    this._vertexAttribArraysEnabled[order] = true;
+
+                    var buffer = vertexBuffer.getBuffer();
+                    this.vertexAttribPointer(buffer, order, vertexBuffer.getSize(), this._gl.FLOAT, false, vertexBuffer.getStrideSize() * 4, vertexBuffer.getOffset() * 4);
+
+                    if (vertexBuffer.getIsInstanced()) {
+                        this._gl.vertexAttribDivisor(order, 1);
+                        if (storeInstanceLocationsAndBuffers) {
+                            this._currentInstanceLocations.push(order);
+                            this._currentInstanceBuffers.push(buffer);
+                        }
+                    }
+                }
+            }
+        }
+
+        public recordVertexArrayObject(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: WebGLBuffer, effect: Effect): WebGLVertexArrayObject {
+            var vao = this._gl.createVertexArray();
+
+            this._gl.bindVertexArray(vao);
+
+            this._bindVertexBuffersAttributes(vertexBuffers, effect, false);
+
+            this._gl.bindVertexArray(null);
+
+            return vao;
+        }
+
+        public bindVertexArrayObject(vertexArrayObject: WebGLVertexArrayObject, indexBuffer: WebGLBuffer): void {
+            if (this._cachedVertexArrayObject !== vertexArrayObject) {
+                this._cachedVertexArrayObject = vertexArrayObject;
+
+                this._gl.bindVertexArray(vertexArrayObject);
+            }
+
+            this._bindIndexBufferWithCache(indexBuffer);
+        }
+
         public bindBuffersDirectly(vertexBuffer: WebGLBuffer, indexBuffer: WebGLBuffer, vertexDeclaration: number[], vertexStrideSize: number, effect: Effect): void {
             if (this._cachedVertexBuffers !== vertexBuffer || this._cachedEffectForVertexBuffers !== effect) {
                 this._cachedVertexBuffers = vertexBuffer;
@@ -1370,11 +1473,7 @@
                 }
             }
 
-            if (this._cachedIndexBuffer !== indexBuffer) {
-                this._cachedIndexBuffer = indexBuffer;
-                this.bindIndexBuffer(indexBuffer);
-                this._uintIndicesCurrentlySet = indexBuffer.is32Bits;
-            }
+            this._bindIndexBufferWithCache(indexBuffer);
         }
 
         public bindBuffers(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: WebGLBuffer, effect: Effect): void {
@@ -1382,40 +1481,10 @@
                 this._cachedVertexBuffers = vertexBuffers;
                 this._cachedEffectForVertexBuffers = effect;
 
-                var attributes = effect.getAttributesNames();
-
-                this.unbindAllAttributes();
-
-                for (var index = 0; index < attributes.length; index++) {
-                    var order = effect.getAttributeLocation(index);
-
-                    if (order >= 0) {
-                        var vertexBuffer = vertexBuffers[attributes[index]];
-
-                        if (!vertexBuffer) {
-                            continue;
-                        }
-
-                        this._gl.enableVertexAttribArray(order);
-                        this._vertexAttribArraysEnabled[order] = true;
-
-                        var buffer = vertexBuffer.getBuffer();
-                        this.vertexAttribPointer(buffer, order, vertexBuffer.getSize(), this._gl.FLOAT, false, vertexBuffer.getStrideSize() * 4, vertexBuffer.getOffset() * 4);
-
-                        if (vertexBuffer.getIsInstanced()) {
-                            this._caps.instancedArrays.vertexAttribDivisorANGLE(order, 1);
-                            this._currentInstanceLocations.push(order);
-                            this._currentInstanceBuffers.push(buffer);
-                        }
-                    }
-                }
+                this._bindVertexBuffersAttributes(vertexBuffers, effect, true);
             }
 
-            if (indexBuffer != null && this._cachedIndexBuffer !== indexBuffer) {
-                this._cachedIndexBuffer = indexBuffer;
-                this.bindIndexBuffer(indexBuffer);
-                this._uintIndicesCurrentlySet = indexBuffer.is32Bits;
-            }
+            this._bindIndexBufferWithCache(indexBuffer);
         }
 
         public unbindInstanceAttributes() {
@@ -1427,7 +1496,7 @@
                     this.bindArrayBuffer(instancesBuffer);
                 }
                 var offsetLocation = this._currentInstanceLocations[i];
-                this._caps.instancedArrays.vertexAttribDivisorANGLE(offsetLocation, 0);
+                this._gl.vertexAttribDivisor(offsetLocation, 0);
             }
             this._currentInstanceBuffers.length = 0;
             this._currentInstanceLocations.length = 0;
@@ -1479,7 +1548,7 @@
                     }
 
                     this.vertexAttribPointer(instancesBuffer, ai.index, ai.attributeSize, ai.attribyteType || this._gl.FLOAT, ai.normalized || false, stride, ai.offset);
-                    this._caps.instancedArrays.vertexAttribDivisorANGLE(ai.index, 1);
+                    this._gl.vertexAttribDivisor(ai.index, 1);
                     this._currentInstanceLocations.push(ai.index);
                     this._currentInstanceBuffers.push(instancesBuffer);
                 }
@@ -1493,7 +1562,7 @@
                     }
 
                     this.vertexAttribPointer(instancesBuffer, offsetLocation, 4, this._gl.FLOAT, false, 64, index * 16);
-                    this._caps.instancedArrays.vertexAttribDivisorANGLE(offsetLocation, 1);
+                    this._gl.vertexAttribDivisor(offsetLocation, 1);
                     this._currentInstanceLocations.push(offsetLocation);
                     this._currentInstanceBuffers.push(instancesBuffer);
                 }
@@ -1515,7 +1584,7 @@
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             if (instancesCount) {
-                this._caps.instancedArrays.drawElementsInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, indexCount, indexFormat, indexStart * mult, instancesCount);
+                this._gl.drawElementsInstanced(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, indexCount, indexFormat, indexStart * mult, instancesCount);
                 return;
             }
 
@@ -1525,10 +1594,10 @@
         public drawPointClouds(verticesStart: number, verticesCount: number, instancesCount?: number): void {
             // Apply states
             this.applyStates();
-            this._drawCalls.addCount(1, false);
+            this._drawCalls.addCount(1, false); 
 
             if (instancesCount) {
-                this._caps.instancedArrays.drawArraysInstancedANGLE(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
+                this._gl.drawArraysInstanced(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
                 return;
             }
 
@@ -1541,7 +1610,7 @@
             this._drawCalls.addCount(1, false);
 
             if (instancesCount) {
-                this._caps.instancedArrays.drawArraysInstancedANGLE(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, verticesStart, verticesCount, instancesCount);
+                this._gl.drawArraysInstanced(useTriangles ? this._gl.TRIANGLES : this._gl.LINES, verticesStart, verticesCount, instancesCount);
                 return;
             }
 
@@ -1591,8 +1660,9 @@
         public createShaderProgram(vertexCode: string, fragmentCode: string, defines: string, context?: WebGLRenderingContext): WebGLProgram {
             context = context || this._gl;
 
-            var vertexShader = compileShader(context, vertexCode, "vertex", defines);
-            var fragmentShader = compileShader(context, fragmentCode, "fragment", defines);
+            var shaderVersion = (this._webGLVersion > 1) ? "#version 300 es\n" : "";
+            var vertexShader = compileShader(context, vertexCode, "vertex", defines, shaderVersion);
+            var fragmentShader = compileShader(context, fragmentCode, "fragment", defines, shaderVersion);
 
             var shaderProgram = context.createProgram();
             context.attachShader(shaderProgram, vertexShader);
@@ -1950,25 +2020,28 @@
             texture.samplingMode = samplingMode;
         }
         /**
-         * Set the compressed texture format to use, based on the formats you have,
-         * the formats supported by the hardware / browser, and those currently implemented
-         * in BJS.
+         * Set the compressed texture format to use, based on the formats you have, and the formats
+         * supported by the hardware / browser.
+         * 
+         * Khronos Texture Container (.ktx) files are used to support this.  This format has the
+         * advantage of being specifically designed for OpenGL.  Header elements directly correspond
+         * to API arguments needed to compressed textures.  This puts the burden on the container
+         * generator to house the arcane code for determining these for current & future formats.
          * 
-         * Note: The result of this call is not taken into account texture is base64 or when
+         * for description see https://www.khronos.org/opengles/sdk/tools/KTX/
+         * for file layout see https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+         * 
+         * Note: The result of this call is not taken into account when a texture is base64 or when
          * using a database / manifest.
          * 
-         * @param {Array<string>} formatsAvailable - Extension names including dot.  Case
-         * and order do not matter.
+         * @param {Array<string>} formatsAvailable- The list of those format families you have created
+         * on your server.  Syntax: '-' + format family + '.ktx'.  (Case and order do not matter.)
+         * 
+         * Current families are astc, dxt, pvrtc, etc2, atc, & etc1.
          * @returns The extension selected.
          */
         public setTextureFormatToUse(formatsAvailable : Array<string>) : string {
             for (var i = 0, len1 = this.texturesSupported.length; i < len1; i++) {
-                // code to allow the formats to be added as they can be developed / hw tested
-                if (this._texturesSupported[i] === '.astc') continue;
-                if (this._texturesSupported[i] === '.pvr' ) continue;
-                if (this._texturesSupported[i] === '.etc1') continue;
-                if (this._texturesSupported[i] === '.etc2') continue;
-                
                 for (var j = 0, len2 = formatsAvailable.length; j < len2; j++) {
                     if (this._texturesSupported[i] === formatsAvailable[j].toLowerCase()) {
                         return this._textureFormatInUse = this._texturesSupported[i];
@@ -1984,6 +2057,7 @@
             var texture = this._gl.createTexture();
 
             var extension: string;
+            var isKTX = false;
             var fromData: any = false;
             if (url.substr(0, 5) === "data:") {
                 fromData = true;
@@ -1995,6 +2069,7 @@
                 if (this._textureFormatInUse && !fromData && !scene.database) {
                     extension = this._textureFormatInUse;
                     url = url.substring(0, lastDot) + this._textureFormatInUse;
+                    isKTX = true;
                 }
             } else {
                 var oldUrl = url;
@@ -2022,40 +2097,44 @@
                 }
             };
             var callback: (arrayBuffer: any) => void;
-            if (isTGA) {
-                callback = (arrayBuffer) => {
-                    var data = new Uint8Array(arrayBuffer);
-
-                    var header = Internals.TGATools.GetTGAHeader(data);
-
-                    prepareWebGLTexture(texture, this._gl, scene, header.width, header.height, invertY, noMipmap, false, () => {
-                        Internals.TGATools.UploadContent(this._gl, data);
-                    }, samplingMode);
-                };
-                if (!(fromData instanceof Array))
-                    Tools.LoadFile(url, arrayBuffer => {
-                        callback(arrayBuffer);
-                    }, null, scene.database, true, onerror);
-                else
-                    callback(buffer);
-
-            } else if (isDDS) {
-                callback = (data) => {
-                    var info = Internals.DDSTools.GetDDSInfo(data);
-
-                    var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap && ((info.width >> (info.mipmapCount - 1)) === 1);
-                    prepareWebGLTexture(texture, this._gl, scene, info.width, info.height, invertY, !loadMipmap, info.isFourCC, () => {
-
-                        Internals.DDSTools.UploadDDSLevels(this._gl, this.getCaps().s3tc, data, info, loadMipmap, 1);
-                    }, samplingMode);
-                };
-
-                if (!(fromData instanceof Array))
-                    Tools.LoadFile(url, data => {
-                        callback(data);
-                    }, null, scene.database, true, onerror);
-                else
-                    callback(buffer);
+            if (isKTX || isTGA || isDDS){
+                if (isKTX) {
+                    callback = (data) => {
+                        var ktx = new Internals.KhronosTextureContainer(data, 1);
+    
+                        prepareWebGLTexture(texture, this._gl, scene, ktx.pixelWidth, ktx.pixelHeight, invertY, false, true, () => {
+                            ktx.uploadLevels(this._gl, !noMipmap);
+                        }, samplingMode);
+                    };
+                } else if (isTGA) {
+                    callback = (arrayBuffer) => {
+                        var data = new Uint8Array(arrayBuffer);
+
+                        var header = Internals.TGATools.GetTGAHeader(data);
+
+                        prepareWebGLTexture(texture, this._gl, scene, header.width, header.height, invertY, noMipmap, false, () => {
+                            Internals.TGATools.UploadContent(this._gl, data);
+                        }, samplingMode);
+                    };
+
+                } else if (isDDS) {
+                    callback = (data) => {
+                        var info = Internals.DDSTools.GetDDSInfo(data);
+
+                        var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap && ((info.width >> (info.mipmapCount - 1)) === 1);
+                        prepareWebGLTexture(texture, this._gl, scene, info.width, info.height, invertY, !loadMipmap, info.isFourCC, () => {
+
+                            Internals.DDSTools.UploadDDSLevels(this._gl, this.getCaps().s3tc, data, info, loadMipmap, 1);
+                        }, samplingMode);
+                    };
+            }
+
+            if (!(fromData instanceof Array))
+                Tools.LoadFile(url, data => {
+                    callback(data);
+                }, null, scene.database, true, onerror);
+            else
+                callback(buffer);
 
             } else {
                 var onload = (img) => {
@@ -2495,10 +2574,41 @@
             texture.references = 1;
             texture.onLoadedCallbacks = [];
 
-            var extension = rootUrl.substr(rootUrl.length - 4, 4).toLowerCase();
-            var isDDS = this.getCaps().s3tc && (extension === ".dds");
+            var isKTX = false;
+            var lastDot = rootUrl.lastIndexOf('.');
+            var extension = rootUrl.substring(lastDot).toLowerCase();
+            if (this._textureFormatInUse && !scene.database) {
+                extension = this._textureFormatInUse;
+                rootUrl = rootUrl.substring(0, lastDot) + this._textureFormatInUse;
+                isKTX = true;
+            }
+            var isDDS = (extension === ".dds");
 
-            if (isDDS) {
+            if (isKTX) {
+                Tools.LoadFile(rootUrl, data => {
+                    var ktx = new Internals.KhronosTextureContainer(data, 6);
+
+                    var loadMipmap = ktx.numberOfMipmapLevels > 1 && !noMipmap;
+
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+                    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
+    
+                    ktx.uploadLevels(this._gl, !noMipmap);
+
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, loadMipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
+                    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);
+
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
+
+                    this.resetTextureCache();
+
+                    texture._width = ktx.pixelWidth;
+                    texture._height = ktx.pixelHeight;
+                    texture.isReady = true;
+                }, null, null, true, onError);
+            } else if (isDDS) {
                 Tools.LoadFile(rootUrl, data => {
                     var info = Internals.DDSTools.GetDDSInfo(data);
 
@@ -3132,126 +3242,128 @@
         }
 
         private _canRenderToFloatTexture(): boolean {
-            try {
-                return this._canRenderToTextureOfType(BABYLON.Engine.TEXTURETYPE_FLOAT, 'OES_texture_float');
-            }
-            catch (e) {
-                return false;
+            if (this._webGLVersion > 1) {
+                return true;
             }
+            return this._canRenderToTextureOfType(BABYLON.Engine.TEXTURETYPE_FLOAT, 'OES_texture_float');
         }
 
-        private _canRenderToHalfFloatTexture(): boolean {            
-            try {
-                return this._canRenderToTextureOfType(BABYLON.Engine.TEXTURETYPE_HALF_FLOAT, 'OES_texture_half_float');
-            }
-            catch (e) {
-                return false;
-            }
+        private _canRenderToHalfFloatTexture(): boolean {       
+            if (this._webGLVersion > 1) {
+                return true;
+            }     
+            return this._canRenderToTextureOfType(BABYLON.Engine.TEXTURETYPE_HALF_FLOAT, 'OES_texture_half_float');
         }
 
         // Thank you : http://stackoverflow.com/questions/28827511/webgl-ios-render-to-floating-point-texture
         private _canRenderToTextureOfType(format: number, extension: string): boolean {
-            var tempcanvas = document.createElement("canvas");
-            tempcanvas.height = 16;
-            tempcanvas.width = 16;
-            var gl = <WebGLRenderingContext>(tempcanvas.getContext("webgl") || tempcanvas.getContext("experimental-webgl"));
-
-            // extension.
-            var ext = gl.getExtension(extension);
-            if (!ext) {
-                return false;
-            }
-
-            // setup GLSL program
-            var vertexCode = `attribute vec4 a_position;
-                void main() {
-                    gl_Position = a_position;
-                }`;
-            var fragmentCode = `precision mediump float;
-                uniform vec4 u_color;
-                uniform sampler2D u_texture;
-
-                void main() {
-                    gl_FragColor = texture2D(u_texture, vec2(0.5, 0.5)) * u_color;
-                }`;
-            var program = this.createShaderProgram(vertexCode, fragmentCode, null, gl);
-            gl.useProgram(program);
-
-            // look up where the vertex data needs to go.
-            var positionLocation = gl.getAttribLocation(program, "a_position");
-            var colorLoc = gl.getUniformLocation(program, "u_color");
-
-            // provide texture coordinates for the rectangle.
-            var positionBuffer = gl.createBuffer();
-            gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
-            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-                -1.0, -1.0,
-                1.0, -1.0,
-                -1.0, 1.0,
-                -1.0, 1.0,
-                1.0, -1.0,
-                1.0, 1.0]), gl.STATIC_DRAW);
-            gl.enableVertexAttribArray(positionLocation);
-            gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
-
-            var whiteTex = gl.createTexture();
-            gl.bindTexture(gl.TEXTURE_2D, whiteTex);
-            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 255, 255, 255]));
-
-            var tex = gl.createTexture();
-            gl.bindTexture(gl.TEXTURE_2D, tex);
-            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, getWebGLTextureType(gl, format), null);
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
-
-            var fb = gl.createFramebuffer();
-            gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
-            gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
-
-            var cleanup = () => {
-                gl.deleteProgram(program);
-                gl.disableVertexAttribArray(positionLocation);
-                gl.deleteBuffer(positionBuffer);
-                gl.deleteFramebuffer(fb);
-                gl.deleteTexture(whiteTex);
-                gl.deleteTexture(tex);
-            };
-
-            var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
-            if (status !== gl.FRAMEBUFFER_COMPLETE) {
-                Tools.Log("GL Support: can **NOT** render to " + format + " texture");
-                cleanup();
-                return false;
-            }
-
-            // Draw the rectangle.
-            gl.bindTexture(gl.TEXTURE_2D, whiteTex);
-            gl.uniform4fv(colorLoc, <any>[0, 10, 20, 1]);
-            gl.drawArrays(gl.TRIANGLES, 0, 6);
+            try {
+                var tempcanvas = document.createElement("canvas");
+                tempcanvas.height = 16;
+                tempcanvas.width = 16;
+                var gl = <WebGLRenderingContext>(tempcanvas.getContext("webgl") || tempcanvas.getContext("experimental-webgl"));
+
+                // extension.
+                var ext = gl.getExtension(extension);
+                if (!ext) {
+                    return false;
+                }
 
-            gl.bindTexture(gl.TEXTURE_2D, tex);
-            gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+                // setup GLSL program
+                var vertexCode = `attribute vec4 a_position;
+                    void main() {
+                        gl_Position = a_position;
+                    }`;
+                var fragmentCode = `precision mediump float;
+                    uniform vec4 u_color;
+                    uniform sampler2D u_texture;
+
+                    void main() {
+                        gl_FragColor = texture2D(u_texture, vec2(0.5, 0.5)) * u_color;
+                    }`;
+                var program = this.createShaderProgram(vertexCode, fragmentCode, null, gl);
+                gl.useProgram(program);
+
+                // look up where the vertex data needs to go.
+                var positionLocation = gl.getAttribLocation(program, "a_position");
+                var colorLoc = gl.getUniformLocation(program, "u_color");
+
+                // provide texture coordinates for the rectangle.
+                var positionBuffer = gl.createBuffer();
+                gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+                    -1.0, -1.0,
+                    1.0, -1.0,
+                    -1.0, 1.0,
+                    -1.0, 1.0,
+                    1.0, -1.0,
+                    1.0, 1.0]), gl.STATIC_DRAW);
+                gl.enableVertexAttribArray(positionLocation);
+                gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
+
+                var whiteTex = gl.createTexture();
+                gl.bindTexture(gl.TEXTURE_2D, whiteTex);
+                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 255, 255, 255]));
+
+                var tex = gl.createTexture();
+                gl.bindTexture(gl.TEXTURE_2D, tex);
+                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, getWebGLTextureType(gl, format), null);
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+
+                var fb = gl.createFramebuffer();
+                gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+                gl.viewport(0, 0, 1, 1);
+
+                var cleanup = () => {
+                    gl.deleteProgram(program);
+                    gl.disableVertexAttribArray(positionLocation);
+                    gl.deleteBuffer(positionBuffer);
+                    gl.deleteFramebuffer(fb);
+                    gl.deleteTexture(whiteTex);
+                    gl.deleteTexture(tex);
+                };
 
-            gl.clearColor(1, 0, 0, 1);
-            gl.clear(gl.COLOR_BUFFER_BIT);
+                var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
+                if (status !== gl.FRAMEBUFFER_COMPLETE) {
+                    Tools.Log("GL Support: can **NOT** render to " + format + " texture");
+                    cleanup();
+                    return false;
+                }
 
-            gl.uniform4fv(colorLoc, <any>[0, 1 / 10, 1 / 20, 1]);
-            gl.drawArrays(gl.TRIANGLES, 0, 6);
+                // Draw the rectangle.
+                gl.bindTexture(gl.TEXTURE_2D, whiteTex);
+                gl.uniform4fv(colorLoc, <any>[0, 10, 20, 1]);
+                gl.drawArrays(gl.TRIANGLES, 0, 6);
+
+                gl.bindTexture(gl.TEXTURE_2D, tex);
+                gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+                gl.clearColor(1, 0, 0, 1);
+                gl.clear(gl.COLOR_BUFFER_BIT);
+
+                gl.uniform4fv(colorLoc, <any>[0, 1 / 10, 1 / 20, 1]);
+                gl.drawArrays(gl.TRIANGLES, 0, 6);
+
+                var pixel = new Uint8Array(4);
+                gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
+                if (pixel[0] !== 0 ||
+                    pixel[1] < 248 ||
+                    pixel[2] < 248 ||
+                    pixel[3] < 254) {
+                    BABYLON.Tools.Log("GL Support: Was not able to actually render to " + format + " texture");
+                    cleanup();
+                    return false;
+                }
 
-            var pixel = new Uint8Array(4);
-            gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
-            if (pixel[0] !== 0 ||
-                pixel[1] < 248 ||
-                pixel[2] < 248 ||
-                pixel[3] < 254) {
-                BABYLON.Tools.Log("GL Support: Was not able to actually render to " + format + " texture");
+                // Succesfully rendered to "format" texture.
                 cleanup();
+                return true;
+            }
+            catch (e) {
                 return false;
             }
-
-            // Succesfully rendered to "format" texture.
-            cleanup();
-            return true;
         }
 
         // Statics

+ 14 - 0
src/babylon.mixins.ts

@@ -27,6 +27,20 @@ interface Window {
     msURL: any;
 }
 
+interface WebGLVertexArrayObject {
+
+}
+
+interface WebGLRenderingContext {
+    drawArraysInstanced(mode: number, first: number, count: number, primcount: number): void;
+    drawElementsInstanced(mode: number, count: number, type: number, offset: number, primcount: number): void;
+    vertexAttribDivisor(index: number, divisor: number): void;
+
+    createVertexArray(): any;
+    bindVertexArray(vao: WebGLVertexArrayObject): void;
+    deleteVertexArray(vao: WebGLVertexArrayObject): void;
+}
+
 interface AudioContext extends EventTarget {
     decodeAudioData(audioData: ArrayBuffer, successCallback: DecodeSuccessCallback, errorCallback?: any): void;
 }