Browse Source

Merge remote-tracking branch 'BabylonJS/master'

MackeyK24 8 years ago
parent
commit
91fef2502f
36 changed files with 3774 additions and 2180 deletions
  1. 36 6
      .vscode/launch.json
  2. 1 0
      Babylon.csproj
  3. 59 0
      Playground/scripts/instanced bones.js
  4. 2 1
      Playground/scripts/scripts.txt
  5. 3 1
      Tools/Gulp/config.json
  6. 302 0
      canvas2D/src/Engine/babylon.atlasPicture.ts
  7. 7 1
      canvas2D/src/Engine/babylon.prim2dBase.ts
  8. 5 2
      canvas2D/src/Engine/babylon.renderablePrim2d.ts
  9. 163 93
      canvas2D/src/Engine/babylon.sprite2d.ts
  10. 51 1
      canvas2D/src/shaders/sprite2d.fragment.fx
  11. 31 8
      canvas2D/src/shaders/sprite2d.vertex.fx
  12. 23 23
      dist/preview release/babylon.core.js
  13. 1430 1359
      dist/preview release/babylon.d.ts
  14. 37 37
      dist/preview release/babylon.js
  15. 406 180
      dist/preview release/babylon.max.js
  16. 34 34
      dist/preview release/babylon.noworker.js
  17. 185 20
      dist/preview release/canvas2D/babylon.canvas2d.d.ts
  18. 397 136
      dist/preview release/canvas2D/babylon.canvas2d.js
  19. 10 10
      dist/preview release/canvas2D/babylon.canvas2d.min.js
  20. 2 2
      dist/preview release/inspector/babylon.inspector.bundle.js
  21. 7 1
      dist/preview release/inspector/babylon.inspector.js
  22. 2 2
      dist/preview release/inspector/babylon.inspector.min.js
  23. 3 1
      dist/preview release/loaders/babylon.glTFFileLoader.js
  24. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  25. 1 0
      dist/preview release/what's new.md
  26. 7 1
      inspector/src/tabs/StatsTab.ts
  27. 45 7
      src/Materials/babylon.effect.ts
  28. 5 0
      src/Materials/babylon.pbrMaterial.ts
  29. 5 0
      src/Materials/babylon.standardMaterial.ts
  30. 1 1
      src/Math/babylon.math.ts
  31. 1 0
      src/Mesh/babylon.abstractMesh.ts
  32. 0 8
      src/Mesh/babylon.geometry.ts
  33. 3 3
      src/Shaders/volumetricLightScattering.fragment.fx
  34. 142 0
      src/Tools/babylon.khronosTextureContainer.ts
  35. 353 241
      src/babylon.engine.ts
  36. 14 0
      src/babylon.mixins.ts

+ 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" 
+            ]
         }
     ]
 }

+ 1 - 0
Babylon.csproj

@@ -173,6 +173,7 @@
     <TypeScriptCompile Include="src\Tools\babylon.decorators.ts" />
     <TypeScriptCompile Include="src\Tools\babylon.dynamicFloatArray.ts" />
     <TypeScriptCompile Include="src\Tools\babylon.earcut.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.khronosTextureContainer.ts" />
     <TypeScriptCompile Include="src\Tools\babylon.observable.ts" />
     <TypeScriptCompile Include="src\Tools\babylon.rectPackingMap.ts" />
     <TypeScriptCompile Include="src\Tools\babylon.stringDictionary.ts" />

+ 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

+ 3 - 1
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",
@@ -479,6 +480,7 @@
         "../../canvas2D/src/Engine/babylon.rectangle2d.ts",
         "../../canvas2D/src/Engine/babylon.ellipse2d.ts",
         "../../canvas2D/src/Engine/babylon.sprite2d.ts",
+        "../../canvas2D/src/Engine/babylon.atlasPicture.ts",
         "../../canvas2D/src/Engine/babylon.text2d.ts",
         "../../canvas2D/src/Engine/babylon.lines2d.ts",
         "../../canvas2D/src/Engine/babylon.canvas2d.ts",
@@ -491,7 +493,7 @@
         "../../canvas2D/src/GUI/babylon.gui.label.ts",
         "../../canvas2D/src/GUI/babylon.gui.button.ts",
         "../../canvas2D/src/lib.d.ts"
-        ],
+      ],
       "shaderFiles": [
           "../../canvas2D/src/shaders/**.fx", "!../../canvas2D/src/shaders/**.js.fx"
       ],

+ 302 - 0
canvas2D/src/Engine/babylon.atlasPicture.ts

