Procházet zdrojové kódy

Merge pull request #3439 from BabylonJS/master

nightly
David Catuhe před 7 roky
rodič
revize
2d911e5f55
25 změnil soubory, kde provedl 8328 přidání a 6335 odebrání
  1. 2943 2934
      Playground/babylon.d.txt
  2. 6 0
      Tools/Gulp/config.json
  3. 3089 3079
      dist/preview release/babylon.d.ts
  4. 31 31
      dist/preview release/babylon.js
  5. 82 31
      dist/preview release/babylon.max.js
  6. 31 31
      dist/preview release/babylon.worker.js
  7. 30 30
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  8. 82 31
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  9. 2 2
      dist/preview release/inspector/babylon.inspector.bundle.js
  10. 6 0
      dist/preview release/inspector/babylon.inspector.js
  11. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  12. 15 0
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  13. 538 0
      dist/preview release/serializers/babylon.glTF2Serializer.js
  14. 1 0
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  15. 539 0
      dist/preview release/serializers/babylonjs.serializers.js
  16. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  17. 16 0
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  18. 31 31
      dist/preview release/viewer/babylon.viewer.js
  19. 7 0
      inspector/src/tabs/StatsTab.ts
  20. 683 0
      serializers/src/glTF/2.0/babylon.glTFSerializer.ts
  21. 50 27
      src/Engine/babylon.engine.ts
  22. 109 101
      src/Instrumentation/babylon.sceneInstrumentation.ts
  23. 18 1
      src/Materials/babylon.effect.ts
  24. 14 1
      src/Materials/babylon.shaderMaterial.ts
  25. 1 1
      src/babylon.scene.ts

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2943 - 2934
Playground/babylon.d.txt


+ 6 - 0
Tools/Gulp/config.json

@@ -1549,6 +1549,12 @@
                     "../../serializers/src/OBJ/babylon.objSerializer.ts"
                 ],
                 "output": "babylon.objSerializer.js"
+            },
+            {
+                "files": [
+                    "../../serializers/src/glTF/2.0/babylon.glTFSerializer.ts"
+                ],
+                "output": "babylon.glTF2Serializer.js"
             }
         ],
         "build": {

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 3089 - 3079
dist/preview release/babylon.d.ts


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 31 - 31
dist/preview release/babylon.js


+ 82 - 31
dist/preview release/babylon.max.js

@@ -8093,6 +8093,7 @@ var BABYLON;
             this.onVRRequestPresentStart = new BABYLON.Observable();
             this._colorWrite = true;
             this._drawCalls = new BABYLON.PerfCounter();
+            this._textureCollisions = new BABYLON.PerfCounter();
             this._renderingQueueLaunched = false;
             this._activeRenderLoops = new Array();
             // Deterministic lockstepMaxSteps
@@ -8119,7 +8120,7 @@ var BABYLON;
             // Cache
             this._internalTexturesCache = new Array();
             this._boundTexturesCache = {};
-            this._boundTexturesOrder = new Array();
+            this._boundTexturesStack = new Array();
             this._compiledEffects = {};
             this._vertexAttribArraysEnabled = [];
             this._uintIndicesCurrentlySet = false;
@@ -8129,7 +8130,7 @@ var BABYLON;
             this._currentInstanceBuffers = new Array();
             this._vaoRecordInProgress = false;
             this._mustWipeVertexAttributes = false;
-            this._nextFreeTextureSlot = 0;
+            this._nextFreeTextureSlots = new Array();
             // Hardware supported Compressed Textures
             this._texturesSupported = new Array();
             this._onVRFullScreenTriggered = function () {
@@ -8681,7 +8682,7 @@ var BABYLON;
         });
         Object.defineProperty(Engine, "Version", {
             get: function () {
-                return "3.2.0-alpha0";
+                return "3.2.0-alpha1";
             },
             enumerable: true,
             configurable: true
@@ -8963,6 +8964,10 @@ var BABYLON;
             this.setDepthBuffer(true);
             this.setDepthFunctionToLessOrEqual();
             this.setDepthWrite(true);
+            // Texture maps
+            for (var slot = 0; slot < this._caps.maxTexturesImageUnits; slot++) {
+                this._nextFreeTextureSlots.push(slot);
+            }
         };
         Object.defineProperty(Engine.prototype, "webGLVersion", {
             get: function () {
@@ -8999,7 +9004,10 @@ var BABYLON;
                 }
                 this._boundTexturesCache[key] = null;
             }
-            this._nextFreeTextureSlot = 0;
+            this._nextFreeTextureSlots = [];
+            for (var slot = 0; slot < this._caps.maxTexturesImageUnits; slot++) {
+                this._nextFreeTextureSlots.push(slot);
+            }
             this._activeChannel = -1;
         };
         Engine.prototype.isDeterministicLockStep = function () {
@@ -9466,7 +9474,7 @@ var BABYLON;
                 gl.blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, texture.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
             }
             if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
-                this._bindTextureDirectly(gl.TEXTURE_2D, texture);
+                this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
                 gl.generateMipmap(gl.TEXTURE_2D);
                 this._bindTextureDirectly(gl.TEXTURE_2D, null);
             }
@@ -10201,6 +10209,11 @@ var BABYLON;
                 return;
             this._gl.uniformMatrix2fv(uniform, false, matrix);
         };
