瀏覽代碼

Merge remote-tracking branch 'BabylonJS/master' into modules

Raanan Weber 7 年之前
父節點
當前提交
3b11c29b0e
共有 42 個文件被更改,包括 20986 次插入20868 次删除
  1. 6677 6677
      Playground/babylon.d.txt
  2. 2 1
      Tools/Gulp/config.json
  3. 4 3
      Tools/Gulp/gulpfile.js
  4. 1 1
      Tools/Gulp/package.json
  5. 7224 7224
      dist/preview release/babylon.d.ts
  6. 23 23
      dist/preview release/babylon.js
  7. 13 5
      dist/preview release/babylon.max.js
  8. 23 23
      dist/preview release/babylon.worker.js
  9. 6536 6536
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  10. 25 25
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  11. 18 6
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  12. 1 0
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  13. 5 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  14. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  15. 1 0
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  16. 5 1
      dist/preview release/loaders/babylon.glTFFileLoader.js
  17. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  18. 5 1
      dist/preview release/loaders/babylonjs.loaders.js
  19. 2 2
      dist/preview release/loaders/babylonjs.loaders.min.js
  20. 1 0
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  21. 20 20
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  22. 60 48
      dist/preview release/serializers/babylon.glTF2Serializer.js
  23. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  24. 60 48
      dist/preview release/serializers/babylonjs.serializers.js
  25. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  26. 20 20
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  27. 38 38
      dist/preview release/viewer/babylon.viewer.js
  28. 二進制
      favicon.ico
  29. 6 3
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  30. 1 0
      loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts
  31. 3 1
      readme.md
  32. 49 0
      serializers/src/glTF/2.0/babylon.glTFData.ts
  33. 18 16
      serializers/src/glTF/2.0/babylon.glTFExporter.ts
  34. 2 40
      serializers/src/glTF/2.0/babylon.glTFSerializer.ts
  35. 0 5
      src/Lights/babylon.light.ts
  36. 5 1
      src/Mesh/babylon.meshBuilder.ts
  37. 13 0
      src/babylon.scene.ts
  38. 6 3
      tests/validation/config.json
  39. 21 14
      tests/validation/integration.js
  40. 89 0
      tests/validation/karma.conf.browserstack.js
  41. 0 74
      tests/validation/karma.conf.saucelabs.js
  42. 3 2
      tests/validation/validation.js

文件差異過大導致無法顯示
+ 6677 - 6677
Playground/babylon.d.txt


+ 2 - 1
Tools/Gulp/config.json

@@ -1565,7 +1565,8 @@
             {
                 "files": [
                     "../../serializers/src/glTF/2.0/babylon.glTFSerializer.ts",
-                    "../../serializers/src/glTF/2.0/babylon.glTFExporter.ts"
+                    "../../serializers/src/glTF/2.0/babylon.glTFExporter.ts",
+                    "../../serializers/src/glTF/2.0/babylon.glTFData.ts"
                 ],
                 "output": "babylon.glTF2Serializer.js"
             }

+ 4 - 3
Tools/Gulp/gulpfile.js