@@ -0,0 +1,302 @@
+module BABYLON {
+    /**
+     * Interface to create your own Loader of Atlas Data file.
+     * Call the AtlasPictureInfoFactory.addLoader to addd your loader instance
+     */
+    export interface IAtlasLoader {
+        loadFile(content: any): { api: AtlasPictureInfo, errorMsg: string, errorCode: number };
+    }
+
+    // Proxy Class for the TexturePacker's JSON Array data format
+    interface IFrame {
+        filename: string;
+        frame: { x: number, y: number, w: number, h: number };
+        rotated: boolean;
+        trimmed: boolean;
+        srpiteSourceSize: { x: number, y: number, w: number, h: number };
+        sourceSize: { x: number, y: number, w: number, h: number };
+    }
+
+    // Proxy Class for the TexturePacker's JSON Array data format
+    interface IMeta {
+        app: string;
+        version: string;
+        image: string;
+        format: string;
+        size: { w: number, h: number }
+        scale: string;
+        smartupdate: string;
+    }
+
+    // Loader class for the TexturePacker's JSON Array data format
+    class JSONArrayLoader implements IAtlasLoader {
+
+        loadFile(content): { api: AtlasPictureInfo, errorMsg: string, errorCode: number } {
+            let errorMsg: string = null;
+            let errorCode: number = 0;
+            let root = null;
+            let api: AtlasPictureInfo = null;
+            try {
+                try {
+                    root = JSON.parse(content);
+                    let frames = <Array<IFrame>>root.frames;
+                    let meta = <IMeta>root.meta;
+
+                    api = new AtlasPictureInfo();
+                    api.atlasSize = new Size(meta.size.w, meta.size.h);
+                    api.subPictures = new StringDictionary<AtlasSubPictureInfo>();
+
+                    for (let f of frames) {
+                        let aspi = new AtlasSubPictureInfo();
+                        aspi.name = f.filename;
+                        aspi.location = new Vector2(f.frame.x, api.atlasSize.height - (f.frame.y + f.frame.h));
+                        aspi.size = new Size(f.frame.w, f.frame.h);
+
+                        api.subPictures.add(aspi.name, aspi);
+                    }
+
+                } catch (ex1) {
+                    errorMsg = "Invalid JSON file";
+                    errorCode = -1;
+                }                 
+            } catch (ex2) {
+                errorMsg = "Unknown Exception: " + ex2;
+                errorCode = -2;
+            } 
+            return { api: api, errorMsg: errorMsg, errorCode: errorCode };
+        }
+    }
+
+    /**
+     * This class will contains information about a sub picture present in an Atlas Picture.
+     */
+    export class AtlasSubPictureInfo {
+        /**
+         * Name of the SubPicture, generally the filename of the initial picture file.
+         */
+        name: string;
+
+        /**
+         * Location of the bottom/left corner of the sub picture from the bottom/left corner the Atlas Picture
+         */
+        location: Vector2;
+
+        /**
+         * Size in pixel of the sub picture
+         */
+        size: Size;
+    }
+
+    /**
+     * This class represent an Atlas Picture, it contains the information of all the sub pictures and the Texture that stores the bitmap.
+     * You get an instance of this class using methods of the AtlasPictureInfoFactory
+     */
+    export class AtlasPictureInfo {
+        /**
+         * Creates many sprite from the Atlas Picture
+         * @param filterCallback a predicate if true is returned then the corresponding sub picture will be used to create a sprite.
+         * The Predicate has many parameters:
+         *  - index: just an index incremented at each sub picture submitted for Sprite creation
+         *  - name: the sub picture's name
+         *  - aspi: the AtlasSubPictureInfo corresponding to the submitted sub picture
+         *  - settings: the Sprite2D creation settings, you can alter this JSON object but BEWARE, the alterations will be kept for subsequent Sprite2D creations!
+         * @param spriteSettings The Sprite2D settings to use for Sprite creation, this JSON object will be passed to the filterCallback for you to alter it, if needed.
+         */
+        createSprites(filterCallback: (index: number, name: string, aspi: AtlasSubPictureInfo, settings: any) => boolean,
+            spriteSettings: {
+                parent?: Prim2DBase,
+                position?: Vector2,
+                x?: number,
+                y?: number,
+                rotation?: number,
+                size?: Size,
+                scale?: number,
+                scaleX?: number,
+                scaleY?: number,
+                dontInheritParentScale?: boolean,
+                opacity?: number,
+                zOrder?: number,
+                origin?: Vector2,
+                scale9?: Vector4,
+                invertY?: boolean,
+                alignToPixel?: boolean,
+                isVisible?: boolean,
+                isPickable?: boolean,
+                isContainer?: boolean,
+                childrenFlatZOrder?: boolean,
+                marginTop?: number | string,
+                marginLeft?: number | string,
+                marginRight?: number | string,
+                marginBottom?: number | string,
+                margin?: number | string,
+                marginHAlignment?: number,
+                marginVAlignment?: number,
+                marginAlignment?: string,
+                paddingTop?: number | string,
+                paddingLeft?: number | string,
+                paddingRight?: number | string,
+                paddingBottom?: number | string,
+                padding?: string,
+            }): Array<Sprite2D> {
+
+            let res = new Array<Sprite2D>();
+
+            let index = 0;
+            this.subPictures.forEach((k, v) => {
+                if (!filterCallback || filterCallback(index++, k, v, spriteSettings)) {
+                    let s = this.createSprite(k, spriteSettings);
+                    if (s) {
+                        res.push(s);
+                    }
+                }
+            });
+            return res;
+        }
+
+        /**
+         * Create one Sprite from a sub picture
+         * @param subPictureName the name of the sub picture to use
+         * @param spriteSettings the Sprite2D settings to use for the Sprite instance creation
+         */
+        createSprite(subPictureName: string, spriteSettings: {
+            parent?: Prim2DBase,
+            position?: Vector2,
+            x?: number,
+            y?: number,
+            rotation?: number,
+            size?: Size,
+            scale?: number,
+            scaleX?: number,
+            scaleY?: number,
+            dontInheritParentScale?: boolean,
+            opacity?: number,
+            zOrder?: number,
+            origin?: Vector2,
+            scale9?: Vector4,
+            invertY?: boolean,
+            alignToPixel?: boolean,
+            isVisible?: boolean,
+            isPickable?: boolean,
+            isContainer?: boolean,
+            childrenFlatZOrder?: boolean,
+            marginTop?: number | string,
+            marginLeft?: number | string,
+            marginRight?: number | string,
+            marginBottom?: number | string,
+            margin?: number | string,
+            marginHAlignment?: number,
+            marginVAlignment?: number,
+            marginAlignment?: string,
+            paddingTop?: number | string,
+            paddingLeft?: number | string,
+            paddingRight?: number | string,
+            paddingBottom?: number | string,
+            padding?: string,
+        }): Sprite2D {
+            let spi = this.subPictures.get(subPictureName);
+            if (!spi) {
+                return null;
+            }
+            if (!spriteSettings) {
+                spriteSettings = {};
+            }
+            let s = <any>spriteSettings;
+            s.id = subPictureName;
+            s.spriteLocation = spi.location;
+            s.spriteSize = spi.size;
+
+            let sprite = new Sprite2D(this.texture, spriteSettings);
+            return sprite;
+        }
+
+        /**
+         * Size of the Atlas Picture
+         */
+        atlasSize: Size;
+
+        /**
+         * String Dictionary of all the sub pictures, the key is the sub picture's name, the value is the info object
+         */
+        subPictures: StringDictionary<AtlasSubPictureInfo>;
+
+        /**
+         * The Texture associated to the Atlas Picture info
+         */
+        texture: Texture;
+    }
+
+    /**
+     * This if the Factory class containing static method to create Atlas Pictures Info objects or add new loaders
+     */
+    export class AtlasPictureInfoFactory {
+        /**
+         * Add a custom loader
+         * @param fileExtension must be the file extension (without the dot) of the file that is loaded by this loader (e.g.: json)
+         * @param plugin the instance of the loader
+         */
+        public static addLoader(fileExtension: string, plugin: IAtlasLoader) {
+            AtlasPictureInfoFactory._initialize();
+
+            let a = AtlasPictureInfoFactory.plugins.getOrAddWithFactory(fileExtension.toLocaleLowerCase(), () => new Array<IAtlasLoader>());
+            a.push(plugin);
+        }
+
+        /**
+         * Load an Atlas Picture Info object from a data file at a given url and with a given texture
+         * @param texture the texture containing the atlas bitmap
+         * @param url the URL of the Atlas Info data file
+         * @param loaded a callback that will be called when the AtlasPictureInfo object will be loaded and ready
+         * @param error a callback that will be called in case of error
+         */
+        public static loadFromUrl(texture: Texture, url: string, loaded: (api: AtlasPictureInfo) => void, error: (msg: string, code: number) => void = null) {
+            AtlasPictureInfoFactory._initialize();
+
+            var xhr = new XMLHttpRequest();
+            xhr.onreadystatechange = () => {
+                if (xhr.readyState === XMLHttpRequest.DONE) {
+                    if (xhr.status === 200) {
+                        let ext = url.split('.').pop().split(/\#|\?/)[0];
+                        let plugins = AtlasPictureInfoFactory.plugins.get(ext.toLocaleLowerCase());
+                        if (!plugins) {
+                            if (error) {
+                                error("couldn't find a plugin for this file extension", -1);
+                            }
+                            return;
+                        }
+                        for (let p of plugins) {
+                            let ret = p.loadFile(xhr.response);
+                            if (ret) {
+                                if (ret.api && loaded) {
+                                    ret.api.texture = texture;
+                                    loaded(ret.api);
+                                } else if (error) {
+                                    error(ret.errorMsg, ret.errorCode);
+                                }
+                                break;
+                            }
+                        }
+                    } else {
+                        if (error) {
+                            error("Couldn't load file through HTTP Request, HTTP Status " + xhr.status, xhr.status);
+                        }
+                    }
+                }
+            }
+            xhr.open("GET", url, true);
+            xhr.send();
+            return null;
+        }
+
+        private static _initialize() {
+            if (AtlasPictureInfoFactory.plugins !== null) {
+                return;
+            }
+
+            AtlasPictureInfoFactory.plugins = new StringDictionary<Array<IAtlasLoader>>();
+            AtlasPictureInfoFactory.addLoader("json", new JSONArrayLoader());
+
+        }
+
+        private static plugins: StringDictionary<Array<IAtlasLoader>> = null;
+    }
+}

+ 7 - 1
canvas2D/src/Engine/babylon.prim2dBase.ts

@@ -1943,7 +1943,10 @@
          */
         @dynamicLevelProperty(SmartPropertyPrim.SMARTPROPERTYPRIM_PROPCOUNT + 6, pi => Prim2DBase.sizeProperty = pi, false, true)
         public get size(): Size {
+            return this.internalGetSize();
+        }
 
+        protected internalGetSize(): Size {
             if (!this._size || this._size.width == null || this._size.height == null) {
 
                 if (Prim2DBase.boundinbBoxReentrency) {
@@ -1961,11 +1964,14 @@
                 return this._boundingSize;
 
             }
-
             return this._size;
         }
 
         public set size(value: Size) {
+            this.internalSetSize(value);
+        }
+
+        protected internalSetSize(value: Size) {
             this._size = value;
         }
 

+ 5 - 2
canvas2D/src/Engine/babylon.renderablePrim2d.ts

@@ -27,6 +27,9 @@
                     // Only map if there's no category assigned to the instance data or if there's a category and it's in the given list
                     if (!attrib.category || categories.indexOf(attrib.category) !== -1) {
                         let index = effect.getAttributeLocationByName(attrib.attributeName);
+                        if (index === - 1) {
+                            throw new Error(`Attribute ${attrib.attributeName} was not found in Effect: ${effect.name}. It's certainly no longer used in the Effect's Shaders`);
+                        }
                         let iai = new InstancingAttributeInfo();
                         iai.index = index;
                         iai.attributeSize = attrib.size / 4; // attrib.size is in byte and we need to store in "component" (i.e float is 1, vec3 is 3)
@@ -952,8 +955,8 @@
             let w = size.width;
             let h = size.height;
             let invZBias = 1 / zBias;
-            let tx = new Vector4(t.m[0] * rgScale.x * 2 / w, t.m[4] * 2 / w, 0/*t.m[8]*/, ((t.m[12] + offX) * rgScale.x * 2 / w) - 1);
-            let ty = new Vector4(t.m[1] * 2 / h, t.m[5] * rgScale.y * 2 / h, 0/*t.m[9]*/, ((t.m[13] + offY) * rgScale.y * 2 / h) - 1);
+            let tx = new Vector4(t.m[0] * rgScale.x * 2 / w, t.m[4] * rgScale.x * 2 / w, 0/*t.m[8]*/, ((t.m[12] + offX) * rgScale.x * 2 / w) - 1);
+            let ty = new Vector4(t.m[1] * rgScale.y * 2 / h, t.m[5] * rgScale.y * 2 / h, 0/*t.m[9]*/, ((t.m[13] + offY) * rgScale.y * 2 / h) - 1);
 
             if (!this.applyActualScaleOnTransform()) {
                 t.m[0] = tx.x, t.m[4] = tx.y, t.m[12] = tx.w;

+ 163 - 93
canvas2D/src/Engine/babylon.sprite2d.ts

@@ -85,51 +85,6 @@
         }
     }
 
-    export class Sprite2DInstanceData extends InstanceDataBase {
-        constructor(partId: number) {
-            super(partId, 1);
-        }
-
-        @instanceData()
-        get topLeftUV(): Vector2 {
-            return null;
-        }
-        set topLeftUV(value: Vector2) {
-        }
-
-        @instanceData()
-        get sizeUV(): Vector2 {
-            return null;
-        }
-        set sizeUV(value: Vector2) {
-        }
-
-        @instanceData()
-        get scaleFactor(): Vector2 {
-            return null;
-        }
-        set scaleFactor(value: Vector2) {
-        }
-
-        @instanceData()
-        get textureSize(): Vector2 {
-            return null;
-        }
-        set textureSize(value: Vector2) {
-        }
-
-        // 3 floats being:
-        // - x: frame number to display
-        // - y: invertY setting
-        // - z: alignToPixel setting
-        @instanceData()
-        get properties(): Vector3 {
-            return null;
-        }
-        set properties(value: Vector3) {
-        }
-    }
-
     @className("Sprite2D", "BABYLON")
     /**
      * Primitive that displays a Sprite/Picture
@@ -137,13 +92,16 @@
     export class Sprite2D extends RenderablePrim2D {
         static SPRITE2D_MAINPARTID = 1;
 
+        static SHAPE2D_CATEGORY_SCALE9 = "Scale9";
+
         public static textureProperty: Prim2DPropInfo;
         public static useAlphaFromTextureProperty: Prim2DPropInfo;
         public static actualSizeProperty: Prim2DPropInfo;
+        public static spriteSizeProperty: Prim2DPropInfo;
         public static spriteLocationProperty: Prim2DPropInfo;
         public static spriteFrameProperty: Prim2DPropInfo;
         public static invertYProperty: Prim2DPropInfo;
-        public static spriteScaleFactorProperty: Prim2DPropInfo;
+        public static spriteScale9Property: Prim2DPropInfo;
 
         @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 1, pi => Sprite2D.textureProperty = pi)
         /**
@@ -174,6 +132,19 @@
             this._updateRenderMode();
         }
 
+        public get size(): Size {
+            if (this._size == null) {
+                return this.spriteSize;
+            }
+            return this.internalGetSize();
+        }
+
+        public set size(value: Size) {
+            this._useSize = value != null;
+            this.internalSetSize(value);
+            this._updateSpriteScaleFactor();
+        }
+
         @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 3, pi => Sprite2D.actualSizeProperty = pi, false, true)
         /**
          * Get/set the actual size of the sprite to display
@@ -189,19 +160,32 @@
             this._actualSize = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, pi => Sprite2D.spriteLocationProperty = pi)
+        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 4, pi => Sprite2D.spriteSizeProperty = pi)
+        /**
+         * Get/set the sprite location (in pixels) in the texture
+         */
+        public get spriteSize(): Size {
+            return this._spriteSize;
+        }
+
+        public set spriteSize(value: Size) {
+            this._spriteSize = value;
+            this._updateSpriteScaleFactor();
+        }
+
+        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 5, pi => Sprite2D.spriteLocationProperty = pi)
         /**
          * Get/set the sprite location (in pixels) in the texture
          */
         public get spriteLocation(): Vector2 {
-            return this._location;
+            return this._spriteLocation;
         }
 
         public set spriteLocation(value: Vector2) {
-            this._location = value;
+            this._spriteLocation = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 5, pi => Sprite2D.spriteFrameProperty = pi)
+        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 6, pi => Sprite2D.spriteFrameProperty = pi)
         /**
          * Get/set the sprite frame to display.
          * The frame number is just an offset applied horizontally, based on the sprite's width. it does not wrap, all the frames must be on the same line.
@@ -214,7 +198,7 @@
             this._spriteFrame = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 6, pi => Sprite2D.invertYProperty = pi)
+        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 7, pi => Sprite2D.invertYProperty = pi)
         /**
          * Get/set if the sprite texture coordinates should be inverted on the Y axis
          */