+        Engine.prototype.setInt = function (uniform, value) {
+            if (!uniform)
+                return;
+            this._gl.uniform1i(uniform, value);
+        };
         Engine.prototype.setFloat = function (uniform, value) {
             if (!uniform)
                 return;
@@ -10351,11 +10364,11 @@ var BABYLON;
             if (this.preventCacheWipeBetweenFrames && !bruteForce) {
                 return;
             }
-            this.resetTextureCache();
             this._currentEffect = null;
             // 6/8/2017: deltakosh: Should not be required anymore.
             // This message is then mostly for the future myself which will scream out loud when seeing that actually it was required :)
             if (bruteForce) {
+                this.resetTextureCache();
                 this._currentProgram = null;
                 this._stencilState.reset();
                 this._depthCullingState.reset();
@@ -11676,29 +11689,33 @@ var BABYLON;
             this._currentEffect = null;
         };
         Engine.prototype._activateTextureChannel = function (channel) {
-            if (this._activeChannel !== channel) {
+            if (this._activeChannel !== channel && channel > -1) {
                 this._gl.activeTexture(this._gl.TEXTURE0 + channel);
                 this._activeChannel = channel;
             }
         };
         Engine.prototype._moveBoundTextureOnTop = function (internalTexture) {
-            var index = this._boundTexturesOrder.indexOf(internalTexture);
-            if (index > -1 && index !== this._boundTexturesOrder.length - 1) {
-                this._boundTexturesOrder.splice(index, 1);
-                this._boundTexturesOrder.push(internalTexture);
+            var index = this._boundTexturesStack.indexOf(internalTexture);
+            if (index > -1 && index !== this._boundTexturesStack.length - 1) {
+                this._boundTexturesStack.splice(index, 1);
+                this._boundTexturesStack.push(internalTexture);
             }
         };
         Engine.prototype._removeDesignatedSlot = function (internalTexture) {
             var currentSlot = internalTexture._designatedSlot;
             internalTexture._designatedSlot = -1;
-            var index = this._boundTexturesOrder.indexOf(internalTexture);
+            var index = this._boundTexturesStack.indexOf(internalTexture);
             if (index > -1) {
-                this._boundTexturesOrder.splice(index, 1);
+                this._boundTexturesStack.splice(index, 1);
+                if (currentSlot > -1) {
+                    this._boundTexturesCache[currentSlot] = null;
+                    this._nextFreeTextureSlots.push(currentSlot);
+                }
             }
             return currentSlot;
         };
-        Engine.prototype._bindTextureDirectly = function (target, texture, isPartOfTextureArray) {
-            if (isPartOfTextureArray === void 0) { isPartOfTextureArray = false; }
+        Engine.prototype._bindTextureDirectly = function (target, texture, doNotBindUniformToTextureChannel) {
+            if (doNotBindUniformToTextureChannel === void 0) { doNotBindUniformToTextureChannel = false; }
             var currentTextureBound = this._boundTexturesCache[this._activeChannel];
             var isTextureForRendering = texture && texture._initialSlot > -1;
             if (currentTextureBound !== texture) {
@@ -11709,13 +11726,17 @@ var BABYLON;
                 if (this._activeChannel >= 0) {
                     this._boundTexturesCache[this._activeChannel] = texture;
                     if (isTextureForRendering) {
-                        this._boundTexturesOrder.push(texture);
+                        var slotIndex = this._nextFreeTextureSlots.indexOf(this._activeChannel);
+                        if (slotIndex > -1) {
+                            this._nextFreeTextureSlots.splice(slotIndex, 1);
+                        }
+                        this._boundTexturesStack.push(texture);
                     }
                 }
             }
             if (isTextureForRendering && this._activeChannel > -1) {
                 texture._designatedSlot = this._activeChannel;
-                if (!isPartOfTextureArray) {
+                if (!doNotBindUniformToTextureChannel) {
                     this._bindSamplerUniformToChannel(texture._initialSlot, this._activeChannel);
                 }
             }
@@ -11759,19 +11780,16 @@ var BABYLON;
             internalTexture._initialSlot = channel;
             if (channel !== internalTexture._designatedSlot) {
                 if (internalTexture._designatedSlot > -1) {
-                    channel = internalTexture._designatedSlot;
+                    return internalTexture._designatedSlot;
                 }
                 else {
-                    if (this._nextFreeTextureSlot > -1) {
-                        channel = this._nextFreeTextureSlot;
-                        this._nextFreeTextureSlot++;
-                        if (this._nextFreeTextureSlot >= this._caps.maxTexturesImageUnits) {
-                            this._nextFreeTextureSlot = -1; // No more free slots, we will recycle
-                        }
-                    }
-                    else {
-                        channel = this._removeDesignatedSlot(this._boundTexturesOrder[0]);
+                    // No slot for this texture, let's pick a new one (if we find a free slot)
+                    if (this._nextFreeTextureSlots.length) {
+                        return this._nextFreeTextureSlots[0];
                     }
+                    // We need to recycle the oldest bound texture, sorry.
+                    this._textureCollisions.addCount(1, false);
+                    return this._removeDesignatedSlot(this._boundTexturesStack[0]);
                 }
             }
             return channel;
@@ -19814,7 +19832,7 @@ var BABYLON;
             }
             return null;
         };
-        Object.defineProperty(Scene.prototype, "Animatables", {
+        Object.defineProperty(Scene.prototype, "animatables", {
             get: function () {
                 return this._activeAnimatables;
             },
@@ -21811,7 +21829,7 @@ var BABYLON;
                     camera = freeCamera;
                 }
                 camera.minZ = radius * 0.01;
-                camera.maxZ = radius * 100;
+                camera.maxZ = radius * 1000;
                 camera.speed = radius * 0.2;
                 this.activeCamera = camera;
                 var canvas = this.getEngine().getRenderingCanvas();
@@ -27118,6 +27136,10 @@ var BABYLON;
             this._fallbacks = null;
             this._prepareEffect();
         };
+        Effect.prototype.getSpecificUniformLocations = function (names) {
+            var engine = this._engine;
+            return engine.getUniforms(this._program, names);
+        };
         Effect.prototype._prepareEffect = function () {
             var attributesNames = this._attributesNames;
             var defines = this.defines;
@@ -27313,6 +27335,14 @@ var BABYLON;
         Effect.prototype.bindUniformBlock = function (blockName, index) {
             this._engine.bindUniformBlock(this._program, blockName, index);
         };
+        Effect.prototype.setInt = function (uniformName, value) {
+            var cache = this._valueCache[uniformName];
+            if (cache !== undefined && cache === value)
+                return this;
+            this._valueCache[uniformName] = value;
+            this._engine.setInt(this.getUniform(uniformName), value);
+            return this;
+        };
         Effect.prototype.setIntArray = function (uniformName, array) {
             this._valueCache[uniformName] = null;
             this._engine.setIntArray(this.getUniform(uniformName), array);
@@ -48306,6 +48336,7 @@ var BABYLON;
             _this._textures = {};
             _this._textureArrays = {};
             _this._floats = {};
+            _this._ints = {};
             _this._floatsArrays = {};
             _this._colors3 = {};
             _this._colors3Arrays = {};
@@ -48364,6 +48395,11 @@ var BABYLON;
             this._floats[name] = value;
             return this;
         };
+        ShaderMaterial.prototype.setInt = function (name, value) {
+            this._checkUniform(name);
+            this._ints[name] = value;
+            return this;
+        };
         ShaderMaterial.prototype.setFloats = function (name, value) {
             this._checkUniform(name);
             this._floatsArrays[name] = value;
@@ -48552,11 +48588,15 @@ var BABYLON;
                 for (name in this._textureArrays) {
                     this._effect.setTextureArray(name, this._textureArrays[name]);
                 }
+                // Int    
+                for (name in this._ints) {
+                    this._effect.setInt(name, this._ints[name]);
+                }
                 // Float    
                 for (name in this._floats) {
                     this._effect.setFloat(name, this._floats[name]);
                 }
-                // Float s   
+                // Floats   
                 for (name in this._floatsArrays) {
                     this._effect.setArray(name, this._floatsArrays[name]);
                 }
@@ -79211,6 +79251,7 @@ var BABYLON;
                     _this._animationsTime.beginMonitoring();
                 }
                 _this.scene.getEngine()._drawCalls.fetchNewFrame();
+                _this.scene.getEngine()._textureCollisions.fetchNewFrame();
             });
             // After render
             this._onAfterRenderObserver = scene.onAfterRenderObservable.add(function () {
@@ -79596,7 +79637,7 @@ var BABYLON;
         });
         Object.defineProperty(SceneInstrumentation.prototype, "drawCallsCounter", {
             /**
-             * Gets the perf counter used for frame time capture
+             * Gets the perf counter used for draw calls
              */
             get: function () {
                 return this.scene.getEngine()._drawCalls;
@@ -79604,6 +79645,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(SceneInstrumentation.prototype, "textureCollisionsCounter", {
+            /**
+             * Gets the perf counter used for texture collisions
+             */
+            get: function () {
+                return this.scene.getEngine()._textureCollisions;
+            },
+            enumerable: true,
+            configurable: true
+        });
         SceneInstrumentation.prototype.dispose = function () {
             this.scene.onAfterRenderObservable.remove(this._onAfterRenderObserver);
             this._onAfterRenderObserver = null;

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 31 - 31
dist/preview release/babylon.worker.js


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 30 - 30
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 82 - 31
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -8093,6 +8093,7 @@ var BABYLON;
             this.onVRRequestPresentStart = new BABYLON.Observable();
             this._colorWrite = true;
             this._drawCalls = new BABYLON.PerfCounter();
+            this._textureCollisions = new BABYLON.PerfCounter();
             this._renderingQueueLaunched = false;
             this._activeRenderLoops = new Array();
             // Deterministic lockstepMaxSteps
@@ -8119,7 +8120,7 @@ var BABYLON;
             // Cache
             this._internalTexturesCache = new Array();
             this._boundTexturesCache = {};
-            this._boundTexturesOrder = new Array();
+            this._boundTexturesStack = new Array();
             this._compiledEffects = {};
             this._vertexAttribArraysEnabled = [];
             this._uintIndicesCurrentlySet = false;
@@ -8129,7 +8130,7 @@ var BABYLON;
             this._currentInstanceBuffers = new Array();
             this._vaoRecordInProgress = false;
             this._mustWipeVertexAttributes = false;
-            this._nextFreeTextureSlot = 0;
+            this._nextFreeTextureSlots = new Array();
             // Hardware supported Compressed Textures
             this._texturesSupported = new Array();
             this._onVRFullScreenTriggered = function () {
@@ -8681,7 +8682,7 @@ var BABYLON;
         });
         Object.defineProperty(Engine, "Version", {
             get: function () {
-                return "3.2.0-alpha0";
+                return "3.2.0-alpha1";
             },
             enumerable: true,
             configurable: true
@@ -8963,6 +8964,10 @@ var BABYLON;
             this.setDepthBuffer(true);
             this.setDepthFunctionToLessOrEqual();
             this.setDepthWrite(true);
+            // Texture maps
+            for (var slot = 0; slot < this._caps.maxTexturesImageUnits; slot++) {
+                this._nextFreeTextureSlots.push(slot);
+            }
         };
         Object.defineProperty(Engine.prototype, "webGLVersion", {
             get: function () {
@@ -8999,7 +9004,10 @@ var BABYLON;
                 }
                 this._boundTexturesCache[key] = null;
             }
-            this._nextFreeTextureSlot = 0;
+            this._nextFreeTextureSlots = [];
+            for (var slot = 0; slot < this._caps.maxTexturesImageUnits; slot++) {
+                this._nextFreeTextureSlots.push(slot);
+            }
             this._activeChannel = -1;
         };
         Engine.prototype.isDeterministicLockStep = function () {
@@ -9466,7 +9474,7 @@ var BABYLON;
                 gl.blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, texture.height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
             }
             if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
-                this._bindTextureDirectly(gl.TEXTURE_2D, texture);
+                this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
                 gl.generateMipmap(gl.TEXTURE_2D);
                 this._bindTextureDirectly(gl.TEXTURE_2D, null);
             }
@@ -10201,6 +10209,11 @@ var BABYLON;
                 return;
             this._gl.uniformMatrix2fv(uniform, false, matrix);
         };
+        Engine.prototype.setInt = function (uniform, value) {
+            if (!uniform)
+                return;
+            this._gl.uniform1i(uniform, value);
+        };
         Engine.prototype.setFloat = function (uniform, value) {
             if (!uniform)
                 return;
@@ -10351,11 +10364,11 @@ var BABYLON;
             if (this.preventCacheWipeBetweenFrames && !bruteForce) {
                 return;
             }
-            this.resetTextureCache();
             this._currentEffect = null;
             // 6/8/2017: deltakosh: Should not be required anymore.
             // This message is then mostly for the future myself which will scream out loud when seeing that actually it was required :)
             if (bruteForce) {
+                this.resetTextureCache();
                 this._currentProgram = null;
                 this._stencilState.reset();
                 this._depthCullingState.reset();
@@ -11676,29 +11689,33 @@ var BABYLON;
             this._currentEffect = null;
         };
         Engine.prototype._activateTextureChannel = function (channel) {
-            if (this._activeChannel !== channel) {
+            if (this._activeChannel !== channel && channel > -1) {
                 this._gl.activeTexture(this._gl.TEXTURE0 + channel);
                 this._activeChannel = channel;
             }
         };
         Engine.prototype._moveBoundTextureOnTop = function (internalTexture) {
-            var index = this._boundTexturesOrder.indexOf(internalTexture);
-            if (index > -1 && index !== this._boundTexturesOrder.length - 1) {
-                this._boundTexturesOrder.splice(index, 1);
-                this._boundTexturesOrder.push(internalTexture);
+            var index = this._boundTexturesStack.indexOf(internalTexture);
+            if (index > -1 && index !== this._boundTexturesStack.length - 1) {
+                this._boundTexturesStack.splice(index, 1);
+                this._boundTexturesStack.push(internalTexture);
             }
         };
         Engine.prototype._removeDesignatedSlot = function (internalTexture) {
             var currentSlot = internalTexture._designatedSlot;
             internalTexture._designatedSlot = -1;
-            var index = this._boundTexturesOrder.indexOf(internalTexture);
+            var index = this._boundTexturesStack.indexOf(internalTexture);
             if (index > -1) {
-                this._boundTexturesOrder.splice(index, 1);
+                this._boundTexturesStack.splice(index, 1);
+                if (currentSlot > -1) {
+                    this._boundTexturesCache[currentSlot] = null;
+                    this._nextFreeTextureSlots.push(currentSlot);
+                }
             }
             return currentSlot;
         };
-        Engine.prototype._bindTextureDirectly = function (target, texture, isPartOfTextureArray) {
-            if (isPartOfTextureArray === void 0) { isPartOfTextureArray = false; }
+        Engine.prototype._bindTextureDirectly = function (target, texture, doNotBindUniformToTextureChannel) {
+            if (doNotBindUniformToTextureChannel === void 0) { doNotBindUniformToTextureChannel = false; }
             var currentTextureBound = this._boundTexturesCache[this._activeChannel];
             var isTextureForRendering = texture && texture._initialSlot > -1;
             if (currentTextureBound !== texture) {
@@ -11709,13 +11726,17 @@ var BABYLON;
                 if (this._activeChannel >= 0) {
                     this._boundTexturesCache[this._activeChannel] = texture;
                     if (isTextureForRendering) {
-                        this._boundTexturesOrder.push(texture);
+                        var slotIndex = this._nextFreeTextureSlots.indexOf(this._activeChannel);
+                        if (slotIndex > -1) {
+                            this._nextFreeTextureSlots.splice(slotIndex, 1);
+                        }
+                        this._boundTexturesStack.push(texture);
                     }
                 }
             }
             if (isTextureForRendering && this._activeChannel > -1) {
                 texture._designatedSlot = this._activeChannel;
-                if (!isPartOfTextureArray) {
+                if (!doNotBindUniformToTextureChannel) {
                     this._bindSamplerUniformToChannel(texture._initialSlot, this._activeChannel);
                 }
             }
@@ -11759,19 +11780,16 @@ var BABYLON;
             internalTexture._initialSlot = channel;
             if (channel !== internalTexture._designatedSlot) {
                 if (internalTexture._designatedSlot > -1) {
-                    channel = internalTexture._designatedSlot;
+                    return internalTexture._designatedSlot;
                 }
                 else {
-                    if (this._nextFreeTextureSlot > -1) {
-                        channel = this._nextFreeTextureSlot;
-                        this._nextFreeTextureSlot++;
-                        if (this._nextFreeTextureSlot >= this._caps.maxTexturesImageUnits) {
-                            this._nextFreeTextureSlot = -1; // No more free slots, we will recycle
-                        }
-                    }
-                    else {
-                        channel = this._removeDesignatedSlot(this._boundTexturesOrder[0]);
+                    // No slot for this texture, let's pick a new one (if we find a free slot)
+                    if (this._nextFreeTextureSlots.length) {
+                        return this._nextFreeTextureSlots[0];
                     }
+                    // We need to recycle the oldest bound texture, sorry.
+                    this._textureCollisions.addCount(1, false);
+                    return this._removeDesignatedSlot(this._boundTexturesStack[0]);
                 }
             }
             return channel;
@@ -19814,7 +19832,7 @@ var BABYLON;
             }
             return null;
         };
-        Object.defineProperty(Scene.prototype, "Animatables", {
+        Object.defineProperty(Scene.prototype, "animatables", {
             get: function () {
                 return this._activeAnimatables;
             },
@@ -21811,7 +21829,7 @@ var BABYLON;
                     camera = freeCamera;
                 }
                 camera.minZ = radius * 0.01;
-                camera.maxZ = radius * 100;
+                camera.maxZ = radius * 1000;
                 camera.speed = radius * 0.2;
                 this.activeCamera = camera;
                 var canvas = this.getEngine().getRenderingCanvas();
@@ -27118,6 +27136,10 @@ var BABYLON;
             this._fallbacks = null;
             this._prepareEffect();
         };
+        Effect.prototype.getSpecificUniformLocations = function (names) {
+            var engine = this._engine;
+            return engine.getUniforms(this._program, names);
+        };
         Effect.prototype._prepareEffect = function () {
             var attributesNames = this._attributesNames;
             var defines = this.defines;
@@ -27313,6 +27335,14 @@ var BABYLON;
         Effect.prototype.bindUniformBlock = function (blockName, index) {
             this._engine.bindUniformBlock(this._program, blockName, index);
         };
+        Effect.prototype.setInt = function (uniformName, value) {
+            var cache = this._valueCache[uniformName];
+            if (cache !== undefined && cache === value)
+                return this;
+            this._valueCache[uniformName] = value;
+            this._engine.setInt(this.getUniform(uniformName), value);
+            return this;
+        };
         Effect.prototype.setIntArray = function (uniformName, array) {
             this._valueCache[uniformName] = null;
             this._engine.setIntArray(this.getUniform(uniformName), array);
@@ -48152,6 +48182,7 @@ var BABYLON;
             _this._textures = {};
             _this._textureArrays = {};
             _this._floats = {};
+            _this._ints = {};
             _this._floatsArrays = {};
             _this._colors3 = {};
             _this._colors3Arrays = {};
@@ -48210,6 +48241,11 @@ var BABYLON;
             this._floats[name] = value;
             return this;
         };
+        ShaderMaterial.prototype.setInt = function (name, value) {
+            this._checkUniform(name);
+            this._ints[name] = value;
+            return this;
+        };
         ShaderMaterial.prototype.setFloats = function (name, value) {
             this._checkUniform(name);
             this._floatsArrays[name] = value;
@@ -48398,11 +48434,15 @@ var BABYLON;
                 for (name in this._textureArrays) {
                     this._effect.setTextureArray(name, this._textureArrays[name]);
                 }
+                // Int    
+                for (name in this._ints) {
+                    this._effect.setInt(name, this._ints[name]);
+                }
                 // Float    
                 for (name in this._floats) {
                     this._effect.setFloat(name, this._floats[name]);
                 }
-                // Float s   
+                // Floats   
                 for (name in this._floatsArrays) {
                     this._effect.setArray(name, this._floatsArrays[name]);
                 }
@@ -78707,6 +78747,7 @@ var BABYLON;
                     _this._animationsTime.beginMonitoring();
                 }
                 _this.scene.getEngine()._drawCalls.fetchNewFrame();
+                _this.scene.getEngine()._textureCollisions.fetchNewFrame();
             });
             // After render
             this._onAfterRenderObserver = scene.onAfterRenderObservable.add(function () {
@@ -79092,7 +79133,7 @@ var BABYLON;
         });
         Object.defineProperty(SceneInstrumentation.prototype, "drawCallsCounter", {
             /**
-             * Gets the perf counter used for frame time capture
+             * Gets the perf counter used for draw calls
              */
             get: function () {
                 return this.scene.getEngine()._drawCalls;
@@ -79100,6 +79141,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(SceneInstrumentation.prototype, "textureCollisionsCounter", {
+            /**
+             * Gets the perf counter used for texture collisions
+             */
+            get: function () {
+                return this.scene.getEngine()._textureCollisions;
+            },
+            enumerable: true,
+            configurable: true
+        });
         SceneInstrumentation.prototype.dispose = function () {
             this.scene.onAfterRenderObservable.remove(this._onAfterRenderObserver);
             this._onAfterRenderObserver = null;

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 2
dist/preview release/inspector/babylon.inspector.bundle.js


+ 6 - 0
dist/preview release/inspector/babylon.inspector.js

@@ -3567,6 +3567,12 @@ var INSPECTOR;
                     elem: elemValue,
                     updateFct: function () { return _this._sceneInstrumentation.drawCallsCounter.current.toString(); }
                 });
+                _this._createStatLabel("Texture collisions", _this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', _this._panel);
+                _this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._sceneInstrumentation.textureCollisionsCounter.current.toString(); }
+                });
                 _this._createStatLabel("Total lights", _this._panel);
                 elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', _this._panel);
                 _this._updatableProperties.push({

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


+ 15 - 0
dist/preview release/serializers/babylon.glTF2Serializer.d.ts

@@ -0,0 +1,15 @@
+
+declare module BABYLON {
+    class GLTFExport {
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes
+         * @param materials
+         * @param glb
+         */
+        static GLTF(meshes: Mesh[], filename: string, glb?: boolean): {
+            [fileName: string]: string | Blob;
+        };
+    }
+}

+ 538 - 0
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -0,0 +1,538 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+var BABYLON;
+(function (BABYLON) {
+    var GLTFExport = /** @class */ (function () {
+        function GLTFExport() {
+        }
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes
+         * @param materials
+         * @param glb
+         */
+        GLTFExport.GLTF = function (meshes, filename, glb) {
+            /**
+             * Creates a buffer view based on teh supplied arguments
+             * @param bufferIndex
+             * @param byteOffset
+             * @param byteLength
+             */
+            function createBufferView(bufferIndex, byteOffset, byteLength) {
+                var bufferview = { buffer: bufferIndex, byteLength: byteLength };
+                if (byteOffset > 0) {
+                    bufferview.byteOffset = byteOffset;
+                }
+                return bufferview;
+            }
+            /**
+             * Creates an accessor based on the supplied arguments
+             * @param bufferviewIndex
+             * @param name
+             * @param type
+             * @param componentType
+             * @param count
+             * @param min
+             * @param max
+             */
+            function createAccessor(bufferviewIndex, name, type, componentType, count, min, max) {
+                var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
+                if (min) {
+                    accessor.min = min;
+                }
+                if (max) {
+                    accessor.max = max;
+                }
+                return accessor;
+            }
+            /**
+             * Calculates the minimum and maximum values of an array of floats, based on stride
+             * @param buff
+             * @param vertexStart
+             * @param vertexCount
+             * @param arrayOffset
+             * @param stride
+             */
+            function calculateMinMax(buff, vertexStart, vertexCount, arrayOffset, stride) {
+                var min = [Infinity, Infinity, Infinity];
+                var max = [-Infinity, -Infinity, -Infinity];
+                var end = vertexStart + vertexCount;
+                if (vertexCount > 0) {
+                    for (var i = vertexStart; i < end; ++i) {
+                        var index = stride * i;
+                        for (var j = 0; j < stride; ++j) {
+                            if (buff[index] < min[j]) {
+                                min[j] = buff[index];
+                            }
+                            if (buff[index] > max[j]) {
+                                max[j] = buff[index];
+                            }
+                            ++index;
+                        }
+                    }
+                }
+                return { min: min, max: max };
+            }
+            /**
+             * Write mesh attribute data to buffer.
+             * Returns the bytelength of the data.
+             * @param vertexBufferType
+             * @param submesh
+             * @param meshAttributeArray
+             * @param strideSize
+             * @param byteOffset
+             * @param dataBuffer
+             * @param useRightHandedSystem
+             */
+            function writeAttributeData(vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem) {
+                var byteOff = byteOffset;
+                var end = submesh.verticesStart + submesh.verticesCount;
+                var byteLength = 0;
+                switch (vertexBufferType) {
+                    case BABYLON.VertexBuffer.PositionKind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                            if (useRightHandedSystem) {
+                                dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                            }
+                            else {
+                                dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                            }
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 12;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.NormalKind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                            if (useRightHandedSystem) {
+                                dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                            }
+                            else {
+                                dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                            }
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 12;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.TangentKind: {
+                        for (var k = submesh.indexStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                            if (useRightHandedSystem) {
+                                dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                            }
+                            else {
+                                dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                            }
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 16;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.ColorKind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 16;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.UVKind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 8;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.UV2Kind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 8;
+                        break;
+                    }
+                    default: {
+                        throw new Error("Unsupported vertex buffer type: " + vertexBufferType);
+                    }
+                }
+                return byteLength;
+            }
+            /**
+             * Generates a glb file from the json and binary data.
+             * Returns an object with the glb file name as the key and data as the value.
+             * @param jsonText
+             * @param binaryBuffer
+             * @param glTFPrefix
+             */
+            function createGLB(jsonText, binaryBuffer, glTFPrefix) {
+                var glbFileName = glTFPrefix + '.glb';
+                var headerLength = 12;
+                var chunkLengthPrefix = 8;
+                var jsonLength = jsonText.length;
+                var jsonRemainder = jsonLength % 4;
+                var binRemainder = binaryBuffer.byteLength % 4;
+                var jsonPadding = jsonRemainder === 0 ? jsonRemainder : 4 - jsonRemainder;
+                var binPadding = binRemainder === 0 ? binRemainder : 4 - binRemainder;
+                var totalByteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding;
+                //header
+                var headerBuffer = new ArrayBuffer(headerLength);
+                var headerBufferView = new DataView(headerBuffer);
+                headerBufferView.setUint32(0, 0x46546C67, true); //glTF
+                headerBufferView.setUint32(4, 2, true); // version
+                headerBufferView.setUint32(8, totalByteLength, true); // total bytes in file
+                //json chunk
+                var jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
+                var jsonChunkBufferView = new DataView(jsonChunkBuffer);
+                jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
+                jsonChunkBufferView.setUint32(4, 0x4E4F534A, true);
+                //json chunk bytes
+                var jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
+                for (var i = 0; i < jsonLength; ++i) {
+                    jsonData[i] = jsonText.charCodeAt(i);
+                }
+                //json padding
+                var jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
+                for (var i = 0; i < jsonPadding; ++i) {
+                    jsonPaddingView[i] = 0x20;
+                }
+                //binary chunk
+                var binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
+                var binaryChunkBufferView = new DataView(binaryChunkBuffer);
+                binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength, true);
+                binaryChunkBufferView.setUint32(4, 0x004E4942, true);
+                // binary padding
+                var binPaddingBuffer = new ArrayBuffer(binPadding);
+                var binPaddingView = new Uint8Array(binPaddingBuffer);
+                for (var i = 0; i < binPadding; ++i) {
+                    binPaddingView[i] = 0;
+                }
+                // binary data
+                var glbFile = new Blob([headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer], { type: 'application/octet-stream' });
+                return _a = {},
+                    _a[glbFileName] = glbFile,
+                    _a;
+                var _a;
+            }
+            /**
+             * Creates a glTF scene based on the array of meshes.
+             * Returns the the total byte offset.
+             * @param gltf
+             * @param totalByteOffset
+             * @param buffer
+             * @param dataBuffer
+             */
+            function createScene(gltf, totalByteOffset, dataBuffer) {
+                var scene = { nodes: new Array() };
+                for (var i = 0; i < meshes.length; ++i) {
+                    // create node to hold translation/rotation/scale and the mesh
+                    var node = { mesh: -1 };
+                    var babylonMesh = meshes[i];
+                    var useRightHandedSystem = babylonMesh.getScene().useRightHandedSystem;
+                    if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
+                        if (useRightHandedSystem) {
+                            node.translation = babylonMesh.position.asArray();
+                        }
+                        else {
+                            node.translation = [babylonMesh.position.x, babylonMesh.position.y, -babylonMesh.position.z];
+                        }
+                    }
+                    if (!(babylonMesh.scaling.x === 1 && babylonMesh.scaling.y === 1 && babylonMesh.scaling.z === 1)) {
+                        if (useRightHandedSystem) {
+                            node.scale = babylonMesh.scaling.asArray();
+                        }
+                        else {
+                            node.scale = [babylonMesh.scaling.x, babylonMesh.scaling.y, -babylonMesh.scaling.z];
+                        }
+                    }
+                    var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(babylonMesh.rotation.y, babylonMesh.rotation.x, babylonMesh.rotation.z);
+                    if (babylonMesh.rotationQuaternion) {
+                        rotationQuaternion = rotationQuaternion.multiply(babylonMesh.rotationQuaternion);
+                    }
+                    if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
+                        if (useRightHandedSystem) {
+                            node.rotation = rotationQuaternion.asArray();
+                        }
+                        else {
+                            node.rotation = [-rotationQuaternion.x, -rotationQuaternion.y, rotationQuaternion.z, rotationQuaternion.w];
+                        }
+                    }
+                    var positionVertexBuffer = void 0;
+                    var positions = void 0;
+                    var positionVertexBufferOffset = void 0;
+                    var positionStrideSize = void 0;
+                    var normalVertexBuffer = void 0;
+                    var normals = void 0;
+                    var normalStrideSize = void 0;
+                    var tangentVertexBuffer = void 0;
+                    var tangents = void 0;
+                    var tangentStrideSize = void 0;
+                    var colorVertexBuffer = void 0;
+                    var colors = void 0;
+                    var colorStrideSize = void 0;
+                    var texCoord0VertexBuffer = void 0;
+                    var texCoords0 = void 0;
+                    var texCoord0StrideSize = void 0;
+                    var texCoord1VertexBuffer = void 0;
+                    var texCoords1 = void 0;
+                    var texCoord1StrideSize = void 0;
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                        positionVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
+                        positions = positionVertexBuffer.getData();
+                        positionVertexBufferOffset = positionVertexBuffer.getOffset();
+                        positionStrideSize = positionVertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                        normalVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
+                        normals = normalVertexBuffer.getData();
+                        normalStrideSize = normalVertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                        tangentVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
+                        tangents = tangentVertexBuffer.getData();
+                        tangentStrideSize = tangentVertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
+                        colorVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
+                        colors = colorVertexBuffer.getData();
+                        colorStrideSize = colorVertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                        texCoord0VertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
+                        texCoords0 = texCoord0VertexBuffer.getData();
+                        texCoord0StrideSize = texCoord0VertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
+                        texCoord1VertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
+                        texCoords1 = texCoord1VertexBuffer.getData();
+                        texCoord1StrideSize = texCoord1VertexBuffer.getStrideSize();
+                    }
+                    // create mesh
+                    var mesh = { primitives: new Array() };
+                    mesh.primitives = [];
+                    if (babylonMesh.name) {
+                        mesh.name = babylonMesh.name;
+                    }
+                    // go through all mesh primitives (submeshes)
+                    for (var j = 0; j < babylonMesh.subMeshes.length; ++j) {
+                        var submesh = babylonMesh.subMeshes[j];
+                        var meshPrimitive = { attributes: {} };
+                        // Loop through each attribute of the submesh (mesh primitive)
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.PositionKind, submesh, positions, positionStrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 12;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var result = calculateMinMax(positions, submesh.verticesStart, submesh.verticesCount, positionVertexBufferOffset, positionStrideSize);
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Position", "VEC3", 5126, submesh.verticesCount, result.min, result.max);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.POSITION = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.NormalKind, submesh, normals, normalStrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 12;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Normal", "VEC3", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.NORMAL = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.TangentKind, submesh, tangents, tangentStrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 16;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Tangent", "VEC4", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.TANGENT = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.ColorKind, submesh, colors, colorStrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 16;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Color", "VEC4", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.COLOR_0 = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.UVKind, submesh, texCoords0, texCoord0StrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 8;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.TEXCOORD_0 = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.UV2Kind, submesh, texCoords1, texCoord1StrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 8;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.TEXCOORD_1 = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.getTotalIndices() > 0) {
+                            if (dataBuffer) {
+                                var indices = babylonMesh.getIndices();
+                                var start = submesh.indexStart;
+                                var end = submesh.indexCount + start;
+                                var byteOff = totalByteOffset;
+                                for (var k = start; k < end; k = k + 3) {
+                                    dataBuffer.setUint32(byteOff, indices[k], true);
+                                    byteOff += 4;
+                                    dataBuffer.setUint32(byteOff, indices[k + 1], true);
+                                    byteOff += 4;
+                                    dataBuffer.setUint32(byteOff, indices[k + 2], true);
+                                    byteOff += 4;
+                                }
+                                var byteLength = submesh.indexCount * 4;
+                                totalByteOffset += byteLength;
+                            }
+                            else {
+                                // Create bufferview
+                                var indicesCount = submesh.indexCount;
+                                var byteLength = indicesCount * 4;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Indices", "SCALAR", 5125, indicesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.indices = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.material) {
+                            if (!gltf.materials) {
+                                gltf.materials = new Array();
+                            }
+                            meshPrimitive.material = gltf.materials.length - 1;
+                        }
+                        mesh.primitives.push(meshPrimitive);
+                    }
+                    gltf.meshes.push(mesh);
+                    node.mesh = gltf.meshes.length - 1;
+                    gltf.nodes.push(node);
+                    scene.nodes.push(gltf.nodes.length - 1);
+                }
+                gltf.scenes.push(scene);
+                return totalByteOffset;
+            }
+            var glTFPrefix = filename.replace(/\.[^/.]+$/, "");
+            var gltf = {
+                buffers: new Array(),
+                bufferViews: new Array(),
+                asset: { generator: "BabylonJS", version: "2.0" },
+                meshes: new Array(),
+                scenes: new Array(),
+                nodes: new Array(),
+                accessors: new Array()
+            };
+            var totalByteOffset = 0;
+            var binaryBuffer;
+            var dataBuffer;
+            // Create scene.  First pass calculates the totalByteOffset.
+            totalByteOffset = createScene(gltf, totalByteOffset, null);
+            var buff = { byteLength: totalByteOffset };
+            if (!glb) {
+                buff.uri = glTFPrefix + '.bin';
+            }
+            gltf.buffers.push(buff);
+            var text = JSON.stringify(gltf, null, 2);
+            binaryBuffer = new ArrayBuffer(totalByteOffset);
+            dataBuffer = new DataView(binaryBuffer);
+            totalByteOffset = 0;
+            // Create scene.  Second pass generates the binary data
+            createScene(gltf, totalByteOffset, dataBuffer);
+            if (glb) {
+                var glbFile = createGLB(text, binaryBuffer, glTFPrefix);
+                return glbFile;
+            }
+            var glTFFileName = glTFPrefix + '.gltf';
+            var glTFBinFile = glTFPrefix + '.bin';
+            var bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
+            return _a = {},
+                _a[glTFFileName] = text,
+                _a[glTFBinFile] = bin,
+                _a;
+            var _a;
+        };
+        return GLTFExport;
+    }());
+    BABYLON.GLTFExport = GLTFExport;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.glTFSerializer.js.map

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 0
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 539 - 0
dist/preview release/serializers/babylonjs.serializers.js

@@ -143,6 +143,545 @@ var BABYLON;
 //# sourceMappingURL=babylon.objSerializer.js.map
 
 
+var BABYLON;
+(function (BABYLON) {
+    var GLTFExport = /** @class */ (function () {
+        function GLTFExport() {
+        }
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes
+         * @param materials
+         * @param glb
+         */
+        GLTFExport.GLTF = function (meshes, filename, glb) {
+            /**
+             * Creates a buffer view based on teh supplied arguments
+             * @param bufferIndex
+             * @param byteOffset
+             * @param byteLength
+             */
+            function createBufferView(bufferIndex, byteOffset, byteLength) {
+                var bufferview = { buffer: bufferIndex, byteLength: byteLength };
+                if (byteOffset > 0) {
+                    bufferview.byteOffset = byteOffset;
+                }
+                return bufferview;
+            }
+            /**
+             * Creates an accessor based on the supplied arguments
+             * @param bufferviewIndex
+             * @param name
+             * @param type
+             * @param componentType
+             * @param count
+             * @param min
+             * @param max
+             */
+            function createAccessor(bufferviewIndex, name, type, componentType, count, min, max) {
+                var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
+                if (min) {
+                    accessor.min = min;
+                }
+                if (max) {
+                    accessor.max = max;
+                }
+                return accessor;
+            }
+            /**
+             * Calculates the minimum and maximum values of an array of floats, based on stride
+             * @param buff
+             * @param vertexStart
+             * @param vertexCount
+             * @param arrayOffset
+             * @param stride
+             */
+            function calculateMinMax(buff, vertexStart, vertexCount, arrayOffset, stride) {
+                var min = [Infinity, Infinity, Infinity];
+                var max = [-Infinity, -Infinity, -Infinity];
+                var end = vertexStart + vertexCount;
+                if (vertexCount > 0) {
+                    for (var i = vertexStart; i < end; ++i) {
+                        var index = stride * i;
+                        for (var j = 0; j < stride; ++j) {
+                            if (buff[index] < min[j]) {
+                                min[j] = buff[index];
+                            }
+                            if (buff[index] > max[j]) {
+                                max[j] = buff[index];
+                            }
+                            ++index;
+                        }
+                    }
+                }
+                return { min: min, max: max };
+            }
+            /**
+             * Write mesh attribute data to buffer.
+             * Returns the bytelength of the data.
+             * @param vertexBufferType
+             * @param submesh
+             * @param meshAttributeArray
+             * @param strideSize
+             * @param byteOffset
+             * @param dataBuffer
+             * @param useRightHandedSystem
+             */
+            function writeAttributeData(vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem) {
+                var byteOff = byteOffset;
+                var end = submesh.verticesStart + submesh.verticesCount;
+                var byteLength = 0;
+                switch (vertexBufferType) {
+                    case BABYLON.VertexBuffer.PositionKind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                            if (useRightHandedSystem) {
+                                dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                            }
+                            else {
+                                dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                            }
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 12;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.NormalKind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                            if (useRightHandedSystem) {
+                                dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                            }
+                            else {
+                                dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                            }
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 12;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.TangentKind: {
+                        for (var k = submesh.indexStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                            if (useRightHandedSystem) {
+                                dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                            }
+                            else {
+                                dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                            }
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 16;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.ColorKind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 16;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.UVKind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 8;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.UV2Kind: {
+                        for (var k = submesh.verticesStart; k < end; ++k) {
+                            var index = k * strideSize;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                            byteOff += 4;
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                            byteOff += 4;
+                        }
+                        byteLength = submesh.verticesCount * 8;
+                        break;
+                    }
+                    default: {
+                        throw new Error("Unsupported vertex buffer type: " + vertexBufferType);
+                    }
+                }
+                return byteLength;
+            }
+            /**
+             * Generates a glb file from the json and binary data.
+             * Returns an object with the glb file name as the key and data as the value.
+             * @param jsonText
+             * @param binaryBuffer
+             * @param glTFPrefix
+             */
+            function createGLB(jsonText, binaryBuffer, glTFPrefix) {
+                var glbFileName = glTFPrefix + '.glb';
+                var headerLength = 12;
+                var chunkLengthPrefix = 8;
+                var jsonLength = jsonText.length;
+                var jsonRemainder = jsonLength % 4;
+                var binRemainder = binaryBuffer.byteLength % 4;
+                var jsonPadding = jsonRemainder === 0 ? jsonRemainder : 4 - jsonRemainder;
+                var binPadding = binRemainder === 0 ? binRemainder : 4 - binRemainder;
+                var totalByteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding;
+                //header
+                var headerBuffer = new ArrayBuffer(headerLength);
+                var headerBufferView = new DataView(headerBuffer);
+                headerBufferView.setUint32(0, 0x46546C67, true); //glTF
+                headerBufferView.setUint32(4, 2, true); // version
+                headerBufferView.setUint32(8, totalByteLength, true); // total bytes in file
+                //json chunk
+                var jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
+                var jsonChunkBufferView = new DataView(jsonChunkBuffer);
+                jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
+                jsonChunkBufferView.setUint32(4, 0x4E4F534A, true);
+                //json chunk bytes
+                var jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
+                for (var i = 0; i < jsonLength; ++i) {
+                    jsonData[i] = jsonText.charCodeAt(i);
+                }
+                //json padding
+                var jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
+                for (var i = 0; i < jsonPadding; ++i) {
+                    jsonPaddingView[i] = 0x20;
+                }
+                //binary chunk
+                var binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
+                var binaryChunkBufferView = new DataView(binaryChunkBuffer);
+                binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength, true);
+                binaryChunkBufferView.setUint32(4, 0x004E4942, true);
+                // binary padding
+                var binPaddingBuffer = new ArrayBuffer(binPadding);
+                var binPaddingView = new Uint8Array(binPaddingBuffer);
+                for (var i = 0; i < binPadding; ++i) {
+                    binPaddingView[i] = 0;
+                }
+                // binary data
+                var glbFile = new Blob([headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer], { type: 'application/octet-stream' });
+                return _a = {},
+                    _a[glbFileName] = glbFile,
+                    _a;
+                var _a;
+            }
+            /**
+             * Creates a glTF scene based on the array of meshes.
+             * Returns the the total byte offset.
+             * @param gltf
+             * @param totalByteOffset
+             * @param buffer
+             * @param dataBuffer
+             */
+            function createScene(gltf, totalByteOffset, dataBuffer) {
+                var scene = { nodes: new Array() };
+                for (var i = 0; i < meshes.length; ++i) {
+                    // create node to hold translation/rotation/scale and the mesh
+                    var node = { mesh: -1 };
+                    var babylonMesh = meshes[i];
+                    var useRightHandedSystem = babylonMesh.getScene().useRightHandedSystem;
+                    if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
+                        if (useRightHandedSystem) {
+                            node.translation = babylonMesh.position.asArray();
+                        }
+                        else {
+                            node.translation = [babylonMesh.position.x, babylonMesh.position.y, -babylonMesh.position.z];
+                        }
+                    }
+                    if (!(babylonMesh.scaling.x === 1 && babylonMesh.scaling.y === 1 && babylonMesh.scaling.z === 1)) {
+                        if (useRightHandedSystem) {
+                            node.scale = babylonMesh.scaling.asArray();
+                        }
+                        else {
+                            node.scale = [babylonMesh.scaling.x, babylonMesh.scaling.y, -babylonMesh.scaling.z];
+                        }
+                    }
+                    var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(babylonMesh.rotation.y, babylonMesh.rotation.x, babylonMesh.rotation.z);
+                    if (babylonMesh.rotationQuaternion) {
+                        rotationQuaternion = rotationQuaternion.multiply(babylonMesh.rotationQuaternion);
+                    }
+                    if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
+                        if (useRightHandedSystem) {
+                            node.rotation = rotationQuaternion.asArray();
+                        }
+                        else {
+                            node.rotation = [-rotationQuaternion.x, -rotationQuaternion.y, rotationQuaternion.z, rotationQuaternion.w];
+                        }
+                    }
+                    var positionVertexBuffer = void 0;
+                    var positions = void 0;
+                    var positionVertexBufferOffset = void 0;
+                    var positionStrideSize = void 0;
+                    var normalVertexBuffer = void 0;
+                    var normals = void 0;
+                    var normalStrideSize = void 0;
+                    var tangentVertexBuffer = void 0;
+                    var tangents = void 0;
+                    var tangentStrideSize = void 0;
+                    var colorVertexBuffer = void 0;
+                    var colors = void 0;
+                    var colorStrideSize = void 0;
+                    var texCoord0VertexBuffer = void 0;
+                    var texCoords0 = void 0;
+                    var texCoord0StrideSize = void 0;
+                    var texCoord1VertexBuffer = void 0;
+                    var texCoords1 = void 0;
+                    var texCoord1StrideSize = void 0;
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                        positionVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
+                        positions = positionVertexBuffer.getData();
+                        positionVertexBufferOffset = positionVertexBuffer.getOffset();
+                        positionStrideSize = positionVertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                        normalVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
+                        normals = normalVertexBuffer.getData();
+                        normalStrideSize = normalVertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                        tangentVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
+                        tangents = tangentVertexBuffer.getData();
+                        tangentStrideSize = tangentVertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
+                        colorVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
+                        colors = colorVertexBuffer.getData();
+                        colorStrideSize = colorVertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                        texCoord0VertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
+                        texCoords0 = texCoord0VertexBuffer.getData();
+                        texCoord0StrideSize = texCoord0VertexBuffer.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
+                        texCoord1VertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
+                        texCoords1 = texCoord1VertexBuffer.getData();
+                        texCoord1StrideSize = texCoord1VertexBuffer.getStrideSize();
+                    }
+                    // create mesh
+                    var mesh = { primitives: new Array() };
+                    mesh.primitives = [];
+                    if (babylonMesh.name) {
+                        mesh.name = babylonMesh.name;
+                    }
+                    // go through all mesh primitives (submeshes)
+                    for (var j = 0; j < babylonMesh.subMeshes.length; ++j) {
+                        var submesh = babylonMesh.subMeshes[j];
+                        var meshPrimitive = { attributes: {} };
+                        // Loop through each attribute of the submesh (mesh primitive)
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.PositionKind, submesh, positions, positionStrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 12;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var result = calculateMinMax(positions, submesh.verticesStart, submesh.verticesCount, positionVertexBufferOffset, positionStrideSize);
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Position", "VEC3", 5126, submesh.verticesCount, result.min, result.max);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.POSITION = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.NormalKind, submesh, normals, normalStrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 12;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Normal", "VEC3", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.NORMAL = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.TangentKind, submesh, tangents, tangentStrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 16;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Tangent", "VEC4", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.TANGENT = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.ColorKind, submesh, colors, colorStrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 16;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Color", "VEC4", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.COLOR_0 = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.UVKind, submesh, texCoords0, texCoord0StrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 8;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.TEXCOORD_0 = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(BABYLON.VertexBuffer.UV2Kind, submesh, texCoords1, texCoord1StrideSize, totalByteOffset, dataBuffer, useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                var byteLength = submesh.verticesCount * 8;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.attributes.TEXCOORD_1 = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.getTotalIndices() > 0) {
+                            if (dataBuffer) {
+                                var indices = babylonMesh.getIndices();
+                                var start = submesh.indexStart;
+                                var end = submesh.indexCount + start;
+                                var byteOff = totalByteOffset;
+                                for (var k = start; k < end; k = k + 3) {
+                                    dataBuffer.setUint32(byteOff, indices[k], true);
+                                    byteOff += 4;
+                                    dataBuffer.setUint32(byteOff, indices[k + 1], true);
+                                    byteOff += 4;
+                                    dataBuffer.setUint32(byteOff, indices[k + 2], true);
+                                    byteOff += 4;
+                                }
+                                var byteLength = submesh.indexCount * 4;
+                                totalByteOffset += byteLength;
+                            }
+                            else {
+                                // Create bufferview
+                                var indicesCount = submesh.indexCount;
+                                var byteLength = indicesCount * 4;
+                                var bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+                                // Create accessor
+                                var accessor = createAccessor(gltf.bufferViews.length - 1, "Indices", "SCALAR", 5125, indicesCount);
+                                gltf.accessors.push(accessor);
+                                meshPrimitive.indices = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.material) {
+                            if (!gltf.materials) {
+                                gltf.materials = new Array();
+                            }
+                            meshPrimitive.material = gltf.materials.length - 1;
+                        }
+                        mesh.primitives.push(meshPrimitive);
+                    }
+                    gltf.meshes.push(mesh);
+                    node.mesh = gltf.meshes.length - 1;
+                    gltf.nodes.push(node);
+                    scene.nodes.push(gltf.nodes.length - 1);
+                }
+                gltf.scenes.push(scene);
+                return totalByteOffset;
+            }
+            var glTFPrefix = filename.replace(/\.[^/.]+$/, "");
+            var gltf = {
+                buffers: new Array(),
+                bufferViews: new Array(),
+                asset: { generator: "BabylonJS", version: "2.0" },
+                meshes: new Array(),
+                scenes: new Array(),
+                nodes: new Array(),
+                accessors: new Array()
+            };
+            var totalByteOffset = 0;
+            var binaryBuffer;
+            var dataBuffer;
+            // Create scene.  First pass calculates the totalByteOffset.
+            totalByteOffset = createScene(gltf, totalByteOffset, null);
+            var buff = { byteLength: totalByteOffset };
+            if (!glb) {
+                buff.uri = glTFPrefix + '.bin';
+            }
+            gltf.buffers.push(buff);
+            var text = JSON.stringify(gltf, null, 2);
+            binaryBuffer = new ArrayBuffer(totalByteOffset);
+            dataBuffer = new DataView(binaryBuffer);
+            totalByteOffset = 0;
+            // Create scene.  Second pass generates the binary data
+            createScene(gltf, totalByteOffset, dataBuffer);
+            if (glb) {
+                var glbFile = createGLB(text, binaryBuffer, glTFPrefix);
+                return glbFile;
+            }
+            var glTFFileName = glTFPrefix + '.gltf';
+            var glTFBinFile = glTFPrefix + '.bin';
+            var bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
+            return _a = {},
+                _a[glTFFileName] = text,
+                _a[glTFBinFile] = bin,
+                _a;
+            var _a;
+        };
+        return GLTFExport;
+    }());
+    BABYLON.GLTFExport = GLTFExport;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.glTFSerializer.js.map
+
+
 (function universalModuleDefinition(root, factory) {
                 var f = factory();
                 if (root && root["BABYLON"]) {

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


+ 16 - 0
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -10,3 +10,19 @@ declare module BABYLON {
         static MTL(mesh: Mesh): string;
     }
 }
+
+
+declare module BABYLON {
+    class GLTFExport {
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes
+         * @param materials
+         * @param glb
+         */
+        static GLTF(meshes: Mesh[], filename: string, glb?: boolean): {
+            [fileName: string]: string | Blob;
+        };
+    }
+}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 31 - 31
dist/preview release/viewer/babylon.viewer.js


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

@@ -86,6 +86,13 @@ module INSPECTOR {
                     updateFct: () => { return this._sceneInstrumentation!.drawCallsCounter.current.toString() }
                 });
 
+                this._createStatLabel("Texture collisions", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: () => { return this._sceneInstrumentation!.textureCollisionsCounter.current.toString() }
+                });
+
                 this._createStatLabel("Total lights", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({

+ 683 - 0
serializers/src/glTF/2.0/babylon.glTFSerializer.ts

@@ -0,0 +1,683 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON {
+    interface OGLTFAsset {
+        generator: string;
+        version: string;
+    }
+    interface OGLTFScene {
+        nodes: number[];
+    }
+    interface OGLTFNode {
+        mesh: number;
+        translation?: number[];
+        scale?: number[];
+        rotation?: number[];
+    }
+    interface OGLTFMeshPrimitive {
+        attributes: { [index: string]: number };
+        indices?: number;
+        material?: number;
+    }
+    interface OGLTFMesh {
+        name?: string;
+        primitives: OGLTFMeshPrimitive[];
+    }
+    interface OGLTFMaterial {
+    }
+    interface OGLTFBuffer {
+        byteLength: number;
+        uri?: string;
+    }
+    interface OGLTFBufferView {
+        buffer: number;
+        byteOffset?: number;
+        byteLength: number;
+    }
+    interface OGLTFAccessor {
+        name: string;
+        bufferView: number;
+        componentType: number;
+        count: number;
+        type: string;
+        min?: number[];
+        max?: number[];
+    }
+    interface OGLTF {
+        buffers: OGLTFBuffer[];
+        asset: OGLTFAsset;
+        meshes: OGLTFMesh[];
+        materials?: OGLTFMaterial[];
+        scenes: OGLTFScene[];
+        nodes: OGLTFNode[];
+        bufferViews: OGLTFBufferView[];
+        accessors: OGLTFAccessor[];
+    }
+
+    export class GLTFExport {
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes 
+         * @param materials 
+         * @param glb 
+         */
+        public static GLTF(meshes: Mesh[], filename: string, glb?: boolean): {[fileName: string]: string | Blob} {
+            /**
+             * Creates a buffer view based on teh supplied arguments
+             * @param bufferIndex 
+             * @param byteOffset 
+             * @param byteLength 
+             */
+            function createBufferView(bufferIndex: number, byteOffset: number, byteLength: number): OGLTFBufferView {
+                let bufferview: OGLTFBufferView = {buffer: bufferIndex, byteLength: byteLength};
+                if (byteOffset > 0) {
+                    bufferview.byteOffset = byteOffset;
+                }
+
+                return bufferview;
+            }
+            /**
+             * Creates an accessor based on the supplied arguments
+             * @param bufferviewIndex 
+             * @param name 
+             * @param type 
+             * @param componentType 
+             * @param count 
+             * @param min 
+             * @param max 
+             */
+            function createAccessor(bufferviewIndex: number, name: string, type: string, componentType: number, count: number, min?: number[], max?: number[]): OGLTFAccessor {
+                let accessor: OGLTFAccessor = {name: name, bufferView: bufferviewIndex, componentType: componentType, count:count, type:type};
+
+                if (min) {
+                    accessor.min = min;
+                }
+                if (max) {
+                    accessor.max = max;
+                }
+
+                return accessor;
+            }
+            /**
+             * Calculates the minimum and maximum values of an array of floats, based on stride
+             * @param buff 
+             * @param vertexStart 
+             * @param vertexCount 
+             * @param arrayOffset 
+             * @param stride 
+             */
+            function calculateMinMax(buff: FloatArray, vertexStart: number, vertexCount: number, arrayOffset: number, stride: number): {min: number[], max: number[]} {
+                let min = [Infinity, Infinity, Infinity];
+                let max = [-Infinity, -Infinity, -Infinity];
+                let end = vertexStart + vertexCount;
+                if (vertexCount > 0) {
+                    for (let i = vertexStart; i < end; ++i) {
+                        let index = stride * i;
+                        for (let j = 0; j < stride; ++j) {
+                            if (buff[index] < min[j]) {
+                                min[j] = buff[index];
+                            }
+                            if (buff[index] > max[j]) {
+                                max[j] = buff[index];
+                            }
+                            ++index;
+                        }
+                    }
+                }
+                return { min, max };
+            }
+            /**
+             * Write mesh attribute data to buffer.
+             * Returns the bytelength of the data.
+             * @param vertexBufferType 
+             * @param submesh 
+             * @param meshAttributeArray 
+             * @param strideSize 
+             * @param byteOffset 
+             * @param dataBuffer 
+             * @param useRightHandedSystem 
+             */
+            function writeAttributeData(vertexBufferType: string, submesh: BABYLON.SubMesh, meshAttributeArray: FloatArray, strideSize: number, byteOffset: number, dataBuffer: DataView, useRightHandedSystem: boolean): number {
+                let byteOff = byteOffset;
+                let end = submesh.verticesStart + submesh.verticesCount;
+                let byteLength = 0;
+
+                switch(vertexBufferType) {
+                    case BABYLON.VertexBuffer.PositionKind: {
+                        for (let k = submesh.verticesStart; k < end; ++k) {
+                            let index = k * strideSize!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                            byteOff += 4!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                            byteOff += 4!;
+                            if (useRightHandedSystem) {
+                                dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 2], true);
+                            }
+                            else {
+                                dataBuffer!.setFloat32(byteOff, -meshAttributeArray![index + 2], true);
+                            }
+                            
+                            byteOff += 4!;
+                        }
+                        byteLength = submesh.verticesCount * 12;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.NormalKind: {
+                        for (let k = submesh.verticesStart; k < end; ++k) {
+                            let index = k * strideSize!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                            byteOff += 4!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                            byteOff += 4!;
+                            if (useRightHandedSystem) {
+                                dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 2], true);
+                            }
+                            else {
+                                dataBuffer!.setFloat32(byteOff, -meshAttributeArray![index + 2], true);
+                            }
+                            
+                            byteOff += 4!;
+                        }
+                        byteLength = submesh.verticesCount * 12;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.TangentKind: {
+                        for (let k = submesh.indexStart; k < end; ++k) {
+                            let index = k * strideSize!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                            byteOff += 4!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                            byteOff += 4!;
+                            if (useRightHandedSystem) {
+                                dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 2], true);
+                            }
+                            else {
+                                dataBuffer!.setFloat32(byteOff, -meshAttributeArray![index + 2], true);
+                            }
+                            byteOff += 4!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 3], true);
+                            byteOff += 4!;
+                        }
+                        byteLength = submesh.verticesCount * 16;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.ColorKind: {
+                        for (let k = submesh.verticesStart; k < end; ++k) {
+                            let index = k * strideSize!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                            byteOff += 4!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                            byteOff += 4!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 2], true);
+                            byteOff += 4!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 3], true);
+                            byteOff += 4!;
+                        }
+                        byteLength = submesh.verticesCount * 16;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.UVKind: {
+                        for (let k = submesh.verticesStart; k < end; ++k) {
+                            let index = k * strideSize!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                            byteOff += 4!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                            byteOff += 4!;
+                        }
+                        byteLength = submesh.verticesCount * 8;
+                        break;
+                    }
+                    case BABYLON.VertexBuffer.UV2Kind: {
+                        for (let k = submesh.verticesStart; k < end; ++k) {
+                            let index = k * strideSize!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                            byteOff += 4!;
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                            byteOff += 4!;
+                        }
+                        byteLength = submesh.verticesCount * 8;
+                        break;
+                    }
+                    default: {
+                        throw new Error("Unsupported vertex buffer type: " + vertexBufferType);
+                    }
+                }
+
+                return byteLength;
+            }
+            /**
+             * Generates a glb file from the json and binary data.  
+             * Returns an object with the glb file name as the key and data as the value.
+             * @param jsonText 
+             * @param binaryBuffer 
+             * @param glTFPrefix 
+             */
+            function createGLB(jsonText: string, binaryBuffer: ArrayBuffer, glTFPrefix: string): {[glbFileName: string]: Blob} {
+                let glbFileName = glTFPrefix + '.glb';
+                let headerLength = 12;
+                let chunkLengthPrefix = 8;
+                let jsonLength = jsonText.length;
+                let jsonRemainder = jsonLength % 4;
+                let binRemainder = binaryBuffer.byteLength % 4;
+                let jsonPadding = jsonRemainder === 0 ? jsonRemainder :  4 - jsonRemainder;
+                let binPadding = binRemainder === 0 ? binRemainder : 4 - binRemainder;
+                let totalByteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding  + binaryBuffer.byteLength + binPadding;
+
+                //header
+                let headerBuffer = new ArrayBuffer(headerLength);
+                let headerBufferView = new DataView(headerBuffer);
+                headerBufferView.setUint32(0, 0x46546C67, true); //glTF
+                headerBufferView.setUint32(4, 2, true); // version
+                headerBufferView.setUint32(8, totalByteLength, true); // total bytes in file
+
+                //json chunk
+                let jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
+                let jsonChunkBufferView = new DataView(jsonChunkBuffer);
+                jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
+                jsonChunkBufferView.setUint32(4, 0x4E4F534A, true);
+
+                //json chunk bytes
+                let jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
+                for (let i = 0; i < jsonLength; ++i) {
+                    jsonData[i] = jsonText.charCodeAt(i);
+                }
+
+                //json padding
+                let jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
+                for (let i = 0; i < jsonPadding; ++i) {
+                    jsonPaddingView[i] = 0x20;
+                }
+
+                //binary chunk
+                let binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
+                let binaryChunkBufferView = new DataView(binaryChunkBuffer);
+                binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength, true);
+                binaryChunkBufferView.setUint32(4, 0x004E4942, true);
+
+                // binary padding
+
+                let binPaddingBuffer = new ArrayBuffer(binPadding);
+                let binPaddingView = new Uint8Array(binPaddingBuffer);
+                for (let i = 0; i < binPadding; ++i) {
+                    binPaddingView[i] = 0;
+                }
+
+                // binary data
+                let glbFile = new Blob([headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer], {type: 'application/octet-stream'});
+
+
+                return { 
+                    [glbFileName]: glbFile 
+                };
+            }
+            /**
+             * Creates a glTF scene based on the array of meshes.
+             * Returns the the total byte offset.
+             * @param gltf 
+             * @param totalByteOffset 
+             * @param buffer 
+             * @param dataBuffer 
+             */
+            function createScene(gltf: OGLTF, totalByteOffset: number, dataBuffer?: DataView | null): number {
+                let scene = {nodes: new Array<number>()};
+
+                for (let i = 0; i < meshes.length; ++i) {
+                    // create node to hold translation/rotation/scale and the mesh
+                    let node: OGLTFNode = {mesh:-1};
+                    let babylonMesh = meshes[i];
+                    let useRightHandedSystem = babylonMesh.getScene().useRightHandedSystem;
+
+                    if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
+                        if (useRightHandedSystem) {
+                            node.translation = babylonMesh.position.asArray();
+                        }
+                        else {
+                            node.translation = [babylonMesh.position.x, babylonMesh.position.y, -babylonMesh.position.z];
+                        }
+                        
+                    }
+                    if (!(babylonMesh.scaling.x === 1 && babylonMesh.scaling.y === 1 && babylonMesh.scaling.z === 1)) {
+                        if (useRightHandedSystem) {
+                            node.scale = babylonMesh.scaling.asArray();
+                        }
+                        else {
+                            node.scale = [babylonMesh.scaling.x, babylonMesh.scaling.y, -babylonMesh.scaling.z];
+                        }
+                    }
+                    let rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(babylonMesh.rotation.y, babylonMesh.rotation.x, babylonMesh.rotation.z);
+                    if (babylonMesh.rotationQuaternion) {
+                        rotationQuaternion = rotationQuaternion.multiply(babylonMesh.rotationQuaternion);
+                    }
+                    if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
+                        if (useRightHandedSystem) {
+                            node.rotation = rotationQuaternion.asArray();
+                        }
+                        else {
+                            node.rotation = [-rotationQuaternion.x, -rotationQuaternion.y, rotationQuaternion.z, rotationQuaternion.w];
+                        }             
+                    }
+
+                    let positionVertexBuffer: Nullable<BABYLON.VertexBuffer>;
+                    let positions: Nullable<FloatArray>;
+                    let positionVertexBufferOffset: Nullable<number>;
+                    let positionStrideSize: Nullable<number>;
+
+                    let normalVertexBuffer: Nullable<VertexBuffer>;
+                    let normals: Nullable<FloatArray>;
+                    let normalStrideSize: Nullable<number>;
+
+                    let tangentVertexBuffer: Nullable<VertexBuffer>;
+                    let tangents: Nullable<FloatArray>;
+                    let tangentStrideSize: Nullable<number>;
+
+                    let colorVertexBuffer: Nullable<VertexBuffer>;
+                    let colors: Nullable<FloatArray>;
+                    let colorStrideSize: Nullable<number>;
+
+                    let texCoord0VertexBuffer: Nullable<VertexBuffer>;
+                    let texCoords0: Nullable<FloatArray>;
+                    let texCoord0StrideSize: Nullable<number>;
+
+                    let texCoord1VertexBuffer: Nullable<VertexBuffer>;
+                    let texCoords1: Nullable<FloatArray>;
+                    let texCoord1StrideSize: Nullable<number>;
+
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                        positionVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
+                        positions = positionVertexBuffer!.getData();
+                        positionVertexBufferOffset = positionVertexBuffer!.getOffset();
+                        positionStrideSize = positionVertexBuffer!.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                        normalVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
+                        normals = normalVertexBuffer!.getData();
+                        normalStrideSize = normalVertexBuffer!.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                        tangentVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
+                        tangents = tangentVertexBuffer!.getData();
+                        tangentStrideSize = tangentVertexBuffer!.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
+                        colorVertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
+                        colors = colorVertexBuffer!.getData();
+                        colorStrideSize = colorVertexBuffer!.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                        texCoord0VertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
+                        texCoords0 = texCoord0VertexBuffer!.getData();
+                        texCoord0StrideSize = texCoord0VertexBuffer!.getStrideSize();
+                    }
+                    if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
+                        texCoord1VertexBuffer = babylonMesh.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
+                        texCoords1 = texCoord1VertexBuffer!.getData();
+                        texCoord1StrideSize = texCoord1VertexBuffer!.getStrideSize();
+                    }
+
+                    // create mesh
+                    let mesh: OGLTFMesh = {primitives: new Array<OGLTFMeshPrimitive>()};
+                    mesh.primitives = [];
+                    if (babylonMesh.name) {
+                        mesh.name = babylonMesh.name;
+                    }
+                    // go through all mesh primitives (submeshes)
+                    for (let j = 0; j < babylonMesh.subMeshes.length; ++j) {
+                        let submesh = babylonMesh.subMeshes[j];
+                        let meshPrimitive:OGLTFMeshPrimitive = {attributes: {}};
+
+                        // Loop through each attribute of the submesh (mesh primitive)
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(
+                                    BABYLON.VertexBuffer.PositionKind,
+                                    submesh,
+                                    positions!,
+                                    positionStrideSize!,
+                                    totalByteOffset,
+                                    dataBuffer,
+                                    useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                let byteLength = submesh.verticesCount * 12;
+                                let bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+
+                                // Create accessor
+                                let result = calculateMinMax(positions!, submesh.verticesStart, submesh.verticesCount, positionVertexBufferOffset!, positionStrideSize!);
+                                let accessor = createAccessor(gltf.bufferViews.length - 1, "Position", "VEC3", 5126, submesh.verticesCount, result.min, result.max);
+                                gltf.accessors.push(accessor);
+
+                                meshPrimitive.attributes.POSITION = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(
+                                    BABYLON.VertexBuffer.NormalKind,
+                                    submesh,
+                                    normals!,
+                                    normalStrideSize!,
+                                    totalByteOffset,
+                                    dataBuffer,
+                                    useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                let byteLength = submesh.verticesCount * 12;
+                                let bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+
+                                // Create accessor
+                                let accessor = createAccessor(gltf.bufferViews.length - 1, "Normal", "VEC3", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+
+                                meshPrimitive.attributes.NORMAL = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(
+                                    BABYLON.VertexBuffer.TangentKind,
+                                    submesh,
+                                    tangents!,
+                                    tangentStrideSize!,
+                                    totalByteOffset,
+                                    dataBuffer,
+                                    useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                let byteLength = submesh.verticesCount * 16;
+                                let bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+
+                                // Create accessor
+                                let accessor = createAccessor(gltf.bufferViews.length - 1, "Tangent", "VEC4", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+
+                                meshPrimitive.attributes.TANGENT = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(
+                                    BABYLON.VertexBuffer.ColorKind,
+                                    submesh,
+                                    colors!,
+                                    colorStrideSize!,
+                                    totalByteOffset,
+                                    dataBuffer,
+                                    useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                let byteLength = submesh.verticesCount * 16;
+                                let bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+
+                                // Create accessor
+                                let accessor = createAccessor(gltf.bufferViews.length - 1, "Color", "VEC4", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+
+                                meshPrimitive.attributes.COLOR_0 = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(
+                                    BABYLON.VertexBuffer.UVKind,
+                                    submesh,
+                                    texCoords0!,
+                                    texCoord0StrideSize!,
+                                    totalByteOffset,
+                                    dataBuffer,
+                                    useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                let byteLength = submesh.verticesCount * 8;
+                                let bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+
+                                // Create accessor
+                                let accessor = createAccessor(gltf.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+
+                                meshPrimitive.attributes.TEXCOORD_0 = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
+                            if (dataBuffer) {
+                                totalByteOffset += writeAttributeData(
+                                    BABYLON.VertexBuffer.UV2Kind,
+                                    submesh,
+                                    texCoords1!,
+                                    texCoord1StrideSize!,
+                                    totalByteOffset,
+                                    dataBuffer,
+                                    useRightHandedSystem);
+                            }
+                            else {
+                                // Create bufferview
+                                let byteLength = submesh.verticesCount * 8;
+                                let bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+
+                                // Create accessor
+                                let accessor = createAccessor(gltf.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                                gltf.accessors.push(accessor);
+
+                                meshPrimitive.attributes.TEXCOORD_1 = gltf.accessors.length - 1;
+                            }
+                        }
+
+                        if (babylonMesh.getTotalIndices() > 0) {
+                            if (dataBuffer) {
+                                let indices = babylonMesh.getIndices();
+                                let start = submesh.indexStart;
+                                let end = submesh.indexCount + start;
+                                let byteOff = totalByteOffset;
+
+                                for (let k = start; k < end; k = k + 3) {
+                                    dataBuffer!.setUint32(byteOff, indices![k], true);
+                                    byteOff += 4;
+                                    dataBuffer!.setUint32(byteOff, indices![k + 1], true);
+                                    byteOff += 4;
+                                    dataBuffer!.setUint32(byteOff, indices![k + 2], true);
+                                    byteOff += 4;
+                                }
+                                
+                                let byteLength = submesh.indexCount * 4;
+                                totalByteOffset += byteLength;
+                            }
+                            else {
+                                // Create bufferview
+                                let indicesCount = submesh.indexCount;
+                                let byteLength = indicesCount * 4;
+                                let bufferview = createBufferView(0, totalByteOffset, byteLength);
+                                totalByteOffset += byteLength;
+                                gltf.bufferViews.push(bufferview);
+
+                                // Create accessor
+                                let accessor = createAccessor(gltf.bufferViews.length - 1, "Indices", "SCALAR", 5125, indicesCount);
+                                gltf.accessors.push(accessor);
+
+                                meshPrimitive.indices = gltf.accessors.length - 1;
+                            }
+                        }
+                        if (babylonMesh.material) {
+                            if (!gltf.materials) {
+                                gltf.materials = new Array<OGLTFMaterial>();
+                            }
+                            meshPrimitive.material = gltf.materials.length - 1;
+                        }
+                        mesh.primitives.push(meshPrimitive);
+                    }
+                    gltf.meshes.push(mesh);
+                    node.mesh = gltf.meshes.length - 1;
+                    gltf.nodes.push(node);
+
+                    scene.nodes.push(gltf.nodes.length - 1);
+                }
+                gltf.scenes.push(scene);
+                return totalByteOffset;
+            }
+
+            let glTFPrefix = filename.replace(/\.[^/.]+$/, "");
+            let gltf: OGLTF = {
+                buffers: new Array<OGLTFBuffer>(), 
+                bufferViews: new Array<OGLTFBufferView>(), 
+                asset: {generator: "BabylonJS", version: "2.0"}, 
+                meshes: new Array<OGLTFMesh>(), 
+                scenes: new Array<OGLTFScene>(),
+                nodes: new Array<OGLTFNode>(),
+                accessors: new Array<OGLTFAccessor>() 
+            };
+
+            let totalByteOffset = 0;
+            let binaryBuffer: ArrayBuffer;
+            let dataBuffer: DataView;
+
+            // Create scene.  First pass calculates the totalByteOffset.
+            totalByteOffset = createScene(gltf, totalByteOffset, null);
+            let buff: OGLTFBuffer = {byteLength: totalByteOffset};
+            if (!glb) {
+                buff.uri = glTFPrefix + '.bin';
+            }
+            
+            gltf.buffers.push(buff);
+
+            let text = JSON.stringify(gltf, null, 2);
+            binaryBuffer = new ArrayBuffer(totalByteOffset);
+            dataBuffer = new DataView(binaryBuffer);
+            totalByteOffset = 0;
+
+            // Create scene.  Second pass generates the binary data
+            createScene(gltf, totalByteOffset, dataBuffer);
+
+            if (glb) { // Creates a glb
+                let glbFile = createGLB(text, binaryBuffer, glTFPrefix);
+
+                return glbFile;
+            }
+
+            let glTFFileName = glTFPrefix + '.gltf';
+            let glTFBinFile = glTFPrefix + '.bin';
+
+            let bin = new Blob([binaryBuffer], {type: 'application/octet-stream'});
+
+            return { 
+                [glTFFileName]: text, 
+                [glTFBinFile]: bin 
+            };
+        }
+    }
+}

+ 50 - 27
src/Engine/babylon.engine.ts

@@ -566,7 +566,7 @@
         }
 
         public static get Version(): string {
-            return "3.2.0-alpha0";
+            return "3.2.0-alpha1";
         }
 
         // Updatable statics so stick with vars here
@@ -708,6 +708,7 @@
         private _loadingScreen: ILoadingScreen;
 
         public _drawCalls = new PerfCounter();
+        public _textureCollisions = new PerfCounter();
 
         private _glVersion: string;
         private _glRenderer: string;
@@ -753,7 +754,7 @@
         private _internalTexturesCache = new Array<InternalTexture>();
         protected _activeChannel: number;
         protected _boundTexturesCache: { [key: string]: Nullable<InternalTexture> } = {};
-        protected _boundTexturesOrder = new Array<InternalTexture>();
+        protected _boundTexturesStack = new Array<InternalTexture>();
         protected _currentEffect: Nullable<Effect>;
         protected _currentProgram: Nullable<WebGLProgram>;
         private _compiledEffects: { [key: string]: Effect } = {}
@@ -790,7 +791,7 @@
 
         private _frameHandler: number;
 
-        private _nextFreeTextureSlot = 0;
+        private _nextFreeTextureSlots = new Array<number>();
 
         // Hardware supported Compressed Textures
         private _texturesSupported = new Array<string>();
@@ -1295,6 +1296,11 @@
             this.setDepthBuffer(true);
             this.setDepthFunctionToLessOrEqual();
             this.setDepthWrite(true);
+
+            // Texture maps
+            for (let slot = 0; slot < this._caps.maxTexturesImageUnits; slot++) {
+                this._nextFreeTextureSlots.push(slot);
+            }
         }
 
         public get webGLVersion(): number {
@@ -1329,7 +1335,10 @@
                 }
                 this._boundTexturesCache[key] = null;
             }
-            this._nextFreeTextureSlot = 0;
+            this._nextFreeTextureSlots = [];
+            for (let slot = 0; slot < this._caps.maxTexturesImageUnits; slot++) {
+                this._nextFreeTextureSlots.push(slot);
+            }
             this._activeChannel = -1;
         }
 
@@ -1890,7 +1899,7 @@
             }
 
             if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
-                this._bindTextureDirectly(gl.TEXTURE_2D, texture);
+                this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
                 gl.generateMipmap(gl.TEXTURE_2D);
                 this._bindTextureDirectly(gl.TEXTURE_2D, null);
             }
@@ -2778,6 +2787,13 @@
             this._gl.uniformMatrix2fv(uniform, false, matrix);
         }
 
+        public setInt(uniform: Nullable<WebGLUniformLocation>, value: number): void {
+            if (!uniform)
+                return;
+
+            this._gl.uniform1i(uniform, value);
+        }
+
         public setFloat(uniform: Nullable<WebGLUniformLocation>, value: number): void {
             if (!uniform)
                 return;
@@ -2956,12 +2972,12 @@
             if (this.preventCacheWipeBetweenFrames && !bruteForce) {
                 return;
             }
-            this.resetTextureCache();
             this._currentEffect = null;
 
             // 6/8/2017: deltakosh: Should not be required anymore.
             // This message is then mostly for the future myself which will scream out loud when seeing that actually it was required :)
             if (bruteForce) {
+                this.resetTextureCache();
                 this._currentProgram = null;
 
                 this._stencilState.reset();
@@ -4588,34 +4604,38 @@
         }
 
         private _activateTextureChannel(channel: number): void {
-            if (this._activeChannel !== channel) {
+            if (this._activeChannel !== channel && channel > -1) {
                 this._gl.activeTexture(this._gl.TEXTURE0 + channel);
                 this._activeChannel = channel;
             }
         }
 
         private _moveBoundTextureOnTop(internalTexture: InternalTexture): void {
-            let index = this._boundTexturesOrder.indexOf(internalTexture);
+            let index = this._boundTexturesStack.indexOf(internalTexture);
 
-            if (index > -1 && index !== this._boundTexturesOrder.length - 1) {
-                this._boundTexturesOrder.splice(index, 1);
-                this._boundTexturesOrder.push(internalTexture);
+            if (index > -1 && index !== this._boundTexturesStack.length - 1) {
+                this._boundTexturesStack.splice(index, 1);
+                this._boundTexturesStack.push(internalTexture);
             }
         }
 
         private _removeDesignatedSlot(internalTexture: InternalTexture): number {
             let currentSlot = internalTexture._designatedSlot;
             internalTexture._designatedSlot = -1;
-            let index = this._boundTexturesOrder.indexOf(internalTexture);
+            let index = this._boundTexturesStack.indexOf(internalTexture);
 
             if (index > -1) {
-                this._boundTexturesOrder.splice(index, 1);
+                this._boundTexturesStack.splice(index, 1);
+                if (currentSlot > -1) {
+                    this._boundTexturesCache[currentSlot] = null;
+                    this._nextFreeTextureSlots.push(currentSlot);
+                }
             }
 
             return currentSlot;
         }
 
-        public _bindTextureDirectly(target: number, texture: Nullable<InternalTexture>, isPartOfTextureArray = false): void {
+        public _bindTextureDirectly(target: number, texture: Nullable<InternalTexture>, doNotBindUniformToTextureChannel = false): void {
             let currentTextureBound = this._boundTexturesCache[this._activeChannel];
             let isTextureForRendering = texture && texture._initialSlot > -1;
 
@@ -4630,14 +4650,18 @@
                     this._boundTexturesCache[this._activeChannel] = texture;
 
                     if (isTextureForRendering) {
-                        this._boundTexturesOrder.push(texture!);
+                        let slotIndex = this._nextFreeTextureSlots.indexOf(this._activeChannel);
+                        if (slotIndex > -1) {
+                            this._nextFreeTextureSlots.splice(slotIndex, 1);
+                        }
+                        this._boundTexturesStack.push(texture!);
                     }
                 }
             }
 
             if (isTextureForRendering && this._activeChannel > -1) {
                 texture!._designatedSlot = this._activeChannel;
-                if (!isPartOfTextureArray) {
+                if (!doNotBindUniformToTextureChannel) {
                     this._bindSamplerUniformToChannel(texture!._initialSlot, this._activeChannel);
                 }
             }
@@ -4683,7 +4707,7 @@
             this._setTexture(channel, texture);
         }
 
-        private _getCorrectTextureChannel(channel: number, internalTexture: Nullable<InternalTexture>) {
+        private _getCorrectTextureChannel(channel: number, internalTexture: Nullable<InternalTexture>): number {
             if (!internalTexture) {
                 return -1;
             }
@@ -4692,17 +4716,16 @@
 
             if (channel !== internalTexture._designatedSlot) {
                 if (internalTexture._designatedSlot > -1) { // Texture is already assigned to a slot
-                    channel = internalTexture._designatedSlot;
-                } else { // Not slot for this texture, let's pick a new one
-                    if (this._nextFreeTextureSlot > -1) { // We can use a free slot
-                        channel = this._nextFreeTextureSlot;
-                        this._nextFreeTextureSlot++;
-                        if (this._nextFreeTextureSlot >= this._caps.maxTexturesImageUnits) {
-                            this._nextFreeTextureSlot = -1; // No more free slots, we will recycle
-                        }
-                    } else { // We need to recycle the oldest bound texture, sorry.
-                        channel = this._removeDesignatedSlot(this._boundTexturesOrder[0]);
+                    return internalTexture._designatedSlot;
+                } else {
+                    // No slot for this texture, let's pick a new one (if we find a free slot)
+                    if (this._nextFreeTextureSlots.length) {
+                        return this._nextFreeTextureSlots[0];
                     }
+
+                    // We need to recycle the oldest bound texture, sorry.
+                    this._textureCollisions.addCount(1, false);
+                    return this._removeDesignatedSlot(this._boundTexturesStack[0]);
                 }
             }
 

+ 109 - 101
src/Instrumentation/babylon.sceneInstrumentation.ts

@@ -4,31 +4,31 @@ module BABYLON {
      */
     export class SceneInstrumentation implements IDisposable {
         private _captureActiveMeshesEvaluationTime = false;
-        private _activeMeshesEvaluationTime = new PerfCounter();  
+        private _activeMeshesEvaluationTime = new PerfCounter();
 
         private _captureRenderTargetsRenderTime = false;
-        private _renderTargetsRenderTime = new PerfCounter();    
-        
+        private _renderTargetsRenderTime = new PerfCounter();
+
         private _captureFrameTime = false;
-        private _frameTime = new PerfCounter();        
+        private _frameTime = new PerfCounter();
 
         private _captureRenderTime = false;
-        private _renderTime = new PerfCounter();           
+        private _renderTime = new PerfCounter();
 
         private _captureInterFrameTime = false;
-        private _interFrameTime = new PerfCounter();    
-        
+        private _interFrameTime = new PerfCounter();
+
         private _captureParticlesRenderTime = false;
-        private _particlesRenderTime = new PerfCounter();       
-          
+        private _particlesRenderTime = new PerfCounter();
+
         private _captureSpritesRenderTime = false;
-        private _spritesRenderTime = new PerfCounter();   
+        private _spritesRenderTime = new PerfCounter();
 
         private _capturePhysicsTime = false;
-        private _physicsTime = new PerfCounter();     
-        
+        private _physicsTime = new PerfCounter();
+
         private _captureAnimationsTime = false;
-        private _animationsTime = new PerfCounter();            
+        private _animationsTime = new PerfCounter();
 
         // Observers
         private _onBeforeActiveMeshesEvaluationObserver: Nullable<Observer<Scene>> = null;
@@ -39,21 +39,21 @@ module BABYLON {
         private _onAfterRenderObserver: Nullable<Observer<Scene>> = null;
 
         private _onBeforeDrawPhaseObserver: Nullable<Observer<Scene>> = null;
-        private _onAfterDrawPhaseObserver: Nullable<Observer<Scene>> = null;        
-        
+        private _onAfterDrawPhaseObserver: Nullable<Observer<Scene>> = null;
+
         private _onBeforeAnimationsObserver: Nullable<Observer<Scene>> = null;
 
         private _onBeforeParticlesRenderingObserver: Nullable<Observer<Scene>> = null;
         private _onAfterParticlesRenderingObserver: Nullable<Observer<Scene>> = null;
 
         private _onBeforeSpritesRenderingObserver: Nullable<Observer<Scene>> = null;
-        private _onAfterSpritesRenderingObserver: Nullable<Observer<Scene>> = null;      
-        
+        private _onAfterSpritesRenderingObserver: Nullable<Observer<Scene>> = null;
+
         private _onBeforePhysicsObserver: Nullable<Observer<Scene>> = null;
-        private _onAfterPhysicsObserver: Nullable<Observer<Scene>> = null;     
-        
-        private _onAfterAnimationsObserver: Nullable<Observer<Scene>> = null;    
-                
+        private _onAfterPhysicsObserver: Nullable<Observer<Scene>> = null;
+
+        private _onAfterAnimationsObserver: Nullable<Observer<Scene>> = null;
+
         // Properties
         /**
          * Gets the perf counter used for active meshes evaluation time
@@ -64,28 +64,28 @@ module BABYLON {
 
         /**
          * Gets the active meshes evaluation time capture status
-         */        
+         */
         public get captureActiveMeshesEvaluationTime(): boolean {
             return this._captureActiveMeshesEvaluationTime;
-        }        
+        }
 
         /**
          * Enable or disable the active meshes evaluation time capture
-         */        
+         */
         public set captureActiveMeshesEvaluationTime(value: boolean) {
             if (value === this._captureActiveMeshesEvaluationTime) {
                 return;
             }
 
-            this._captureActiveMeshesEvaluationTime = value;            
+            this._captureActiveMeshesEvaluationTime = value;
 
             if (value) {
-                this._onBeforeActiveMeshesEvaluationObserver = this.scene.onBeforeActiveMeshesEvaluationObservable.add(()=>{
+                this._onBeforeActiveMeshesEvaluationObserver = this.scene.onBeforeActiveMeshesEvaluationObservable.add(() => {
                     Tools.StartPerformanceCounter("Active meshes evaluation");
                     this._activeMeshesEvaluationTime.beginMonitoring();
                 });
 
-                this._onAfterActiveMeshesEvaluationObserver = this.scene.onAfterActiveMeshesEvaluationObservable.add(()=>{                    
+                this._onAfterActiveMeshesEvaluationObserver = this.scene.onAfterActiveMeshesEvaluationObservable.add(() => {
                     Tools.EndPerformanceCounter("Active meshes evaluation");
                     this._activeMeshesEvaluationTime.endMonitoring();
                 });
@@ -110,11 +110,11 @@ module BABYLON {
          */
         public get captureRenderTargetsRenderTime(): boolean {
             return this._captureRenderTargetsRenderTime;
-        }        
+        }
 
         /**
          * Enable or disable the render targets render time capture
-         */        
+         */
         public set captureRenderTargetsRenderTime(value: boolean) {
             if (value === this._captureRenderTargetsRenderTime) {
                 return;
@@ -123,12 +123,12 @@ module BABYLON {
             this._captureRenderTargetsRenderTime = value;
 
             if (value) {
-                this._onBeforeRenderTargetsRenderObserver = this.scene.OnBeforeRenderTargetsRenderObservable.add(()=>{
+                this._onBeforeRenderTargetsRenderObserver = this.scene.OnBeforeRenderTargetsRenderObservable.add(() => {
                     Tools.StartPerformanceCounter("Render targets rendering");
                     this._renderTargetsRenderTime.beginMonitoring();
                 });
 
-                this._onAfterRenderTargetsRenderObserver = this.scene.OnAfterRenderTargetsRenderObservable.add(()=>{                    
+                this._onAfterRenderTargetsRenderObserver = this.scene.OnAfterRenderTargetsRenderObservable.add(() => {
                     Tools.EndPerformanceCounter("Render targets rendering");
                     this._renderTargetsRenderTime.endMonitoring(false);
                 });
@@ -139,7 +139,7 @@ module BABYLON {
                 this.scene.OnAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver);
                 this._onAfterRenderTargetsRenderObserver = null;
             }
-        }        
+        }
 
         /**
          * Gets the perf counter used for particles render time
@@ -153,11 +153,11 @@ module BABYLON {
          */
         public get captureParticlesRenderTime(): boolean {
             return this._captureParticlesRenderTime;
-        }        
+        }
 
         /**
          * Enable or disable the particles render time capture
-         */        
+         */
         public set captureParticlesRenderTime(value: boolean) {
             if (value === this._captureParticlesRenderTime) {
                 return;
@@ -166,12 +166,12 @@ module BABYLON {
             this._captureParticlesRenderTime = value;
 
             if (value) {
-                this._onBeforeParticlesRenderingObserver = this.scene.onBeforeParticlesRenderingObservable.add(()=>{                    
+                this._onBeforeParticlesRenderingObserver = this.scene.onBeforeParticlesRenderingObservable.add(() => {
                     Tools.StartPerformanceCounter("Particles");
                     this._particlesRenderTime.beginMonitoring();
                 });
 
-                this._onAfterParticlesRenderingObserver = this.scene.onAfterParticlesRenderingObservable.add(()=>{                                        
+                this._onAfterParticlesRenderingObserver = this.scene.onAfterParticlesRenderingObservable.add(() => {
                     Tools.EndPerformanceCounter("Particles");
                     this._particlesRenderTime.endMonitoring(false);
                 });
@@ -182,7 +182,7 @@ module BABYLON {
                 this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver);
                 this._onAfterParticlesRenderingObserver = null;
             }
-        }        
+        }
 
         /**
          * Gets the perf counter used for sprites render time
@@ -196,11 +196,11 @@ module BABYLON {
          */
         public get captureSpritesRenderTime(): boolean {
             return this._captureSpritesRenderTime;
-        }        
+        }
 
         /**
          * Enable or disable the sprites render time capture
-         */        
+         */
         public set captureSpritesRenderTime(value: boolean) {
             if (value === this._captureSpritesRenderTime) {
                 return;
@@ -209,12 +209,12 @@ module BABYLON {
             this._captureSpritesRenderTime = value;
 
             if (value) {
-                this._onBeforeSpritesRenderingObserver = this.scene.onBeforeSpritesRenderingObservable.add(()=>{                    
+                this._onBeforeSpritesRenderingObserver = this.scene.onBeforeSpritesRenderingObservable.add(() => {
                     Tools.StartPerformanceCounter("Sprites");
                     this._spritesRenderTime.beginMonitoring();
                 });
 
-                this._onAfterSpritesRenderingObserver = this.scene.onAfterSpritesRenderingObservable.add(()=>{                                        
+                this._onAfterSpritesRenderingObserver = this.scene.onAfterSpritesRenderingObservable.add(() => {
                     Tools.EndPerformanceCounter("Sprites");
                     this._spritesRenderTime.endMonitoring(false);
                 });
@@ -225,7 +225,7 @@ module BABYLON {
                 this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver);
                 this._onAfterSpritesRenderingObserver = null;
             }
-        }      
+        }
 
         /**
          * Gets the perf counter used for physics time
@@ -239,11 +239,11 @@ module BABYLON {
          */
         public get capturePhysicsTime(): boolean {
             return this._capturePhysicsTime;
-        }        
+        }
 
         /**
          * Enable or disable the physics time capture
-         */        
+         */
         public set capturePhysicsTime(value: boolean) {
             if (value === this._capturePhysicsTime) {
                 return;
@@ -252,12 +252,12 @@ module BABYLON {
             this._capturePhysicsTime = value;
 
             if (value) {
-                this._onBeforePhysicsObserver = this.scene.onBeforePhysicsObservable.add(()=>{                    
+                this._onBeforePhysicsObserver = this.scene.onBeforePhysicsObservable.add(() => {
                     Tools.StartPerformanceCounter("Physics");
                     this._physicsTime.beginMonitoring();
                 });
 
-                this._onAfterPhysicsObserver = this.scene.onAfterPhysicsObservable.add(()=>{                                        
+                this._onAfterPhysicsObserver = this.scene.onAfterPhysicsObservable.add(() => {
                     Tools.EndPerformanceCounter("Physics");
                     this._physicsTime.endMonitoring();
                 });
@@ -268,7 +268,7 @@ module BABYLON {
                 this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver);
                 this._onAfterPhysicsObserver = null;
             }
-        }      
+        }
 
 
         /**
@@ -283,11 +283,11 @@ module BABYLON {
          */
         public get captureAnimationsTime(): boolean {
             return this._captureAnimationsTime;
-        }        
+        }
 
         /**
          * Enable or disable the animations time capture
-         */        
+         */
         public set captureAnimationsTime(value: boolean) {
             if (value === this._captureAnimationsTime) {
                 return;
@@ -296,74 +296,74 @@ module BABYLON {
             this._captureAnimationsTime = value;
 
             if (value) {
-                this._onAfterAnimationsObserver = this.scene.onAfterAnimationsObservable.add(()=>{                    
+                this._onAfterAnimationsObserver = this.scene.onAfterAnimationsObservable.add(() => {
                     this._animationsTime.endMonitoring();
                 });
             } else {
                 this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver);
                 this._onAfterAnimationsObserver = null;
             }
-        }              
-        
+        }
+
         /**
          * Gets the perf counter used for frame time capture
          */
         public get frameTimeCounter(): PerfCounter {
             return this._frameTime;
-        }               
-       
+        }
+
         /**
          * Gets the frame time capture status
-         */        
+         */
         public get captureFrameTime(): boolean {
             return this._captureFrameTime;
-        }        
+        }
 
         /**
          * Enable or disable the frame time capture
-         */        
+         */
         public set captureFrameTime(value: boolean) {
             this._captureFrameTime = value;
-        }       
-     
+        }
+
         /**
          * Gets the perf counter used for inter-frames time capture
          */
         public get interFrameTimeCounter(): PerfCounter {
             return this._interFrameTime;
-        }               
-       
+        }
+
         /**
          * Gets the inter-frames time capture status
-         */        
+         */
         public get captureInterFrameTime(): boolean {
             return this._captureInterFrameTime;
-        }        
+        }
 
         /**
          * Enable or disable the inter-frames time capture
-         */        
+         */
         public set captureInterFrameTime(value: boolean) {
             this._captureInterFrameTime = value;
-        }     
+        }
 
         /**
          * Gets the perf counter used for render time capture
          */
         public get renderTimeCounter(): PerfCounter {
             return this._renderTime;
-        }               
-       
+        }
+
         /**
          * Gets the render time capture status
-         */        
+         */
         public get captureRenderTime(): boolean {
             return this._captureRenderTime;
-        }        
+        }
 
         /**
          * Enable or disable the render time capture
-         */        
+         */
         public set captureRenderTime(value: boolean) {
             if (value === this._captureRenderTime) {
                 return;
@@ -372,30 +372,37 @@ module BABYLON {
             this._captureRenderTime = value;
 
             if (value) {
-                this._onBeforeDrawPhaseObserver = this.scene.onBeforeDrawPhaseObservable.add(()=>{
-                    this._renderTime.beginMonitoring(); 
+                this._onBeforeDrawPhaseObserver = this.scene.onBeforeDrawPhaseObservable.add(() => {
+                    this._renderTime.beginMonitoring();
                     Tools.StartPerformanceCounter("Main render");
                 });
 
-                this._onAfterDrawPhaseObserver = this.scene.onAfterDrawPhaseObservable.add(()=>{
-                    this._renderTime.endMonitoring(false); 
+                this._onAfterDrawPhaseObserver = this.scene.onAfterDrawPhaseObservable.add(() => {
+                    this._renderTime.endMonitoring(false);
                     Tools.EndPerformanceCounter("Main render");
-                });                
+                });
             } else {
                 this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver);
                 this._onBeforeDrawPhaseObserver = null;
                 this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver);
                 this._onAfterDrawPhaseObserver = null;
             }
-        }            
+        }
 
         /**
-         * Gets the perf counter used for frame time capture
+         * Gets the perf counter used for draw calls
          */
         public get drawCallsCounter(): PerfCounter {
             return this.scene.getEngine()._drawCalls;
-        }            
-    
+        }
+
+        /**
+         * Gets the perf counter used for texture collisions 
+         */
+        public get textureCollisionsCounter(): PerfCounter {
+            return this.scene.getEngine()._textureCollisions;
+        }
+
         public constructor(public scene: Scene) {
             // Before render
             this._onBeforeAnimationsObserver = scene.onBeforeAnimationsObservable.add(() => {
@@ -410,11 +417,11 @@ module BABYLON {
                 if (this._captureFrameTime) {
                     Tools.StartPerformanceCounter("Scene rendering");
                     this._frameTime.beginMonitoring();
-                }   
+                }
 
                 if (this._captureInterFrameTime) {
-                    this._interFrameTime.endMonitoring(); 
-                }                   
+                    this._interFrameTime.endMonitoring();
+                }
 
                 if (this._captureParticlesRenderTime) {
                     this._particlesRenderTime.fetchNewFrame();
@@ -429,22 +436,23 @@ module BABYLON {
                 }
 
                 this.scene.getEngine()._drawCalls.fetchNewFrame();
+                this.scene.getEngine()._textureCollisions.fetchNewFrame();
             });
 
             // After render
             this._onAfterRenderObserver = scene.onAfterRenderObservable.add(() => {
                 if (this._captureFrameTime) {
                     Tools.EndPerformanceCounter("Scene rendering");
-                    this._frameTime.endMonitoring();                    
+                    this._frameTime.endMonitoring();
                 }
 
                 if (this._captureRenderTime) {
-                    this._renderTime.endMonitoring(false);                    
-                }                
+                    this._renderTime.endMonitoring(false);
+                }
 
                 if (this._captureInterFrameTime) {
-                    this._interFrameTime.beginMonitoring();  
-                }                
+                    this._interFrameTime.beginMonitoring();
+                }
             });
         }
 
@@ -459,11 +467,11 @@ module BABYLON {
             this._onAfterActiveMeshesEvaluationObserver = null;
 
             this.scene.OnBeforeRenderTargetsRenderObservable.remove(this._onBeforeRenderTargetsRenderObserver);
-            this._onBeforeRenderTargetsRenderObserver = null;   
-            
+            this._onBeforeRenderTargetsRenderObserver = null;
+
             this.scene.OnAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver);
-            this._onAfterRenderTargetsRenderObserver = null;      
-            
+            this._onAfterRenderTargetsRenderObserver = null;
+
             this.scene.onBeforeAnimationsObservable.remove(this._onBeforeAnimationsObserver);
             this._onBeforeAnimationsObserver = null;
 
@@ -471,29 +479,29 @@ module BABYLON {
             this._onBeforeParticlesRenderingObserver = null;
 
             this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver);
-            this._onAfterParticlesRenderingObserver = null;         
-            
+            this._onAfterParticlesRenderingObserver = null;
+
             this.scene.onBeforeSpritesRenderingObservable.remove(this._onBeforeSpritesRenderingObserver);
             this._onBeforeSpritesRenderingObserver = null;
 
             this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver);
-            this._onAfterSpritesRenderingObserver = null;       
-            
+            this._onAfterSpritesRenderingObserver = null;
+
             this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver);
             this._onBeforeDrawPhaseObserver = null;
 
             this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver);
-            this._onAfterDrawPhaseObserver = null;    
-            
+            this._onAfterDrawPhaseObserver = null;
+
             this.scene.onBeforePhysicsObservable.remove(this._onBeforePhysicsObserver);
             this._onBeforePhysicsObserver = null;
 
             this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver);
-            this._onAfterPhysicsObserver = null;    
-            
+            this._onAfterPhysicsObserver = null;
+
             this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver);
-            this._onAfterAnimationsObserver = null;            
-                
+            this._onAfterAnimationsObserver = null;
+
             (<any>this.scene) = null;
         }
     }

+ 18 - 1
src/Materials/babylon.effect.ts

@@ -544,6 +544,11 @@
             this._prepareEffect();
         }
 
+        public getSpecificUniformLocations(names: string[]): Nullable<WebGLUniformLocation>[] {
+            let engine = this._engine;
+            return engine.getUniforms(this._program, names);
+        }
+
         public _prepareEffect() {
             let attributesNames = this._attributesNames;
             let defines = this.defines;
@@ -553,7 +558,7 @@
             var previousProgram = this._program;
 
             try {
-                var engine = this._engine;
+                let engine = this._engine;
 
                 if (this._vertexSourceCodeOverride && this._fragmentSourceCodeOverride) {
                     this._program = engine.createRawShaderProgram(this._vertexSourceCodeOverride, this._fragmentSourceCodeOverride, undefined, this._transformFeedbackVaryings);
@@ -769,6 +774,18 @@
             this._engine.bindUniformBlock(this._program, blockName, index);
         }
 
+        public setInt(uniformName: string, value: number): Effect {
+            var cache = this._valueCache[uniformName];
+            if (cache !== undefined && cache === value)
+                return this;
+
+            this._valueCache[uniformName] = value;
+
+            this._engine.setInt(this.getUniform(uniformName), value);
+
+            return this;
+        }
+
         public setIntArray(uniformName: string, array: Int32Array): Effect {
             this._valueCache[uniformName] = null;
             this._engine.setIntArray(this.getUniform(uniformName), array);

+ 14 - 1
src/Materials/babylon.shaderMaterial.ts

@@ -5,6 +5,7 @@
         private _textures: { [name: string]: Texture } = {};
         private _textureArrays: { [name: string]: Texture[] } = {};
         private _floats: { [name: string]: number } = {};
+        private _ints: { [name: string]: number } = {};
         private _floatsArrays: { [name: string]: number[] } = {};
         private _colors3: { [name: string]: Color3 } = {};
         private _colors3Arrays: { [name: string]: number[] } = {};
@@ -81,6 +82,13 @@
             return this;
         }
 
+        public setInt(name: string, value: number): ShaderMaterial {
+            this._checkUniform(name);
+            this._ints[name] = value;
+
+            return this;
+        }
+
         public setFloats(name: string, value: number[]): ShaderMaterial {
             this._checkUniform(name);
             this._floatsArrays[name] = value;
@@ -322,12 +330,17 @@
                     this._effect.setTextureArray(name, this._textureArrays[name]);
                 }
 
+                // Int    
+                for (name in this._ints) {
+                    this._effect.setInt(name, this._ints[name]);
+                }
+
                 // Float    
                 for (name in this._floats) {
                     this._effect.setFloat(name, this._floats[name]);
                 }
 
-                // Float s   
+                // Floats   
                 for (name in this._floatsArrays) {
                     this._effect.setArray(name, this._floatsArrays[name]);
                 }

+ 1 - 1
src/babylon.scene.ts

@@ -2035,7 +2035,7 @@
             return null;
         }
 
-        public get Animatables(): Animatable[] {
+        public get animatables(): Animatable[] {
             return this._activeAnimatables;
         }