@@ -423,7 +423,8 @@ var buildExternalLibrary = function (library, settings, watch) {
  * The default task, concat and min the main BJS files.
  */
 gulp.task("default", function (cb) {
-    runSequence("typescript-all", "intellisense", "tests-saucelabs", cb);
+    // runSequence("typescript-all", "intellisense", "tests-browserStack", cb);
+    runSequence("typescript-all", "intellisense", cb);
 });
 
 gulp.task("mainBuild", function (cb) {
@@ -573,14 +574,14 @@ gulp.task("tests-integration", function (done) {
     server.start();
 });
 
-gulp.task("tests-saucelabs", function (done) {
+gulp.task("tests-browserStack", function (done) {
     if (!process.env.TRAVIS) {
         done();
         return;
     }
 
     var kamaServerOptions = {
-        configFile: __dirname + "/../../tests/validation/karma.conf.saucelabs.js",
+        configFile: __dirname + "/../../tests/validation/karma.conf.browserstack.js",
         singleRun: true
     };
 

+ 1 - 1
Tools/Gulp/package.json

@@ -46,7 +46,7 @@
         "webpack-stream": "^4.0.0",
         "karma": "^2.0.0",
         "karma-chrome-launcher": "^2.2.0",
-        "karma-sauce-launcher": "^1.2.0",
+        "karma-browserstack-launcher": "^1.3.0",
         "mocha": "^4.0.1",
         "chai": "^4.1.2",
         "sinon": "^4.1.3",

文件差異過大導致無法顯示
+ 7224 - 7224
dist/preview release/babylon.d.ts


文件差異過大導致無法顯示
+ 23 - 23
dist/preview release/babylon.js


+ 13 - 5
dist/preview release/babylon.max.js

@@ -16570,11 +16570,6 @@ var BABYLON;
             }
             // Animations
             this.getScene().stopAnimation(this);
-            // Remove from meshes
-            for (var _i = 0, _a = this.getScene().meshes; _i < _a.length; _i++) {
-                var mesh = _a[_i];
-                mesh._removeLightSource(this);
-            }
             this._uniformBuffer.dispose();
             // Remove from scene
             this.getScene().removeLight(this);
@@ -20255,6 +20250,11 @@ var BABYLON;
         Scene.prototype.removeLight = function (toRemove) {
             var index = this.lights.indexOf(toRemove);
             if (index !== -1) {
+                // Remove from meshes
+                for (var _i = 0, _a = this.meshes; _i < _a.length; _i++) {
+                    var mesh = _a[_i];
+                    mesh._removeLightSource(toRemove);
+                }
                 // Remove from the scene if mesh found
                 this.lights.splice(index, 1);
                 this.sortLightsByPriority();
@@ -20289,6 +20289,14 @@ var BABYLON;
         Scene.prototype.addLight = function (newLight) {
             this.lights.push(newLight);
             this.sortLightsByPriority();
+            // Add light to all meshes (To support if the light is removed and then readded)
+            for (var _i = 0, _a = this.meshes; _i < _a.length; _i++) {
+                var mesh = _a[_i];
+                if (mesh._lightSources.indexOf(newLight) === -1) {
+                    mesh._lightSources.push(newLight);
+                    mesh._resyncLightSources();
+                }
+            }
             this.onNewLightAddedObservable.notifyObservers(newLight);
         };
         Scene.prototype.sortLightsByPriority = function () {

文件差異過大導致無法顯示
+ 23 - 23
dist/preview release/babylon.worker.js


文件差異過大導致無法顯示
+ 6536 - 6536
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


文件差異過大導致無法顯示
+ 25 - 25
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 18 - 6
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -16570,11 +16570,6 @@ var BABYLON;
             }
             // Animations
             this.getScene().stopAnimation(this);
-            // Remove from meshes
-            for (var _i = 0, _a = this.getScene().meshes; _i < _a.length; _i++) {
-                var mesh = _a[_i];
-                mesh._removeLightSource(this);
-            }
             this._uniformBuffer.dispose();
             // Remove from scene
             this.getScene().removeLight(this);
@@ -20255,6 +20250,11 @@ var BABYLON;
         Scene.prototype.removeLight = function (toRemove) {
             var index = this.lights.indexOf(toRemove);
             if (index !== -1) {
+                // Remove from meshes
+                for (var _i = 0, _a = this.meshes; _i < _a.length; _i++) {
+                    var mesh = _a[_i];
+                    mesh._removeLightSource(toRemove);
+                }
                 // Remove from the scene if mesh found
                 this.lights.splice(index, 1);
                 this.sortLightsByPriority();
@@ -20289,6 +20289,14 @@ var BABYLON;
         Scene.prototype.addLight = function (newLight) {
             this.lights.push(newLight);
             this.sortLightsByPriority();
+            // Add light to all meshes (To support if the light is removed and then readded)
+            for (var _i = 0, _a = this.meshes; _i < _a.length; _i++) {
+                var mesh = _a[_i];
+                if (mesh._lightSources.indexOf(newLight) === -1) {
+                    mesh._lightSources.push(newLight);
+                    mesh._resyncLightSources();
+                }
+            }
             this.onNewLightAddedObservable.notifyObservers(newLight);
         };
         Scene.prototype.sortLightsByPriority = function () {
@@ -84302,7 +84310,6 @@ var BABYLON;
                     return;
                 }
                 node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                node.babylonMesh.hasVertexAlpha = true;
                 this._loadTransform(node);
                 if (node.mesh != null) {
                     var mesh = GLTFLoader._GetProperty(this._gltf.meshes, node.mesh);
@@ -84357,6 +84364,7 @@ var BABYLON;
                         var primitive = primitives_1[_i];
                         vertexData.merge(primitive.vertexData);
                     }
+                    node.babylonMesh.hasVertexAlpha = mesh.hasVertexAlpha;
                     new BABYLON.Geometry(node.babylonMesh.name, _this._babylonScene, vertexData, false, node.babylonMesh);
                     // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                     // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
@@ -84565,6 +84573,10 @@ var BABYLON;
                             }
                             case "COLOR_0": {
                                 vertexData.colors = _this._convertToFloat4ColorArray(context, data, accessor);
+                                var hasVertexAlpha = GLTFLoader._GetNumComponents(context, accessor.type) === 4;
+                                if (!mesh.hasVertexAlpha && hasVertexAlpha) {
+                                    mesh.hasVertexAlpha = hasVertexAlpha;
+                                }
                                 break;
                             }
                             default: {

+ 1 - 0
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -311,6 +311,7 @@ declare module BABYLON.GLTF2 {
         primitives: IGLTFMeshPrimitive[];
         weights?: number[];
         index: number;
+        hasVertexAlpha: boolean;
     }
     interface IGLTFNode extends IGLTFChildRootProperty {
         camera?: number;

+ 5 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -745,7 +745,6 @@ var BABYLON;
                     return;
                 }
                 node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                node.babylonMesh.hasVertexAlpha = true;
                 this._loadTransform(node);
                 if (node.mesh != null) {
                     var mesh = GLTFLoader._GetProperty(this._gltf.meshes, node.mesh);
@@ -800,6 +799,7 @@ var BABYLON;
                         var primitive = primitives_1[_i];
                         vertexData.merge(primitive.vertexData);
                     }
+                    node.babylonMesh.hasVertexAlpha = mesh.hasVertexAlpha;
                     new BABYLON.Geometry(node.babylonMesh.name, _this._babylonScene, vertexData, false, node.babylonMesh);
                     // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                     // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
@@ -1008,6 +1008,10 @@ var BABYLON;
                             }
                             case "COLOR_0": {
                                 vertexData.colors = _this._convertToFloat4ColorArray(context, data, accessor);
+                                var hasVertexAlpha = GLTFLoader._GetNumComponents(context, accessor.type) === 4;
+                                if (!mesh.hasVertexAlpha && hasVertexAlpha) {
+                                    mesh.hasVertexAlpha = hasVertexAlpha;
+                                }
                                 break;
                             }
                             default: {

文件差異過大導致無法顯示
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 1 - 0
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -868,6 +868,7 @@ declare module BABYLON.GLTF2 {
         primitives: IGLTFMeshPrimitive[];
         weights?: number[];
         index: number;
+        hasVertexAlpha: boolean;
     }
     interface IGLTFNode extends IGLTFChildRootProperty {
         camera?: number;

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

@@ -2911,7 +2911,6 @@ var BABYLON;
                     return;
                 }
                 node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                node.babylonMesh.hasVertexAlpha = true;
                 this._loadTransform(node);
                 if (node.mesh != null) {
                     var mesh = GLTFLoader._GetProperty(this._gltf.meshes, node.mesh);
@@ -2966,6 +2965,7 @@ var BABYLON;
                         var primitive = primitives_1[_i];
                         vertexData.merge(primitive.vertexData);
                     }
+                    node.babylonMesh.hasVertexAlpha = mesh.hasVertexAlpha;
                     new BABYLON.Geometry(node.babylonMesh.name, _this._babylonScene, vertexData, false, node.babylonMesh);
                     // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                     // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
@@ -3174,6 +3174,10 @@ var BABYLON;
                             }
                             case "COLOR_0": {
                                 vertexData.colors = _this._convertToFloat4ColorArray(context, data, accessor);
+                                var hasVertexAlpha = GLTFLoader._GetNumComponents(context, accessor.type) === 4;
+                                if (!mesh.hasVertexAlpha && hasVertexAlpha) {
+                                    mesh.hasVertexAlpha = hasVertexAlpha;
+                                }
                                 break;
                             }
                             default: {

文件差異過大導致無法顯示
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 5 - 1
dist/preview release/loaders/babylonjs.loaders.js

@@ -3867,7 +3867,6 @@ var BABYLON;
                     return;
                 }
                 node.babylonMesh = new BABYLON.Mesh(node.name || "mesh" + node.index, this._babylonScene);
-                node.babylonMesh.hasVertexAlpha = true;
                 this._loadTransform(node);
                 if (node.mesh != null) {
                     var mesh = GLTFLoader._GetProperty(this._gltf.meshes, node.mesh);
@@ -3922,6 +3921,7 @@ var BABYLON;
                         var primitive = primitives_1[_i];
                         vertexData.merge(primitive.vertexData);
                     }
+                    node.babylonMesh.hasVertexAlpha = mesh.hasVertexAlpha;
                     new BABYLON.Geometry(node.babylonMesh.name, _this._babylonScene, vertexData, false, node.babylonMesh);
                     // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                     // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
@@ -4130,6 +4130,10 @@ var BABYLON;
                             }
                             case "COLOR_0": {
                                 vertexData.colors = _this._convertToFloat4ColorArray(context, data, accessor);
+                                var hasVertexAlpha = GLTFLoader._GetNumComponents(context, accessor.type) === 4;
+                                if (!mesh.hasVertexAlpha && hasVertexAlpha) {
+                                    mesh.hasVertexAlpha = hasVertexAlpha;
+                                }
                                 break;
                             }
                             default: {

文件差異過大導致無法顯示
+ 2 - 2
dist/preview release/loaders/babylonjs.loaders.min.js


+ 1 - 0
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -966,6 +966,7 @@ declare module BABYLON.GLTF2 {
         primitives: IGLTFMeshPrimitive[];
         weights?: number[];
         index: number;
+        hasVertexAlpha: boolean;
     }
     interface IGLTFNode extends IGLTFChildRootProperty {
         camera?: number;

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

@@ -10,9 +10,7 @@ declare module BABYLON {
          * @returns {[fileName: string]: string | Blob} Returns an object with a .gltf, .glb and associates textures
          * as keys and their data and paths as values.
          */
-        static GLTF(scene: BABYLON.Scene, filename: string): {
-            [fileName: string]: string | Blob;
-        };
+        static GLTF(scene: BABYLON.Scene, filename: string): _GLTFData;
         /**
          *
          * @param meshes
@@ -20,17 +18,7 @@ declare module BABYLON {
          *
          * @returns {[fileName: string]: string | Blob} Returns an object with a .glb filename as key and data as value
          */
-        static GLB(scene: BABYLON.Scene, filename: string): {
-            [fileName: string]: string | Blob;
-        };
-        /**
-         * Downloads data from glTF object.
-         *
-         * @param gltfData glTF object with keys being file names and values being data
-         */
-        static downloadFiles(gltfData: {
-            [fileName: string]: string | Blob;
-        }): void;
+        static GLB(scene: BABYLON.Scene, filename: string): _GLTFData;
     }
 }
 
@@ -108,9 +96,7 @@ declare module BABYLON {
          * @returns {[x: string]: string | Blob} object with glTF json tex filename
          * and binary file name as keys and their data as values
          */
-        _generateGLTF(glTFPrefix: string): {
-            [x: string]: string | Blob;
-        };
+        _generateGLTF(glTFPrefix: string): _GLTFData;
         /**
          * Creates a binary buffer for glTF
          *
@@ -126,9 +112,7 @@ declare module BABYLON {
          *
          * @returns {[glbFileName: string]: Blob} object with glb filename as key and data as value
          */
-        _generateGLB(glTFPrefix: string): {
-            [glbFileName: string]: Blob;
-        };
+        _generateGLB(glTFPrefix: string): _GLTFData;
         /**
          * Sets the TRS for each node
          * @param node
@@ -160,3 +144,19 @@ declare module BABYLON {
         private createScene(babylonScene, byteOffset, dataBuffer?);
     }
 }
+
+declare module BABYLON {
+    /**
+     * Class for holding and downloading glTF file data
+     */
+    class _GLTFData {
+        _glTFFiles: {
+            [fileName: string]: string | Blob;
+        };
+        constructor();
+        /**
+         * Downloads glTF data.
+         */
+        downloadFiles(): void;
+    }
+}

+ 60 - 48
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -30,42 +30,6 @@ var BABYLON;
             var gltfGenerator = new BABYLON._GLTF2Exporter(scene);
             return gltfGenerator._generateGLB(glTFPrefix);
         };
-        /**
-         * Downloads data from glTF object.
-         *
-         * @param gltfData glTF object with keys being file names and values being data
-         */
-        GLTF2Export.downloadFiles = function (gltfData) {
-            /**
-             * Checks for a matching suffix at the end of a string (for ES5 and lower)
-             * @param str
-             * @param suffix
-             *
-             * @returns {boolean} indicating whether the suffix matches or not
-             */
-            function endsWith(str, suffix) {
-                return str.indexOf(suffix, str.length - suffix.length) !== -1;
-            }
-            for (var key in gltfData) {
-                var link = document.createElement('a');
-                document.body.appendChild(link);
-                link.setAttribute("type", "hidden");
-                link.download = key;
-                var blob = gltfData[key];
-                var mimeType = void 0;
-                if (endsWith(key, ".glb")) {
-                    mimeType = { type: "model/gltf-binary" };
-                }
-                else if (endsWith(key, ".bin")) {
-                    mimeType = { type: "application/octet-stream" };
-                }
-                else if (endsWith(key, ".gltf")) {
-                    mimeType = { type: "model/gltf+json" };
-                }
-                link.href = window.URL.createObjectURL(new Blob([blob], mimeType));
-                link.click();
-            }
-        };
         return GLTF2Export;
     }());
     BABYLON.GLTF2Export = GLTF2Export;
@@ -282,7 +246,7 @@ var BABYLON;
          */
         _GLTF2Exporter.prototype.generateJSON = function (glb, glTFPrefix, prettyPrint) {
             var buffer = { byteLength: this.totalByteLength };
-            var glTf = {
+            var glTF = {
                 buffers: [buffer],
                 asset: this.asset,
                 meshes: this.meshes,
@@ -292,12 +256,12 @@ var BABYLON;
                 accessors: this.accessors
             };
             if (this.scenes.length > 0) {
-                glTf.scene = 0;
+                glTF.scene = 0;
             }
             if (!glb) {
                 buffer.uri = glTFPrefix + ".bin";
             }
-            var jsonText = prettyPrint ? JSON.stringify(glTf, null, 2) : JSON.stringify(glTf);
+            var jsonText = prettyPrint ? JSON.stringify(glTF, null, 2) : JSON.stringify(glTF);
             return jsonText;
         };
         /**
@@ -313,11 +277,10 @@ var BABYLON;
             var bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
             var glTFFileName = glTFPrefix + '.gltf';
             var glTFBinFile = glTFPrefix + '.bin';
-            return _a = {},
-                _a[glTFFileName] = jsonText,
-                _a[glTFBinFile] = bin,
-                _a;
-            var _a;
+            var container = new BABYLON._GLTFData();
+            container._glTFFiles[glTFFileName] = jsonText;
+            container._glTFFiles[glTFBinFile] = bin;
+            return container;
         };
         /**
          * Creates a binary buffer for glTF
@@ -386,10 +349,9 @@ var BABYLON;
             }
             // binary data
             var glbFile = new Blob([headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer], { type: 'application/octet-stream' });
-            return _a = {},
-                _a[glbFileName] = glbFile,
-                _a;
-            var _a;
+            var container = new BABYLON._GLTFData();
+            container._glTFFiles[glbFileName] = glbFile;
+            return container;
         };
         /**
          * Sets the TRS for each node
@@ -647,3 +609,53 @@ var BABYLON;
 })(BABYLON || (BABYLON = {}));
 
 //# sourceMappingURL=babylon.glTFExporter.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * Class for holding and downloading glTF file data
+     */
+    var _GLTFData = /** @class */ (function () {
+        function _GLTFData() {
+            this._glTFFiles = {};
+        }
+        /**
+         * Downloads glTF data.
+         */
+        _GLTFData.prototype.downloadFiles = function () {
+            /**
+            * Checks for a matching suffix at the end of a string (for ES5 and lower)
+            * @param str
+            * @param suffix
+            *
+            * @returns {boolean} indicating whether the suffix matches or not
+            */
+            function endsWith(str, suffix) {
+                return str.indexOf(suffix, str.length - suffix.length) !== -1;
+            }
+            for (var key in this._glTFFiles) {
+                var link = document.createElement('a');
+                document.body.appendChild(link);
+                link.setAttribute("type", "hidden");
+                link.download = key;
+                var blob = this._glTFFiles[key];
+                var mimeType = void 0;
+                if (endsWith(key, ".glb")) {
+                    mimeType = { type: "model/gltf-binary" };
+                }
+                else if (endsWith(key, ".bin")) {
+                    mimeType = { type: "application/octet-stream" };
+                }
+                else if (endsWith(key, ".gltf")) {
+                    mimeType = { type: "model/gltf+json" };
+                }
+                link.href = window.URL.createObjectURL(new Blob([blob], mimeType));
+                link.click();
+            }
+        };
+        return _GLTFData;
+    }());
+    BABYLON._GLTFData = _GLTFData;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.glTFData.js.map

文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 60 - 48
dist/preview release/serializers/babylonjs.serializers.js

@@ -174,42 +174,6 @@ var BABYLON;
             var gltfGenerator = new BABYLON._GLTF2Exporter(scene);
             return gltfGenerator._generateGLB(glTFPrefix);
         };
-        /**
-         * Downloads data from glTF object.
-         *
-         * @param gltfData glTF object with keys being file names and values being data
-         */
-        GLTF2Export.downloadFiles = function (gltfData) {
-            /**
-             * Checks for a matching suffix at the end of a string (for ES5 and lower)
-             * @param str
-             * @param suffix
-             *
-             * @returns {boolean} indicating whether the suffix matches or not
-             */
-            function endsWith(str, suffix) {
-                return str.indexOf(suffix, str.length - suffix.length) !== -1;
-            }
-            for (var key in gltfData) {
-                var link = document.createElement('a');
-                document.body.appendChild(link);
-                link.setAttribute("type", "hidden");
-                link.download = key;
-                var blob = gltfData[key];
-                var mimeType = void 0;
-                if (endsWith(key, ".glb")) {
-                    mimeType = { type: "model/gltf-binary" };
-                }
-                else if (endsWith(key, ".bin")) {
-                    mimeType = { type: "application/octet-stream" };
-                }
-                else if (endsWith(key, ".gltf")) {
-                    mimeType = { type: "model/gltf+json" };
-                }
-                link.href = window.URL.createObjectURL(new Blob([blob], mimeType));
-                link.click();
-            }
-        };
         return GLTF2Export;
     }());
     BABYLON.GLTF2Export = GLTF2Export;
@@ -426,7 +390,7 @@ var BABYLON;
          */
         _GLTF2Exporter.prototype.generateJSON = function (glb, glTFPrefix, prettyPrint) {
             var buffer = { byteLength: this.totalByteLength };
-            var glTf = {
+            var glTF = {
                 buffers: [buffer],
                 asset: this.asset,
                 meshes: this.meshes,
@@ -436,12 +400,12 @@ var BABYLON;
                 accessors: this.accessors
             };
             if (this.scenes.length > 0) {
-                glTf.scene = 0;
+                glTF.scene = 0;
             }
             if (!glb) {
                 buffer.uri = glTFPrefix + ".bin";
             }
-            var jsonText = prettyPrint ? JSON.stringify(glTf, null, 2) : JSON.stringify(glTf);
+            var jsonText = prettyPrint ? JSON.stringify(glTF, null, 2) : JSON.stringify(glTF);
             return jsonText;
         };
         /**
@@ -457,11 +421,10 @@ var BABYLON;
             var bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
             var glTFFileName = glTFPrefix + '.gltf';
             var glTFBinFile = glTFPrefix + '.bin';
-            return _a = {},
-                _a[glTFFileName] = jsonText,
-                _a[glTFBinFile] = bin,
-                _a;
-            var _a;
+            var container = new BABYLON._GLTFData();
+            container._glTFFiles[glTFFileName] = jsonText;
+            container._glTFFiles[glTFBinFile] = bin;
+            return container;
         };
         /**
          * Creates a binary buffer for glTF
@@ -530,10 +493,9 @@ var BABYLON;
             }
             // binary data
             var glbFile = new Blob([headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer], { type: 'application/octet-stream' });
-            return _a = {},
-                _a[glbFileName] = glbFile,
-                _a;
-            var _a;
+            var container = new BABYLON._GLTFData();
+            container._glTFFiles[glbFileName] = glbFile;
+            return container;
         };
         /**
          * Sets the TRS for each node
@@ -792,6 +754,56 @@ var BABYLON;
 
 //# sourceMappingURL=babylon.glTFExporter.js.map
 
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * Class for holding and downloading glTF file data
+     */
+    var _GLTFData = /** @class */ (function () {
+        function _GLTFData() {
+            this._glTFFiles = {};
+        }
+        /**
+         * Downloads glTF data.
+         */
+        _GLTFData.prototype.downloadFiles = function () {
+            /**
+            * Checks for a matching suffix at the end of a string (for ES5 and lower)
+            * @param str
+            * @param suffix
+            *
+            * @returns {boolean} indicating whether the suffix matches or not
+            */
+            function endsWith(str, suffix) {
+                return str.indexOf(suffix, str.length - suffix.length) !== -1;
+            }
+            for (var key in this._glTFFiles) {
+                var link = document.createElement('a');
+                document.body.appendChild(link);
+                link.setAttribute("type", "hidden");
+                link.download = key;
+                var blob = this._glTFFiles[key];
+                var mimeType = void 0;
+                if (endsWith(key, ".glb")) {
+                    mimeType = { type: "model/gltf-binary" };
+                }
+                else if (endsWith(key, ".bin")) {
+                    mimeType = { type: "application/octet-stream" };
+                }
+                else if (endsWith(key, ".gltf")) {
+                    mimeType = { type: "model/gltf+json" };
+                }
+                link.href = window.URL.createObjectURL(new Blob([blob], mimeType));
+                link.click();
+            }
+        };
+        return _GLTFData;
+    }());
+    BABYLON._GLTFData = _GLTFData;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.glTFData.js.map
+
 
 (function universalModuleDefinition(root, factory) {
                 var f = factory();

文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


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

@@ -23,9 +23,7 @@ declare module BABYLON {
          * @returns {[fileName: string]: string | Blob} Returns an object with a .gltf, .glb and associates textures
          * as keys and their data and paths as values.
          */
-        static GLTF(scene: BABYLON.Scene, filename: string): {
-            [fileName: string]: string | Blob;
-        };
+        static GLTF(scene: BABYLON.Scene, filename: string): _GLTFData;
         /**
          *
          * @param meshes
@@ -33,17 +31,7 @@ declare module BABYLON {
          *
          * @returns {[fileName: string]: string | Blob} Returns an object with a .glb filename as key and data as value
          */
-        static GLB(scene: BABYLON.Scene, filename: string): {
-            [fileName: string]: string | Blob;
-        };
-        /**
-         * Downloads data from glTF object.
-         *
-         * @param gltfData glTF object with keys being file names and values being data
-         */
-        static downloadFiles(gltfData: {
-            [fileName: string]: string | Blob;
-        }): void;
+        static GLB(scene: BABYLON.Scene, filename: string): _GLTFData;
     }
 }
 
@@ -121,9 +109,7 @@ declare module BABYLON {
          * @returns {[x: string]: string | Blob} object with glTF json tex filename
          * and binary file name as keys and their data as values
          */
-        _generateGLTF(glTFPrefix: string): {
-            [x: string]: string | Blob;
-        };
+        _generateGLTF(glTFPrefix: string): _GLTFData;
         /**
          * Creates a binary buffer for glTF
          *
@@ -139,9 +125,7 @@ declare module BABYLON {
          *
          * @returns {[glbFileName: string]: Blob} object with glb filename as key and data as value
          */
-        _generateGLB(glTFPrefix: string): {
-            [glbFileName: string]: Blob;
-        };
+        _generateGLB(glTFPrefix: string): _GLTFData;
         /**
          * Sets the TRS for each node
          * @param node
@@ -173,3 +157,19 @@ declare module BABYLON {
         private createScene(babylonScene, byteOffset, dataBuffer?);
     }
 }
+
+declare module BABYLON {
+    /**
+     * Class for holding and downloading glTF file data
+     */
+    class _GLTFData {
+        _glTFFiles: {
+            [fileName: string]: string | Blob;
+        };
+        constructor();
+        /**
+         * Downloads glTF data.
+         */
+        downloadFiles(): void;
+    }
+}

文件差異過大導致無法顯示
+ 38 - 38
dist/preview release/viewer/babylon.viewer.js


二進制
favicon.ico


+ 6 - 3
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -358,7 +358,6 @@ module BABYLON.GLTF2 {
             }
 
             node.babylonMesh = new Mesh(node.name || "mesh" + node.index, this._babylonScene);
-            node.babylonMesh.hasVertexAlpha = true;
 
             this._loadTransform(node);
 
@@ -426,7 +425,7 @@ module BABYLON.GLTF2 {
                 for (const primitive of primitives) {
                     vertexData.merge(primitive.vertexData);
                 }
-
+                node.babylonMesh.hasVertexAlpha = mesh.hasVertexAlpha;
                 new Geometry(node.babylonMesh.name, this._babylonScene, vertexData, false, node.babylonMesh);
 
                 // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
@@ -647,6 +646,10 @@ module BABYLON.GLTF2 {
                         }
                         case "COLOR_0": {
                             vertexData.colors = this._convertToFloat4ColorArray(context, data, accessor);
+                            const hasVertexAlpha = GLTFLoader._GetNumComponents(context, accessor.type) === 4;
+                            if (!mesh.hasVertexAlpha && hasVertexAlpha) {
+                                mesh.hasVertexAlpha = hasVertexAlpha;
+                            }
                             break;
                         }
                         default: {
@@ -678,7 +681,7 @@ module BABYLON.GLTF2 {
                 });
             }
         }
-
+        
         private _createMorphTargets(context: string, node: IGLTFNode, mesh: IGLTFMesh): void {
             const primitives = mesh.primitives;
 

+ 1 - 0
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -217,6 +217,7 @@ module BABYLON.GLTF2 {
 
         // Runtime values
         index: number;
+        hasVertexAlpha: boolean;
     }
 
     export interface IGLTFNode extends IGLTFChildRootProperty {

+ 3 - 1
readme.md

@@ -3,7 +3,9 @@ Babylon.js
 
 Getting started? Play directly with the Babylon.js API via our [playground](http://www.babylonjs.com/playground). It contains also lot of simple samples to learn how to use it.
 
-[![Build Status](https://travis-ci.org/BabylonJS/Babylon.js.svg)](https://travis-ci.org/BabylonJS/Babylon.js)
+[![Build Status](https://travis-ci.org/BabylonJS/Babylon.js.svg)](https://travis-ci.org/BabylonJS/Babylon.js) 
+
+[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=d0pZcGpNU0NPeTM5cEpEc2lTU2JJTWc2Mk9NSlAzTzdIaFBpTnF2TjBycz0tLStUWkVBejdVQmN4Y0dIUVVYeU0yc2c9PQ==--4e576f7b7c21279bc6d026b6f51796f58134856b)](https://www.browserstack.com/automate/public-build/d0pZcGpNU0NPeTM5cEpEc2lTU2JJTWc2Mk9NSlAzTzdIaFBpTnF2TjBycz0tLStUWkVBejdVQmN4Y0dIUVVYeU0yc2c9PQ==--4e576f7b7c21279bc6d026b6f51796f58134856b)
 
 **Any questions?** Here is our official [forum](http://www.html5gamedevs.com/forum/16-babylonjs/) on www.html5gamedevs.com.
 

+ 49 - 0
serializers/src/glTF/2.0/babylon.glTFData.ts

@@ -0,0 +1,49 @@
+module BABYLON {
+    /**
+     * Class for holding and downloading glTF file data
+     */
+    export class _GLTFData {
+        _glTFFiles: { [fileName: string]: string | Blob };
+
+        public constructor() {
+            this._glTFFiles = {};
+        }
+        /**
+         * Downloads glTF data.
+         */
+        public downloadFiles(): void {
+            /**
+            * Checks for a matching suffix at the end of a string (for ES5 and lower)
+            * @param str 
+            * @param suffix 
+            * 
+            * @returns {boolean} indicating whether the suffix matches or not
+            */
+            function endsWith(str: string, suffix: string): boolean {
+                return str.indexOf(suffix, str.length - suffix.length) !== -1;
+            }
+            for (let key in this._glTFFiles) {
+                let link = document.createElement('a');
+                document.body.appendChild(link);
+                link.setAttribute("type", "hidden");
+                link.download = key;
+                let blob = this._glTFFiles[key];
+                let mimeType;
+
+                if (endsWith(key, ".glb")) {
+                    mimeType = { type: "model/gltf-binary" };
+                }
+                else if (endsWith(key, ".bin")) {
+                    mimeType = { type: "application/octet-stream" };
+                }
+                else if (endsWith(key, ".gltf")) {
+                    mimeType = { type: "model/gltf+json" };
+                }
+
+                link.href = window.URL.createObjectURL(new Blob([blob], mimeType));
+                link.click();
+            }
+
+        }
+    }
+}

+ 18 - 16
serializers/src/glTF/2.0/babylon.glTFExporter.ts

@@ -52,8 +52,8 @@ module BABYLON {
         bufferViews: _IGLTFBufferView[];
         accessors: _IGLTFAccessor[];
     }
-    export class _GLTF2Exporter {
 
+    export class _GLTF2Exporter {
         private bufferViews: _IGLTFBufferView[];
         private accessors: _IGLTFAccessor[];
         private nodes: _IGLTFNode[];
@@ -75,7 +75,7 @@ module BABYLON {
             let totalByteLength = 0;
 
             totalByteLength = this.createScene(this.babylonScene, totalByteLength);
-            
+
             this.totalByteLength = totalByteLength;
         }
 
@@ -282,7 +282,7 @@ module BABYLON {
         private generateJSON(glb: boolean, glTFPrefix?: string, prettyPrint?: boolean): string {
             let buffer: _IGLTFBuffer = { byteLength: this.totalByteLength };
 
-            let glTf: _IGLTF = {
+            let glTF: _IGLTF = {
                 buffers: [buffer],
                 asset: this.asset,
                 meshes: this.meshes,
@@ -292,14 +292,14 @@ module BABYLON {
                 accessors: this.accessors
             };
             if (this.scenes.length > 0) {
-                glTf.scene = 0;
+                glTF.scene = 0;
             }
 
             if (!glb) {
                 buffer.uri = glTFPrefix + ".bin";
             }
 
-            let jsonText = prettyPrint ? JSON.stringify(glTf, null, 2) : JSON.stringify(glTf);
+            let jsonText = prettyPrint ? JSON.stringify(glTF, null, 2) : JSON.stringify(glTF);
 
             return jsonText;
         }
@@ -310,7 +310,7 @@ module BABYLON {
          * @returns {[x: string]: string | Blob} object with glTF json tex filename 
          * and binary file name as keys and their data as values
          */
-        public _generateGLTF(glTFPrefix: string): { [x: string]: string | Blob } {
+        public _generateGLTF(glTFPrefix: string): _GLTFData {
             const jsonText = this.generateJSON(false, glTFPrefix, true);
             const binaryBuffer = this.generateBinary();
             const bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
@@ -318,10 +318,11 @@ module BABYLON {
             const glTFFileName = glTFPrefix + '.gltf';
             const glTFBinFile = glTFPrefix + '.bin';
 
-            return {
-                [glTFFileName]: jsonText,
-                [glTFBinFile]: bin
-            };
+            let container = new _GLTFData();
+            container._glTFFiles[glTFFileName] = jsonText;
+            container._glTFFiles[glTFBinFile] = bin;
+
+            return container;
         }
         /**
          * Creates a binary buffer for glTF
@@ -333,7 +334,7 @@ module BABYLON {
             let binaryBuffer = new ArrayBuffer(this.totalByteLength);
             let dataBuffer = new DataView(binaryBuffer);
             byteOffset = this.createScene(this.babylonScene, byteOffset, dataBuffer);
-            
+
             return binaryBuffer;
         }
         /**
@@ -345,7 +346,7 @@ module BABYLON {
          * 
          * @returns {[glbFileName: string]: Blob} object with glb filename as key and data as value
          */
-        public _generateGLB(glTFPrefix: string): { [glbFileName: string]: Blob } {
+        public _generateGLB(glTFPrefix: string): _GLTFData {
             const jsonText = this.generateJSON(true);
             const binaryBuffer = this.generateBinary();
             let glbFileName = glTFPrefix + '.glb';
@@ -399,9 +400,10 @@ module BABYLON {
             // binary data
             let glbFile = new Blob([headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer], { type: 'application/octet-stream' });
 
-            return {
-                [glbFileName]: glbFile
-            };
+            let container = new _GLTFData();
+            container._glTFFiles[glbFileName] = glbFile;
+
+            return container;
         }
         /**
          * Sets the TRS for each node
@@ -689,7 +691,7 @@ module BABYLON {
          * @returns {number} bytelength + byteoffset
          */
         private createScene(babylonScene: BABYLON.Scene, byteOffset: number, dataBuffer?: DataView): number {
-            if (babylonScene.meshes.length > 0 ) {
+            if (babylonScene.meshes.length > 0) {
                 let babylonMeshes = babylonScene.meshes;
                 let scene = { nodes: new Array<number>() };
 

+ 2 - 40
serializers/src/glTF/2.0/babylon.glTFSerializer.ts

@@ -11,7 +11,7 @@ module BABYLON {
          * @returns {[fileName: string]: string | Blob} Returns an object with a .gltf, .glb and associates textures
          * as keys and their data and paths as values.
          */
-        public static GLTF(scene: BABYLON.Scene, filename: string): {[fileName: string]: string | Blob} {
+        public static GLTF(scene: BABYLON.Scene, filename: string): _GLTFData {
             let glTFPrefix = filename.replace(/\.[^/.]+$/, "");
             let gltfGenerator = new _GLTF2Exporter(scene);
 
@@ -24,49 +24,11 @@ module BABYLON {
          * 
          * @returns {[fileName: string]: string | Blob} Returns an object with a .glb filename as key and data as value
          */
-        public static GLB(scene: BABYLON.Scene, filename: string): {[fileName: string]: string | Blob} {
+        public static GLB(scene: BABYLON.Scene, filename: string): _GLTFData {
             let glTFPrefix = filename.replace(/\.[^/.]+$/, "");        
             let gltfGenerator = new _GLTF2Exporter(scene);
 
             return gltfGenerator._generateGLB(glTFPrefix);
         }
-        /**
-         * Downloads data from glTF object.
-         * 
-         * @param gltfData glTF object with keys being file names and values being data
-         */
-        public static downloadFiles(gltfData: {[fileName: string]: string | Blob} ): void {
-            /**
-             * Checks for a matching suffix at the end of a string (for ES5 and lower)
-             * @param str 
-             * @param suffix 
-             * 
-             * @returns {boolean} indicating whether the suffix matches or not
-             */
-            function endsWith(str: string, suffix: string): boolean {
-                return str.indexOf(suffix, str.length - suffix.length) !== -1;
-            }
-            for (let key in gltfData) {
-                let link = document.createElement('a');
-                document.body.appendChild(link);
-                link.setAttribute("type", "hidden");
-                link.download = key;
-                let blob = gltfData[key];
-                let mimeType;
-                
-                if (endsWith(key, ".glb")) {
-                    mimeType = {type: "model/gltf-binary"};
-                }
-                else if (endsWith(key, ".bin")) {
-                    mimeType = {type: "application/octet-stream"};
-                }
-                else if (endsWith(key, ".gltf")) {
-                    mimeType = {type: "model/gltf+json"};
-                }
-
-                link.href = window.URL.createObjectURL(new Blob([blob], mimeType));
-                link.click();
-            }
-        }
     }
 }

+ 0 - 5
src/Lights/babylon.light.ts

@@ -397,11 +397,6 @@
             // Animations
             this.getScene().stopAnimation(this);
 
-            // Remove from meshes
-            for (var mesh of this.getScene().meshes) {
-                mesh._removeLightSource(this);
-            }
-
             this._uniformBuffer.dispose();
 
             // Remove from scene

+ 5 - 1
src/Mesh/babylon.meshBuilder.ts

@@ -775,6 +775,10 @@
                     throw new Error("Unable to get 2d context for CreateGroundFromHeightMap");
                 }
 
+                if (scene.isDisposed) {
+                    return;
+                }
+
                 var bufferWidth = img.width;
                 var bufferHeight = img.height;
                 canvas.width = bufferWidth;
@@ -1264,7 +1268,7 @@
 
                 var angle = 0;
                 var returnScale: { (i: number, distance: number): number; } = () => { return scale !== null ? scale : 1; };
-                var returnRotation: { (i: number, distance: number): number; } = () => { return rotation!== null ? rotation : 0; };
+                var returnRotation: { (i: number, distance: number): number; } = () => { return rotation !== null ? rotation : 0; };
                 var rotate: { (i: number, distance: number): number; } = custom && rotateFunction ? rotateFunction : returnRotation;
                 var scl: { (i: number, distance: number): number; } = custom && scaleFunction ? scaleFunction : returnScale;
                 var index = (cap === Mesh.NO_CAP || cap === Mesh.CAP_END) ? 0 : 2;

+ 13 - 0
src/babylon.scene.ts

@@ -2278,6 +2278,11 @@
         public removeLight(toRemove: Light): number {
             var index = this.lights.indexOf(toRemove);
             if (index !== -1) {
+                // Remove from meshes
+                for (var mesh of this.meshes) {
+                    mesh._removeLightSource(toRemove);
+                }
+
                 // Remove from the scene if mesh found
                 this.lights.splice(index, 1);
                 this.sortLightsByPriority();
@@ -2314,6 +2319,14 @@
             this.lights.push(newLight);
             this.sortLightsByPriority();
 
+            // Add light to all meshes (To support if the light is removed and then readded)
+            for (var mesh of this.meshes) {
+                if(mesh._lightSources.indexOf(newLight) === -1){
+                    mesh._lightSources.push(newLight);
+                    mesh._resyncLightSources();
+                }
+            }
+
             this.onNewLightAddedObservable.notifyObservers(newLight);
         }
 

+ 6 - 3
tests/validation/config.json

@@ -241,7 +241,8 @@
       "renderCount": 20,
       "scriptToRun": "/Demos/GLTF1CesiumMan/index.js",
       "functionToCall": "createScene",
-      "referenceImage": "gltf1CesiumMan.png"
+      "referenceImage": "gltf1CesiumMan.png",
+      "excludeFromAutomaticTesting": true
     },
     {
       "title": "GLTF Mesh Primitive Attribute Test",
@@ -330,7 +331,8 @@
       "scriptToRun": "/Demos/Procedural/proceduralTexture.js",
       "functionToCall": "CreateProceduralTextureTestScene",
       "referenceImage": "procedural.png",
-      "replace": "./land, https://cdn.rawgit.com/BabylonJS/Website/06ecbea7/Demos/Procedural/land"
+      "replace": "./land, https://cdn.rawgit.com/BabylonJS/Website/06ecbea7/Demos/Procedural/land",
+      "excludeFromAutomaticTesting": true
     },
     {
       "title": "Instances",
@@ -338,7 +340,8 @@
       "scriptToRun": "/Demos/Instances/instances.js",
       "functionToCall": "CreateInstancesTestScene",
       "referenceImage": "instances.png",
-      "replace": "ground., Ground."
+      "replace": "ground., Ground.",
+      "excludeFromAutomaticTesting": true
     },
     {
       "title": "Instanced Bones",

+ 21 - 14
tests/validation/integration.js

@@ -1,4 +1,4 @@
-window.__karma__.loaded = function() {};
+window.__karma__.loaded = function () { };
 
 // Loading tests
 var xhr = new XMLHttpRequest();
@@ -10,38 +10,45 @@ xhr.addEventListener("load", function () {
 
         config = JSON.parse(xhr.responseText);
 
-        describe("Validation Tests", function() {
+        describe("Validation Tests", function () {
             before(function (done) {
                 this.timeout(180000);
                 require = null;
                 BABYLONDEVTOOLS.Loader
-                .require('/tests/validation/validation.js')
-                .useDist()
-                .load(function() {
-                    done();            
-                });
+                    .require('/tests/validation/validation.js')
+                    .useDist()
+                    .load(function () {
+                        var info = engine.getGlInfo();
+                        console.log("Webgl Version: " + info.version);
+                        console.log("Webgl Vendor: " + info.vendor);
+                        console.log("Webgl Renderer: " + info.renderer);
+                        done();
+                    });
             });
-        
+
             // Run tests
             for (let index = 0; index < config.tests.length; index++) {
                 var test = config.tests[index];
-                if (test.onlyVisual) {
+                if (test.onlyVisual || test.excludeFromAutomaticTesting) {
                     continue;
                 }
 
-                it(test.title, function(done) {
+                it(test.title, function (done) {
                     this.timeout(240000);
-        
+
                     try {
-                        runTest(index, function(result) {
+                        runTest(index, function(result, screenshot) {
                             try {
                                 expect(result).to.be.true; 
                                 done();
                             }
                             catch (e) {
+                                if (screenshot) {
+                                    console.error(screenshot);
+                                }
                                 done(e);
                             }
-                        });                
+                        });
                     }
                     catch (e) {
                         done(e);
@@ -50,7 +57,7 @@ xhr.addEventListener("load", function () {
             };
         });
 
-        window.__karma__.start();          
+        window.__karma__.start();
     }
 }, false);
 

+ 89 - 0
tests/validation/karma.conf.browserstack.js

@@ -0,0 +1,89 @@
+module.exports = function (config) {
+    'use strict';
+    config.set({
+
+        basePath: '../../',
+        captureTimeout: 3e5,
+        browserNoActivityTimeout: 3e5,
+        browserDisconnectTimeout: 3e5,
+        browserDisconnectTolerance: 3,
+
+        urlRoot: '/karma',
+
+        frameworks: ['mocha', 'chai', 'sinon'],
+
+        files: [
+            './Tools/DevLoader/BabylonLoader.js',
+            './tests/validation/index.css',
+            './tests/validation/integration.js',
+            './favicon.ico',
+            { pattern: 'dist/**/*', watched: false, included: false, served: true },
+            { pattern: 'assets/**/*', watched: false, included: false, served: true },
+            { pattern: 'tests/**/*', watched: false, included: false, served: true },
+            { pattern: 'Playground/scenes/**/*', watched: false, included: false, served: true },
+            { pattern: 'Playground/textures/**/*', watched: false, included: false, served: true },
+            { pattern: 'Playground/sounds/**/*', watched: false, included: false, served: true },
+            { pattern: 'Tools/DevLoader/**/*', watched: false, included: false, served: true },            
+            { pattern: 'Tools/Gulp/config.json', watched: false, included: false, served: true },
+        ],
+        proxies: {
+            '/': '/base/'
+        },
+
+        port: 1338,
+        colors: true,
+        autoWatch: false,
+        singleRun: false,
+
+        // level of logging
+        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+        logLevel: config.LOG_INFO,
+
+        browserStack: {
+            project: 'Babylon JS Validation Tests',
+            video: false,
+            debug : 'true',
+            timeout: 900
+        },
+        customLaunchers: {
+            bs_chrome_win: {
+                base: 'BrowserStack',
+                browser: 'Chrome',
+                browser_version: '63.0',
+                os: 'Windows',
+                os_version: '10'
+            },
+            bs_edge_win: {
+                base: 'BrowserStack',
+                browser: 'Edge',
+                browser_version: '16.0',
+                os: 'Windows',
+                os_version: '10'
+            },
+            bs_firefox_win: {
+                base: 'BrowserStack',
+                browser: 'Firefox',
+                browser_version: '57.0',
+                os: 'Windows',
+                os_version: '10'
+            },
+            bs_chrome_android: {
+                base: 'BrowserStack',
+                os: 'Android',
+                os_version : '8.0',
+                device : 'Google Pixel',
+                real_mobile : 'true'
+            },
+            bs_safari_ios: {
+                base: 'BrowserStack',
+                os: 'ios',
+                os_version : '10.3',
+                device : 'iPhone 7',
+                real_mobile : 'true'
+            }
+        },
+        browsers: ['bs_firefox_win', 'bs_chrome_android'],
+        reporters: ['dots', 'BrowserStack'],
+        singleRun: true
+    });
+};

+ 0 - 74
tests/validation/karma.conf.saucelabs.js

@@ -1,74 +0,0 @@
-// sl_ie_11: {
-//     base: 'SauceLabs',
-//     browserName: 'internet explorer',
-//     platform: 'Windows 10',
-//     version: '11'
-// },
-// sl_android: {
-//     base: 'SauceLabs',
-//     browserName: 'Browser',
-//     platform: 'Android',
-//     version: '4.4',
-//     deviceName: 'Samsung Galaxy S3 Emulator',
-//     deviceOrientation: 'portrait'
-// }
-
-module.exports = function (config) {
-    'use strict';
-    config.set({
-
-        basePath: '../../',
-        browserNoActivityTimeout: 1800000,
-
-        urlRoot: '/karma',
-
-        frameworks: ['mocha', 'chai', 'sinon'],
-
-        files: [
-            './Tools/DevLoader/BabylonLoader.js',
-            './tests/validation/index.css',
-            './tests/validation/integration.js',
-            { pattern: 'dist/**/*', watched: false, included: false, served: true },
-            { pattern: 'assets/**/*', watched: false, included: false, served: true },
-            { pattern: 'tests/**/*', watched: false, included: false, served: true },
-            { pattern: 'Playground/scenes/**/*', watched: false, included: false, served: true },
-            { pattern: 'Playground/textures/**/*', watched: false, included: false, served: true },
-            { pattern: 'Playground/sounds/**/*', watched: false, included: false, served: true },
-            { pattern: 'Tools/DevLoader/**/*', watched: false, included: false, served: true },            
-            { pattern: 'Tools/Gulp/config.json', watched: false, included: false, served: true },
-        ],
-        proxies: {
-            '/': '/base/'
-        },
-
-        port: 1338,
-        colors: true,
-        autoWatch: false,
-        singleRun: false,
-
-        // level of logging
-        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
-        logLevel: config.LOG_ERROR,
-
-        sauceLabs: {
-            testName: 'Babylon JS Validation Tests',
-            startConnect: false,
-            tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER,
-            build: process.env.TRAVIS_BUILD_NUMBER,
-            tags: [process.env.TRAVIS_BRANCH, process.env.TRAVIS_PULL_REQUEST],
-            public: 'public'
-        },
-        customLaunchers: {
-            sl_firefox: {
-              base: 'SauceLabs',
-              browserName: 'firefox',
-              platform: 'Windows 10',
-              version: '57',
-              tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
-            }
-        },
-        browsers: ['sl_firefox'],
-        reporters: ['dots', 'saucelabs'],
-        singleRun: true
-    });
-};

+ 3 - 2
tests/validation/validation.js

@@ -120,11 +120,12 @@ function evaluate(test, resultCanvas, result, renderImage, index, waitRing, done
     }
     waitRing.classList.add("hidden");
 
-    renderImage.src = saveRenderImage(renderData, canvas);
+    var renderB64 = saveRenderImage(renderData, canvas);
+    renderImage.src = renderB64;
 
     currentScene.dispose();
 
-    done(testRes);
+    done(testRes, renderB64);
 }
 
 function processCurrentScene(test, resultCanvas, result, renderImage, index, waitRing, done) {