@@ -226,42 +210,25 @@
             this._invertY = value;
         }
 
-        @instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 7, pi => Sprite2D.spriteScaleFactorProperty = pi)
+        @modelLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 8, pi => Sprite2D.spriteScale9Property = pi)
         /**
-         * Get/set the sprite location (in pixels) in the texture
+         * Get/set the texture that contains the sprite to display
          */
-        public get spriteScaleFactor(): Vector2 {
-            return this._spriteScaleFactor;
+        public get isScale9(): boolean {
+            return this._scale9!==null;
         }
 
-        public set spriteScaleFactor(value: Vector2) {
-            this._spriteScaleFactor = value;
-        }
+        //@instanceLevelProperty(RenderablePrim2D.RENDERABLEPRIM2D_PROPCOUNT + 7, pi => Sprite2D.spriteScaleFactorProperty = pi)
+        ///**
+        // * Get/set the sprite location (in pixels) in the texture
+        // */
+        //public get spriteScaleFactor(): Vector2 {
+        //    return this._spriteScaleFactor;
+        //}
 
-        /**
-         * Sets the scale of the sprite using a BABYLON.Size(w,h).
-         * Keeps proportion by taking the maximum of the two scale for x and y.
-         * @param {Size} size Size(width,height)
-         */
-        public scaleToSize(size: Size) {
-            var baseSize = this.size;
-            if (baseSize == null || !this.texture.isReady()) {
-                // we're probably at initiation of the scene, size is not set
-                if (this.texture.isReady()) {
-                    baseSize = <Size>this.texture.getBaseSize();
-                }
-                else {
-                    // the texture is not ready, wait for it to load before calling scaleToSize again
-                    var thisObject = <Sprite2D>this;
-                    this.texture.onLoadObservable.add(function () {
-                            thisObject.scaleToSize(size); 
-                        });
-                    return;
-                }
-            }
-            
-            this.scale = Math.max(size.height / baseSize.height, size.width / baseSize.width);
-        }
+        //public set spriteScaleFactor(value: Vector2) {
+        //    this._spriteScaleFactor = value;
+        //}
 
         /**
          * Get/set if the sprite rendering should be aligned to the target rendering device pixel or not
@@ -295,6 +262,10 @@
             return true;
         }
 
+        public get isSizeAuto(): boolean {
+            return this.size == null;
+        }
+
         /**
          * Create an 2D Sprite primitive
          * @param texture the texture that stores the sprite to render
@@ -305,13 +276,15 @@
          * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
+         * - size: the size of the sprite displayed in the canvas, if not specified the spriteSize will be used
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - zOrder: override the zOrder with the specified value
          * - origin: define the normalized origin point location, default [0.5;0.5]
-         * - spriteSize: the size of the sprite (in pixels), if null the size of the given texture will be used, default is null.
+         * - spriteSize: the size of the sprite (in pixels) as it is stored in the texture, if null the size of the given texture will be used, default is null.
          * - spriteLocation: the location (in pixels) in the texture of the top/left corner of the Sprite to display, default is null (0,0)
-         * - spriteScaleFactor: say you want to display a sprite twice as big as its bitmap which is 64,64, you set the spriteSize to 128,128 and have to set the spriteScaleFactory to 0.5,0.5 in order to address only the 64,64 pixels of the bitmaps. Default is 1,1.
+         * - spriteScaleFactor: DEPRECATED. Old behavior: say you want to display a sprite twice as big as its bitmap which is 64,64, you set the spriteSize to 128,128 and have to set the spriteScaleFactory to 0.5,0.5 in order to address only the 64,64 pixels of the bitmaps. Default is 1,1.
+         * - scale9: draw the sprite as a Scale9 sprite, see http://yannickloriot.com/2013/03/9-patch-technique-in-cocos2d/ for more info. x, y, w, z are left, bottom, right, top coordinate of the resizable box
          * - invertY: if true the texture Y will be inverted, default is false.
          * - alignToPixel: if true the sprite's texels will be aligned to the rendering viewport pixels, ensuring the best rendering quality but slow animations won't be done as smooth as if you set false. If false a texel could lies between two pixels, being blended by the texture sampling mode you choose, the rendering result won't be as good, but very slow animation will be overall better looking. Default is true: content will be aligned.
          * - isVisible: true if the sprite must be visible, false for hidden. Default is true.
@@ -341,6 +314,7 @@
             x                     ?: number,
             y                     ?: number,
             rotation              ?: number,
+            size                  ?: Size,
             scale                 ?: number,
             scaleX                ?: number,
             scaleY                ?: number,
@@ -351,6 +325,7 @@
             spriteSize            ?: Size,
             spriteLocation        ?: Vector2,
             spriteScaleFactor     ?: Vector2,
+            scale9                ?: Vector4,
             invertY               ?: boolean,
             alignToPixel          ?: boolean,
             isVisible             ?: boolean,
@@ -381,31 +356,37 @@
             this.texture = texture;
             this.texture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.texture.wrapV = Texture.CLAMP_ADDRESSMODE;
-            this.size = (settings.spriteSize!=null) ? settings.spriteSize.clone() : null;
+            this._useSize = false;
+            this.spriteSize = (settings.spriteSize!=null) ? settings.spriteSize.clone() : null;
             this.spriteLocation = (settings.spriteLocation!=null) ? settings.spriteLocation.clone() : new Vector2(0, 0);
-            this.spriteScaleFactor = (settings.spriteScaleFactor!=null) ? settings.spriteScaleFactor : new Vector2(1, 1);
+            if (settings.size != null) {
+                this.size = settings.size;
+            }
             this.spriteFrame = 0;
             this.invertY = (settings.invertY == null) ? false : settings.invertY;
             this.alignToPixel = (settings.alignToPixel == null) ? true : settings.alignToPixel;
             this.useAlphaFromTexture = true;
+            this._scale9 = (settings.scale9 != null) ? settings.scale9.clone() : null;
 
             // If the user doesn't set a size, we'll use the texture's one, but if the texture is not loading, we HAVE to set a temporary dummy size otherwise the positioning engine will switch the marginAlignement to stretch/stretch, and WE DON'T WANT THAT.
             // The fucking delayed texture sprite bug is fixed!
             if (settings.spriteSize == null) {
-                this.size = new Size(10, 10);
+                this.spriteSize = new Size(10, 10);
             }
 
             if (settings.spriteSize == null || !texture.isReady()) {
                 if (texture.isReady()) {
                     let s = texture.getBaseSize();
-                    this.size = new Size(s.width, s.height);
+                    this.spriteSize = new Size(s.width, s.height);
+                    this._updateSpriteScaleFactor();
                 } else {
 
                     texture.onLoadObservable.add(() => {
                         if (settings.spriteSize == null) {
                             let s = texture.getBaseSize();
-                        this.size = new Size(s.width, s.height);
+                            this.spriteSize = new Size(s.width, s.height);
                         }
+                        this._updateSpriteScaleFactor();
                         this._positioningDirty();
                         this._setLayoutDirty();
                         this._instanceDirtyFlags |= Prim2DBase.originProperty.flagId | Sprite2D.textureProperty.flagId;  // To make sure the sprite is issued again for render
@@ -453,6 +434,18 @@
             return renderCache;
         }
 
+        protected getUsedShaderCategories(dataPart: InstanceDataBase): string[] {
+            var cat = super.getUsedShaderCategories(dataPart);
+
+            if (dataPart.id === Sprite2D.SPRITE2D_MAINPARTID) {
+                let useScale9 = this._scale9 != null;
+                if (useScale9) {
+                    cat.push(Sprite2D.SHAPE2D_CATEGORY_SCALE9);
+                }
+            }
+            return cat;
+        }
+
         protected createInstanceDataParts(): InstanceDataBase[] {
             return [new Sprite2DInstanceData(Sprite2D.SPRITE2D_MAINPARTID)];
         }
@@ -487,11 +480,14 @@
                     d.properties = Vector3.Zero();
                     d.textureSize = Vector2.Zero();
                     d.scaleFactor = Vector2.Zero();
+                    if (this.isScale9) {
+                        d.scale9 = Vector4.Zero();
+                    }
                 } else {
                     let ts = this.texture.getBaseSize();
+                    let ss = this.spriteSize;
                     let sl = this.spriteLocation;
-                    let ss = this.actualSize;
-                    let ssf = this.spriteScaleFactor;
+                    let ssf = this.actualScale;
                     d.topLeftUV = new Vector2(sl.x / ts.width, sl.y / ts.height);
                     let suv = new Vector2(ss.width / ts.width, ss.height / ts.height);
                     d.sizeUV = suv;
@@ -503,6 +499,12 @@
                     d.properties = Sprite2D._prop;
 
                     d.textureSize = new Vector2(ts.width, ts.height);
+
+                    let scale9 = this._scale9;
+                    if (scale9 != null) {
+                        let normalizedScale9 = new Vector4(scale9.x * suv.x / ss.width, scale9.y * suv.y / ss.height, scale9.z * suv.x / ss.width, scale9.w * suv.y / ss.height);
+                        d.scale9 = normalizedScale9;
+                    }
                 }
             }
             return true;
@@ -525,13 +527,81 @@
             return this.texture!=null && this.texture.hasAlpha && this.useAlphaFromTexture;
         }
 
+        private _updateSpriteScaleFactor() {
+            if (!this._useSize) {
+                return;
+            }
+
+            let sS = this.spriteSize;
+            let s = this.size;
+            if (s == null || sS == null) {
+                return;
+            }
+            this.scaleX = s.width / sS.width;
+            this.scaleY = s.height / sS.height;
+        }
+
         private _texture: Texture;
         private _oldTextureHasAlpha: boolean;
         private _useAlphaFromTexture: boolean;
-        private _location: Vector2;
-        private _spriteScaleFactor: Vector2;
+        private _useSize: boolean;
+        private _spriteLocation: Vector2;
+        private _spriteSize: Size;
         private _spriteFrame: number;
+        private _scale9: Vector4;
         private _invertY: boolean;
         private _alignToPixel: boolean;
     }
+
+    export class Sprite2DInstanceData extends InstanceDataBase {
+        constructor(partId: number) {
+            super(partId, 1);
+        }
+
+        @instanceData()
+        get topLeftUV(): Vector2 {
+            return null;
+        }
+        set topLeftUV(value: Vector2) {
+        }
+
+        @instanceData()
+        get sizeUV(): Vector2 {
+            return null;
+        }
+        set sizeUV(value: Vector2) {
+        }
+
+        @instanceData(Sprite2D.SHAPE2D_CATEGORY_SCALE9)
+        get scaleFactor(): Vector2 {
+            return null;
+        }
+        set scaleFactor(value: Vector2) {
+        }
+
+        @instanceData()
+        get textureSize(): Vector2 {
+            return null;
+        }
+        set textureSize(value: Vector2) {
+        }
+
+        // 3 floats being:
+        // - x: frame number to display
+        // - y: invertY setting
+        // - z: alignToPixel setting
+        @instanceData()
+        get properties(): Vector3 {
+            return null;
+        }
+        set properties(value: Vector3) {
+        }
+
+        @instanceData(Sprite2D.SHAPE2D_CATEGORY_SCALE9)
+        get scale9(): Vector4 {
+            return null;
+        }
+        set scale9(value: Vector4) {
+        }
+    }
 }

+ 51 - 1
canvas2D/src/shaders/sprite2d.fragment.fx

@@ -1,11 +1,61 @@
 varying vec2 vUV;
 varying float vOpacity;
+
+#ifdef Scale9
+varying vec2 vTopLeftUV;
+varying vec2 vBottomRightUV;
+varying vec4 vScale9;
+varying vec2 vScaleFactor;
+#endif
+
 uniform bool alphaTest;
 uniform sampler2D diffuseSampler;
 
 void main(void) {
-	vec4 color = texture2D(diffuseSampler, vUV);
+	
+	vec2 uv = vUV;
+
+#ifdef Scale9
+
+	vec2 sizeUV = vBottomRightUV - vTopLeftUV;
+
+	// Compute Horizontal (U) Coordinate
+	float leftPartUV = vTopLeftUV.x + (vScale9.x / vScaleFactor.x);
+	float rightPartUV = vTopLeftUV.x + sizeUV.x - ((sizeUV.x - vScale9.z) / vScaleFactor.x);
+
+	if (vUV.x < leftPartUV) {
+		uv.x = vTopLeftUV.x + ((vUV.x- vTopLeftUV.x) * vScaleFactor.x);
+	}
+
+	else if (vUV.x > rightPartUV) {
+		uv.x = vTopLeftUV.x + vScale9.z + ((vUV.x - rightPartUV) * vScaleFactor.x);
+	}
+
+	else {
+		float r = (vUV.x - leftPartUV) / (rightPartUV - leftPartUV);
+		uv.x = vTopLeftUV.x + vScale9.x + ((vScale9.z-vScale9.x) * r);
+	}
+
+	// Compute Vertical (V) Coordinate
+	float topPartUV = (vTopLeftUV.y + (vScale9.y / vScaleFactor.y));
+	float bottomPartUV = (vTopLeftUV.y + sizeUV.y - ((sizeUV.y - vScale9.w) / vScaleFactor.y));
+
+	if (vUV.y < topPartUV) {
+		uv.y = vTopLeftUV.y + ((vUV.y - vTopLeftUV.y) * vScaleFactor.y);
+	}
+
+	else if (vUV.y > bottomPartUV) {
+		uv.y = vTopLeftUV.y + vScale9.w + ((vUV.y - bottomPartUV) * vScaleFactor.y);
+	}
+
+	else {
+		float r = (vUV.y - topPartUV) / (bottomPartUV - topPartUV);
+		uv.y = vTopLeftUV.y + vScale9.y + ((vScale9.w - vScale9.y) * r);
+	}
+
+#endif
 
+	vec4 color = texture2D(diffuseSampler, uv);
 	if (alphaTest)
 	{
 		if (color.a < 0.95) {

+ 31 - 8
canvas2D/src/shaders/sprite2d.vertex.fx

@@ -10,12 +10,18 @@ attribute float index;
 
 att vec2 topLeftUV;
 att vec2 sizeUV;
+#ifdef Scale9
 att vec2 scaleFactor;
+#endif
 att vec2 textureSize;
 
 // x: frame, y: invertY, z: alignToPixel
 att vec3 properties;
 
+#ifdef Scale9
+att vec4 scale9;
+#endif
+
 att vec2 zBias;
 att vec4 transformX;
 att vec4 transformY;
@@ -27,14 +33,17 @@ att float opacity;
 varying vec2 vUV;
 varying float vOpacity;
 
+#ifdef Scale9
+varying vec2 vTopLeftUV;
+varying vec2 vBottomRightUV;
+varying vec4 vScale9;
+varying vec2 vScaleFactor;
+#endif
+
 void main(void) {
 
 	vec2 pos2;
 
-	//vec2 off = vec2(1.0 / textureSize.x, 1.0 / textureSize.y);
-	vec2 off = vec2(0.0, 0.0);
-	vec2 sfSizeUV = sizeUV * scaleFactor;
-
 	float frame = properties.x;
 	float invertY = properties.y;
 	float alignToPixel = properties.z;
@@ -42,25 +51,25 @@ void main(void) {
 	// Left/Top
 	if (index == 0.0) {
 		pos2 = vec2(0.0, 0.0);
-		vUV = vec2(topLeftUV.x + (frame*sfSizeUV.x) + off.x, topLeftUV.y - off.y);
+		vUV = vec2(topLeftUV.x + (frame*sizeUV.x), topLeftUV.y);
 	}
 
 	// Left/Bottom
 	else if (index == 1.0) {
 		pos2 = vec2(0.0,  1.0);
-		vUV = vec2(topLeftUV.x + (frame*sfSizeUV.x) + off.x, (topLeftUV.y + sfSizeUV.y));
+		vUV = vec2(topLeftUV.x + (frame*sizeUV.x), (topLeftUV.y + sizeUV.y));
 	}
 
 	// Right/Bottom
 	else if (index == 2.0) {
 		pos2 = vec2( 1.0,  1.0);
-		vUV = vec2(topLeftUV.x + sfSizeUV.x + (frame*sfSizeUV.x), (topLeftUV.y + sfSizeUV.y));
+		vUV = vec2(topLeftUV.x + sizeUV.x + (frame*sizeUV.x), (topLeftUV.y + sizeUV.y));
 	}
 
 	// Right/Top
 	else if (index == 3.0) {
 		pos2 = vec2( 1.0, 0.0);
-		vUV = vec2(topLeftUV.x + sfSizeUV.x + (frame*sfSizeUV.x), topLeftUV.y - off.y);
+		vUV = vec2(topLeftUV.x + sizeUV.x + (frame*sizeUV.x), topLeftUV.y);
 	}
 
 	if (invertY == 1.0) {
@@ -75,6 +84,20 @@ void main(void) {
 		pos.xy = pos2.xy * sizeUV * textureSize;
 	}
 
+#ifdef Scale9
+	if (invertY == 1.0) {
+		vTopLeftUV = vec2(topLeftUV.x, 1.0 - (topLeftUV.y + sizeUV.y));
+		vBottomRightUV = vec2(topLeftUV.x + sizeUV.x, 1.0 - topLeftUV.y);
+		vScale9 = vec4(scale9.x, sizeUV.y - scale9.w, scale9.z, sizeUV.y - scale9.y);
+	}
+	else {
+		vTopLeftUV = topLeftUV;
+		vBottomRightUV = vec2(topLeftUV.x, topLeftUV.y + sizeUV.y);
+		vScale9 = scale9;
+	}
+	vScaleFactor = scaleFactor;
+#endif
+
 	vOpacity = opacity;
 	pos.z = 1.0;
 	pos.w = 1.0;

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


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


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


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


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


+ 185 - 20
dist/preview release/canvas2D/babylon.canvas2d.d.ts

@@ -1674,6 +1674,8 @@ declare module BABYLON {
          * Use this property to set a new Size object, otherwise to change only the width/height use Prim2DBase.width or height properties.
          */
         size: Size;
+        protected internalGetSize(): Size;
+        protected internalSetSize(value: Size): void;
         /**
          * Direct access to the size.width value of the primitive
          * Use this property when you only want to change one component of the size property
@@ -2728,36 +2730,26 @@ declare module BABYLON {
         render(instanceInfo: GroupInstanceInfo, context: Render2DContext): boolean;
         dispose(): boolean;
     }
-    class Sprite2DInstanceData extends InstanceDataBase {
-        constructor(partId: number);
-        topLeftUV: Vector2;
-        sizeUV: Vector2;
-        scaleFactor: Vector2;
-        textureSize: Vector2;
-        properties: Vector3;
-    }
     class Sprite2D extends RenderablePrim2D {
         static SPRITE2D_MAINPARTID: number;
+        static SHAPE2D_CATEGORY_SCALE9: string;
         static textureProperty: Prim2DPropInfo;
         static useAlphaFromTextureProperty: Prim2DPropInfo;
         static actualSizeProperty: Prim2DPropInfo;
+        static spriteSizeProperty: Prim2DPropInfo;
         static spriteLocationProperty: Prim2DPropInfo;
         static spriteFrameProperty: Prim2DPropInfo;
         static invertYProperty: Prim2DPropInfo;
-        static spriteScaleFactorProperty: Prim2DPropInfo;
+        static spriteScale9Property: Prim2DPropInfo;
         texture: Texture;
         useAlphaFromTexture: boolean;
+        size: Size;
         actualSize: Size;
+        spriteSize: Size;
         spriteLocation: Vector2;
         spriteFrame: number;
         invertY: boolean;
-        spriteScaleFactor: Vector2;
-        /**
-         * Sets the scale of the sprite using a BABYLON.Size(w,h).
-         * Keeps proportion by taking the maximum of the two scale for x and y.
-         * @param {Size} size Size(width,height)
-         */
-        scaleToSize(size: Size): void;
+        readonly isScale9: boolean;
         /**
          * Get/set if the sprite rendering should be aligned to the target rendering device pixel or not
          */
@@ -2768,6 +2760,7 @@ declare module BABYLON {
          */
         getAnimatables(): IAnimatable[];
         protected levelIntersect(intersectInfo: IntersectInfo2D): boolean;
+        readonly isSizeAuto: boolean;
         /**
          * Create an 2D Sprite primitive
          * @param texture the texture that stores the sprite to render
@@ -2778,13 +2771,15 @@ declare module BABYLON {
          * - position: the X & Y positions relative to its parent. Alternatively the x and y properties can be set. Default is [0;0]
          * - rotation: the initial rotation (in radian) of the primitive. default is 0
          * - scale: the initial scale of the primitive. default is 1. You can alternatively use scaleX &| scaleY to apply non uniform scale
+         * - size: the size of the sprite displayed in the canvas, if not specified the spriteSize will be used
          * - dontInheritParentScale: if set the parent's scale won't be taken into consideration to compute the actualScale property
          * - opacity: set the overall opacity of the primitive, 1 to be opaque (default), less than 1 to be transparent.
          * - zOrder: override the zOrder with the specified value
          * - origin: define the normalized origin point location, default [0.5;0.5]
-         * - spriteSize: the size of the sprite (in pixels), if null the size of the given texture will be used, default is null.
+         * - spriteSize: the size of the sprite (in pixels) as it is stored in the texture, if null the size of the given texture will be used, default is null.
          * - spriteLocation: the location (in pixels) in the texture of the top/left corner of the Sprite to display, default is null (0,0)
-         * - spriteScaleFactor: say you want to display a sprite twice as big as its bitmap which is 64,64, you set the spriteSize to 128,128 and have to set the spriteScaleFactory to 0.5,0.5 in order to address only the 64,64 pixels of the bitmaps. Default is 1,1.
+         * - spriteScaleFactor: DEPRECATED. Old behavior: say you want to display a sprite twice as big as its bitmap which is 64,64, you set the spriteSize to 128,128 and have to set the spriteScaleFactory to 0.5,0.5 in order to address only the 64,64 pixels of the bitmaps. Default is 1,1.
+         * - scale9: draw the sprite as a Scale9 sprite, see http://yannickloriot.com/2013/03/9-patch-technique-in-cocos2d/ for more info. x, y, w, z are left, bottom, right, top coordinate of the resizable box
          * - invertY: if true the texture Y will be inverted, default is false.
          * - alignToPixel: if true the sprite's texels will be aligned to the rendering viewport pixels, ensuring the best rendering quality but slow animations won't be done as smooth as if you set false. If false a texel could lies between two pixels, being blended by the texture sampling mode you choose, the rendering result won't be as good, but very slow animation will be overall better looking. Default is true: content will be aligned.
          * - isVisible: true if the sprite must be visible, false for hidden. Default is true.
@@ -2813,6 +2808,7 @@ declare module BABYLON {
             x?: number;
             y?: number;
             rotation?: number;
+            size?: Size;
             scale?: number;
             scaleX?: number;
             scaleY?: number;
@@ -2823,6 +2819,7 @@ declare module BABYLON {
             spriteSize?: Size;
             spriteLocation?: Vector2;
             spriteScaleFactor?: Vector2;
+            scale9?: Vector4;
             invertY?: boolean;
             alignToPixel?: boolean;
             isVisible?: boolean;
@@ -2845,6 +2842,7 @@ declare module BABYLON {
         });
         protected createModelRenderCache(modelKey: string): ModelRenderCache;
         protected setupModelRenderCache(modelRenderCache: ModelRenderCache): Sprite2DRenderCache;
+        protected getUsedShaderCategories(dataPart: InstanceDataBase): string[];
         protected createInstanceDataParts(): InstanceDataBase[];
         private static _prop;
         private static layoutConstructMode;
@@ -2854,15 +2852,182 @@ declare module BABYLON {
         protected _mustUpdateInstance(): boolean;
         protected _useTextureAlpha(): boolean;
         protected _shouldUseAlphaFromTexture(): boolean;
+        private _updateSpriteScaleFactor();
         private _texture;
         private _oldTextureHasAlpha;
         private _useAlphaFromTexture;
-        private _location;
-        private _spriteScaleFactor;
+        private _useSize;
+        private _spriteLocation;
+        private _spriteSize;
         private _spriteFrame;
+        private _scale9;
         private _invertY;
         private _alignToPixel;
     }
+    class Sprite2DInstanceData extends InstanceDataBase {
+        constructor(partId: number);
+        topLeftUV: Vector2;
+        sizeUV: Vector2;
+        scaleFactor: Vector2;
+        textureSize: Vector2;
+        properties: Vector3;
+        scale9: Vector4;
+    }
+}
+
+declare module BABYLON {
+    /**
+     * Interface to create your own Loader of Atlas Data file.
+     * Call the AtlasPictureInfoFactory.addLoader to addd your loader instance
+     */
+    interface IAtlasLoader {
+        loadFile(content: any): {
+            api: AtlasPictureInfo;
+            errorMsg: string;
+            errorCode: number;
+        };
+    }
+    /**
+     * This class will contains information about a sub picture present in an Atlas Picture.
+     */
+    class AtlasSubPictureInfo {
+        /**
+         * Name of the SubPicture, generally the filename of the initial picture file.
+         */
+        name: string;
+        /**
+         * Location of the bottom/left corner of the sub picture from the bottom/left corner the Atlas Picture
+         */
+        location: Vector2;
+        /**
+         * Size in pixel of the sub picture
+         */
+        size: Size;
+    }
+    /**
+     * This class represent an Atlas Picture, it contains the information of all the sub pictures and the Texture that stores the bitmap.
+     * You get an instance of this class using methods of the AtlasPictureInfoFactory
+     */
+    class AtlasPictureInfo {
+        /**
+         * Creates many sprite from the Atlas Picture
+         * @param filterCallback a predicate if true is returned then the corresponding sub picture will be used to create a sprite.
+         * The Predicate has many parameters:
+         *  - index: just an index incremented at each sub picture submitted for Sprite creation
+         *  - name: the sub picture's name
+         *  - aspi: the AtlasSubPictureInfo corresponding to the submitted sub picture
+         *  - settings: the Sprite2D creation settings, you can alter this JSON object but BEWARE, the alterations will be kept for subsequent Sprite2D creations!
+         * @param spriteSettings The Sprite2D settings to use for Sprite creation, this JSON object will be passed to the filterCallback for you to alter it, if needed.
+         */
+        createSprites(filterCallback: (index: number, name: string, aspi: AtlasSubPictureInfo, settings: any) => boolean, spriteSettings: {
+            parent?: Prim2DBase;
+            position?: Vector2;
+            x?: number;
+            y?: number;
+            rotation?: number;
+            size?: Size;
+            scale?: number;
+            scaleX?: number;
+            scaleY?: number;
+            dontInheritParentScale?: boolean;
+            opacity?: number;
+            zOrder?: number;
+            origin?: Vector2;
+            scale9?: Vector4;
+            invertY?: boolean;
+            alignToPixel?: boolean;
+            isVisible?: boolean;
+            isPickable?: boolean;
+            isContainer?: boolean;
+            childrenFlatZOrder?: boolean;
+            marginTop?: number | string;
+            marginLeft?: number | string;
+            marginRight?: number | string;
+            marginBottom?: number | string;
+            margin?: number | string;
+            marginHAlignment?: number;
+            marginVAlignment?: number;
+            marginAlignment?: string;
+            paddingTop?: number | string;
+            paddingLeft?: number | string;
+            paddingRight?: number | string;
+            paddingBottom?: number | string;
+            padding?: string;
+        }): Array<Sprite2D>;
+        /**
+         * Create one Sprite from a sub picture
+         * @param subPictureName the name of the sub picture to use
+         * @param spriteSettings the Sprite2D settings to use for the Sprite instance creation
+         */
+        createSprite(subPictureName: string, spriteSettings: {
+            parent?: Prim2DBase;
+            position?: Vector2;
+            x?: number;
+            y?: number;
+            rotation?: number;
+            size?: Size;
+            scale?: number;
+            scaleX?: number;
+            scaleY?: number;
+            dontInheritParentScale?: boolean;
+            opacity?: number;
+            zOrder?: number;
+            origin?: Vector2;
+            scale9?: Vector4;
+            invertY?: boolean;
+            alignToPixel?: boolean;
+            isVisible?: boolean;
+            isPickable?: boolean;
+            isContainer?: boolean;
+            childrenFlatZOrder?: boolean;
+            marginTop?: number | string;
+            marginLeft?: number | string;
+            marginRight?: number | string;
+            marginBottom?: number | string;
+            margin?: number | string;
+            marginHAlignment?: number;
+            marginVAlignment?: number;
+            marginAlignment?: string;
+            paddingTop?: number | string;
+            paddingLeft?: number | string;
+            paddingRight?: number | string;
+            paddingBottom?: number | string;
+            padding?: string;
+        }): Sprite2D;
+        /**
+         * Size of the Atlas Picture
+         */
+        atlasSize: Size;
+        /**
+         * String Dictionary of all the sub pictures, the key is the sub picture's name, the value is the info object
+         */
+        subPictures: StringDictionary<AtlasSubPictureInfo>;
+        /**
+         * The Texture associated to the Atlas Picture info
+         */
+        texture: Texture;
+    }
+    /**
+     * This if the Factory class containing static method to create Atlas Pictures Info objects or add new loaders
+     */
+    class AtlasPictureInfoFactory {
+        /**
+         * Add a custom loader
+         * @param fileExtension must be the file extension (without the dot) of the file that is loaded by this loader (e.g.: json)
+         * @param plugin the instance of the loader
+         */
+        static addLoader(fileExtension: string, plugin: IAtlasLoader): void;
+        /**
+         * Load an Atlas Picture Info object from a data file at a given url and with a given texture
+         * @param texture the texture containing the atlas bitmap
+         * @param url the URL of the Atlas Info data file
+         * @param loaded a callback that will be called when the AtlasPictureInfo object will be loaded and ready
+         * @param error a callback that will be called in case of error
+         */
+        static loadFromUrl(texture: Texture, url: string, loaded: (api: AtlasPictureInfo) => void, error?: (msg: string, code: number) => void): any;
+        private static _initialize();
+        private static plugins;
+    }
 }
 
 declare module BABYLON {

File diff suppressed because it is too large
+ 397 - 136
dist/preview release/canvas2D/babylon.canvas2d.js


File diff suppressed because it is too large
+ 10 - 10
dist/preview release/canvas2D/babylon.canvas2d.min.js


File diff suppressed because it is too large
+ 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

File diff suppressed because it is too large
+ 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;

File diff suppressed because it is too large
+ 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;
 }