Просмотр исходного кода

Merge remote-tracking branch 'BabylonJS/master' into animations-viewer

Raanan Weber 7 лет назад
Родитель
Сommit
1687ea8c6f
100 измененных файлов с 39604 добавлено и 38549 удалено
  1. 11042 10942
      Playground/babylon.d.txt
  2. 2 0
      Playground/scenes/TwoQuads/TwoQuads.gltf
  3. 1 1
      Tools/Gulp/gulp-addModuleExports.js
  4. 10051 9952
      dist/preview release/babylon.d.ts
  5. 55 55
      dist/preview release/babylon.js
  6. 304 55
      dist/preview release/babylon.max.js
  7. 56 56
      dist/preview release/babylon.worker.js
  8. 14326 14227
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  9. 57 57
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  10. 380 97
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  11. 381 98
      dist/preview release/customConfigurations/minimalGLTFViewer/es6.js
  12. 305 56
      dist/preview release/es6.js
  13. 1 1
      dist/preview release/gltf2Interface/package.json
  14. 3 3
      dist/preview release/gui/babylon.gui.min.js
  15. 1 1
      dist/preview release/gui/package.json
  16. 4 4
      dist/preview release/inspector/babylon.inspector.bundle.js
  17. 21 2
      dist/preview release/inspector/babylon.inspector.d.ts
  18. 159 8
      dist/preview release/inspector/babylon.inspector.js
  19. 4 4
      dist/preview release/inspector/babylon.inspector.min.js
  20. 1 1
      dist/preview release/inspector/package.json
  21. 15 2
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  22. 27 0
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  23. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  24. 21 8
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  25. 74 41
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  26. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  27. 22 8
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  28. 75 41
      dist/preview release/loaders/babylon.glTFFileLoader.js
  29. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  30. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  31. 75 41
      dist/preview release/loaders/babylonjs.loaders.js
  32. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  33. 22 8
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  34. 2 2
      dist/preview release/loaders/package.json
  35. 1 0
      dist/preview release/materialsLibrary/babylon.customMaterial.d.ts
  36. 1 0
      dist/preview release/materialsLibrary/babylon.customMaterial.js
  37. 2 2
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  38. 1 1
      dist/preview release/materialsLibrary/babylon.furMaterial.js
  39. 1 1
      dist/preview release/materialsLibrary/babylon.furMaterial.min.js
  40. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  41. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  42. 2 1
      dist/preview release/materialsLibrary/babylonjs.materials.js
  43. 5 5
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  44. 1 0
      dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts
  45. 1 1
      dist/preview release/materialsLibrary/package.json
  46. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  47. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  48. 1 1
      dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js
  49. 1 1
      dist/preview release/postProcessesLibrary/package.json
  50. 1 1
      dist/preview release/proceduralTexturesLibrary/package.json
  51. 42 0
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  52. 259 31
      dist/preview release/serializers/babylon.glTF2Serializer.js
  53. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  54. 259 31
      dist/preview release/serializers/babylonjs.serializers.js
  55. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  56. 42 0
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  57. 2 2
      dist/preview release/serializers/package.json
  58. 2 110
      dist/preview release/typedocValidationBaseline.json
  59. 69 69
      dist/preview release/viewer/babylon.viewer.js
  60. 305 56
      dist/preview release/viewer/babylon.viewer.max.js
  61. 1 1
      dist/preview release/viewer/package.json
  62. 5 3
      dist/preview release/what's new.md
  63. 1 0
      loaders/src/glTF/1.0/babylon.glTFLoader.ts
  64. 4 4
      loaders/src/glTF/2.0/Extensions/KHR_lights.ts
  65. 16 19
      loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts
  66. 8 4
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  67. 119 112
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  68. 4 4
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  69. 28 2
      loaders/src/glTF/babylon.glTFFileLoader.ts
  70. 35 1972
      materialsLibrary/src/custom/babylon.customMaterial.ts
  71. 0 4
      materialsLibrary/src/fur/fur.vertex.fx
  72. 1 1
      package.json
  73. 4 4
      serializers/src/glTF/2.0/babylon.glTFExporter.ts
  74. 468 159
      serializers/src/glTF/2.0/babylon.glTFMaterial.ts
  75. 27 1
      src/Animations/babylon.animationGroup.ts
  76. 7 2
      src/Cameras/babylon.camera.ts
  77. 1 1
      src/Engine/babylon.engine.ts
  78. 13 2
      src/Helpers/babylon.environmentHelper.ts
  79. 5 4
      src/Helpers/babylon.videoDome.ts
  80. 5 3
      src/Lights/babylon.light.ts
  81. 1 2
      src/Materials/Background/babylon.backgroundMaterial.ts
  82. 1 0
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  83. 1 1
      src/Materials/Textures/babylon.texture.ts
  84. 4 0
      src/Materials/Textures/babylon.videoTexture.ts
  85. 65 1
      src/Materials/babylon.imageProcessingConfiguration.ts
  86. 1 0
      src/Materials/babylon.standardMaterial.ts
  87. 6 5
      src/Mesh/Compression/babylon.dracoCompression.ts
  88. 5 5
      src/Mesh/babylon.abstractMesh.ts
  89. 2 2
      src/Mesh/babylon.instancedMesh.ts
  90. 4 4
      src/Mesh/babylon.mesh.ts
  91. 13 20
      src/Mesh/babylon.transformNode.ts
  92. 99 40
      src/PostProcess/RenderPipeline/Pipelines/babylon.defaultRenderingPipeline.ts
  93. 21 8
      src/PostProcess/babylon.circleOfConfusionPostProcess.ts
  94. 64 52
      src/PostProcess/babylon.depthOfFieldEffect.ts
  95. 40 0
      src/PostProcess/babylon.imageProcessingPostProcess.ts
  96. 13 1
      src/PostProcess/babylon.postProcess.ts
  97. 1 1
      src/Rendering/babylon.depthRenderer.ts
  98. 4 4
      src/Shaders/ShadersInclude/helperFunctions.fx
  99. 5 0
      src/Shaders/ShadersInclude/imageProcessingDeclaration.fx
  100. 0 0
      src/Shaders/background.fragment.fx

Разница между файлами не показана из-за своего большого размера
+ 11042 - 10942
Playground/babylon.d.txt


+ 2 - 0
Playground/scenes/TwoQuads/TwoQuads.gltf

@@ -121,6 +121,7 @@
   ],
   "nodes": [
     {
+      "name": "node0",
       "mesh": 0,
       "translation": [
         -0.55,
@@ -129,6 +130,7 @@
       ]
     },
     {
+      "name": "node1",
       "mesh": 0,
       "translation": [
         0.55,

+ 1 - 1
Tools/Gulp/gulp-addModuleExports.js

@@ -25,7 +25,7 @@ module.exports = function (varName, subModule, extendsRoot, externalUsingBabylon
     if(typeof exports === 'object' && typeof module === 'object')
         module.exports = factory(${subModule || extendsRoot ? 'require("babylonjs")' : ''});
     else if(typeof define === 'function' && define.amd)
-        define("${varName.module}", ${subModule || extendsRoot ? '["babylonjs"],' : ''} factory);
+        define("${varName.module}", ${subModule || extendsRoot ? '["babylonjs"],' : '[],'} factory);
     else if(typeof exports === 'object')
         exports["${varName.module}"] = factory(${subModule || extendsRoot ? 'require("babylonjs")' : ''});
     else {

Разница между файлами не показана из-за своего большого размера
+ 10051 - 9952
dist/preview release/babylon.d.ts


Разница между файлами не показана из-за своего большого размера
+ 55 - 55
dist/preview release/babylon.js


Разница между файлами не показана из-за своего большого размера
+ 304 - 55
dist/preview release/babylon.max.js


Разница между файлами не показана из-за своего большого размера
+ 56 - 56
dist/preview release/babylon.worker.js


Разница между файлами не показана из-за своего большого размера
+ 14326 - 14227
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Разница между файлами не показана из-за своего большого размера
+ 57 - 57
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


Разница между файлами не показана из-за своего большого размера
+ 380 - 97
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


Разница между файлами не показана из-за своего большого размера
+ 381 - 98
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js


Разница между файлами не показана из-за своего большого размера
+ 305 - 56
dist/preview release/es6.js


+ 1 - 1
dist/preview release/gltf2Interface/package.json

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

Разница между файлами не показана из-за своего большого размера
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


+ 1 - 1
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

Разница между файлами не показана из-за своего большого размера
+ 4 - 4
dist/preview release/inspector/babylon.inspector.bundle.js


+ 21 - 2
dist/preview release/inspector/babylon.inspector.d.ts

@@ -420,13 +420,20 @@ declare module INSPECTOR {
         private _headerRow;
         private _detailRows;
         private _sortDirection;
+        private _searchDetails;
+        private _details;
         constructor(dr?: Array<PropertyLine>);
         details: Array<PropertyLine>;
         protected _build(): void;
         /** Updates the HTML of the detail panel */
-        update(): void;
+        update(_items?: Array<PropertyLine>): void;
+        /** Add the search bar for the details */
+        private _addSearchBarDetails();
+        /** Search an element by name  */
+        searchByName(searchName: string): void;
         /** Add all lines in the html div. Does not sort them! */
         private _addDetails();
+        private _addSearchDetails(_items);
         /**
          * Sort the details row by comparing the given property of each row
          */
@@ -435,6 +442,10 @@ declare module INSPECTOR {
          * Removes all data in the detail panel but keep the header row
          */
         clean(): void;
+        /**
+         * Clean the rows only
+         */
+        cleanRow(): void;
         /** Overrides basicelement.dispose */
         dispose(): void;
         /**
@@ -670,13 +681,21 @@ declare module INSPECTOR {
      * At each keypress on the input, the treepanel will be filtered.
      */
     class SearchBar extends BasicElement {
-        private _tab;
+        private _propTab;
         private _inputElement;
         constructor(tab: PropertyTab);
         /** Delete all characters typped in the input element */
         reset(): void;
         update(): void;
     }
+    class SearchBarDetails extends BasicElement {
+        private _detailTab;
+        private _inputElement;
+        constructor(tab: DetailPanel);
+        /** Delete all characters typped in the input element */
+        reset(): void;
+        update(): void;
+    }
 }
 
 declare module INSPECTOR {

+ 159 - 8
dist/preview release/inspector/babylon.inspector.js

@@ -1164,6 +1164,9 @@ var INSPECTOR;
         Object.defineProperty(DetailPanel.prototype, "details", {
             set: function (detailsRow) {
                 this.clean();
+                //add the searchBar
+                this._addSearchBarDetails();
+                this._details = INSPECTOR.Helpers.CreateDiv('details', this._div);
                 this._detailRows = detailsRow;
                 // Refresh HTML
                 this.update();
@@ -1179,16 +1182,49 @@ var INSPECTOR;
             this._div.appendChild(this._headerRow);
         };
         /** Updates the HTML of the detail panel */
-        DetailPanel.prototype.update = function () {
+        DetailPanel.prototype.update = function (_items) {
             this._sortDetails('name', 1);
-            this._addDetails();
+            // Check the searchbar
+            if (_items) {
+                this.cleanRow();
+                this._addSearchDetails(_items);
+                //console.log(_items);
+            }
+            else {
+                this._addDetails();
+                //console.log("np");
+            }
+        };
+        /** Add the search bar for the details */
+        DetailPanel.prototype._addSearchBarDetails = function () {
+            var searchDetails = INSPECTOR.Helpers.CreateDiv('searchbar-details', this._div);
+            // Create search bar
+            this._searchDetails = new INSPECTOR.SearchBarDetails(this);
+            searchDetails.appendChild(this._searchDetails.toHtml());
+            this._div.appendChild(searchDetails);
+        };
+        /** Search an element by name  */
+        DetailPanel.prototype.searchByName = function (searchName) {
+            var rows = [];
+            for (var _i = 0, _a = this._detailRows; _i < _a.length; _i++) {
+                var row = _a[_i];
+                if (row.name.indexOf(searchName) >= 0) {
+                    rows.push(row);
+                }
+            }
+            this.update(rows);
         };
         /** Add all lines in the html div. Does not sort them! */
         DetailPanel.prototype._addDetails = function () {
-            var details = INSPECTOR.Helpers.CreateDiv('details', this._div);
             for (var _i = 0, _a = this._detailRows; _i < _a.length; _i++) {
                 var row = _a[_i];
-                details.appendChild(row.toHtml());
+                this._details.appendChild(row.toHtml());
+            }
+        };
+        DetailPanel.prototype._addSearchDetails = function (_items) {
+            for (var _i = 0, _items_1 = _items; _i < _items_1.length; _i++) {
+                var row = _items_1[_i];
+                this._details.appendChild(row.toHtml());
             }
         };
         /**
@@ -1251,6 +1287,17 @@ var INSPECTOR;
             // Header row
             this._div.appendChild(this._headerRow);
         };
+        /**
+         * Clean the rows only
+         */
+        DetailPanel.prototype.cleanRow = function () {
+            // Delete all details row
+            for (var _i = 0, _a = this._detailRows; _i < _a.length; _i++) {
+                var pline = _a[_i];
+                pline.dispose();
+            }
+            INSPECTOR.Helpers.CleanDiv(this._details);
+        };
         /** Overrides basicelement.dispose */
         DetailPanel.prototype.dispose = function () {
             // Delete all details row
@@ -1630,6 +1677,57 @@ var INSPECTOR;
             }
             else {
                 this._valueDiv.childNodes[0].nodeValue = this._displayValueContent();
+                //Doing the Hexa convertion
+                if ((this._property.type == "Color3" && this._children.length == 5 && this._children[1].value == true) || (this._property.type == "Color4" && this._children.length == 6 && this._children[1].value == true)) {
+                    if (this._children[0] != undefined && this._children[0].name == "hex") {
+                        var hexLineString = this._children[0].value;
+                        var rValue = (parseInt((hexLineString.slice(1, 3)), 16)) * (1 / 255);
+                        var rValueRound = Math.round(100 * rValue) / 100;
+                        this.value.r = rValueRound;
+                        var gValue = (parseInt((hexLineString.slice(3, 5)), 16)) * (1 / 255);
+                        var gValueRound = Math.round(100 * gValue) / 100;
+                        this.value.g = gValueRound;
+                        var bValue = (parseInt((hexLineString.slice(5, 7)), 16)) * (1 / 255);
+                        var bValueRound = Math.round(100 * bValue) / 100;
+                        this.value.b = bValueRound;
+                        if (this._children[2].name == "a") {
+                            var aValue = (parseInt((hexLineString.slice(7, 9)), 16)) * (1 / 255);
+                            var aValueRound = Math.round(100 * aValue) / 100;
+                            this.value.a = aValueRound;
+                        }
+                    }
+                }
+                else if (this._property.type == "Color3" || this._property.type == "Color4") {
+                    if (this._property.value.hex != undefined && this._property.value.hex != null) {
+                        var hexLineInfos = [];
+                        var valHexR = ((this._property.value.r * 255) | 0).toString(16);
+                        hexLineInfos.push(valHexR);
+                        if (valHexR == "0") {
+                            hexLineInfos.push("0");
+                        }
+                        var valHexG = ((this._property.value.g * 255) | 0).toString(16);
+                        hexLineInfos.push(valHexG);
+                        if (valHexG == "0") {
+                            hexLineInfos.push("0");
+                        }
+                        var valHexB = ((this._property.value.b * 255) | 0).toString(16);
+                        hexLineInfos.push(valHexB);
+                        if (valHexB == "0") {
+                            hexLineInfos.push("0");
+                        }
+                        if (this._property.value.a != undefined) {
+                            var valHexA = ((this._property.value.a * 255) | 0).toString(16);
+                            hexLineInfos.push(valHexA);
+                            if (valHexA == "0") {
+                                hexLineInfos.push("0");
+                            }
+                        }
+                        hexLineInfos.unshift("#");
+                        var hexLineString = hexLineInfos.join("");
+                        this._property.value.hex = hexLineString;
+                        hexLineInfos.length = 0;
+                    }
+                }
             }
             for (var _i = 0, _a = this._elements; _i < _a.length; _i++) {
                 var elem = _a[_i];
@@ -1714,11 +1812,35 @@ var INSPECTOR;
                         var child = new PropertyLine(infos, this, this._level + PropertyLine._MARGIN_LEFT);
                         this._children.push(child);
                     }
+                    //Add the Hexa converter
+                    if ((propToDisplay.indexOf('r') && propToDisplay.indexOf('g') && propToDisplay.indexOf('b') && propToDisplay.indexOf('a')) == 0) {
+                        var hexLineInfos = [];
+                        var hexLinePropCheck = new INSPECTOR.Property("hexEnable", this._property.value);
+                        hexLinePropCheck.value = false;
+                        var hexLineCheck = new PropertyLine(hexLinePropCheck, this, this._level + PropertyLine._MARGIN_LEFT);
+                        this._children.unshift(hexLineCheck);
+                        for (var _c = 0, propToDisplay_2 = propToDisplay; _c < propToDisplay_2.length; _c++) {
+                            var prop = propToDisplay_2[_c];
+                            var infos = new INSPECTOR.Property(prop, this._property.value);
+                            var valHex = ((infos.value * 255) | 0).toString(16);
+                            hexLineInfos.push(valHex);
+                            if (valHex == "0") {
+                                hexLineInfos.push("0");
+                            }
+                        }
+                        hexLineInfos.push("#");
+                        hexLineInfos.reverse();
+                        var hexLineString = hexLineInfos.join("");
+                        var hexLineProp = new INSPECTOR.Property("hex", this._property.value);
+                        hexLineProp.value = hexLineString;
+                        var hexLine = new PropertyLine(hexLineProp, this, this._level + PropertyLine._MARGIN_LEFT);
+                        this._children.unshift(hexLine);
+                    }
                 }
                 // otherwise display it    
                 if (this._div.parentNode) {
-                    for (var _c = 0, _d = this._children; _c < _d.length; _c++) {
-                        var child = _d[_c];
+                    for (var _d = 0, _e = this._children; _d < _e.length; _d++) {
+                        var child = _e[_d];
                         this._div.parentNode.insertBefore(child.toHtml(), this._div.nextSibling);
                     }
                 }
@@ -2112,7 +2234,7 @@ var INSPECTOR;
         __extends(SearchBar, _super);
         function SearchBar(tab) {
             var _this = _super.call(this) || this;
-            _this._tab = tab;
+            _this._propTab = tab;
             _this._div.classList.add('searchbar');
             var filter = INSPECTOR.Inspector.DOCUMENT.createElement('i');
             filter.className = 'fa fa-search';
@@ -2123,7 +2245,7 @@ var INSPECTOR;
             _this._div.appendChild(_this._inputElement);
             _this._inputElement.addEventListener('keyup', function (evt) {
                 var filter = _this._inputElement.value;
-                _this._tab.filter(filter);
+                _this._propTab.filter(filter);
             });
             return _this;
         }
@@ -2137,6 +2259,35 @@ var INSPECTOR;
         return SearchBar;
     }(INSPECTOR.BasicElement));
     INSPECTOR.SearchBar = SearchBar;
+    var SearchBarDetails = /** @class */ (function (_super) {
+        __extends(SearchBarDetails, _super);
+        function SearchBarDetails(tab) {
+            var _this = _super.call(this) || this;
+            _this._detailTab = tab;
+            _this._div.classList.add('searchbar');
+            var filter = INSPECTOR.Inspector.DOCUMENT.createElement('i');
+            filter.className = 'fa fa-search';
+            _this._div.appendChild(filter);
+            // Create input
+            _this._inputElement = INSPECTOR.Inspector.DOCUMENT.createElement('input');
+            _this._inputElement.placeholder = 'Filter by name...';
+            _this._div.appendChild(_this._inputElement);
+            _this._inputElement.addEventListener('keyup', function (evt) {
+                var filter = _this._inputElement.value;
+                _this._detailTab.searchByName(filter);
+            });
+            return _this;
+        }
+        /** Delete all characters typped in the input element */
+        SearchBarDetails.prototype.reset = function () {
+            this._inputElement.value = '';
+        };
+        SearchBarDetails.prototype.update = function () {
+            // Nothing to update
+        };
+        return SearchBarDetails;
+    }(INSPECTOR.BasicElement));
+    INSPECTOR.SearchBarDetails = SearchBarDetails;
 })(INSPECTOR || (INSPECTOR = {}));
 
 var __extends = (this && this.__extends) || (function () {

Разница между файлами не показана из-за своего большого размера
+ 4 - 4
dist/preview release/inspector/babylon.inspector.min.js


+ 1 - 1
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 15 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -52,6 +52,7 @@ declare module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -107,13 +108,19 @@ declare module BABYLON {
          */
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         private _onTextureLoadedObserver;
-        onTextureLoaded: (Texture: BaseTexture) => void;
+        onTextureLoaded: (texture: BaseTexture) => void;
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
         readonly onMaterialLoadedObservable: Observable<Material>;
         private _onMaterialLoadedObserver;
-        onMaterialLoaded: (Material: Material) => void;
+        onMaterialLoaded: (material: Material) => void;
+        /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         */
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
+        private _onAnimationGroupLoadedObserver;
+        onAnimationGroupLoaded: (animationGroup: AnimationGroup) => void;
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
@@ -136,6 +143,11 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        whenCompleteAsync(): Promise<void>;
+        /**
          * The loader state or null if not active.
          */
         readonly loaderState: Nullable<GLTFLoaderState>;
@@ -569,6 +581,7 @@ declare module BABYLON.GLTF1 {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;

+ 27 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -77,6 +77,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -139,6 +143,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -169,6 +183,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -1837,6 +1863,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;

Разница между файлами не показана из-за своего большого размера
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 21 - 8
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -52,6 +52,7 @@ declare module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -107,13 +108,19 @@ declare module BABYLON {
          */
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         private _onTextureLoadedObserver;
-        onTextureLoaded: (Texture: BaseTexture) => void;
+        onTextureLoaded: (texture: BaseTexture) => void;
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
         readonly onMaterialLoadedObservable: Observable<Material>;
         private _onMaterialLoadedObserver;
-        onMaterialLoaded: (Material: Material) => void;
+        onMaterialLoaded: (material: Material) => void;
+        /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         */
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
+        private _onAnimationGroupLoadedObserver;
+        onAnimationGroupLoaded: (animationGroup: AnimationGroup) => void;
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
@@ -136,6 +143,11 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        whenCompleteAsync(): Promise<void>;
+        /**
          * The loader state or null if not active.
          */
         readonly loaderState: Nullable<GLTFLoaderState>;
@@ -290,6 +302,7 @@ declare module BABYLON.GLTF2 {
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         readonly onMaterialLoadedObservable: Observable<Material>;
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         readonly onCompleteObservable: Observable<IGLTFLoader>;
         readonly state: Nullable<GLTFLoaderState>;
@@ -337,7 +350,7 @@ declare module BABYLON.GLTF2 {
         private _buildArrayBuffer<T>(typedArray, data, byteOffset, count, numComponents, byteStride?);
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessPropertiesAsync(context, material);
-        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Promise<void>;
+        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void>;
         _createMaterial(material: ILoaderMaterial): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial): void;
@@ -373,7 +386,7 @@ declare module BABYLON.GLTF2 {
         /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** Helper method called by a loader extension to load an glTF extension. */
@@ -385,7 +398,7 @@ declare module BABYLON.GLTF2 {
         /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
         static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading uris. */
         static _LoadUriAsync(loader: GLTFLoader, context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
     }
@@ -404,7 +417,7 @@ declare module BABYLON.GLTF2.Extensions {
         private _loadingMaterialLOD;
         private _loadMaterialSignals;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /**
          * Gets an array of LOD properties from lowest to highest.
@@ -428,8 +441,8 @@ declare module BABYLON.GLTF2.Extensions {
 declare module BABYLON.GLTF2.Extensions {
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
-        private _loadSpecularGlossinessPropertiesAsync(loader, context, material, properties);
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
+        private _loadSpecularGlossinessPropertiesAsync(context, material, properties);
     }
 }
 

+ 74 - 41
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -77,6 +77,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -139,6 +143,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -169,6 +183,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -499,6 +525,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
             }
@@ -586,9 +613,12 @@ var BABYLON;
                     if (_this.compileShadowGenerators) {
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
-                    return Promise.all(promises).then(function () {
+                    var resultPromise = Promise.all(promises).then(function () {
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
+                    });
+                    resultPromise.then(function () {
+                        _this._rootBabylonMesh.setEnabled(true);
                         BABYLON.Tools.SetImmediate(function () {
                             if (!_this._disposed) {
                                 Promise.all(_this._completePromises).then(function () {
@@ -603,6 +633,7 @@ var BABYLON;
                             }
                         });
                     });
+                    return resultPromise;
                 }).catch(function (error) {
                     BABYLON.Tools.Error("glTF Loader: " + error.message);
                     _this._clear();
@@ -681,6 +712,7 @@ var BABYLON;
             };
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this._babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = { _babylonMesh: this._rootBabylonMesh };
                 switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -844,7 +876,6 @@ var BABYLON;
                 var _this = this;
                 var promises = new Array();
                 var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                babylonMesh.setEnabled(false);
                 node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
                 node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
@@ -857,12 +888,12 @@ var BABYLON;
                 }
                 else {
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
+                        babylonMesh.material = babylonMaterial;
+                    }));
                 }
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
-                return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                });
+                return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
@@ -1157,13 +1188,14 @@ var BABYLON;
                     var channel = _a[_i];
                     promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
                 }
+                this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
                 return Promise.all(promises).then(function () {
                     babylonAnimationGroup.normalize();
                 });
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-                if (!targetNode._babylonMesh) {
+                if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                 }
                 var sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
@@ -1481,25 +1513,24 @@ var BABYLON;
                 this._loadMaterialAlphaProperties(context, material);
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
+                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
                 if (promise) {
                     return promise;
                 }
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial;
-                    return material._loaded;
+                if (!material._loaded) {
+                    var promises = new Array();
+                    var babylonMaterial = this._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
+                    promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                    this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(function () { });
                 }
-                var promises = new Array();
-                var babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
-                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                babylonMesh.material = babylonMaterial;
-                return (material._loaded = Promise.all(promises).then(function () { }));
+                assign(material._babylonMaterial);
+                return material._loaded;
             };
             GLTFLoader.prototype._createMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "material" + material._index, this._babylonScene);
@@ -1868,7 +1899,7 @@ var BABYLON;
             /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
             GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
-            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
+            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
             GLTFLoaderExtension.prototype._loadUriAsync = function (context, uri) { return null; };
             // #endregion
@@ -1905,8 +1936,8 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
             };
             /** Helper method called by the loader to allow extensions to override loading materials. */
-            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
-                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
+            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh, assign); });
             };
             /** Helper method called by the loader to allow extensions to override loading uris. */
             GLTFLoaderExtension._LoadUriAsync = function (loader, context, uri) {
@@ -1994,7 +2025,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     // Don't load material LODs if already loading a node LOD.
                     if (this._loadingNodeLOD) {
@@ -2011,7 +2042,10 @@ var BABYLON;
                                     _this._loadMaterialSignals[materialLOD._index] = new BABYLON.Deferred();
                                 }
                             }
-                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(function () {
+                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
+                                if (indexLOD !== 0) {
+                                    assign(materialLOD._babylonMaterial);
+                                }
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     if (_this._loadMaterialSignals[materialIndex]) {
@@ -2198,26 +2232,25 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
                         material._babylonMeshes.push(babylonMesh);
-                        if (material._loaded) {
-                            babylonMesh.material = material._babylonMaterial;
-                            return material._loaded;
+                        if (!material._loaded) {
+                            var promises = new Array();
+                            var babylonMaterial = _this._loader._createMaterial(material);
+                            material._babylonMaterial = babylonMaterial;
+                            promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
+                            promises.push(_this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                            _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            material._loaded = Promise.all(promises).then(function () { });
                         }
-                        var promises = new Array();
-                        var babylonMaterial = _this._loader._createMaterial(material);
-                        material._babylonMaterial = babylonMaterial;
-                        promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
-                        promises.push(_this._loadSpecularGlossinessPropertiesAsync(_this._loader, context, material, extension));
-                        _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        babylonMesh.material = babylonMaterial;
-                        return (material._loaded = Promise.all(promises).then(function () { }));
+                        assign(material._babylonMaterial);
+                        return material._loaded;
                     });
                 };
-                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -2230,18 +2263,18 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader._loadMaterialAlphaProperties(context, material);
+                    this._loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
                 return KHR_materials_pbrSpecularGlossiness;

Разница между файлами не показана из-за своего большого размера
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 22 - 8
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -52,6 +52,7 @@ declare module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -107,13 +108,19 @@ declare module BABYLON {
          */
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         private _onTextureLoadedObserver;
-        onTextureLoaded: (Texture: BaseTexture) => void;
+        onTextureLoaded: (texture: BaseTexture) => void;
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
         readonly onMaterialLoadedObservable: Observable<Material>;
         private _onMaterialLoadedObserver;
-        onMaterialLoaded: (Material: Material) => void;
+        onMaterialLoaded: (material: Material) => void;
+        /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         */
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
+        private _onAnimationGroupLoadedObserver;
+        onAnimationGroupLoaded: (animationGroup: AnimationGroup) => void;
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
@@ -136,6 +143,11 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        whenCompleteAsync(): Promise<void>;
+        /**
          * The loader state or null if not active.
          */
         readonly loaderState: Nullable<GLTFLoaderState>;
@@ -569,6 +581,7 @@ declare module BABYLON.GLTF1 {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
@@ -845,6 +858,7 @@ declare module BABYLON.GLTF2 {
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         readonly onMaterialLoadedObservable: Observable<Material>;
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         readonly onCompleteObservable: Observable<IGLTFLoader>;
         readonly state: Nullable<GLTFLoaderState>;
@@ -892,7 +906,7 @@ declare module BABYLON.GLTF2 {
         private _buildArrayBuffer<T>(typedArray, data, byteOffset, count, numComponents, byteStride?);
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessPropertiesAsync(context, material);
-        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Promise<void>;
+        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void>;
         _createMaterial(material: ILoaderMaterial): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial): void;
@@ -928,7 +942,7 @@ declare module BABYLON.GLTF2 {
         /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** Helper method called by a loader extension to load an glTF extension. */
@@ -940,7 +954,7 @@ declare module BABYLON.GLTF2 {
         /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
         static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading uris. */
         static _LoadUriAsync(loader: GLTFLoader, context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
     }
@@ -959,7 +973,7 @@ declare module BABYLON.GLTF2.Extensions {
         private _loadingMaterialLOD;
         private _loadMaterialSignals;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /**
          * Gets an array of LOD properties from lowest to highest.
@@ -983,8 +997,8 @@ declare module BABYLON.GLTF2.Extensions {
 declare module BABYLON.GLTF2.Extensions {
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
-        private _loadSpecularGlossinessPropertiesAsync(loader, context, material, properties);
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
+        private _loadSpecularGlossinessPropertiesAsync(context, material, properties);
     }
 }
 

+ 75 - 41
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -77,6 +77,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -139,6 +143,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -169,6 +183,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -1837,6 +1863,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;
@@ -2675,6 +2702,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
             }
@@ -2762,9 +2790,12 @@ var BABYLON;
                     if (_this.compileShadowGenerators) {
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
-                    return Promise.all(promises).then(function () {
+                    var resultPromise = Promise.all(promises).then(function () {
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
+                    });
+                    resultPromise.then(function () {
+                        _this._rootBabylonMesh.setEnabled(true);
                         BABYLON.Tools.SetImmediate(function () {
                             if (!_this._disposed) {
                                 Promise.all(_this._completePromises).then(function () {
@@ -2779,6 +2810,7 @@ var BABYLON;
                             }
                         });
                     });
+                    return resultPromise;
                 }).catch(function (error) {
                     BABYLON.Tools.Error("glTF Loader: " + error.message);
                     _this._clear();
@@ -2857,6 +2889,7 @@ var BABYLON;
             };
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this._babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = { _babylonMesh: this._rootBabylonMesh };
                 switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -3020,7 +3053,6 @@ var BABYLON;
                 var _this = this;
                 var promises = new Array();
                 var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                babylonMesh.setEnabled(false);
                 node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
                 node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
@@ -3033,12 +3065,12 @@ var BABYLON;
                 }
                 else {
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
+                        babylonMesh.material = babylonMaterial;
+                    }));
                 }
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
-                return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                });
+                return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
@@ -3333,13 +3365,14 @@ var BABYLON;
                     var channel = _a[_i];
                     promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
                 }
+                this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
                 return Promise.all(promises).then(function () {
                     babylonAnimationGroup.normalize();
                 });
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-                if (!targetNode._babylonMesh) {
+                if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                 }
                 var sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
@@ -3657,25 +3690,24 @@ var BABYLON;
                 this._loadMaterialAlphaProperties(context, material);
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
+                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
                 if (promise) {
                     return promise;
                 }
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial;
-                    return material._loaded;
+                if (!material._loaded) {
+                    var promises = new Array();
+                    var babylonMaterial = this._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
+                    promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                    this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(function () { });
                 }
-                var promises = new Array();
-                var babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
-                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                babylonMesh.material = babylonMaterial;
-                return (material._loaded = Promise.all(promises).then(function () { }));
+                assign(material._babylonMaterial);
+                return material._loaded;
             };
             GLTFLoader.prototype._createMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "material" + material._index, this._babylonScene);
@@ -4044,7 +4076,7 @@ var BABYLON;
             /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
             GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
-            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
+            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
             GLTFLoaderExtension.prototype._loadUriAsync = function (context, uri) { return null; };
             // #endregion
@@ -4081,8 +4113,8 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
             };
             /** Helper method called by the loader to allow extensions to override loading materials. */
-            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
-                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
+            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh, assign); });
             };
             /** Helper method called by the loader to allow extensions to override loading uris. */
             GLTFLoaderExtension._LoadUriAsync = function (loader, context, uri) {
@@ -4170,7 +4202,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     // Don't load material LODs if already loading a node LOD.
                     if (this._loadingNodeLOD) {
@@ -4187,7 +4219,10 @@ var BABYLON;
                                     _this._loadMaterialSignals[materialLOD._index] = new BABYLON.Deferred();
                                 }
                             }
-                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(function () {
+                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
+                                if (indexLOD !== 0) {
+                                    assign(materialLOD._babylonMaterial);
+                                }
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     if (_this._loadMaterialSignals[materialIndex]) {
@@ -4374,26 +4409,25 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
                         material._babylonMeshes.push(babylonMesh);
-                        if (material._loaded) {
-                            babylonMesh.material = material._babylonMaterial;
-                            return material._loaded;
+                        if (!material._loaded) {
+                            var promises = new Array();
+                            var babylonMaterial = _this._loader._createMaterial(material);
+                            material._babylonMaterial = babylonMaterial;
+                            promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
+                            promises.push(_this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                            _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            material._loaded = Promise.all(promises).then(function () { });
                         }
-                        var promises = new Array();
-                        var babylonMaterial = _this._loader._createMaterial(material);
-                        material._babylonMaterial = babylonMaterial;
-                        promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
-                        promises.push(_this._loadSpecularGlossinessPropertiesAsync(_this._loader, context, material, extension));
-                        _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        babylonMesh.material = babylonMaterial;
-                        return (material._loaded = Promise.all(promises).then(function () { }));
+                        assign(material._babylonMaterial);
+                        return material._loaded;
                     });
                 };
-                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -4406,18 +4440,18 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader._loadMaterialAlphaProperties(context, material);
+                    this._loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
                 return KHR_materials_pbrSpecularGlossiness;

Разница между файлами не показана из-за своего большого размера
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


+ 75 - 41
dist/preview release/loaders/babylonjs.loaders.js

@@ -1073,6 +1073,10 @@ var BABYLON;
              */
             this.onMaterialLoadedObservable = new BABYLON.Observable();
             /**
+             * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+             */
+            this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
+            /**
              * Raised when the asset is completely loaded, immediately before the loader is disposed.
              * For assets with LODs, raised when all of the LODs are complete.
              * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -1135,6 +1139,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(GLTFFileLoader.prototype, "onAnimationGroupLoaded", {
+            set: function (callback) {
+                if (this._onAnimationGroupLoadedObserver) {
+                    this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+                }
+                this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
             set: function (callback) {
                 if (this._onCompleteObserver) {
@@ -1165,6 +1179,18 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        GLTFFileLoader.prototype.whenCompleteAsync = function () {
+            var _this = this;
+            return new Promise(function (resolve) {
+                _this.onCompleteObservable.add(function () {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        };
         Object.defineProperty(GLTFFileLoader.prototype, "loaderState", {
             /**
              * The loader state or null if not active.
@@ -2833,6 +2859,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.state = null;
@@ -3653,6 +3680,7 @@ var BABYLON;
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
                 this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onAnimationGroupLoadedObservable = new BABYLON.Observable();
                 this.onExtensionLoadedObservable = new BABYLON.Observable();
                 this.onCompleteObservable = new BABYLON.Observable();
             }
@@ -3740,9 +3768,12 @@ var BABYLON;
                     if (_this.compileShadowGenerators) {
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
-                    return Promise.all(promises).then(function () {
+                    var resultPromise = Promise.all(promises).then(function () {
                         _this._state = BABYLON.GLTFLoaderState.Ready;
                         _this._startAnimations();
+                    });
+                    resultPromise.then(function () {
+                        _this._rootBabylonMesh.setEnabled(true);
                         BABYLON.Tools.SetImmediate(function () {
                             if (!_this._disposed) {
                                 Promise.all(_this._completePromises).then(function () {
@@ -3757,6 +3788,7 @@ var BABYLON;
                             }
                         });
                     });
+                    return resultPromise;
                 }).catch(function (error) {
                     BABYLON.Tools.Error("glTF Loader: " + error.message);
                     _this._clear();
@@ -3835,6 +3867,7 @@ var BABYLON;
             };
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this._babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = { _babylonMesh: this._rootBabylonMesh };
                 switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -3998,7 +4031,6 @@ var BABYLON;
                 var _this = this;
                 var promises = new Array();
                 var babylonMesh = new BABYLON.Mesh((mesh.name || node._babylonMesh.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-                babylonMesh.setEnabled(false);
                 node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
                 node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
                 this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
@@ -4011,12 +4043,12 @@ var BABYLON;
                 }
                 else {
                     var material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                    promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh, function (babylonMaterial) {
+                        babylonMesh.material = babylonMaterial;
+                    }));
                 }
                 this.onMeshLoadedObservable.notifyObservers(babylonMesh);
-                return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                });
+                return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
@@ -4311,13 +4343,14 @@ var BABYLON;
                     var channel = _a[_i];
                     promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
                 }
+                this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
                 return Promise.all(promises).then(function () {
                     babylonAnimationGroup.normalize();
                 });
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {
                 var targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-                if (!targetNode._babylonMesh) {
+                if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                     return Promise.resolve();
                 }
                 var sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
@@ -4635,25 +4668,24 @@ var BABYLON;
                 this._loadMaterialAlphaProperties(context, material);
                 return Promise.all(promises).then(function () { });
             };
-            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+            GLTFLoader.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
+                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
                 if (promise) {
                     return promise;
                 }
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial;
-                    return material._loaded;
+                if (!material._loaded) {
+                    var promises = new Array();
+                    var babylonMaterial = this._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
+                    promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                    this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(function () { });
                 }
-                var promises = new Array();
-                var babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
-                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                babylonMesh.material = babylonMaterial;
-                return (material._loaded = Promise.all(promises).then(function () { }));
+                assign(material._babylonMaterial);
+                return material._loaded;
             };
             GLTFLoader.prototype._createMaterial = function (material) {
                 var babylonMaterial = new BABYLON.PBRMaterial(material.name || "material" + material._index, this._babylonScene);
@@ -5022,7 +5054,7 @@ var BABYLON;
             /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
             GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
-            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
+            GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
             GLTFLoaderExtension.prototype._loadUriAsync = function (context, uri) { return null; };
             // #endregion
@@ -5059,8 +5091,8 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
             };
             /** Helper method called by the loader to allow extensions to override loading materials. */
-            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
-                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
+            GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh, assign); });
             };
             /** Helper method called by the loader to allow extensions to override loading uris. */
             GLTFLoaderExtension._LoadUriAsync = function (loader, context, uri) {
@@ -5139,7 +5171,7 @@ var BABYLON;
                         return firstPromise;
                     });
                 };
-                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                MSFT_lod.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     // Don't load material LODs if already loading a node LOD.
                     if (this._loadingNodeLOD) {
@@ -5156,7 +5188,10 @@ var BABYLON;
                                     _this._loadMaterialSignals[materialLOD._index] = new BABYLON.Deferred();
                                 }
                             }
-                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(function () {
+                            var promise = _this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh, indexLOD === 0 ? assign : function () { }).then(function () {
+                                if (indexLOD !== 0) {
+                                    assign(materialLOD._babylonMaterial);
+                                }
                                 if (indexLOD !== materialLODs.length - 1) {
                                     var materialIndex = materialLODs[indexLOD + 1]._index;
                                     if (_this._loadMaterialSignals[materialIndex]) {
@@ -5325,26 +5360,25 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, babylonMesh, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (context, extension) {
                         material._babylonMeshes = material._babylonMeshes || [];
                         material._babylonMeshes.push(babylonMesh);
-                        if (material._loaded) {
-                            babylonMesh.material = material._babylonMaterial;
-                            return material._loaded;
+                        if (!material._loaded) {
+                            var promises = new Array();
+                            var babylonMaterial = _this._loader._createMaterial(material);
+                            material._babylonMaterial = babylonMaterial;
+                            promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
+                            promises.push(_this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                            _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            material._loaded = Promise.all(promises).then(function () { });
                         }
-                        var promises = new Array();
-                        var babylonMaterial = _this._loader._createMaterial(material);
-                        material._babylonMaterial = babylonMaterial;
-                        promises.push(_this._loader._loadMaterialBasePropertiesAsync(context, material));
-                        promises.push(_this._loadSpecularGlossinessPropertiesAsync(_this._loader, context, material, extension));
-                        _this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        babylonMesh.material = babylonMaterial;
-                        return (material._loaded = Promise.all(promises).then(function () { }));
+                        assign(material._babylonMaterial);
+                        return material._loaded;
                     });
                 };
-                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (loader, context, material, properties) {
+                KHR_materials_pbrSpecularGlossiness.prototype._loadSpecularGlossinessPropertiesAsync = function (context, material, properties) {
                     var promises = new Array();
                     var babylonMaterial = material._babylonMaterial;
                     if (properties.diffuseFactor) {
@@ -5357,18 +5391,18 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
                         babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                     }
-                    loader._loadMaterialAlphaProperties(context, material);
+                    this._loader._loadMaterialAlphaProperties(context, material);
                     return Promise.all(promises).then(function () { });
                 };
                 return KHR_materials_pbrSpecularGlossiness;

Разница между файлами не показана из-за своего большого размера
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 22 - 8
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -154,6 +154,7 @@ declare module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -209,13 +210,19 @@ declare module BABYLON {
          */
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         private _onTextureLoadedObserver;
-        onTextureLoaded: (Texture: BaseTexture) => void;
+        onTextureLoaded: (texture: BaseTexture) => void;
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
         readonly onMaterialLoadedObservable: Observable<Material>;
         private _onMaterialLoadedObserver;
-        onMaterialLoaded: (Material: Material) => void;
+        onMaterialLoaded: (material: Material) => void;
+        /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the material.
+         */
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
+        private _onAnimationGroupLoadedObserver;
+        onAnimationGroupLoaded: (animationGroup: AnimationGroup) => void;
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
@@ -238,6 +245,11 @@ declare module BABYLON {
         private _onExtensionLoadedObserver;
         onExtensionLoaded: (extension: IGLTFLoaderExtension) => void;
         /**
+         * Gets a promise that resolves when the asset to be completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        whenCompleteAsync(): Promise<void>;
+        /**
          * The loader state or null if not active.
          */
         readonly loaderState: Nullable<GLTFLoaderState>;
@@ -671,6 +683,7 @@ declare module BABYLON.GLTF1 {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         state: Nullable<GLTFLoaderState>;
@@ -947,6 +960,7 @@ declare module BABYLON.GLTF2 {
         readonly onMeshLoadedObservable: Observable<AbstractMesh>;
         readonly onTextureLoadedObservable: Observable<BaseTexture>;
         readonly onMaterialLoadedObservable: Observable<Material>;
+        readonly onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         readonly onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
         readonly onCompleteObservable: Observable<IGLTFLoader>;
         readonly state: Nullable<GLTFLoaderState>;
@@ -994,7 +1008,7 @@ declare module BABYLON.GLTF2 {
         private _buildArrayBuffer<T>(typedArray, data, byteOffset, count, numComponents, byteStride?);
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessPropertiesAsync(context, material);
-        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Promise<void>;
+        _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void>;
         _createMaterial(material: ILoaderMaterial): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial): void;
@@ -1030,7 +1044,7 @@ declare module BABYLON.GLTF2 {
         /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** Helper method called by a loader extension to load an glTF extension. */
@@ -1042,7 +1056,7 @@ declare module BABYLON.GLTF2 {
         /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
         static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading uris. */
         static _LoadUriAsync(loader: GLTFLoader, context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
     }
@@ -1061,7 +1075,7 @@ declare module BABYLON.GLTF2.Extensions {
         private _loadingMaterialLOD;
         private _loadMaterialSignals;
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /**
          * Gets an array of LOD properties from lowest to highest.
@@ -1085,8 +1099,8 @@ declare module BABYLON.GLTF2.Extensions {
 declare module BABYLON.GLTF2.Extensions {
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
-        private _loadSpecularGlossinessPropertiesAsync(loader, context, material, properties);
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
+        private _loadSpecularGlossinessPropertiesAsync(context, material, properties);
     }
 }
 

+ 2 - 2
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "3.2.0-alphaA"
+        "babylonjs-gltf2interface": "3.2.0-alphaB"
     },
     "peerDependencies": {
         "babylonjs": ">=3.2.0-alpha"

+ 1 - 0
dist/preview release/materialsLibrary/babylon.customMaterial.d.ts

@@ -73,6 +73,7 @@ declare module BABYLON {
         SAMPLER3DBGRMAP: boolean;
         IMAGEPROCESSINGPOSTPROCESS: boolean;
         EXPOSURE: boolean;
+        GRAIN: boolean;
         constructor();
         setReflectionMode(modeToEnable: string): void;
     }

+ 1 - 0
dist/preview release/materialsLibrary/babylon.customMaterial.js

@@ -94,6 +94,7 @@ var BABYLON;
             _this.SAMPLER3DBGRMAP = false;
             _this.IMAGEPROCESSINGPOSTPROCESS = false;
             _this.EXPOSURE = false;
+            _this.GRAIN = false;
             _this.rebuild();
             return _this;
         }

Разница между файлами не показана из-за своего большого размера
+ 2 - 2
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/materialsLibrary/babylon.furMaterial.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/materialsLibrary/babylon.furMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 2 - 1
dist/preview release/materialsLibrary/babylonjs.materials.js


Разница между файлами не показана из-за своего большого размера
+ 5 - 5
dist/preview release/materialsLibrary/babylonjs.materials.min.js


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

@@ -579,6 +579,7 @@ declare module BABYLON {
         SAMPLER3DBGRMAP: boolean;
         IMAGEPROCESSINGPOSTPROCESS: boolean;
         EXPOSURE: boolean;
+        GRAIN: boolean;
         constructor();
         setReflectionMode(modeToEnable: string): void;
     }

+ 1 - 1
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js


+ 1 - 1
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -295,6 +295,7 @@ declare module BABYLON.GLTF2 {
          * Allows the maximum specular power to be defined for material calculations.
          */
         private static maxSpecularPower;
+        private static epsilon;
         /**
          * Gets the materials from a Babylon scene and converts them to glTF materials.
          * @param scene
@@ -312,6 +313,18 @@ declare module BABYLON.GLTF2 {
             };
         }, hasTextureCoords: boolean): void;
         /**
+         * Makes a copy of the glTF material without the texture parameters.
+         * @param originalMaterial - original glTF material.
+         * @returns glTF material without texture parameters
+         */
+        static StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        /**
+         * Specifies if the material has any texture parameters present.
+         * @param material - glTF Material.
+         * @returns boolean specifying if texture parameters are present
+         */
+        static HasTexturesPresent(material: IMaterial): boolean;
+        /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
          * @param babylonStandardMaterial
          * @returns - glTF Metallic Roughness Material representation
@@ -364,6 +377,35 @@ declare module BABYLON.GLTF2 {
             };
         }, hasTextureCoords: boolean): void;
         /**
+         * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
+         * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+         * @param color - Color source to calculate brightness from.
+         * @returns number representing the perceived brightness, or zero if color is undefined.
+         */
+        static GetPerceivedBrightness(color: Color3): number;
+        /**
+         * Returns the maximum color component value.
+         * @param color
+         * @returns maximum color component value, or zero if color is null or undefined.
+         */
+        static GetMaxComponent(color: Color3): number;
+        /**
+         * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
+         * @param babylonPBRMaterial - BJS PBR Metallic Roughness Material.
+         * @param mimeType - mime type to use for the textures.
+         * @param images - array of glTF image interfaces.
+         * @param textures - array of glTF texture interfaces.
+         * @param materials - array of glTF material interfaces.
+         * @param imageData - map of image file name to data.
+         * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
+         */
+        static ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        }, hasTextureCoords: boolean): void;
+        /**
          * Extracts a texture from a Babylon texture into file data and glTF data.
          * @param babylonTexture - Babylon texture to extract.
          * @param mimeType - Mime Type of the babylonTexture.

+ 259 - 31
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -80,7 +80,7 @@ var BABYLON;
                 this.materials = new Array();
                 this.textures = new Array();
                 this.imageData = {};
-                this.convertToRightHandedSystem = !this.babylonScene.useRightHandedSystem;
+                this.convertToRightHandedSystem = this.babylonScene.useRightHandedSystem ? false : true;
                 if (options) {
                     this.options = options;
                 }
@@ -411,10 +411,10 @@ var BABYLON;
              * @param babylonMesh - Babylon mesh used as the source for the transformation data.
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonMesh) {
-                if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
+                if (!babylonMesh.position.equalsToFloats(0, 0, 0)) {
                     node.translation = this.convertToRightHandedSystem ? _Exporter.GetRightHandedVector3(babylonMesh.position).asArray() : babylonMesh.position.asArray();
                 }
-                if (!(babylonMesh.scaling.x === 1 && babylonMesh.scaling.y === 1 && babylonMesh.scaling.z === 1)) {
+                if (!babylonMesh.scaling.equalsToFloats(1, 1, 1)) {
                     node.scale = babylonMesh.scaling.asArray();
                 }
                 var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(babylonMesh.rotation.y, babylonMesh.rotation.x, babylonMesh.rotation.z);
@@ -527,6 +527,7 @@ var BABYLON;
                         }
                     }
                     if (babylonMesh.subMeshes) {
+                        var uvCoordsPresent = false;
                         // go through all mesh primitives (submeshes)
                         for (var _a = 0, _b = babylonMesh.subMeshes; _a < _b.length; _a++) {
                             var submesh = _b[_a];
@@ -573,10 +574,12 @@ var BABYLON;
                                                         }
                                                         case BABYLON.VertexBuffer.UVKind: {
                                                             meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
+                                                            uvCoordsPresent = true;
                                                             break;
                                                         }
                                                         case BABYLON.VertexBuffer.UV2Kind: {
                                                             meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
+                                                            uvCoordsPresent = true;
                                                             break;
                                                         }
                                                         default: {
@@ -596,21 +599,31 @@ var BABYLON;
                                 }
                             }
                             if (bufferMesh.material) {
-                                if (bufferMesh.material instanceof BABYLON.StandardMaterial || bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
-                                    var materialIndex = babylonMesh.getScene().materials.indexOf(bufferMesh.material);
-                                    meshPrimitive.material = materialIndex;
+                                var materialIndex = null;
+                                if (bufferMesh.material instanceof BABYLON.StandardMaterial || bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial || bufferMesh.material instanceof BABYLON.PBRMaterial) {
+                                    materialIndex = babylonMesh.getScene().materials.indexOf(bufferMesh.material);
                                 }
                                 else if (bufferMesh.material instanceof BABYLON.MultiMaterial) {
                                     var babylonMultiMaterial = bufferMesh.material;
                                     var material = babylonMultiMaterial.subMaterials[submesh.materialIndex];
                                     if (material) {
-                                        var materialIndex = babylonMesh.getScene().materials.indexOf(material);
-                                        meshPrimitive.material = materialIndex;
+                                        materialIndex = babylonMesh.getScene().materials.indexOf(material);
                                     }
                                 }
                                 else {
                                     BABYLON.Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                                 }
+                                if (materialIndex != null) {
+                                    if (uvCoordsPresent || !GLTF2._GLTFMaterial.HasTexturesPresent(this.materials[materialIndex])) {
+                                        meshPrimitive.material = materialIndex;
+                                    }
+                                    else {
+                                        // If no texture coordinate information is present, make a copy of the material without the textures to be glTF compliant.
+                                        var newMat = GLTF2._GLTFMaterial.StripTexturesFromMaterial(this.materials[materialIndex]);
+                                        this.materials.push(newMat);
+                                        meshPrimitive.material = this.materials.length - 1;
+                                    }
+                                }
                             }
                             mesh.primitives.push(meshPrimitive);
                         }
@@ -636,30 +649,30 @@ var BABYLON;
                     this.binaryBuffer = new ArrayBuffer(this.totalByteLength);
                     var dataBuffer = new DataView(this.binaryBuffer);
                     for (var i = 0; i < babylonMeshes.length; ++i) {
-                        if (this.options &&
-                            this.options.shouldExportMesh != undefined &&
-                            !this.options.shouldExportMesh(babylonMeshes[i])) {
-                            continue;
-                        }
-                        else {
-                            var babylonMesh = babylonMeshes[i];
-                            // Build Hierarchy with the node map.
-                            var glTFNodeIndex = this.nodeMap[babylonMesh.uniqueId];
-                            var glTFNode = this.nodes[glTFNodeIndex];
-                            if (!babylonMesh.parent) {
+                        var babylonMesh = babylonMeshes[i];
+                        // Build Hierarchy with the node map.
+                        var glTFNodeIndex = this.nodeMap[babylonMesh.uniqueId];
+                        var glTFNode = this.nodes[glTFNodeIndex];
+                        if (!babylonMesh.parent) {
+                            if (this.options &&
+                                this.options.shouldExportMesh != undefined &&
+                                !this.options.shouldExportMesh(babylonMesh)) {
+                                BABYLON.Tools.Log("Omitting " + babylonMesh.name + " from scene.");
+                            }
+                            else {
                                 scene.nodes.push(glTFNodeIndex);
                             }
-                            var directDescendents = babylonMesh.getDescendants(true);
-                            if (!glTFNode.children && directDescendents && directDescendents.length) {
-                                glTFNode.children = [];
-                                for (var _i = 0, directDescendents_1 = directDescendents; _i < directDescendents_1.length; _i++) {
-                                    var descendent = directDescendents_1[_i];
-                                    glTFNode.children.push(this.nodeMap[descendent.uniqueId]);
-                                }
+                        }
+                        var directDescendents = babylonMesh.getDescendants(true);
+                        if (!glTFNode.children && directDescendents && directDescendents.length) {
+                            glTFNode.children = [];
+                            for (var _i = 0, directDescendents_1 = directDescendents; _i < directDescendents_1.length; _i++) {
+                                var descendent = directDescendents_1[_i];
+                                glTFNode.children.push(this.nodeMap[descendent.uniqueId]);
                             }
-                            var mesh = { primitives: new Array() };
-                            byteOffset = this.setPrimitiveAttributes(mesh, babylonMesh, byteOffset, dataBuffer);
                         }
+                        var mesh = { primitives: new Array() };
+                        byteOffset = this.setPrimitiveAttributes(mesh, babylonMesh, byteOffset, dataBuffer);
                     }
                     this.scenes.push(scene);
                 }
@@ -804,7 +817,53 @@ var BABYLON;
                     else if (babylonMaterial instanceof BABYLON.PBRMetallicRoughnessMaterial) {
                         _GLTFMaterial.ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                     }
+                    else if (babylonMaterial instanceof BABYLON.PBRMaterial) {
+                        _GLTFMaterial.ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                    }
+                    else {
+                        BABYLON.Tools.Error("Unsupported material type: " + babylonMaterial.name);
+                    }
+                }
+            };
+            /**
+             * Makes a copy of the glTF material without the texture parameters.
+             * @param originalMaterial - original glTF material.
+             * @returns glTF material without texture parameters
+             */
+            _GLTFMaterial.StripTexturesFromMaterial = function (originalMaterial) {
+                var newMaterial = {};
+                if (originalMaterial) {
+                    newMaterial.name = originalMaterial.name;
+                    newMaterial.doubleSided = originalMaterial.doubleSided;
+                    newMaterial.alphaMode = originalMaterial.alphaMode;
+                    newMaterial.alphaCutoff = originalMaterial.alphaCutoff;
+                    newMaterial.emissiveFactor = originalMaterial.emissiveFactor;
+                    var originalPBRMetallicRoughness = originalMaterial.pbrMetallicRoughness;
+                    if (originalPBRMetallicRoughness) {
+                        newMaterial.pbrMetallicRoughness = {};
+                        newMaterial.pbrMetallicRoughness.baseColorFactor = originalPBRMetallicRoughness.baseColorFactor;
+                        newMaterial.pbrMetallicRoughness.metallicFactor = originalPBRMetallicRoughness.metallicFactor;
+                        newMaterial.pbrMetallicRoughness.roughnessFactor = originalPBRMetallicRoughness.roughnessFactor;
+                    }
+                }
+                return newMaterial;
+            };
+            /**
+             * Specifies if the material has any texture parameters present.
+             * @param material - glTF Material.
+             * @returns boolean specifying if texture parameters are present
+             */
+            _GLTFMaterial.HasTexturesPresent = function (material) {
+                if (material.emissiveTexture || material.normalTexture || material.occlusionTexture) {
+                    return true;
+                }
+                var pbrMat = material.pbrMetallicRoughness;
+                if (pbrMat) {
+                    if (pbrMat.baseColorTexture || pbrMat.metallicRoughnessTexture) {
+                        return true;
+                    }
                 }
+                return false;
             };
             /**
              * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
@@ -914,6 +973,27 @@ var BABYLON;
                         }
                     }
                 }
+                else if (babylonMaterial instanceof BABYLON.PBRMaterial) {
+                    var babylonPBRMaterial = babylonMaterial;
+                    switch (babylonPBRMaterial.transparencyMode) {
+                        case BABYLON.PBRMaterial.PBRMATERIAL_OPAQUE: {
+                            return "OPAQUE" /* OPAQUE */;
+                        }
+                        case BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND: {
+                            return "BLEND" /* BLEND */;
+                        }
+                        case BABYLON.PBRMaterial.PBRMATERIAL_ALPHATEST: {
+                            return "MASK" /* MASK */;
+                        }
+                        case BABYLON.PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND: {
+                            BABYLON.Tools.Warn(babylonMaterial.name + ": GLTF Exporter | Alpha test and blend mode not supported in glTF.  Alpha blend used instead.");
+                            return "BLEND" /* BLEND */;
+                        }
+                        default: {
+                            throw new Error("Unsupported alpha mode " + babylonPBRMaterial.transparencyMode);
+                        }
+                    }
+                }
                 else {
                     throw new Error("Unsupported Babylon material type");
                 }
@@ -959,9 +1039,13 @@ var BABYLON;
                         glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
                     }
                     if (babylonStandardMaterial.ambientTexture) {
-                        var glTFOcclusionTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
-                        if (glTFOcclusionTexture) {
-                            glTFMaterial.occlusionTexture = glTFOcclusionTexture;
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            var occlusionTexture = {
+                                index: glTFTexture.index
+                            };
+                            glTFMaterial.occlusionTexture = occlusionTexture;
+                            occlusionTexture.strength = 1.0;
                         }
                     }
                 }
@@ -1056,6 +1140,149 @@ var BABYLON;
                 materials.push(glTFMaterial);
             };
             /**
+             * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
+             * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+             * @param color - Color source to calculate brightness from.
+             * @returns number representing the perceived brightness, or zero if color is undefined.
+             */
+            _GLTFMaterial.GetPerceivedBrightness = function (color) {
+                if (color) {
+                    return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
+                }
+                return 0;
+            };
+            /**
+             * Returns the maximum color component value.
+             * @param color
+             * @returns maximum color component value, or zero if color is null or undefined.
+             */
+            _GLTFMaterial.GetMaxComponent = function (color) {
+                if (color) {
+                    return Math.max(color.r, Math.max(color.g, color.b));
+                }
+                return 0;
+            };
+            /**
+             * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
+             * @param babylonPBRMaterial - BJS PBR Metallic Roughness Material.
+             * @param mimeType - mime type to use for the textures.
+             * @param images - array of glTF image interfaces.
+             * @param textures - array of glTF texture interfaces.
+             * @param materials - array of glTF material interfaces.
+             * @param imageData - map of image file name to data.
+             * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
+             */
+            _GLTFMaterial.ConvertPBRMaterial = function (babylonPBRMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+                var glTFPbrMetallicRoughness = {};
+                var glTFMaterial = {
+                    name: babylonPBRMaterial.name
+                };
+                var useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
+                if (babylonPBRMaterial) {
+                    if (useMetallicRoughness) {
+                        glTFPbrMetallicRoughness.baseColorFactor = [
+                            babylonPBRMaterial.albedoColor.r,
+                            babylonPBRMaterial.albedoColor.g,
+                            babylonPBRMaterial.albedoColor.b,
+                            babylonPBRMaterial.alpha
+                        ];
+                        if (babylonPBRMaterial.metallic != null) {
+                            if (babylonPBRMaterial.metallic !== 1) {
+                                glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
+                            }
+                        }
+                        if (babylonPBRMaterial.roughness != null) {
+                            if (babylonPBRMaterial.roughness !== 1) {
+                                glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
+                            }
+                        }
+                    }
+                    else {
+                        var diffuseColor = babylonPBRMaterial.albedoColor || BABYLON.Color3.Black();
+                        var specularColor = babylonPBRMaterial.reflectionColor || BABYLON.Color3.Black();
+                        var diffusePerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(diffuseColor);
+                        var specularPerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(specularColor);
+                        var oneMinusSpecularStrength = 1 - _GLTFMaterial.GetMaxComponent(babylonPBRMaterial.reflectionColor);
+                        var metallic = _GLTFMaterial.SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
+                        var glossiness = babylonPBRMaterial.microSurface || 0;
+                        var baseColorFromDiffuse = diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this.dielectricSpecular.r) / Math.max(1 - metallic, this.epsilon));
+                        var baseColorFromSpecular = specularColor.subtract(this.dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this.epsilon));
+                        var baseColor = BABYLON.Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
+                        baseColor = baseColor.clampToRef(0, 1, baseColor);
+                        glTFPbrMetallicRoughness.baseColorFactor = [
+                            baseColor.r,
+                            baseColor.g,
+                            baseColor.b,
+                            babylonPBRMaterial.alpha
+                        ];
+                        if (metallic !== 1) {
+                            glTFPbrMetallicRoughness.metallicFactor = metallic;
+                        }
+                        if (glossiness) {
+                            glTFPbrMetallicRoughness.roughnessFactor = 1 - glossiness;
+                        }
+                    }
+                    if (babylonPBRMaterial.backFaceCulling) {
+                        if (!babylonPBRMaterial.twoSidedLighting) {
+                            BABYLON.Tools.Warn(babylonPBRMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
+                        }
+                        glTFMaterial.doubleSided = true;
+                    }
+                    if (hasTextureCoords) {
+                        if (babylonPBRMaterial.albedoTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture) {
+                                glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                            }
+                        }
+                        if (babylonPBRMaterial.bumpTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture) {
+                                glTFMaterial.normalTexture = glTFTexture;
+                            }
+                        }
+                        if (babylonPBRMaterial.ambientTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture) {
+                                var occlusionTexture = {
+                                    index: glTFTexture.index
+                                };
+                                glTFMaterial.occlusionTexture = occlusionTexture;
+                                if (babylonPBRMaterial.ambientTextureStrength) {
+                                    occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
+                                }
+                            }
+                        }
+                        if (babylonPBRMaterial.emissiveTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture != null) {
+                                glTFMaterial.emissiveTexture = glTFTexture;
+                            }
+                        }
+                        if (babylonPBRMaterial.metallicTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture != null) {
+                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
+                            }
+                        }
+                    }
+                    if (!babylonPBRMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
+                        glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                    }
+                    if (babylonPBRMaterial.transparencyMode != null) {
+                        var alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
+                        if (alphaMode !== "OPAQUE" /* OPAQUE */) {
+                            glTFMaterial.alphaMode = alphaMode;
+                            if (alphaMode === "BLEND" /* BLEND */) {
+                                glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
+                            }
+                        }
+                    }
+                }
+                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
+                materials.push(glTFMaterial);
+            };
+            /**
              * Extracts a texture from a Babylon texture into file data and glTF data.
              * @param babylonTexture - Babylon texture to extract.
              * @param mimeType - Mime Type of the babylonTexture.
@@ -1143,6 +1370,7 @@ var BABYLON;
              * Allows the maximum specular power to be defined for material calculations.
              */
             _GLTFMaterial.maxSpecularPower = 1024;
+            _GLTFMaterial.epsilon = 1e-6;
             return _GLTFMaterial;
         }());
         GLTF2._GLTFMaterial = _GLTFMaterial;

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 259 - 31
dist/preview release/serializers/babylonjs.serializers.js

@@ -234,7 +234,7 @@ var BABYLON;
                 this.materials = new Array();
                 this.textures = new Array();
                 this.imageData = {};
-                this.convertToRightHandedSystem = !this.babylonScene.useRightHandedSystem;
+                this.convertToRightHandedSystem = this.babylonScene.useRightHandedSystem ? false : true;
                 if (options) {
                     this.options = options;
                 }
@@ -565,10 +565,10 @@ var BABYLON;
              * @param babylonMesh - Babylon mesh used as the source for the transformation data.
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonMesh) {
-                if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
+                if (!babylonMesh.position.equalsToFloats(0, 0, 0)) {
                     node.translation = this.convertToRightHandedSystem ? _Exporter.GetRightHandedVector3(babylonMesh.position).asArray() : babylonMesh.position.asArray();
                 }
-                if (!(babylonMesh.scaling.x === 1 && babylonMesh.scaling.y === 1 && babylonMesh.scaling.z === 1)) {
+                if (!babylonMesh.scaling.equalsToFloats(1, 1, 1)) {
                     node.scale = babylonMesh.scaling.asArray();
                 }
                 var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(babylonMesh.rotation.y, babylonMesh.rotation.x, babylonMesh.rotation.z);
@@ -681,6 +681,7 @@ var BABYLON;
                         }
                     }
                     if (babylonMesh.subMeshes) {
+                        var uvCoordsPresent = false;
                         // go through all mesh primitives (submeshes)
                         for (var _a = 0, _b = babylonMesh.subMeshes; _a < _b.length; _a++) {
                             var submesh = _b[_a];
@@ -727,10 +728,12 @@ var BABYLON;
                                                         }
                                                         case BABYLON.VertexBuffer.UVKind: {
                                                             meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
+                                                            uvCoordsPresent = true;
                                                             break;
                                                         }
                                                         case BABYLON.VertexBuffer.UV2Kind: {
                                                             meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
+                                                            uvCoordsPresent = true;
                                                             break;
                                                         }
                                                         default: {
@@ -750,21 +753,31 @@ var BABYLON;
                                 }
                             }
                             if (bufferMesh.material) {
-                                if (bufferMesh.material instanceof BABYLON.StandardMaterial || bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial) {
-                                    var materialIndex = babylonMesh.getScene().materials.indexOf(bufferMesh.material);
-                                    meshPrimitive.material = materialIndex;
+                                var materialIndex = null;
+                                if (bufferMesh.material instanceof BABYLON.StandardMaterial || bufferMesh.material instanceof BABYLON.PBRMetallicRoughnessMaterial || bufferMesh.material instanceof BABYLON.PBRMaterial) {
+                                    materialIndex = babylonMesh.getScene().materials.indexOf(bufferMesh.material);
                                 }
                                 else if (bufferMesh.material instanceof BABYLON.MultiMaterial) {
                                     var babylonMultiMaterial = bufferMesh.material;
                                     var material = babylonMultiMaterial.subMaterials[submesh.materialIndex];
                                     if (material) {
-                                        var materialIndex = babylonMesh.getScene().materials.indexOf(material);
-                                        meshPrimitive.material = materialIndex;
+                                        materialIndex = babylonMesh.getScene().materials.indexOf(material);
                                     }
                                 }
                                 else {
                                     BABYLON.Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                                 }
+                                if (materialIndex != null) {
+                                    if (uvCoordsPresent || !GLTF2._GLTFMaterial.HasTexturesPresent(this.materials[materialIndex])) {
+                                        meshPrimitive.material = materialIndex;
+                                    }
+                                    else {
+                                        // If no texture coordinate information is present, make a copy of the material without the textures to be glTF compliant.
+                                        var newMat = GLTF2._GLTFMaterial.StripTexturesFromMaterial(this.materials[materialIndex]);
+                                        this.materials.push(newMat);
+                                        meshPrimitive.material = this.materials.length - 1;
+                                    }
+                                }
                             }
                             mesh.primitives.push(meshPrimitive);
                         }
@@ -790,30 +803,30 @@ var BABYLON;
                     this.binaryBuffer = new ArrayBuffer(this.totalByteLength);
                     var dataBuffer = new DataView(this.binaryBuffer);
                     for (var i = 0; i < babylonMeshes.length; ++i) {
-                        if (this.options &&
-                            this.options.shouldExportMesh != undefined &&
-                            !this.options.shouldExportMesh(babylonMeshes[i])) {
-                            continue;
-                        }
-                        else {
-                            var babylonMesh = babylonMeshes[i];
-                            // Build Hierarchy with the node map.
-                            var glTFNodeIndex = this.nodeMap[babylonMesh.uniqueId];
-                            var glTFNode = this.nodes[glTFNodeIndex];
-                            if (!babylonMesh.parent) {
+                        var babylonMesh = babylonMeshes[i];
+                        // Build Hierarchy with the node map.
+                        var glTFNodeIndex = this.nodeMap[babylonMesh.uniqueId];
+                        var glTFNode = this.nodes[glTFNodeIndex];
+                        if (!babylonMesh.parent) {
+                            if (this.options &&
+                                this.options.shouldExportMesh != undefined &&
+                                !this.options.shouldExportMesh(babylonMesh)) {
+                                BABYLON.Tools.Log("Omitting " + babylonMesh.name + " from scene.");
+                            }
+                            else {
                                 scene.nodes.push(glTFNodeIndex);
                             }
-                            var directDescendents = babylonMesh.getDescendants(true);
-                            if (!glTFNode.children && directDescendents && directDescendents.length) {
-                                glTFNode.children = [];
-                                for (var _i = 0, directDescendents_1 = directDescendents; _i < directDescendents_1.length; _i++) {
-                                    var descendent = directDescendents_1[_i];
-                                    glTFNode.children.push(this.nodeMap[descendent.uniqueId]);
-                                }
+                        }
+                        var directDescendents = babylonMesh.getDescendants(true);
+                        if (!glTFNode.children && directDescendents && directDescendents.length) {
+                            glTFNode.children = [];
+                            for (var _i = 0, directDescendents_1 = directDescendents; _i < directDescendents_1.length; _i++) {
+                                var descendent = directDescendents_1[_i];
+                                glTFNode.children.push(this.nodeMap[descendent.uniqueId]);
                             }
-                            var mesh = { primitives: new Array() };
-                            byteOffset = this.setPrimitiveAttributes(mesh, babylonMesh, byteOffset, dataBuffer);
                         }
+                        var mesh = { primitives: new Array() };
+                        byteOffset = this.setPrimitiveAttributes(mesh, babylonMesh, byteOffset, dataBuffer);
                     }
                     this.scenes.push(scene);
                 }
@@ -958,7 +971,53 @@ var BABYLON;
                     else if (babylonMaterial instanceof BABYLON.PBRMetallicRoughnessMaterial) {
                         _GLTFMaterial.ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                     }
+                    else if (babylonMaterial instanceof BABYLON.PBRMaterial) {
+                        _GLTFMaterial.ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                    }
+                    else {
+                        BABYLON.Tools.Error("Unsupported material type: " + babylonMaterial.name);
+                    }
+                }
+            };
+            /**
+             * Makes a copy of the glTF material without the texture parameters.
+             * @param originalMaterial - original glTF material.
+             * @returns glTF material without texture parameters
+             */
+            _GLTFMaterial.StripTexturesFromMaterial = function (originalMaterial) {
+                var newMaterial = {};
+                if (originalMaterial) {
+                    newMaterial.name = originalMaterial.name;
+                    newMaterial.doubleSided = originalMaterial.doubleSided;
+                    newMaterial.alphaMode = originalMaterial.alphaMode;
+                    newMaterial.alphaCutoff = originalMaterial.alphaCutoff;
+                    newMaterial.emissiveFactor = originalMaterial.emissiveFactor;
+                    var originalPBRMetallicRoughness = originalMaterial.pbrMetallicRoughness;
+                    if (originalPBRMetallicRoughness) {
+                        newMaterial.pbrMetallicRoughness = {};
+                        newMaterial.pbrMetallicRoughness.baseColorFactor = originalPBRMetallicRoughness.baseColorFactor;
+                        newMaterial.pbrMetallicRoughness.metallicFactor = originalPBRMetallicRoughness.metallicFactor;
+                        newMaterial.pbrMetallicRoughness.roughnessFactor = originalPBRMetallicRoughness.roughnessFactor;
+                    }
+                }
+                return newMaterial;
+            };
+            /**
+             * Specifies if the material has any texture parameters present.
+             * @param material - glTF Material.
+             * @returns boolean specifying if texture parameters are present
+             */
+            _GLTFMaterial.HasTexturesPresent = function (material) {
+                if (material.emissiveTexture || material.normalTexture || material.occlusionTexture) {
+                    return true;
+                }
+                var pbrMat = material.pbrMetallicRoughness;
+                if (pbrMat) {
+                    if (pbrMat.baseColorTexture || pbrMat.metallicRoughnessTexture) {
+                        return true;
+                    }
                 }
+                return false;
             };
             /**
              * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
@@ -1068,6 +1127,27 @@ var BABYLON;
                         }
                     }
                 }
+                else if (babylonMaterial instanceof BABYLON.PBRMaterial) {
+                    var babylonPBRMaterial = babylonMaterial;
+                    switch (babylonPBRMaterial.transparencyMode) {
+                        case BABYLON.PBRMaterial.PBRMATERIAL_OPAQUE: {
+                            return "OPAQUE" /* OPAQUE */;
+                        }
+                        case BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND: {
+                            return "BLEND" /* BLEND */;
+                        }
+                        case BABYLON.PBRMaterial.PBRMATERIAL_ALPHATEST: {
+                            return "MASK" /* MASK */;
+                        }
+                        case BABYLON.PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND: {
+                            BABYLON.Tools.Warn(babylonMaterial.name + ": GLTF Exporter | Alpha test and blend mode not supported in glTF.  Alpha blend used instead.");
+                            return "BLEND" /* BLEND */;
+                        }
+                        default: {
+                            throw new Error("Unsupported alpha mode " + babylonPBRMaterial.transparencyMode);
+                        }
+                    }
+                }
                 else {
                     throw new Error("Unsupported Babylon material type");
                 }
@@ -1113,9 +1193,13 @@ var BABYLON;
                         glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
                     }
                     if (babylonStandardMaterial.ambientTexture) {
-                        var glTFOcclusionTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
-                        if (glTFOcclusionTexture) {
-                            glTFMaterial.occlusionTexture = glTFOcclusionTexture;
+                        var glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
+                        if (glTFTexture) {
+                            var occlusionTexture = {
+                                index: glTFTexture.index
+                            };
+                            glTFMaterial.occlusionTexture = occlusionTexture;
+                            occlusionTexture.strength = 1.0;
                         }
                     }
                 }
@@ -1210,6 +1294,149 @@ var BABYLON;
                 materials.push(glTFMaterial);
             };
             /**
+             * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
+             * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+             * @param color - Color source to calculate brightness from.
+             * @returns number representing the perceived brightness, or zero if color is undefined.
+             */
+            _GLTFMaterial.GetPerceivedBrightness = function (color) {
+                if (color) {
+                    return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
+                }
+                return 0;
+            };
+            /**
+             * Returns the maximum color component value.
+             * @param color
+             * @returns maximum color component value, or zero if color is null or undefined.
+             */
+            _GLTFMaterial.GetMaxComponent = function (color) {
+                if (color) {
+                    return Math.max(color.r, Math.max(color.g, color.b));
+                }
+                return 0;
+            };
+            /**
+             * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
+             * @param babylonPBRMaterial - BJS PBR Metallic Roughness Material.
+             * @param mimeType - mime type to use for the textures.
+             * @param images - array of glTF image interfaces.
+             * @param textures - array of glTF texture interfaces.
+             * @param materials - array of glTF material interfaces.
+             * @param imageData - map of image file name to data.
+             * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
+             */
+            _GLTFMaterial.ConvertPBRMaterial = function (babylonPBRMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords) {
+                var glTFPbrMetallicRoughness = {};
+                var glTFMaterial = {
+                    name: babylonPBRMaterial.name
+                };
+                var useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
+                if (babylonPBRMaterial) {
+                    if (useMetallicRoughness) {
+                        glTFPbrMetallicRoughness.baseColorFactor = [
+                            babylonPBRMaterial.albedoColor.r,
+                            babylonPBRMaterial.albedoColor.g,
+                            babylonPBRMaterial.albedoColor.b,
+                            babylonPBRMaterial.alpha
+                        ];
+                        if (babylonPBRMaterial.metallic != null) {
+                            if (babylonPBRMaterial.metallic !== 1) {
+                                glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
+                            }
+                        }
+                        if (babylonPBRMaterial.roughness != null) {
+                            if (babylonPBRMaterial.roughness !== 1) {
+                                glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
+                            }
+                        }
+                    }
+                    else {
+                        var diffuseColor = babylonPBRMaterial.albedoColor || BABYLON.Color3.Black();
+                        var specularColor = babylonPBRMaterial.reflectionColor || BABYLON.Color3.Black();
+                        var diffusePerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(diffuseColor);
+                        var specularPerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(specularColor);
+                        var oneMinusSpecularStrength = 1 - _GLTFMaterial.GetMaxComponent(babylonPBRMaterial.reflectionColor);
+                        var metallic = _GLTFMaterial.SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
+                        var glossiness = babylonPBRMaterial.microSurface || 0;
+                        var baseColorFromDiffuse = diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this.dielectricSpecular.r) / Math.max(1 - metallic, this.epsilon));
+                        var baseColorFromSpecular = specularColor.subtract(this.dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this.epsilon));
+                        var baseColor = BABYLON.Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
+                        baseColor = baseColor.clampToRef(0, 1, baseColor);
+                        glTFPbrMetallicRoughness.baseColorFactor = [
+                            baseColor.r,
+                            baseColor.g,
+                            baseColor.b,
+                            babylonPBRMaterial.alpha
+                        ];
+                        if (metallic !== 1) {
+                            glTFPbrMetallicRoughness.metallicFactor = metallic;
+                        }
+                        if (glossiness) {
+                            glTFPbrMetallicRoughness.roughnessFactor = 1 - glossiness;
+                        }
+                    }
+                    if (babylonPBRMaterial.backFaceCulling) {
+                        if (!babylonPBRMaterial.twoSidedLighting) {
+                            BABYLON.Tools.Warn(babylonPBRMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
+                        }
+                        glTFMaterial.doubleSided = true;
+                    }
+                    if (hasTextureCoords) {
+                        if (babylonPBRMaterial.albedoTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture) {
+                                glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                            }
+                        }
+                        if (babylonPBRMaterial.bumpTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture) {
+                                glTFMaterial.normalTexture = glTFTexture;
+                            }
+                        }
+                        if (babylonPBRMaterial.ambientTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture) {
+                                var occlusionTexture = {
+                                    index: glTFTexture.index
+                                };
+                                glTFMaterial.occlusionTexture = occlusionTexture;
+                                if (babylonPBRMaterial.ambientTextureStrength) {
+                                    occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
+                                }
+                            }
+                        }
+                        if (babylonPBRMaterial.emissiveTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture != null) {
+                                glTFMaterial.emissiveTexture = glTFTexture;
+                            }
+                        }
+                        if (babylonPBRMaterial.metallicTexture) {
+                            var glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
+                            if (glTFTexture != null) {
+                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
+                            }
+                        }
+                    }
+                    if (!babylonPBRMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
+                        glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                    }
+                    if (babylonPBRMaterial.transparencyMode != null) {
+                        var alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
+                        if (alphaMode !== "OPAQUE" /* OPAQUE */) {
+                            glTFMaterial.alphaMode = alphaMode;
+                            if (alphaMode === "BLEND" /* BLEND */) {
+                                glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
+                            }
+                        }
+                    }
+                }
+                glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
+                materials.push(glTFMaterial);
+            };
+            /**
              * Extracts a texture from a Babylon texture into file data and glTF data.
              * @param babylonTexture - Babylon texture to extract.
              * @param mimeType - Mime Type of the babylonTexture.
@@ -1297,6 +1524,7 @@ var BABYLON;
              * Allows the maximum specular power to be defined for material calculations.
              */
             _GLTFMaterial.maxSpecularPower = 1024;
+            _GLTFMaterial.epsilon = 1e-6;
             return _GLTFMaterial;
         }());
         GLTF2._GLTFMaterial = _GLTFMaterial;

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


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

@@ -310,6 +310,7 @@ declare module BABYLON.GLTF2 {
          * Allows the maximum specular power to be defined for material calculations.
          */
         private static maxSpecularPower;
+        private static epsilon;
         /**
          * Gets the materials from a Babylon scene and converts them to glTF materials.
          * @param scene
@@ -327,6 +328,18 @@ declare module BABYLON.GLTF2 {
             };
         }, hasTextureCoords: boolean): void;
         /**
+         * Makes a copy of the glTF material without the texture parameters.
+         * @param originalMaterial - original glTF material.
+         * @returns glTF material without texture parameters
+         */
+        static StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial;
+        /**
+         * Specifies if the material has any texture parameters present.
+         * @param material - glTF Material.
+         * @returns boolean specifying if texture parameters are present
+         */
+        static HasTexturesPresent(material: IMaterial): boolean;
+        /**
          * Converts a Babylon StandardMaterial to a glTF Metallic Roughness Material.
          * @param babylonStandardMaterial
          * @returns - glTF Metallic Roughness Material representation
@@ -379,6 +392,35 @@ declare module BABYLON.GLTF2 {
             };
         }, hasTextureCoords: boolean): void;
         /**
+         * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
+         * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+         * @param color - Color source to calculate brightness from.
+         * @returns number representing the perceived brightness, or zero if color is undefined.
+         */
+        static GetPerceivedBrightness(color: Color3): number;
+        /**
+         * Returns the maximum color component value.
+         * @param color
+         * @returns maximum color component value, or zero if color is null or undefined.
+         */
+        static GetMaxComponent(color: Color3): number;
+        /**
+         * Converts a Babylon PBR Metallic Roughness Material to a glTF Material.
+         * @param babylonPBRMaterial - BJS PBR Metallic Roughness Material.
+         * @param mimeType - mime type to use for the textures.
+         * @param images - array of glTF image interfaces.
+         * @param textures - array of glTF texture interfaces.
+         * @param materials - array of glTF material interfaces.
+         * @param imageData - map of image file name to data.
+         * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
+         */
+        static ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: {
+            [fileName: string]: {
+                data: Uint8Array;
+                mimeType: ImageMimeType;
+            };
+        }, hasTextureCoords: boolean): void;
+        /**
          * Extracts a texture from a Babylon texture into file data and glTF data.
          * @param babylonTexture - Babylon texture to extract.
          * @param mimeType - Mime Type of the babylonTexture.

+ 2 - 2
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "3.2.0-alphaA"
+        "babylonjs-gltf2interface": "3.2.0-alphaB"
     },
     "peerDependencies": {
         "babylonjs": ">=3.2.0-alpha"

+ 2 - 110
dist/preview release/typedocValidationBaseline.json

@@ -1,7 +1,7 @@
 {
-  "errors": 7219,
+  "errors": 7193,
   "babylon.typedoc.json": {
-    "errors": 7219,
+    "errors": 7193,
     "AnimationKeyInterpolation": {
       "Enumeration": {
         "Comments": {
@@ -38823,114 +38823,6 @@
         }
       }
     },
-    "IImageProcessingConfigurationDefines": {
-      "Property": {
-        "COLORCURVES": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "COLORGRADING": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "COLORGRADING3D": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "CONTRAST": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "EXPOSURE": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "IMAGEPROCESSING": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "IMAGEPROCESSINGPOSTPROCESS": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "SAMPLER3DBGRMAP": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "SAMPLER3DGREENDEPTH": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "TONEMAPPING": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "VIGNETTE": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "VIGNETTEBLENDMODEMULTIPLY": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "VIGNETTEBLENDMODEOPAQUE": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        }
-      }
-    },
     "ILoadingScreen": {
       "Interface": {
         "Comments": {

Разница между файлами не показана из-за своего большого размера
+ 69 - 69
dist/preview release/viewer/babylon.viewer.js


Разница между файлами не показана из-за своего большого размера
+ 305 - 56
dist/preview release/viewer/babylon.viewer.max.js


+ 1 - 1
dist/preview release/viewer/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-viewer",
     "description": "A simple-to-use viewer based on BabylonJS to display 3D elements natively",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 5 - 3
dist/preview release/what's new.md

@@ -14,7 +14,7 @@
 - Added [VideoDome](http://doc.babylonjs.com/how_to/360videodome) class to easily support 360 videos ([DavidHGillen](https://github.com/DavidHGillen))
 - Added [GlowLayer](https://doc.babylonjs.com/how_to/glow_layer) to easily support glow from emissive materials ([sebavan](https://github.com/sebavan))
 - New [AssetContainer](http://doc.babylonjs.com/how_to/how_to_use_assetcontainer) Class and loading methods ([trevordev](https://github.com/trevordev))
-- Added depth of field, MSAA, sharpening and chromatic aberration effect to the default pipeline ([trevordev](https://github.com/trevordev))
+- Added depth of field, MSAA, sharpening, chromatic aberration and grain effect to the default pipeline ([trevordev](https://github.com/trevordev))
 
 ## Updates
 
@@ -84,6 +84,7 @@
 - WebVRExperienceHelper will create an empty controller model so that controller interactions can be used while the actual model is still loading ([trevordev](https://github.com/trevordev))
 - Default fragment shader will clamp negative values to avoid underflow, webVR post processing will render to eye texture size ([trevordev](https://github.com/trevordev))
 - Supports Environment Drag and Drop in Sandbox ([sebavan](https://github.com/sebavan))
+- EnvironmentHelper has no an onError observable to handle errors when loading the textures ([RaananW](https://github.com/RaananW))
 
 ## Bug fixes
 
@@ -91,7 +92,7 @@
 - SPS solid particle `.pivot` property now also behaves like the standard mesh pivot. Former behavior (particle translation) can be kept with the particle property `.translateFromPivot` set to true ([jbousquie](https://github.com/jbousquie))
 - Texture extension detection in `Engine.CreateTexture` ([sebavan](https://github.com/sebavan))
 - SPS internal temporary vector3 instead of Tmp.Vector3 to avoid possible concurrent uses ([jbousquie](https://github.com/jbousquie))
-- Fixed a bug when calling load on an empty assets manager - [#3739](https://github.com/BabylonJS/Babylon.js/issues/3739). ([RaananW](https://github.com/RaananW))
+- Fixed a bug when calling load on an empty assets manager - [#3739](https://github.com/BabylonJS/Babylon.js/issues/3739) ([RaananW](https://github.com/RaananW))
 - Enabling teleportation in the vr helper class caused a redundant post process to be added ([trevordev](https://github.com/trevordev))
 - (Viewer) Fixed a bug where loading another mesh positioned it incorrectly ([RaananW](https://github.com/RaananW))
 - Scale vr controllers by deviceScale when it is set in VRExperienceHelper ([trevordev](https://github.com/trevordev))
@@ -99,7 +100,8 @@
 - AMD "define" declaration is no longer anonymous ([RaananW](https://github.com/RaananW))
 - Collision worker didn't initialize instanced meshes correctly - [#3819](https://github.com/BabylonJS/Babylon.js/issues/3819) ([RaananW](https://github.com/RaananW))
 - postMessage calls in webworkers were fixed. ([RaananW](https://github.com/RaananW))
-- fixed WebCam Texture on Firefox and Edge - [#3825](https://github.com/BabylonJS/Babylon.js/issues/3825) ([sebavan](https://github.com/sebavan))
+- Fixed WebCam Texture on Firefox and Edge - [#3825](https://github.com/BabylonJS/Babylon.js/issues/3825) ([sebavan](https://github.com/sebavan))
+- Add onLoadObservable on VideoTexture - [#3845](https://github.com/BabylonJS/Babylon.js/issues/3845) ([sebavan](https://github.com/sebavan))
 
 ## Breaking changes
 

+ 1 - 0
loaders/src/glTF/1.0/babylon.glTFLoader.ts

@@ -1571,6 +1571,7 @@ module BABYLON.GLTF1 {
         public onMeshLoadedObservable = new Observable<AbstractMesh>();
         public onTextureLoadedObservable = new Observable<BaseTexture>();
         public onMaterialLoadedObservable = new Observable<Material>();
+        public onAnimationGroupLoadedObservable = new Observable<AnimationGroup>();
         public onCompleteObservable = new Observable<IGLTFLoader>();
         public onExtensionLoadedObservable = new Observable<IGLTFLoaderExtension>();
 

+ 4 - 4
loaders/src/glTF/2.0/Extensions/KHR_lights.ts

@@ -40,7 +40,7 @@ module BABYLON.GLTF2.Extensions {
 
                 const light = GLTFLoader._GetProperty(context, this._lights, extension.light);
                 if (light.type !== LightType.AMBIENT) {
-                    throw new Error(context + ": Only ambient lights are allowed on a scene");
+                    throw new Error(`${context}: Only ambient lights are allowed on a scene`);
                 }
 
                 this._loader._babylonScene.ambientColor = light.color ? Color3.FromArray(light.color) : Color3.Black();
@@ -59,7 +59,7 @@ module BABYLON.GLTF2.Extensions {
                 const name = node._babylonMesh!.name;
                 switch (light.type) {
                     case LightType.AMBIENT: {
-                        throw new Error(context + ": Ambient lights are not allowed on a node");
+                        throw new Error(`${context}: Ambient lights are not allowed on a node`);
                     }
                     case LightType.DIRECTIONAL: {
                         babylonLight = new DirectionalLight(name, Vector3.Forward(), this._loader._babylonScene);
@@ -78,7 +78,7 @@ module BABYLON.GLTF2.Extensions {
                         break;
                     }
                     default: {
-                        throw new Error(context + ": Invalid light type " + light.type);
+                        throw new Error(`${context}: Invalid light type (${light.type})`);
                     }
                 }
 
@@ -93,7 +93,7 @@ module BABYLON.GLTF2.Extensions {
         private get _lights(): Array<ILight> {
             const extensions = this._loader._gltf.extensions;
             if (!extensions || !extensions[this.name]) {
-                throw new Error("#/extensions: " + this.name + " not found");
+                throw new Error(`#/extensions: '${this.name}' not found`);
             }
 
             const extension = extensions[this.name] as ILights;

+ 16 - 19
loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts

@@ -16,33 +16,30 @@ module BABYLON.GLTF2.Extensions {
     export class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         public readonly name = NAME;
 
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             return this._loadExtensionAsync<IKHRMaterialsPbrSpecularGlossiness>(context, material, (context, extension) => {
                 material._babylonMeshes = material._babylonMeshes || [];
                 material._babylonMeshes.push(babylonMesh);
 
-                if (material._loaded) {
-                    babylonMesh.material = material._babylonMaterial!;
-                    return material._loaded;
-                }
-
-                const promises = new Array<Promise<void>>();
+                if (!material._loaded) {
+                    const promises = new Array<Promise<void>>();
 
-                const babylonMaterial = this._loader._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
+                    const babylonMaterial = this._loader._createMaterial(material);
+                    material._babylonMaterial = babylonMaterial;
 
-                promises.push(this._loader._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadSpecularGlossinessPropertiesAsync(this._loader, context, material, extension));
+                    promises.push(this._loader._loadMaterialBasePropertiesAsync(context, material));
+                    promises.push(this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
 
-                this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-
-                babylonMesh.material = babylonMaterial;
+                    this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                    material._loaded = Promise.all(promises).then(() => {});
+                }
 
-                return (material._loaded = Promise.all(promises).then(() => {}));
+                assign(material._babylonMaterial!);
+                return material._loaded;
             });
         }
 
-        private _loadSpecularGlossinessPropertiesAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, properties: IKHRMaterialsPbrSpecularGlossiness): Promise<void> {
+        private _loadSpecularGlossinessPropertiesAsync(context: string, material: ILoaderMaterial, properties: IKHRMaterialsPbrSpecularGlossiness): Promise<void> {
             const promises = new Array<Promise<void>>();
 
             const babylonMaterial = material._babylonMaterial as PBRMaterial;
@@ -59,13 +56,13 @@ module BABYLON.GLTF2.Extensions {
             babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
 
             if (properties.diffuseTexture) {
-                promises.push(loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, texture => {
+                promises.push(this._loader._loadTextureAsync(`${context}/diffuseTexture`, properties.diffuseTexture, texture => {
                     babylonMaterial.albedoTexture = texture;
                 }));
             }
 
             if (properties.specularGlossinessTexture) {
-                promises.push(loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, texture => {
+                promises.push(this._loader._loadTextureAsync(`${context}/specularGlossinessTexture`, properties.specularGlossinessTexture, texture => {
                     babylonMaterial.reflectivityTexture = texture;
                 }));
 
@@ -73,7 +70,7 @@ module BABYLON.GLTF2.Extensions {
                 babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
             }
 
-            loader._loadMaterialAlphaProperties(context, material);
+            this._loader._loadMaterialAlphaProperties(context, material);
 
             return Promise.all(promises).then(() => {});
         }

+ 8 - 4
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -39,7 +39,7 @@ module BABYLON.GLTF2.Extensions {
                         }
                     }
 
-                    const promise = this._loader._loadNodeAsync("#/nodes/" + nodeLOD._index, nodeLOD).then(() => {
+                    const promise = this._loader._loadNodeAsync(`#/nodes/${nodeLOD._index}`, nodeLOD).then(() => {
                         if (indexLOD !== 0) {
                             const previousNodeLOD = nodeLODs[indexLOD - 1];
                             previousNodeLOD._babylonMesh!.setEnabled(false);
@@ -68,7 +68,7 @@ module BABYLON.GLTF2.Extensions {
             });
         }
 
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             // Don't load material LODs if already loading a node LOD.
             if (this._loadingNodeLOD) {
                 return null;
@@ -89,7 +89,11 @@ module BABYLON.GLTF2.Extensions {
                         }
                     }
 
-                    const promise = this._loader._loadMaterialAsync("#/materials/" + materialLOD._index, materialLOD, babylonMesh).then(() => {
+                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD._index}`, materialLOD, babylonMesh, indexLOD === 0 ? assign : () => {}).then(() => {
+                        if (indexLOD !== 0) {
+                            assign(materialLOD._babylonMaterial!);
+                        }
+
                         if (indexLOD !== materialLODs.length - 1) {
                             const materialIndex = materialLODs[indexLOD + 1]._index;
                             if (this._loadMaterialSignals[materialIndex]) {
@@ -141,7 +145,7 @@ module BABYLON.GLTF2.Extensions {
             const properties = new Array<T>();
 
             for (let i = ids.length - 1; i >= 0; i--) {
-                properties.push(GLTFLoader._GetProperty(context + "/ids/" + ids[i], array, ids[i]));
+                properties.push(GLTFLoader._GetProperty(`${context}/ids/${ids[i]}`, array, ids[i]));
                 if (properties.length === this.maxLODsToLoad) {
                     return properties;
                 }

+ 119 - 112
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -34,7 +34,7 @@ module BABYLON.GLTF2 {
         private static _Factories: { [name: string]: (loader: GLTFLoader) => GLTFLoaderExtension } = {};
         public static _Register(name: string, factory: (loader: GLTFLoader) => GLTFLoaderExtension): void {
             if (GLTFLoader._Factories[name]) {
-                Tools.Error("Extension with the name '" + name + "' already exists");
+                Tools.Error(`Extension with the name '${name}' already exists`);
                 return;
             }
 
@@ -54,6 +54,7 @@ module BABYLON.GLTF2 {
         public readonly onMeshLoadedObservable = new Observable<AbstractMesh>();
         public readonly onTextureLoadedObservable = new Observable<BaseTexture>();
         public readonly onMaterialLoadedObservable = new Observable<Material>();
+        public readonly onAnimationGroupLoadedObservable = new Observable<AnimationGroup>();
         public readonly onExtensionLoadedObservable = new Observable<IGLTFLoaderExtension>();
         public readonly onCompleteObservable = new Observable<IGLTFLoader>();
 
@@ -92,7 +93,7 @@ module BABYLON.GLTF2 {
                     nodes = names.map(name => {
                         const node = nodeMap[name];
                         if (!node) {
-                            throw new Error("Failed to find node " + name);
+                            throw new Error(`Failed to find node '${name}'`);
                         }
 
                         return node;
@@ -131,8 +132,8 @@ module BABYLON.GLTF2 {
                     promises.push(this._loadNodesAsync(nodes));
                 }
                 else {
-                    const scene = GLTFLoader._GetProperty("#/scene", this._gltf.scenes, this._gltf.scene || 0);
-                    promises.push(this._loadSceneAsync("#/scenes/" + scene._index, scene));
+                    const scene = GLTFLoader._GetProperty(`#/scene`, this._gltf.scenes, this._gltf.scene || 0);
+                    promises.push(this._loadSceneAsync(`#/scenes/${scene._index}`, scene));
                 }
 
                 if (this.compileMaterials) {
@@ -143,9 +144,13 @@ module BABYLON.GLTF2 {
                     promises.push(this._compileShadowGeneratorsAsync());
                 }
 
-                return Promise.all(promises).then(() => {
+                const resultPromise = Promise.all(promises).then(() => {
                     this._state = GLTFLoaderState.Ready;
                     this._startAnimations();
+                });
+
+                resultPromise.then(() => {
+                    this._rootBabylonMesh.setEnabled(true);
 
                     Tools.SetImmediate(() => {
                         if (!this._disposed) {
@@ -155,14 +160,16 @@ module BABYLON.GLTF2 {
                                 this.onCompleteObservable.clear();
                                 this._clear();
                             }).catch(error => {
-                                Tools.Error("glTF Loader: " + error.message);
+                                Tools.Error(`glTF Loader: ${error.message}`);
                                 this._clear();
                             });
                         }
                     });
                 });
+
+                return resultPromise;
             }).catch(error => {
-                Tools.Error("glTF Loader: " + error.message);
+                Tools.Error(`glTF Loader: ${error.message}`);
                 this._clear();
                 throw error;
             });
@@ -188,7 +195,7 @@ module BABYLON.GLTF2 {
                 if (buffers && buffers[0] && !buffers[0].uri) {
                     const binaryBuffer = buffers[0];
                     if (binaryBuffer.byteLength < data.bin.byteLength - 3 || binaryBuffer.byteLength > data.bin.byteLength) {
-                        Tools.Warn("Binary buffer length (" + binaryBuffer.byteLength + ") from JSON does not match chunk length (" + data.bin.byteLength + ")");
+                        Tools.Warn(`Binary buffer length (${binaryBuffer.byteLength}) from JSON does not match chunk length (${data.bin.byteLength})`);
                     }
 
                     binaryBuffer._data = Promise.resolve(data.bin);
@@ -245,6 +252,8 @@ module BABYLON.GLTF2 {
 
         private _createRootNode(): ILoaderNode {
             this._rootBabylonMesh = new Mesh("__root__", this._babylonScene);
+            this._rootBabylonMesh.setEnabled(false);
+
             const rootNode = { _babylonMesh: this._rootBabylonMesh } as ILoaderNode;
             switch (this.coordinateSystemMode) {
                 case GLTFLoaderCoordinateSystemMode.AUTO: {
@@ -260,7 +269,7 @@ module BABYLON.GLTF2 {
                     break;
                 }
                 default: {
-                    throw new Error("Invalid coordinate system mode " + this.coordinateSystemMode);
+                    throw new Error(`Invalid coordinate system mode (${this.coordinateSystemMode})`);
                 }
             }
 
@@ -272,7 +281,7 @@ module BABYLON.GLTF2 {
             const promises = new Array<Promise<void>>();
 
             for (let node of nodes) {
-                promises.push(this._loadNodeAsync("#/nodes/" + node._index, node));
+                promises.push(this._loadNodeAsync(`#/nodes/${node._index}`, node));
             }
 
             promises.push(this._loadAnimationsAsync());
@@ -289,8 +298,8 @@ module BABYLON.GLTF2 {
             const promises = new Array<Promise<void>>();
 
             for (let index of scene.nodes) {
-                const node = GLTFLoader._GetProperty(context + "/nodes/" + index, this._gltf.nodes, index);
-                promises.push(this._loadNodeAsync("#/nodes/" + node._index, node));
+                const node = GLTFLoader._GetProperty(`${context}/nodes/${index}`, this._gltf.nodes, index);
+                promises.push(this._loadNodeAsync(`#/nodes/${node._index}`, node));
             }
 
             promises.push(this._loadAnimationsAsync());
@@ -360,7 +369,7 @@ module BABYLON.GLTF2 {
                     break;
                 }
                 default: {
-                    Tools.Error("Invalid animation start mode " + this.animationStartMode);
+                    Tools.Error(`Invalid animation start mode (${this.animationStartMode})`);
                     return;
                 }
             }
@@ -373,12 +382,12 @@ module BABYLON.GLTF2 {
             }
 
             if (node._babylonMesh) {
-                throw new Error(context + ": Invalid recursive node hierarchy");
+                throw new Error(`${context}: Invalid recursive node hierarchy`);
             }
 
             const promises = new Array<Promise<void>>();
 
-            const babylonMesh = new Mesh(node.name || "node" + node._index, this._babylonScene, node._parent._babylonMesh);
+            const babylonMesh = new Mesh(node.name || `node${node._index}`, this._babylonScene, node._parent._babylonMesh);
             node._babylonMesh = babylonMesh;
 
             node._babylonAnimationTargets = node._babylonAnimationTargets || [];
@@ -387,14 +396,14 @@ module BABYLON.GLTF2 {
             GLTFLoader._LoadTransform(node, babylonMesh);
 
             if (node.mesh != undefined) {
-                const mesh = GLTFLoader._GetProperty(context + "/mesh", this._gltf.meshes, node.mesh);
-                promises.push(this._loadMeshAsync("#/meshes/" + mesh._index, node, mesh));
+                const mesh = GLTFLoader._GetProperty(`${context}/mesh`, this._gltf.meshes, node.mesh);
+                promises.push(this._loadMeshAsync(`#/meshes/${mesh._index}`, node, mesh));
             }
 
             if (node.children) {
                 for (const index of node.children) {
-                    const childNode = GLTFLoader._GetProperty(context + "/children/" + index, this._gltf.nodes, index);
-                    promises.push(this._loadNodeAsync("#/nodes/" + index, childNode));
+                    const childNode = GLTFLoader._GetProperty(`${context}/children/${index}`, this._gltf.nodes, index);
+                    promises.push(this._loadNodeAsync(`#/nodes/${index}`, childNode));
                 }
             }
 
@@ -410,7 +419,7 @@ module BABYLON.GLTF2 {
 
             const primitives = mesh.primitives;
             if (!primitives || primitives.length === 0) {
-                throw new Error(context + ": Primitives are missing");
+                throw new Error(`${context}: Primitives are missing`);
             }
 
             ArrayItem.Assign(primitives);
@@ -419,8 +428,8 @@ module BABYLON.GLTF2 {
             }
 
             if (node.skin != undefined) {
-                const skin = GLTFLoader._GetProperty(context + "/skin", this._gltf.skins, node.skin);
-                promises.push(this._loadSkinAsync("#/skins/" + skin._index, node, mesh, skin));
+                const skin = GLTFLoader._GetProperty(`${context}/skin`, this._gltf.skins, node.skin);
+                promises.push(this._loadSkinAsync(`#/skins/${skin._index}`, node, mesh, skin));
             }
 
             return Promise.all(promises).then(() => {
@@ -436,7 +445,6 @@ module BABYLON.GLTF2 {
             const promises = new Array<Promise<void>>();
 
             const babylonMesh = new Mesh((mesh.name || node._babylonMesh!.name) + "_" + primitive._index, this._babylonScene, node._babylonMesh);
-            babylonMesh.setEnabled(false);
 
             node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
             node._primitiveBabylonMeshes[primitive._index] = babylonMesh;
@@ -452,15 +460,15 @@ module BABYLON.GLTF2 {
                 babylonMesh.material = this._getDefaultMaterial();
             }
             else {
-                const material = GLTFLoader._GetProperty(context + "/material", this._gltf.materials, primitive.material);
-                promises.push(this._loadMaterialAsync("#/materials/" + material._index, material, babylonMesh));
+                const material = GLTFLoader._GetProperty(`${context}/material}`, this._gltf.materials, primitive.material);
+                promises.push(this._loadMaterialAsync(`#/materials/${material._index}`, material, babylonMesh, babylonMaterial => {
+                    babylonMesh.material = babylonMaterial;
+                }));
             }
 
             this.onMeshLoadedObservable.notifyObservers(babylonMesh);
 
-            return Promise.all(promises).then(() => {
-                babylonMesh.setEnabled(true);
-            });
+            return Promise.all(promises).then(() => {});
         }
 
         private _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Promise<VertexData> {
@@ -471,12 +479,12 @@ module BABYLON.GLTF2 {
 
             const attributes = primitive.attributes;
             if (!attributes) {
-                throw new Error(context + ": Attributes are missing");
+                throw new Error(`${context}: Attributes are missing`);
             }
 
             if (primitive.mode != undefined && primitive.mode !== MeshPrimitiveMode.TRIANGLES) {
                 // TODO: handle other primitive modes
-                throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
+                throw new Error(`${context}: Mode (${primitive.mode}) is not currently supported`);
             }
 
             const promises = new Array<Promise<void>>();
@@ -486,7 +494,7 @@ module BABYLON.GLTF2 {
             if (primitive.indices == undefined) {
                 const positionAccessorIndex = attributes["POSITION"];
                 if (positionAccessorIndex != undefined) {
-                    const accessor = GLTFLoader._GetProperty(context + "/attributes/POSITION", this._gltf.accessors, positionAccessorIndex);
+                    const accessor = GLTFLoader._GetProperty(`${context}/attributes/POSITION`, this._gltf.accessors, positionAccessorIndex);
                     babylonVertexData.indices = new Uint32Array(accessor.count);
                     for (let i = 0; i < babylonVertexData.indices.length; i++) {
                         babylonVertexData.indices[i] = i;
@@ -494,8 +502,8 @@ module BABYLON.GLTF2 {
                 }
             }
             else {
-                const indicesAccessor = GLTFLoader._GetProperty(context + "/indices", this._gltf.accessors, primitive.indices);
-                promises.push(this._loadAccessorAsync("#/accessors/" + indicesAccessor._index, indicesAccessor).then(data => {
+                const indicesAccessor = GLTFLoader._GetProperty(`${context}/indices`, this._gltf.accessors, primitive.indices);
+                promises.push(this._loadAccessorAsync(`#/accessors/${indicesAccessor._index}`, indicesAccessor).then(data => {
                     babylonVertexData.indices = data as IndicesArray;
                 }));
             }
@@ -510,9 +518,9 @@ module BABYLON.GLTF2 {
                     babylonMesh._delayInfo.push(kind);
                 }
 
-                const accessor = GLTFLoader._GetProperty(context + "/attributes/" + attribute, this._gltf.accessors, attributes[attribute]);
+                const accessor = GLTFLoader._GetProperty(`${context}/attributes/${attribute}`, this._gltf.accessors, attributes[attribute]);
 
-                promises.push(this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(data => {
+                promises.push(this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
                     let attributeData = GLTFLoader._ConvertToFloat32Array(context, accessor, data);
 
                     if (attribute === "COLOR_0") {
@@ -550,13 +558,13 @@ module BABYLON.GLTF2 {
                 node._numMorphTargets = primitive.targets.length;
             }
             else if (primitive.targets.length !== node._numMorphTargets) {
-                throw new Error(context + ": Primitives do not have the same number of targets");
+                throw new Error(`${context}: Primitives do not have the same number of targets`);
             }
 
             babylonMesh.morphTargetManager = new MorphTargetManager();
             for (let index = 0; index < primitive.targets.length; index++) {
                 const weight = node.weights ? node.weights[index] : mesh.weights ? mesh.weights[index] : 0;
-                babylonMesh.morphTargetManager.addTarget(new MorphTarget("morphTarget" + index, weight));
+                babylonMesh.morphTargetManager.addTarget(new MorphTarget(`morphTarget${index}`, weight));
                 // TODO: tell the target whether it has positions, normals, tangents
             }
         }
@@ -571,7 +579,7 @@ module BABYLON.GLTF2 {
             const morphTargetManager = babylonMesh.morphTargetManager!;
             for (let index = 0; index < morphTargetManager.numTargets; index++) {
                 const babylonMorphTarget = morphTargetManager.getTarget(index);
-                promises.push(this._loadMorphTargetVertexDataAsync(context + "/targets/" + index, babylonVertexData, primitive.targets[index], babylonMorphTarget));
+                promises.push(this._loadMorphTargetVertexDataAsync(`${context}/targets/${index}`, babylonVertexData, primitive.targets[index], babylonMorphTarget));
             }
 
             return Promise.all(promises).then(() => {});
@@ -585,8 +593,8 @@ module BABYLON.GLTF2 {
                     return;
                 }
 
-                const accessor = GLTFLoader._GetProperty(context + "/" + attribute, this._gltf.accessors, attributes[attribute]);
-                promises.push(this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(data => {
+                const accessor = GLTFLoader._GetProperty(`${context}/${attribute}`, this._gltf.accessors, attributes[attribute]);
+                promises.push(this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
                     setData(data as Float32Array);
                 }));
             };
@@ -644,7 +652,7 @@ module BABYLON.GLTF2 {
                         break;
                     }
                     default: {
-                        throw new Error(context + ": Invalid component type " + accessor.componentType);
+                        throw new Error(`${context}: Invalid component type (${accessor.componentType})`);
                     }
                 }
             }
@@ -714,7 +722,7 @@ module BABYLON.GLTF2 {
             // TODO: split into two parts so that bones are created before inverseBindMatricesData is loaded (for compiling materials).
 
             return (skin._loaded = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(inverseBindMatricesData => {
-                const skeletonId = "skeleton" + skin._index;
+                const skeletonId = `skeleton${skin._index}`;
                 const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
                 skin._babylonSkeleton = babylonSkeleton;
                 this._loadBones(context, skin, inverseBindMatricesData);
@@ -727,14 +735,14 @@ module BABYLON.GLTF2 {
                 return Promise.resolve(null);
             }
 
-            const accessor = GLTFLoader._GetProperty(context + "/inverseBindMatrices", this._gltf.accessors, skin.inverseBindMatrices);
-            return this._loadAccessorAsync("#/accessors/" + accessor._index, accessor).then(data => {
+            const accessor = GLTFLoader._GetProperty(`${context}/inverseBindMatrices`, this._gltf.accessors, skin.inverseBindMatrices);
+            return this._loadAccessorAsync(`#/accessors/${accessor._index}`, accessor).then(data => {
                 return data as Float32Array;
             });
         }
 
         private _createBone(node: ILoaderNode, skin: ILoaderSkin, parent: Nullable<Bone>, localMatrix: Matrix, baseMatrix: Matrix, index: number): Bone {
-            const babylonBone = new Bone(node.name || "joint" + node._index, skin._babylonSkeleton!, parent, localMatrix, null, baseMatrix, index);
+            const babylonBone = new Bone(node.name || `joint${node._index}`, skin._babylonSkeleton!, parent, localMatrix, null, baseMatrix, index);
 
             node._babylonAnimationTargets = node._babylonAnimationTargets || [];
             node._babylonAnimationTargets.push(babylonBone);
@@ -745,7 +753,7 @@ module BABYLON.GLTF2 {
         private _loadBones(context: string, skin: ILoaderSkin, inverseBindMatricesData: Nullable<Float32Array>): void {
             const babylonBones: { [index: number]: Bone } = {};
             for (const index of skin.joints) {
-                const node = GLTFLoader._GetProperty(context + "/joints/" + index, this._gltf.nodes, index);
+                const node = GLTFLoader._GetProperty(`${context}/joints/${index}`, this._gltf.nodes, index);
                 this._loadBone(node, skin, inverseBindMatricesData, babylonBones);
             }
         }
@@ -794,14 +802,14 @@ module BABYLON.GLTF2 {
 
             for (let index = 0; index < animations.length; index++) {
                 const animation = animations[index];
-                promises.push(this._loadAnimationAsync("#/animations/" + index, animation));
+                promises.push(this._loadAnimationAsync(`#/animations/${index}`, animation));
             }
 
             return Promise.all(promises).then(() => {});
         }
 
         private _loadAnimationAsync(context: string, animation: ILoaderAnimation): Promise<void> {
-            const babylonAnimationGroup = new AnimationGroup(animation.name || "animation" + animation._index, this._babylonScene);
+            const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation._index}`, this._babylonScene);
             animation._babylonAnimationGroup = babylonAnimationGroup;
 
             const promises = new Array<Promise<void>>();
@@ -810,22 +818,24 @@ module BABYLON.GLTF2 {
             ArrayItem.Assign(animation.samplers);
 
             for (const channel of animation.channels) {
-                promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
+                promises.push(this._loadAnimationChannelAsync(`${context}/channels/${channel._index}`, context, animation, channel, babylonAnimationGroup));
             }
 
+            this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
+
             return Promise.all(promises).then(() => {
                 babylonAnimationGroup.normalize();
             });
         }
 
         private _loadAnimationChannelAsync(context: string, animationContext: string, animation: ILoaderAnimation, channel: ILoaderAnimationChannel, babylonAnimationGroup: AnimationGroup): Promise<void> {
-            const targetNode = GLTFLoader._GetProperty(context + "/target/node", this._gltf.nodes, channel.target.node);
-            if (!targetNode._babylonMesh) {
+            const targetNode = GLTFLoader._GetProperty(`${context}/target/node`, this._gltf.nodes, channel.target.node);
+            if (!targetNode._babylonMesh || targetNode.skin != undefined) {
                 return Promise.resolve();
             }
 
-            const sampler = GLTFLoader._GetProperty(context + "/sampler", animation.samplers, channel.sampler);
-            return this._loadAnimationSamplerAsync(animationContext + "/samplers/" + channel.sampler, sampler).then(data => {
+            const sampler = GLTFLoader._GetProperty(`${context}/sampler`, animation.samplers, channel.sampler);
+            return this._loadAnimationSamplerAsync(`${animationContext}/samplers/${channel.sampler}`, sampler).then(data => {
                 let targetPath: string;
                 let animationType: number;
                 switch (channel.target.path) {
@@ -850,7 +860,7 @@ module BABYLON.GLTF2 {
                         break;
                     }
                     default: {
-                        throw new Error(context + ": Invalid target path " + channel.target.path);
+                        throw new Error(`${context}: Invalid target path (${channel.target.path})`);
                     }
                 }
 
@@ -938,7 +948,7 @@ module BABYLON.GLTF2 {
 
                 if (targetPath === "influence") {
                     for (let targetIndex = 0; targetIndex < targetNode._numMorphTargets!; targetIndex++) {
-                        const animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
+                        const animationName = `${babylonAnimationGroup.name}_channel${babylonAnimationGroup.targetedAnimations.length}`;
                         const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys.map(key => ({
                             frame: key.frame,
@@ -954,7 +964,7 @@ module BABYLON.GLTF2 {
                     }
                 }
                 else {
-                    const animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
+                    const animationName = `${babylonAnimationGroup.name}_channel${babylonAnimationGroup.targetedAnimations.length}`;
                     const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                     babylonAnimation.setKeys(keys);
 
@@ -980,21 +990,21 @@ module BABYLON.GLTF2 {
                     break;
                 }
                 default: {
-                    throw new Error(context + ": Invalid interpolation " + sampler.interpolation);
+                    throw new Error(`${context}: Invalid interpolation (${sampler.interpolation})`);
                 }
             }
 
             let inputData: Nullable<Float32Array>;
             let outputData: Nullable<Float32Array>;
 
-            const inputAccessor = GLTFLoader._GetProperty(context + "/input", this._gltf.accessors, sampler.input);
-            const outputAccessor = GLTFLoader._GetProperty(context + "/output", this._gltf.accessors, sampler.output);
+            const inputAccessor = GLTFLoader._GetProperty(`${context}/input`, this._gltf.accessors, sampler.input);
+            const outputAccessor = GLTFLoader._GetProperty(`${context}/output`, this._gltf.accessors, sampler.output);
 
             sampler._data = Promise.all([
-                this._loadAccessorAsync("#/accessors/" + inputAccessor._index, inputAccessor).then(data => {
+                this._loadAccessorAsync(`#/accessors/${inputAccessor._index}`, inputAccessor).then(data => {
                     inputData = data as Float32Array;
                 }),
-                this._loadAccessorAsync("#/accessors/" + outputAccessor._index, outputAccessor).then(data => {
+                this._loadAccessorAsync(`#/accessors/${outputAccessor._index}`, outputAccessor).then(data => {
                     outputData = data as Float32Array;
                 })
             ]).then(() => {
@@ -1014,7 +1024,7 @@ module BABYLON.GLTF2 {
             }
 
             if (!buffer.uri) {
-                throw new Error(context + ": Uri is missing");
+                throw new Error(`${context}: Uri is missing`);
             }
 
             buffer._data = this._loadUriAsync(context, buffer.uri);
@@ -1027,13 +1037,13 @@ module BABYLON.GLTF2 {
                 return bufferView._data;
             }
 
-            const buffer = GLTFLoader._GetProperty(context + "/buffer", this._gltf.buffers, bufferView.buffer);
-            bufferView._data = this._loadBufferAsync("#/buffers/" + buffer._index, buffer).then(bufferData => {
+            const buffer = GLTFLoader._GetProperty(`${context}/buffer`, this._gltf.buffers, bufferView.buffer);
+            bufferView._data = this._loadBufferAsync(`#/buffers/${buffer._index}`, buffer).then(bufferData => {
                 try {
                     return new Uint8Array(bufferData.buffer, bufferData.byteOffset + (bufferView.byteOffset || 0), bufferView.byteLength);
                 }
                 catch (e) {
-                    throw new Error(context + ": " + e.message);
+                    throw new Error(`${context}: ${e.message}`);
                 }
             });
 
@@ -1042,21 +1052,21 @@ module BABYLON.GLTF2 {
 
         private _loadAccessorAsync(context: string, accessor: ILoaderAccessor): Promise<TypedArray> {
             if (accessor.sparse) {
-                throw new Error(context + ": Sparse accessors are not currently supported");
+                throw new Error(`${context}: Sparse accessors are not currently supported`);
             }
 
             if (accessor._data) {
                 return accessor._data;
             }
 
-            const bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, accessor.bufferView);
-            accessor._data = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView).then(bufferViewData => {
+            const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
+            accessor._data = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView).then(bufferViewData => {
                 const numComponents = GLTFLoader._GetNumComponents(context, accessor.type);
                 const byteOffset = accessor.byteOffset || 0;
                 const byteStride = bufferView.byteStride;
 
                 if (byteStride === 0) {
-                    Tools.Warn(context + ": Byte stride of 0 is not valid");
+                    Tools.Warn(`${context}: Byte stride of 0 is not valid`);
                 }
 
                 try {
@@ -1080,12 +1090,12 @@ module BABYLON.GLTF2 {
                             return this._buildArrayBuffer(Float32Array, bufferViewData, byteOffset, accessor.count, numComponents, byteStride);
                         }
                         default: {
-                            throw new Error(context + ": Invalid component type " + accessor.componentType);
+                            throw new Error(`${context}: Invalid component type (${accessor.componentType})`);
                         }
                     }
                 }
                 catch (e) {
-                    throw new Error(context + ": " + e);
+                    throw new Error(`${context}: ${e.messsage}`);
                 }
             });
 
@@ -1156,13 +1166,13 @@ module BABYLON.GLTF2 {
                 babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
 
                 if (properties.baseColorTexture) {
-                    promises.push(this._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, texture => {
+                    promises.push(this._loadTextureAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
                         babylonMaterial.albedoTexture = texture;
                     }));
                 }
 
                 if (properties.metallicRoughnessTexture) {
-                    promises.push(this._loadTextureAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, texture => {
+                    promises.push(this._loadTextureAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, texture => {
                         babylonMaterial.metallicTexture = texture;
                     }));
 
@@ -1177,8 +1187,8 @@ module BABYLON.GLTF2 {
             return Promise.all(promises).then(() => {});
         }
 
-        public _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh);
+        public _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void> {
+            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
             if (promise) {
                 return promise;
             }
@@ -1186,28 +1196,25 @@ module BABYLON.GLTF2 {
             material._babylonMeshes = material._babylonMeshes || [];
             material._babylonMeshes.push(babylonMesh);
 
-            if (material._loaded) {
-                babylonMesh.material = material._babylonMaterial!;
-                return material._loaded;
-            }
-
-            const promises = new Array<Promise<void>>();
-
-            const babylonMaterial = this._createMaterial(material);
-            material._babylonMaterial = babylonMaterial;
+            if (!material._loaded) {
+                const promises = new Array<Promise<void>>();
 
-            promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-            promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                const babylonMaterial = this._createMaterial(material);
+                material._babylonMaterial = babylonMaterial;
 
-            this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
+                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
 
-            babylonMesh.material = babylonMaterial;
+                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                material._loaded = Promise.all(promises).then(() => {});
+            }
 
-            return (material._loaded = Promise.all(promises).then(() => {}));
+            assign(material._babylonMaterial!);
+            return material._loaded;
         }
 
         public _createMaterial(material: ILoaderMaterial): PBRMaterial {
-            const babylonMaterial = new PBRMaterial(material.name || "material" + material._index, this._babylonScene);
+            const babylonMaterial = new PBRMaterial(material.name || `material${material._index}`, this._babylonScene);
             babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
             return babylonMaterial;
         }
@@ -1224,7 +1231,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.normalTexture) {
-                promises.push(this._loadTextureAsync(context + "/normalTexture", material.normalTexture, texture => {
+                promises.push(this._loadTextureAsync(`${context}/normalTexture`, material.normalTexture, texture => {
                     babylonMaterial.bumpTexture = texture;
                 }));
 
@@ -1236,7 +1243,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.occlusionTexture) {
-                promises.push(this._loadTextureAsync(context + "/occlusionTexture", material.occlusionTexture, texture => {
+                promises.push(this._loadTextureAsync(`${context}/occlusionTexture`, material.occlusionTexture, texture => {
                     babylonMaterial.ambientTexture = texture;
                 }));
 
@@ -1247,7 +1254,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.emissiveTexture) {
-                promises.push(this._loadTextureAsync(context + "/emissiveTexture", material.emissiveTexture, texture => {
+                promises.push(this._loadTextureAsync(`${context}/emissiveTexture`, material.emissiveTexture, texture => {
                     babylonMaterial.emissiveTexture = texture;
                 }));
             }
@@ -1295,19 +1302,19 @@ module BABYLON.GLTF2 {
                     break;
                 }
                 default: {
-                    throw new Error(context + ": Invalid alpha mode " + material.alphaMode);
+                    throw new Error(`${context}: Invalid alpha mode (${material.alphaMode})`);
                 }
             }
         }
 
         public _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void> {
-            const texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
-            context = "#/textures/" + textureInfo.index;
+            const texture = GLTFLoader._GetProperty(`${context}/index`, this._gltf.textures, textureInfo.index);
+            context = `#/textures/${textureInfo.index}`;
 
             const promises = new Array<Promise<void>>();
 
-            const sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(context + "/sampler", this._gltf.samplers, texture.sampler));
-            const samplerData = this._loadSampler("#/samplers/" + sampler._index, sampler);
+            const sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(`${context}/sampler`, this._gltf.samplers, texture.sampler));
+            const samplerData = this._loadSampler(`#/samplers/${sampler._index}`, sampler);
 
             const deferred = new Deferred<void>();
             const babylonTexture = new Texture(null, this._babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, () => {
@@ -1316,18 +1323,18 @@ module BABYLON.GLTF2 {
                 }
             }, (message, exception) => {
                 if (!this._disposed) {
-                    deferred.reject(new Error(context + ": " + (exception && exception.message) ? exception.message : message || "Failed to load texture"));
+                    deferred.reject(new Error(`${context}: ${(exception && exception.message) ? exception.message : message || "Failed to load texture"}`));
                 }
             });
             promises.push(deferred.promise);
 
-            babylonTexture.name = texture.name || "texture" + texture._index;
+            babylonTexture.name = texture.name || `texture${texture._index}`;
             babylonTexture.wrapU = samplerData.wrapU;
             babylonTexture.wrapV = samplerData.wrapV;
             babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
 
-            const image = GLTFLoader._GetProperty(context + "/source", this._gltf.images, texture.source);
-            promises.push(this._loadImageAsync("#/images/" + image._index, image).then(objectURL => {
+            const image = GLTFLoader._GetProperty(`${context}/source`, this._gltf.images, texture.source);
+            promises.push(this._loadImageAsync(`#/images/${image._index}`, image).then(objectURL => {
                 babylonTexture.updateURL(objectURL);
             }));
 
@@ -1360,8 +1367,8 @@ module BABYLON.GLTF2 {
                 promise = this._loadUriAsync(context, image.uri);
             }
             else {
-                const bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, image.bufferView);
-                promise = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
+                const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, image.bufferView);
+                promise = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView);
             }
 
             image._objectURL = promise.then(data => {
@@ -1378,7 +1385,7 @@ module BABYLON.GLTF2 {
             }
 
             if (!GLTFLoader._ValidateUri(uri)) {
-                throw new Error(context + ": Uri '" + uri + "' is invalid");
+                throw new Error(`${context}: Uri '${uri}' is invalid`);
             }
 
             if (Tools.IsBase64(uri)) {
@@ -1406,7 +1413,7 @@ module BABYLON.GLTF2 {
                     }
                 }, this._babylonScene.database, true, (request, exception) => {
                     if (!this._disposed) {
-                        reject(new LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request));
+                        reject(new LoadFileError(`${context}: Failed to load '${uri}'${request ? ": " + request.status + " " + request.statusText : ""}`, request));
                     }
                 }) as IFileRequestInfo;
 
@@ -1437,7 +1444,7 @@ module BABYLON.GLTF2 {
 
         public static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T {
             if (!array || index == undefined || !array[index]) {
-                throw new Error(context + ": Failed to find index " + index);
+                throw new Error(`${context}: Failed to find index (${index})`);
             }
 
             return array[index];
@@ -1452,7 +1459,7 @@ module BABYLON.GLTF2 {
                 case TextureWrapMode.MIRRORED_REPEAT: return Texture.MIRROR_ADDRESSMODE;
                 case TextureWrapMode.REPEAT: return Texture.WRAP_ADDRESSMODE;
                 default:
-                    Tools.Warn(context + ": Invalid texture wrap mode " + mode);
+                    Tools.Warn(`${context}: Invalid texture wrap mode (${mode})`);
                     return Texture.WRAP_ADDRESSMODE;
             }
         }
@@ -1471,13 +1478,13 @@ module BABYLON.GLTF2 {
                     case TextureMinFilter.NEAREST_MIPMAP_LINEAR: return Texture.LINEAR_NEAREST_MIPLINEAR;
                     case TextureMinFilter.LINEAR_MIPMAP_LINEAR: return Texture.LINEAR_LINEAR_MIPLINEAR;
                     default:
-                        Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                        Tools.Warn(`${context}: Invalid texture minification filter (${minFilter})`);
                         return Texture.LINEAR_LINEAR_MIPLINEAR;
                 }
             }
             else {
                 if (magFilter !== TextureMagFilter.NEAREST) {
-                    Tools.Warn(context + ": Invalid texture magnification filter " + magFilter);
+                    Tools.Warn(`${context}: Invalid texture magnification filter (${magFilter})`);
                 }
 
                 switch (minFilter) {
@@ -1488,7 +1495,7 @@ module BABYLON.GLTF2 {
                     case TextureMinFilter.NEAREST_MIPMAP_LINEAR: return Texture.NEAREST_NEAREST_MIPLINEAR;
                     case TextureMinFilter.LINEAR_MIPMAP_LINEAR: return Texture.NEAREST_LINEAR_MIPLINEAR;
                     default:
-                        Tools.Warn(context + ": Invalid texture minification filter " + minFilter);
+                        Tools.Warn(`${context}: Invalid texture minification filter (${minFilter})`);
                         return Texture.NEAREST_NEAREST_MIPNEAREST;
                 }
             }
@@ -1505,7 +1512,7 @@ module BABYLON.GLTF2 {
                 case "MAT4": return 16;
             }
 
-            throw new Error(context + ": Invalid type " + type);
+            throw new Error(`${context}: Invalid type (${type})`);
         }
 
         private static _ValidateUri(uri: string): boolean {

+ 4 - 4
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -27,7 +27,7 @@ module BABYLON.GLTF2 {
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> { return null; }
 
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>> { return null; }
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> { return null; }
 
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>> { return null; }
@@ -51,7 +51,7 @@ module BABYLON.GLTF2 {
             delete extensions[this.name];
 
             try {
-                return actionAsync(context + "/extensions/" + this.name, extension);
+                return actionAsync(`${context}/extensions/${this.name}`, extension);
             }
             finally {
                 // Restore the extension after executing the action.
@@ -75,8 +75,8 @@ module BABYLON.GLTF2 {
         }
 
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>> {
-            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, babylonMesh));
+        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, babylonMesh, assign));
         }
 
         /** Helper method called by the loader to allow extensions to override loading uris. */

+ 28 - 2
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -63,6 +63,7 @@ module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
+        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
@@ -150,7 +151,7 @@ module BABYLON {
         public readonly onTextureLoadedObservable = new Observable<BaseTexture>();
 
         private _onTextureLoadedObserver: Nullable<Observer<BaseTexture>>;
-        public set onTextureLoaded(callback: (Texture: BaseTexture) => void) {
+        public set onTextureLoaded(callback: (texture: BaseTexture) => void) {
             if (this._onTextureLoadedObserver) {
                 this.onTextureLoadedObservable.remove(this._onTextureLoadedObserver);
             }
@@ -163,7 +164,7 @@ module BABYLON {
         public readonly onMaterialLoadedObservable = new Observable<Material>();
 
         private _onMaterialLoadedObserver: Nullable<Observer<Material>>;
-        public set onMaterialLoaded(callback: (Material: Material) => void) {
+        public set onMaterialLoaded(callback: (material: Material) => void) {
             if (this._onMaterialLoadedObserver) {
                 this.onMaterialLoadedObservable.remove(this._onMaterialLoadedObserver);
             }
@@ -171,6 +172,19 @@ module BABYLON {
         }
 
         /**
+         * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
+         */
+        public readonly onAnimationGroupLoadedObservable = new Observable<AnimationGroup>();
+
+        private _onAnimationGroupLoadedObserver: Nullable<Observer<AnimationGroup>>;
+        public set onAnimationGroupLoaded(callback: (animationGroup: AnimationGroup) => void) {
+            if (this._onAnimationGroupLoadedObserver) {
+                this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
+            }
+            this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
+        }
+
+        /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -213,6 +227,18 @@ module BABYLON {
         }
 
         /**
+         * Gets a promise that resolves when the asset is completely loaded.
+         * @returns A promise that resolves when the asset is completely loaded.
+         */
+        public whenCompleteAsync(): Promise<void> {
+            return new Promise(resolve => {
+                this.onCompleteObservable.add(() => {
+                    resolve();
+                }, undefined, undefined, undefined, true);
+            });
+        }
+
+        /**
          * The loader state or null if not active.
          */
         public get loaderState(): Nullable<GLTFLoaderState> {

Разница между файлами не показана из-за своего большого размера
+ 35 - 1972
materialsLibrary/src/custom/babylon.customMaterial.ts


+ 0 - 4
materialsLibrary/src/fur/fur.vertex.fx

@@ -115,12 +115,8 @@ float r = Rand(position);
 	#endif
 	
 	#ifdef NORMAL
-	#ifdef HIGHLEVEL
-	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)) * aNormal);
-	#else
 	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
 	#endif
-	#endif
 	
 //END FUR
 	gl_Position = viewProjection * finalWorld * vec4(newPosition, 1.0);

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "3.2.0-alphaA",
+    "version": "3.2.0-alphaB",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 4 - 4
serializers/src/glTF/2.0/babylon.glTFExporter.ts

@@ -719,13 +719,13 @@ module BABYLON.GLTF2 {
                             else {
                                 Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
                             }
-                            if (materialIndex != null) {
-                                if (uvCoordsPresent || !_GLTFMaterial.HasTexturesPresent(this.materials[materialIndex])) {
+                            if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
+                                if (uvCoordsPresent || !_GLTFMaterial._HasTexturesPresent(this.materials[materialIndex])) {
                                     meshPrimitive.material = materialIndex;
                                 }
                                 else {
                                     // If no texture coordinate information is present, make a copy of the material without the textures to be glTF compliant.
-                                    const newMat = _GLTFMaterial.StripTexturesFromMaterial(this.materials[materialIndex]);
+                                    const newMat = _GLTFMaterial._StripTexturesFromMaterial(this.materials[materialIndex]);
                                     this.materials.push(newMat);
                                     meshPrimitive.material = this.materials.length - 1;
                                 }
@@ -750,7 +750,7 @@ module BABYLON.GLTF2 {
                 const babylonMeshes = babylonScene.meshes;
                 const scene = { nodes: new Array<number>() };
 
-                _GLTFMaterial.ConvertMaterialsToGLTF(babylonScene.materials, ImageMimeType.JPEG, this.images, this.textures, this.materials, this.imageData, true);
+                _GLTFMaterial._ConvertMaterialsToGLTF(babylonScene.materials, ImageMimeType.PNG, this.images, this.textures, this.materials, this.imageData, true);
                 const result = this.createNodeMap(babylonScene, byteOffset);
                 this.nodeMap = result.nodeMap;
                 this.totalByteLength = result.byteOffset;

+ 468 - 159
serializers/src/glTF/2.0/babylon.glTFMaterial.ts

@@ -1,6 +1,50 @@
 /// <reference path="../../../../dist/babylon.glTF2Interface.d.ts"/>
 
 module BABYLON.GLTF2 {
+    /** 
+     * Interface for storing specular glossiness factors.
+    */
+    interface _IPBRSpecularGlossiness {
+        /** 
+         * Represents the linear diffuse factors of the material.
+        */
+        diffuseColor: BABYLON.Color3;
+        /** 
+         * Represents the linear specular factors of the material.
+        */
+        specularColor: BABYLON.Color3;
+        /** 
+         * Represents the smoothness of the material.
+        */
+        glossiness: number;
+    }
+
+    /** 
+     * Interface for storing metallic roughness factors.
+    */
+    interface _IPBRMetallicRoughness {
+        /** 
+         * Represents the albedo color of the material.
+        */
+        baseColor: BABYLON.Color3;
+        /** 
+         * Represents the metallness of the material.
+        */
+        metallic: number;
+        /** 
+         * Represents the roughness of the material.
+        */
+        roughness: number;
+        /** 
+         * The metallic roughness texture as a base64 string.
+        */
+        metallicRoughnessTextureBase64?: Nullable<string>;
+        /** 
+         * The base color texture as a base64 string.
+        */
+        baseColorTextureBase64?: Nullable<string>;
+    }
+
     /**
      * Utility methods for working with glTF material conversion properties.  This class should only be used internally.
      */
@@ -8,39 +52,54 @@ module BABYLON.GLTF2 {
         /**
          * Represents the dielectric specular values for R, G and B.
          */
-        private static readonly dielectricSpecular: Color3 = new Color3(0.04, 0.04, 0.04);
+        private static readonly _dielectricSpecular: Color3 = new Color3(0.04, 0.04, 0.04);
 
         /**
          * Allows the maximum specular power to be defined for material calculations.
          */
-        private static maxSpecularPower = 1024;
+        private static _maxSpecularPower = 1024;
 
-        private static epsilon = 1e-6;
+        /**
+         * Numeric tolerance value
+         */
+        private static _epsilon = 1e-6;
+
+        /**
+         * Specifies if two colors are approximately equal in value.
+         * @param color1 - first color to compare to.
+         * @param color2 - second color to compare to.
+         * @param epsilon - threshold value
+         */
+        private static FuzzyEquals(color1: Color3, color2: Color3, epsilon: number): boolean {
+            return Scalar.WithinEpsilon(color1.r, color2.r, epsilon) &&
+                Scalar.WithinEpsilon(color1.g, color2.g, epsilon) &&
+                Scalar.WithinEpsilon(color1.b, color2.b, epsilon);
+        }
 
         /**
          * Gets the materials from a Babylon scene and converts them to glTF materials.
-         * @param scene
-         * @param mimeType
-         * @param images
-         * @param textures
-         * @param materials
-         * @param imageData
-         * @param hasTextureCoords
+         * @param scene - babylonjs scene.
+         * @param mimeType - texture mime type.
+         * @param images - array of images.
+         * @param textures - array of textures.
+         * @param materials - array of materials.
+         * @param imageData - mapping of texture names to base64 textures
+         * @param hasTextureCoords - specifies if texture coordinates are present on the material.
          */
-        public static ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
+        public static _ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
             for (let i = 0; i < babylonMaterials.length; ++i) {
                 const babylonMaterial = babylonMaterials[i];
                 if (babylonMaterial instanceof StandardMaterial) {
-                    _GLTFMaterial.ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                    _GLTFMaterial._ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                 }
                 else if (babylonMaterial instanceof PBRMetallicRoughnessMaterial) {
-                    _GLTFMaterial.ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                    _GLTFMaterial._ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                 }
                 else if (babylonMaterial instanceof PBRMaterial) {
-                    _GLTFMaterial.ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
+                    _GLTFMaterial._ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, materials, imageData, hasTextureCoords);
                 }
                 else {
-                    Tools.Error("Unsupported material type: " + babylonMaterial.name);
+                    throw new Error("Unsupported material type: " + babylonMaterial.name);
                 }
             }
         }
@@ -50,7 +109,7 @@ module BABYLON.GLTF2 {
          * @param originalMaterial - original glTF material.
          * @returns glTF material without texture parameters
          */
-        public static StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial {
+        public static _StripTexturesFromMaterial(originalMaterial: IMaterial): IMaterial {
             let newMaterial: IMaterial = {};
             if (originalMaterial) {
                 newMaterial.name = originalMaterial.name;
@@ -74,7 +133,7 @@ module BABYLON.GLTF2 {
          * @param material - glTF Material.
          * @returns boolean specifying if texture parameters are present
          */
-        public static HasTexturesPresent(material: IMaterial): boolean {
+        public static _HasTexturesPresent(material: IMaterial): boolean {
             if (material.emissiveTexture || material.normalTexture || material.occlusionTexture) {
                 return true;
             }
@@ -93,7 +152,7 @@ module BABYLON.GLTF2 {
          * @param babylonStandardMaterial 
          * @returns - glTF Metallic Roughness Material representation
          */
-        public static ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness {
+        public static _ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial: StandardMaterial): IMaterialPbrMetallicRoughness {
             const P0 = new BABYLON.Vector2(0, 1);
             const P1 = new BABYLON.Vector2(0, 0.1);
             const P2 = new BABYLON.Vector2(0, 0.1);
@@ -108,7 +167,7 @@ module BABYLON.GLTF2 {
              * @param p3 - fourth control point.
              * @returns - number result of cubic bezier curve at the specified t.
              */
-            function cubicBezierCurve(t: number, p0: number, p1: number, p2: number, p3: number): number {
+            function _cubicBezierCurve(t: number, p0: number, p1: number, p2: number, p3: number): number {
                 return (
                     (1 - t) * (1 - t) * (1 - t) * p0 +
                     3 * (1 - t) * (1 - t) * t * p1 +
@@ -124,16 +183,16 @@ module BABYLON.GLTF2 {
              * @param specularPower - specular power of standard material.
              * @returns - Number representing the roughness value.
              */
-            function solveForRoughness(specularPower: number): number {
+            function _solveForRoughness(specularPower: number): number {
                 var t = Math.pow(specularPower / P3.x, 0.333333);
-                return cubicBezierCurve(t, P0.y, P1.y, P2.y, P3.y);
+                return _cubicBezierCurve(t, P0.y, P1.y, P2.y, P3.y);
             }
 
             let diffuse = babylonStandardMaterial.diffuseColor.toLinearSpace().scale(0.5);
             let opacity = babylonStandardMaterial.alpha;
-            let specularPower = Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this.maxSpecularPower);
+            let specularPower = Scalar.Clamp(babylonStandardMaterial.specularPower, 0, this._maxSpecularPower);
 
-            const roughness = solveForRoughness(specularPower);
+            const roughness = _solveForRoughness(specularPower);
 
             const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {
                 baseColorFactor: [
@@ -156,15 +215,15 @@ module BABYLON.GLTF2 {
          * @param oneMinusSpecularStrength - one minus the specular strength
          * @returns - metallic value
          */
-        public static SolveMetallic(diffuse: number, specular: number, oneMinusSpecularStrength: number): number {
-            if (specular < _GLTFMaterial.dielectricSpecular.r) {
-                _GLTFMaterial.dielectricSpecular
+        public static _SolveMetallic(diffuse: number, specular: number, oneMinusSpecularStrength: number): number {
+            if (specular < _GLTFMaterial._dielectricSpecular.r) {
+                _GLTFMaterial._dielectricSpecular
                 return 0;
             }
 
-            const a = _GLTFMaterial.dielectricSpecular.r;
-            const b = diffuse * oneMinusSpecularStrength / (1.0 - _GLTFMaterial.dielectricSpecular.r) + specular - 2.0 * _GLTFMaterial.dielectricSpecular.r;
-            const c = _GLTFMaterial.dielectricSpecular.r - specular;
+            const a = _GLTFMaterial._dielectricSpecular.r;
+            const b = diffuse * oneMinusSpecularStrength / (1.0 - _GLTFMaterial._dielectricSpecular.r) + specular - 2.0 * _GLTFMaterial._dielectricSpecular.r;
+            const c = _GLTFMaterial._dielectricSpecular.r - specular;
             const D = b * b - 4.0 * a * c;
             return BABYLON.Scalar.Clamp((-b + Math.sqrt(D)) / (2.0 * a), 0, 1);
         }
@@ -174,7 +233,7 @@ module BABYLON.GLTF2 {
          * @param babylonMaterial - Babylon Material
          * @returns - The Babylon alpha mode value
          */
-        public static GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode {
+        public static _GetAlphaMode(babylonMaterial: Material): MaterialAlphaMode {
             if (babylonMaterial instanceof StandardMaterial) {
                 const babylonStandardMaterial = babylonMaterial as StandardMaterial;
                 if ((babylonStandardMaterial.alpha != 1.0) ||
@@ -245,12 +304,12 @@ module BABYLON.GLTF2 {
          * @param imageData - map of image file name to data.
          * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
          */
-        public static ConvertStandardMaterial(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
+        public static _ConvertStandardMaterial(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
             Tools.Warn(babylonStandardMaterial.name + ": Standard Material is currently not fully supported/implemented in glTF serializer");
-            const glTFPbrMetallicRoughness = _GLTFMaterial.ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
+            const glTFPbrMetallicRoughness = _GLTFMaterial._ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
 
             const glTFMaterial: IMaterial = { name: babylonStandardMaterial.name };
-            if (babylonStandardMaterial.backFaceCulling) {
+            if (babylonStandardMaterial.backFaceCulling != null && !babylonStandardMaterial.backFaceCulling) {
                 if (!babylonStandardMaterial.twoSidedLighting) {
                     Tools.Warn(babylonStandardMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
                 }
@@ -258,26 +317,26 @@ module BABYLON.GLTF2 {
             }
             if (hasTextureCoords) {
                 if (babylonStandardMaterial.diffuseTexture) {
-                    const glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, imageData);
                     if (glTFTexture != null) {
                         glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                     }
                 }
                 if (babylonStandardMaterial.bumpTexture) {
-                    const glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData)
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, imageData)
                     if (glTFTexture) {
                         glTFMaterial.normalTexture = glTFTexture;
                     }
                 }
                 if (babylonStandardMaterial.emissiveTexture) {
-                    const glTFEmissiveTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, imageData)
+                    const glTFEmissiveTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, imageData)
                     if (glTFEmissiveTexture) {
                         glTFMaterial.emissiveTexture = glTFEmissiveTexture;
                     }
                     glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
                 }
                 if (babylonStandardMaterial.ambientTexture) {
-                    const glTFTexture = _GLTFMaterial.ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, imageData);
                     if (glTFTexture) {
                         const occlusionTexture: IMaterialOcclusionTextureInfo = {
                             index: glTFTexture.index
@@ -297,7 +356,7 @@ module BABYLON.GLTF2 {
                     Tools.Warn(babylonStandardMaterial.name + ": glTF 2.0 does not support alpha mode: " + babylonStandardMaterial.alphaMode.toString());
                 }
             }
-            if (babylonStandardMaterial.emissiveColor) {
+            if (babylonStandardMaterial.emissiveColor && !this.FuzzyEquals(babylonStandardMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
                 glTFMaterial.emissiveFactor = babylonStandardMaterial.emissiveColor.asArray();
             }
 
@@ -316,7 +375,7 @@ module BABYLON.GLTF2 {
          * @param imageData - map of image file name to data.
          * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
          */
-        public static ConvertPBRMetallicRoughnessMaterial(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
+        public static _ConvertPBRMetallicRoughnessMaterial(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
             const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {};
 
             if (babylonPBRMetalRoughMaterial.baseColor) {
@@ -328,10 +387,10 @@ module BABYLON.GLTF2 {
                 ];
             }
 
-            if (babylonPBRMetalRoughMaterial.metallic != null) {
+            if (babylonPBRMetalRoughMaterial.metallic != null && babylonPBRMetalRoughMaterial.metallic !== 1) {
                 glTFPbrMetallicRoughness.metallicFactor = babylonPBRMetalRoughMaterial.metallic;
             }
-            if (babylonPBRMetalRoughMaterial.roughness != null) {
+            if (babylonPBRMetalRoughMaterial.roughness != null && babylonPBRMetalRoughMaterial.roughness !== 1) {
                 glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMetalRoughMaterial.roughness;
             }
 
@@ -344,19 +403,19 @@ module BABYLON.GLTF2 {
 
             if (hasTextureCoords) {
                 if (babylonPBRMetalRoughMaterial.baseTexture != null) {
-                    const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, imageData);
                     if (glTFTexture != null) {
                         glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                     }
                 }
                 if (babylonPBRMetalRoughMaterial.normalTexture) {
-                    const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, imageData);
                     if (glTFTexture) {
                         glTFMaterial.normalTexture = glTFTexture;
                     }
                 }
                 if (babylonPBRMetalRoughMaterial.occlusionTexture) {
-                    const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, imageData);
                     if (glTFTexture) {
                         glTFMaterial.occlusionTexture = glTFTexture;
                         if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
@@ -365,18 +424,18 @@ module BABYLON.GLTF2 {
                     }
                 }
                 if (babylonPBRMetalRoughMaterial.emissiveTexture) {
-                    const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, imageData);
                     if (glTFTexture != null) {
                         glTFMaterial.emissiveTexture = glTFTexture;
                     }
                 }
             }
 
-            if (babylonPBRMetalRoughMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
+            if (this.FuzzyEquals(babylonPBRMetalRoughMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
                 glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
             }
             if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
-                const alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMetalRoughMaterial);
+                const alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMetalRoughMaterial);
 
                 if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
                     glTFMaterial.alphaMode = alphaMode;
@@ -392,12 +451,255 @@ module BABYLON.GLTF2 {
         }
 
         /**
+         * Converts an image typed array buffer to a base64 image.
+         * @param buffer - typed array buffer.
+         * @param width - width of the image.
+         * @param height - height of the image.
+         * @param mimeType - mimetype of the image.
+         * @returns - base64 image string.
+         */
+        private static _CreateBase64FromCanvas(buffer: Uint8ClampedArray, width: number, height: number, mimeType: ImageMimeType): string {
+            const imageCanvas = document.createElement('canvas');
+            imageCanvas.id = "WriteCanvas";
+            const ctx = imageCanvas.getContext('2d') as CanvasRenderingContext2D;
+            imageCanvas.width = width;
+            imageCanvas.height = height;
+
+            const imgData = ctx.createImageData(width, height);
+
+            imgData.data.set(buffer);
+            ctx.putImageData(imgData, 0, 0);
+
+            return imageCanvas.toDataURL(mimeType);
+        }
+
+        /**
+         * Generates a white texture based on the specified width and height.
+         * @param width - width of the texture in pixels.
+         * @param height - height of the texture in pixels.
+         * @param scene - babylonjs scene.
+         * @returns - white texture.
+         */
+        private static _CreateWhiteTexture(width: number, height: number, scene: Scene): Texture {
+            const data = new Uint8Array(width * height * 4);
+
+            for (let i = 0; i < data.length; ++i) {
+                data[i] = 255;
+            }
+
+            const rawTexture = RawTexture.CreateRGBATexture(data, width, height, scene);
+
+            return rawTexture;
+        }
+
+        /**
+         * Resizes the two source textures to the same dimensions.  If a texture is null, a default white texture is generated.  If both textures are null, returns null.
+         * @param texture1 - first texture to resize.
+         * @param texture2 - second texture to resize.
+         * @param scene - babylonjs scene.
+         * @returns resized textures or null.
+         */
+        private static _ResizeTexturesToSameDimensions(texture1: BaseTexture, texture2: BaseTexture, scene: Scene): { "texture1": BaseTexture, "texture2": BaseTexture } {
+            let texture1Size = texture1 ? texture1.getSize() : { width: 0, height: 0 };
+            let texture2Size = texture2 ? texture2.getSize() : { width: 0, height: 0 };
+            let resizedTexture1;
+            let resizedTexture2;
+
+            if (texture1Size.width < texture2Size.width) {
+                if (texture1) {
+                    resizedTexture1 = TextureTools.CreateResizedCopy(texture1 as Texture, texture2Size.width, texture2Size.height, true);
+                }
+                else {
+                    resizedTexture1 = this._CreateWhiteTexture(texture2Size.width, texture2Size.height, scene);
+                }
+                resizedTexture2 = texture2;
+            }
+            else if (texture1Size.width > texture2Size.width) {
+                if (texture2) {
+                    resizedTexture2 = TextureTools.CreateResizedCopy(texture2 as Texture, texture1Size.width, texture1Size.height, true);
+                }
+                else {
+                    resizedTexture2 = this._CreateWhiteTexture(texture1Size.width, texture1Size.height, scene);
+                }
+                resizedTexture1 = texture1;
+            }
+            else {
+                resizedTexture1 = texture1;
+                resizedTexture2 = texture2;
+            }
+
+            return {
+                "texture1": resizedTexture1,
+                "texture2": resizedTexture2
+            }
+        }
+
+        /**
+         * Convert Specular Glossiness Textures to Metallic Roughness.
          * See link below for info on the material conversions from PBR Metallic/Roughness and Specular/Glossiness
          * @link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+         * @param diffuseTexture - texture used to store diffuse information.
+         * @param specularGlossinessTexture - texture used to store specular and glossiness information.
+         * @param factors - specular glossiness material factors.
+         * @param mimeType - the mime type to use for the texture.
+         * @returns pbr metallic roughness interface or null.
+         */
+        private static _ConvertSpecularGlossinessTexturesToMetallicRoughness(diffuseTexture: BaseTexture, specularGlossinessTexture: BaseTexture, factors: _IPBRSpecularGlossiness, mimeType: ImageMimeType): Nullable<_IPBRMetallicRoughness> {
+            if (!(diffuseTexture || specularGlossinessTexture)) {
+                return null;
+            }
+
+            const scene = diffuseTexture ? diffuseTexture.getScene() : specularGlossinessTexture.getScene();
+            if (!scene) {
+                throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Scene from textures is missing!");
+            }
+
+            const resizedTextures = this._ResizeTexturesToSameDimensions(diffuseTexture, specularGlossinessTexture, scene);
+
+            let diffuseSize = resizedTextures.texture1.getSize();
+
+            let diffuseBuffer: Uint8Array;
+            let specularGlossinessBuffer: Uint8Array;
+
+            const width = diffuseSize.width;
+            const height = diffuseSize.height;
+
+            let pixels = (resizedTextures.texture1.readPixels());
+            if (pixels instanceof Uint8Array) {
+                diffuseBuffer = (resizedTextures.texture1.readPixels()) as Uint8Array;
+            }
+            else {
+                throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture1.name);
+            }
+            pixels = resizedTextures.texture2.readPixels();
+
+            if (pixels instanceof Uint8Array) {
+                specularGlossinessBuffer = (resizedTextures.texture2.readPixels()) as Uint8Array;
+            }
+            else {
+                throw new Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture2.name);
+            }
+
+            const byteLength = specularGlossinessBuffer.byteLength;
+
+            const metallicRoughnessBuffer = new Uint8Array(byteLength);
+            const baseColorBuffer = new Uint8Array(byteLength);
+
+            const strideSize = 4;
+            const maxBaseColor = Color3.Black();
+            let maxMetallic = 0;
+            let maxRoughness = 0;
+
+            for (let h = 0; h < height; ++h) {
+                for (let w = 0; w < width; ++w) {
+                    const offset = (width * h + w) * strideSize;
+
+                    const diffuseColor = Color3.FromInts(diffuseBuffer[offset], diffuseBuffer[offset + 1], diffuseBuffer[offset + 2]).multiply(factors.diffuseColor);
+                    const specularColor = Color3.FromInts(specularGlossinessBuffer[offset], specularGlossinessBuffer[offset + 1], specularGlossinessBuffer[offset + 2]).multiply(factors.specularColor);
+                    const glossiness = (specularGlossinessBuffer[offset + 3] / 255) * factors.glossiness;
+
+                    const specularGlossiness: _IPBRSpecularGlossiness = {
+                        diffuseColor: diffuseColor,
+                        specularColor: specularColor,
+                        glossiness: glossiness
+                    };
+
+                    const metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+                    maxBaseColor.r = Math.max(maxBaseColor.r, metallicRoughness.baseColor.r);
+                    maxBaseColor.g = Math.max(maxBaseColor.g, metallicRoughness.baseColor.g);
+                    maxBaseColor.b = Math.max(maxBaseColor.b, metallicRoughness.baseColor.b);
+                    maxMetallic = Math.max(maxMetallic, metallicRoughness.metallic);
+                    maxRoughness = Math.max(maxRoughness, metallicRoughness.roughness);
+
+                    baseColorBuffer[offset] = metallicRoughness.baseColor.r * 255;
+                    baseColorBuffer[offset + 1] = metallicRoughness.baseColor.g * 255;
+                    baseColorBuffer[offset + 2] = metallicRoughness.baseColor.b * 255;
+                    baseColorBuffer[offset + 3] = resizedTextures.texture1.hasAlpha ? diffuseBuffer[offset + 3] : 255;
+
+                    metallicRoughnessBuffer[offset] = 255;
+                    metallicRoughnessBuffer[offset + 1] = metallicRoughness.roughness * 255;
+                    metallicRoughnessBuffer[offset + 2] = metallicRoughness.metallic * 255;
+                    metallicRoughnessBuffer[offset + 3] = 255;
+                }
+            }
+
+            // Retrieves the metallic roughness factors from the maximum texture values.
+            const metallicRoughnessFactors: _IPBRMetallicRoughness = {
+                baseColor: maxBaseColor,
+                metallic: maxMetallic,
+                roughness: maxRoughness
+            };
+
+            let writeOutMetallicRoughnessTexture = false;
+            let writeOutBaseColorTexture = false;
+
+            for (let h = 0; h < height; ++h) {
+                for (let w = 0; w < width; ++w) {
+                    const destinationOffset = (width * h + w) * strideSize;
+
+                    baseColorBuffer[destinationOffset] /= metallicRoughnessFactors.baseColor.r > this._epsilon ? metallicRoughnessFactors.baseColor.r : 1;
+                    baseColorBuffer[destinationOffset + 1] /= metallicRoughnessFactors.baseColor.g > this._epsilon ? metallicRoughnessFactors.baseColor.g : 1;
+                    baseColorBuffer[destinationOffset + 2] /= metallicRoughnessFactors.baseColor.b > this._epsilon ? metallicRoughnessFactors.baseColor.b : 1;
+
+                    const baseColorPixel = Color3.FromArray([baseColorBuffer[destinationOffset], baseColorBuffer[destinationOffset + 1], baseColorBuffer[destinationOffset + 2]]);
+
+                    if (!this.FuzzyEquals(baseColorPixel, Color3.White(), this._epsilon)) {
+                        writeOutBaseColorTexture = true;
+                    }
+
+                    metallicRoughnessBuffer[destinationOffset + 1] /= metallicRoughnessFactors.roughness > this._epsilon ? metallicRoughnessFactors.roughness : 1;
+                    metallicRoughnessBuffer[destinationOffset + 2] /= metallicRoughnessFactors.metallic > this._epsilon ? metallicRoughnessFactors.metallic : 1;
+
+                    const metallicRoughnessPixel = Color3.FromArray([metallicRoughnessBuffer[destinationOffset], metallicRoughnessBuffer[destinationOffset + 1], metallicRoughnessBuffer[destinationOffset + 2]]);
+
+                    if (!this.FuzzyEquals(metallicRoughnessPixel, Color3.White(), this._epsilon)) {
+                        writeOutMetallicRoughnessTexture = true;
+                    }
+                }
+            }
+
+            if (writeOutMetallicRoughnessTexture) {
+                const metallicRoughnessBase64 = this._CreateBase64FromCanvas(metallicRoughnessBuffer, width, height, mimeType);
+                metallicRoughnessFactors.metallicRoughnessTextureBase64 = metallicRoughnessBase64;
+            }
+            if (writeOutBaseColorTexture) {
+                const baseColorBase64 = this._CreateBase64FromCanvas(baseColorBuffer, width, height, mimeType);
+                metallicRoughnessFactors.baseColorTextureBase64 = baseColorBase64;
+            }
+
+            return metallicRoughnessFactors;
+        }
+
+        /**
+         * Converts specular glossiness material properties to metallic roughness.
+         * @param specularGlossiness - interface with specular glossiness material properties.
+         * @returns - interface with metallic roughness material properties.
+         */
+        private static _ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness: _IPBRSpecularGlossiness): _IPBRMetallicRoughness {
+            const diffusePerceivedBrightness = _GLTFMaterial._GetPerceivedBrightness(specularGlossiness.diffuseColor);
+            const specularPerceivedBrightness = _GLTFMaterial._GetPerceivedBrightness(specularGlossiness.specularColor);
+            const oneMinusSpecularStrength = 1 - _GLTFMaterial._GetMaxComponent(specularGlossiness.specularColor);
+            const metallic = _GLTFMaterial._SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
+            const baseColorFromDiffuse = specularGlossiness.diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this._dielectricSpecular.r) / Math.max(1 - metallic, this._epsilon));
+            const baseColorFromSpecular = specularGlossiness.specularColor.subtract(this._dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this._epsilon));
+            let baseColor = Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
+            baseColor = baseColor.clampToRef(0, 1, baseColor);
+
+            const metallicRoughness: _IPBRMetallicRoughness = {
+                baseColor: baseColor,
+                metallic: metallic,
+                roughness: 1 - specularGlossiness.glossiness
+            }
+
+            return metallicRoughness;
+        }
+
+        /**
+         * Calculates the surface reflectance, independent of lighting conditions.
          * @param color - Color source to calculate brightness from.
          * @returns number representing the perceived brightness, or zero if color is undefined. 
          */
-        public static GetPerceivedBrightness(color: Color3): number {
+        private static _GetPerceivedBrightness(color: Color3): number {
             if (color) {
                 return Math.sqrt(0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b);
             }
@@ -409,7 +711,7 @@ module BABYLON.GLTF2 {
          * @param color 
          * @returns maximum color component value, or zero if color is null or undefined.
          */
-        public static GetMaxComponent(color: Color3): number {
+        private static _GetMaxComponent(color: Color3): number {
             if (color) {
                 return Math.max(color.r, Math.max(color.g, color.b));
             }
@@ -426,115 +728,118 @@ module BABYLON.GLTF2 {
          * @param imageData - map of image file name to data.
          * @param hasTextureCoords - specifies if texture coordinates are present on the submesh to determine if textures should be applied.
          */
-        public static ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
+        public static _ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
             const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {};
+            let metallicRoughness: Nullable<_IPBRMetallicRoughness>;
             const glTFMaterial: IMaterial = {
                 name: babylonPBRMaterial.name
             };
             const useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
 
-            if (babylonPBRMaterial) {
-                if (useMetallicRoughness) {
-                    glTFPbrMetallicRoughness.baseColorFactor = [
-                        babylonPBRMaterial.albedoColor.r,
-                        babylonPBRMaterial.albedoColor.g,
-                        babylonPBRMaterial.albedoColor.b,
-                        babylonPBRMaterial.alpha
-                    ];
-                    if (babylonPBRMaterial.metallic != null) {
-                        if (babylonPBRMaterial.metallic !== 1) {
-                            glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
-                        }
-                    }
-                    if (babylonPBRMaterial.roughness != null) {
-                        if (babylonPBRMaterial.roughness !== 1) {
-                            glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
-                        }
-                    }
+            if (!useMetallicRoughness) {
+                const specGloss: _IPBRSpecularGlossiness = {
+                    diffuseColor: babylonPBRMaterial.albedoColor || Color3.White(),
+                    specularColor: babylonPBRMaterial.reflectivityColor || Color3.White(),
+                    glossiness: babylonPBRMaterial.microSurface || 1,
+                };
+                if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                    throw new Error("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture currently not supported");
+                }
+
+                metallicRoughness = this._ConvertSpecularGlossinessTexturesToMetallicRoughness(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType);
+
+                if (!metallicRoughness) {
+                    metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specGloss);
                 }
                 else {
-                    const diffuseColor = babylonPBRMaterial.albedoColor || Color3.Black();
-                    const specularColor = babylonPBRMaterial.reflectionColor || Color3.Black();
-                    const diffusePerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(diffuseColor);
-                    const specularPerceivedBrightness = _GLTFMaterial.GetPerceivedBrightness(specularColor);
-                    const oneMinusSpecularStrength = 1 - _GLTFMaterial.GetMaxComponent(babylonPBRMaterial.reflectionColor);
-                    const metallic = _GLTFMaterial.SolveMetallic(diffusePerceivedBrightness, specularPerceivedBrightness, oneMinusSpecularStrength);
-                    const glossiness = babylonPBRMaterial.microSurface || 0;
-                    const baseColorFromDiffuse = diffuseColor.scale(oneMinusSpecularStrength / (1.0 - this.dielectricSpecular.r) / Math.max(1 - metallic, this.epsilon));
-                    const baseColorFromSpecular = specularColor.subtract(this.dielectricSpecular.scale(1 - metallic)).scale(1 / Math.max(metallic, this.epsilon));
-                    let baseColor = Color3.Lerp(baseColorFromDiffuse, baseColorFromSpecular, metallic * metallic);
-                    baseColor = baseColor.clampToRef(0, 1, baseColor);
-
-                    glTFPbrMetallicRoughness.baseColorFactor = [
-                        baseColor.r,
-                        baseColor.g,
-                        baseColor.b,
-                        babylonPBRMaterial.alpha
-                    ];
-                    if (metallic !== 1) {
-                        glTFPbrMetallicRoughness.metallicFactor = metallic;
-                    }
-                    if (glossiness) {
-                        glTFPbrMetallicRoughness.roughnessFactor = 1 - glossiness;
-                    }
-                }
-                if (babylonPBRMaterial.backFaceCulling) {
-                    if (!babylonPBRMaterial.twoSidedLighting) {
-                        Tools.Warn(babylonPBRMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
-                    }
-                    glTFMaterial.doubleSided = true;
-                }
-                if (hasTextureCoords) {
-                    if (babylonPBRMaterial.albedoTexture) {
-                        const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
-                        if (glTFTexture) {
-                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                    if (metallicRoughness.baseColorTextureBase64) {
+                        const glTFBaseColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughness.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, images, textures, imageData);
+                        if (glTFBaseColorTexture != null) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
                         }
                     }
-                    if (babylonPBRMaterial.bumpTexture) {
-                        const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
-                        if (glTFTexture) {
-                            glTFMaterial.normalTexture = glTFTexture;
+                    if (metallicRoughness.metallicRoughnessTextureBase64) {
+                        const glTFMRColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughness.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, images, textures, imageData);
+                        if (glTFMRColorTexture != null) {
+                            glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
                         }
                     }
-                    if (babylonPBRMaterial.ambientTexture) {
-                        const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
-                        if (glTFTexture) {
-                            let occlusionTexture: IMaterialOcclusionTextureInfo = {
-                                index: glTFTexture.index
-                            };
+                }
+            }
 
-                            glTFMaterial.occlusionTexture = occlusionTexture;
+            if (!(this.FuzzyEquals(babylonPBRMaterial.albedoColor, Color3.White(), this._epsilon) && babylonPBRMaterial.alpha >= this._epsilon)) {
+                glTFPbrMetallicRoughness.baseColorFactor = [
+                    babylonPBRMaterial.albedoColor.r,
+                    babylonPBRMaterial.albedoColor.g,
+                    babylonPBRMaterial.albedoColor.b,
+                    babylonPBRMaterial.alpha
+                ];
+            }
 
-                            if (babylonPBRMaterial.ambientTextureStrength) {
-                                occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
-                            }
-                        }
+
+            if (babylonPBRMaterial.metallic != null && babylonPBRMaterial.metallic !== 1) {
+                glTFPbrMetallicRoughness.metallicFactor = babylonPBRMaterial.metallic;
+            }
+            if (babylonPBRMaterial.roughness != null && babylonPBRMaterial.roughness !== 1) {
+                glTFPbrMetallicRoughness.roughnessFactor = babylonPBRMaterial.roughness;
+            }
+
+            if (babylonPBRMaterial.backFaceCulling != null && !babylonPBRMaterial.backFaceCulling) {
+                if (!babylonPBRMaterial.twoSidedLighting) {
+                    Tools.Warn(babylonPBRMaterial.name + ": Back-face culling enabled and two-sided lighting disabled is not supported in glTF.");
+                }
+                glTFMaterial.doubleSided = true;
+            }
+            if (hasTextureCoords) {
+                if (useMetallicRoughness && babylonPBRMaterial.albedoTexture) {
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, imageData);
+                    if (glTFTexture) {
+                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
                     }
-                    if (babylonPBRMaterial.emissiveTexture) {
-                        const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
-                        if (glTFTexture != null) {
-                            glTFMaterial.emissiveTexture = glTFTexture;
-                        }
+                }
+                if (babylonPBRMaterial.bumpTexture) {
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, imageData);
+                    if (glTFTexture) {
+                        glTFMaterial.normalTexture = glTFTexture;
                     }
-                    if (babylonPBRMaterial.metallicTexture) {
-                        const glTFTexture = _GLTFMaterial.ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
-                        if (glTFTexture != null) {
-                            glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
+                }
+                if (babylonPBRMaterial.ambientTexture) {
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, imageData);
+                    if (glTFTexture) {
+                        let occlusionTexture: IMaterialOcclusionTextureInfo = {
+                            index: glTFTexture.index
+                        };
+
+                        glTFMaterial.occlusionTexture = occlusionTexture;
+
+                        if (babylonPBRMaterial.ambientTextureStrength) {
+                            occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
                         }
                     }
                 }
-                if (!babylonPBRMaterial.emissiveColor.equalsFloats(0.0, 0.0, 0.0)) {
-                    glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+                if (babylonPBRMaterial.emissiveTexture) {
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, imageData);
+                    if (glTFTexture != null) {
+                        glTFMaterial.emissiveTexture = glTFTexture;
+                    }
+                }
+                if (babylonPBRMaterial.metallicTexture) {
+                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, imageData);
+                    if (glTFTexture != null) {
+                        glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
+                    }
                 }
-                if (babylonPBRMaterial.transparencyMode != null) {
-                    const alphaMode = _GLTFMaterial.GetAlphaMode(babylonPBRMaterial);
+            }
+            if (!this.FuzzyEquals(babylonPBRMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
+                glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
+            }
+            if (babylonPBRMaterial.transparencyMode != null) {
+                const alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMaterial);
 
-                    if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
-                        glTFMaterial.alphaMode = alphaMode;
-                        if (alphaMode === MaterialAlphaMode.BLEND) {
-                            glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
-                        }
+                if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
+                    glTFMaterial.alphaMode = alphaMode;
+                    if (alphaMode === MaterialAlphaMode.BLEND) {
+                        glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
                     }
                 }
             }
@@ -550,20 +855,14 @@ module BABYLON.GLTF2 {
          * @param images - Array of glTF images.
          * @param textures - Array of glTF textures.
          * @param imageData - map of image file name and data.
-         * @return - glTF texture, or null if the texture format is not supported.
+         * @return - glTF texture info, or null if the texture format is not supported.
          */
-        public static ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable<ITextureInfo> {
-            let textureInfo: Nullable<ITextureInfo> = null;
-
-            let glTFTexture = {
-                source: images.length
-            };
-
+        private static _ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable<ITextureInfo> {
             let textureName = "texture_" + (textures.length - 1).toString();
             let textureData = babylonTexture.getInternalTexture();
 
             if (textureData != null) {
-                textureName = textureData.url;
+                textureName = textureData.url || textureName;
             }
 
             textureName = Tools.GetFilename(textureName);
@@ -578,28 +877,38 @@ module BABYLON.GLTF2 {
                 extension = ".png";
             }
             else {
-                Tools.Error("Unsupported mime type " + mimeType);
+                throw new Error("Unsupported mime type " + mimeType);
             }
             textureName = baseFile + extension;
 
 
             const pixels = babylonTexture.readPixels() as Uint8Array;
 
-            const imageCanvas = document.createElement('canvas');
-            imageCanvas.id = "ImageCanvas";
-
-            const ctx = <CanvasRenderingContext2D>imageCanvas.getContext('2d');
             const size = babylonTexture.getSize();
-            imageCanvas.width = size.width;
-            imageCanvas.height = size.height;
 
-            const imgData = ctx.createImageData(size.width, size.height);
+            const base64Data = this._CreateBase64FromCanvas(pixels, size.width, size.height, mimeType);
 
+            return this._GetTextureInfoFromBase64(base64Data, textureName, mimeType, images, textures, imageData);
+        }
 
-            imgData.data.set(pixels);
-            ctx.putImageData(imgData, 0, 0);
-            const base64Data = imageCanvas.toDataURL(mimeType);
-            const binStr = atob(base64Data.split(',')[1]);
+        /**
+         * Builds a texture from base64 string.
+         * @param base64Texture - base64 texture string.
+         * @param textureName - Name to use for the texture.
+         * @param mimeType - image mime type for the texture.
+         * @param images - array of images.
+         * @param textures - array of textures.
+         * @param imageData - map of image data.
+         * @returns - glTF texture info, or null if the texture format is not supported.
+         */
+        private static _GetTextureInfoFromBase64(base64Texture: string, textureName: string, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable<ITextureInfo> {
+            let textureInfo: Nullable<ITextureInfo> = null;
+
+            const glTFTexture = {
+                source: images.length
+            };
+
+            const binStr = atob(base64Texture.split(',')[1]);
             const arr = new Uint8Array(binStr.length);
             for (let i = 0; i < binStr.length; ++i) {
                 arr[i] = binStr.charCodeAt(i);
@@ -607,7 +916,7 @@ module BABYLON.GLTF2 {
             const imageValues = { data: arr, mimeType: mimeType };
 
             imageData[textureName] = imageValues;
-            if (mimeType === ImageMimeType.JPEG) {
+            if (mimeType === ImageMimeType.JPEG || mimeType === ImageMimeType.PNG) {
                 const glTFImage: IImage = {
                     uri: textureName
                 }

+ 27 - 1
src/Animations/babylon.animationGroup.ts

@@ -59,6 +59,13 @@ module BABYLON {
             return this._targetedAnimations;
         }
 
+        /**
+         * returning the list of animatables controlled by this animation group.
+         */
+        public get animatables(): Array<Animatable> {
+            return this._animatables;
+        }
+
         public constructor(public name: string, scene: Nullable<Scene> = null) {
             this._scene = scene || Engine.LastCreatedScene!;
 
@@ -245,6 +252,25 @@ module BABYLON {
         }
 
         /**
+         * Goes to a specific frame in this animation group
+         * 
+         * @param frame the frame number to go to
+         * @return the animationGroup
+         */
+        public goToFrame(frame: number): AnimationGroup {
+            if (!this._isStarted) {
+                return this;
+            }
+
+            for (var index = 0; index < this._animatables.length; index++) {
+                let animatable = this._animatables[index];
+                animatable.goToFrame(frame);
+            }
+
+            return this;
+        }
+
+        /**
          * Dispose all associated resources
          */
         public dispose(): void {
@@ -258,4 +284,4 @@ module BABYLON {
             }
         }
     }
-}
+}

+ 7 - 2
src/Cameras/babylon.camera.ts

@@ -589,7 +589,12 @@
             return new Ray(origin, direction, length);
         }
 
-        public dispose(): void {
+        /**
+         * Releases resources associated with this node.
+         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
+         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
+         */
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
             // Observables
             this.onViewMatrixChangedObservable.clear();
             this.onProjectionMatrixChangedObservable.clear();
@@ -639,7 +644,7 @@
             // Active Meshes
             this._activeMeshes.dispose();
 
-            super.dispose();
+            super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
 
         // ---- Camera rigs section ----

+ 1 - 1
src/Engine/babylon.engine.ts

@@ -586,7 +586,7 @@
         }
 
         public static get Version(): string {
-            return "3.2.0-alphaA";
+            return "3.2.0-alphaB";
         }
 
         // Updatable statics so stick with vars here

+ 13 - 2
src/Helpers/babylon.environmentHelper.ts

@@ -310,6 +310,12 @@ module BABYLON {
         private _options: IEnvironmentHelperOptions;
 
         /**
+         * This observable will be notified with any error during the creation of the environment, 
+         * mainly texture creation errors.
+         */
+        public onErrorObservable: Observable<{ message?: string, exception?: any }>;
+
+        /**
          * constructor
          * @param options 
          * @param scene The scene to add the material to
@@ -320,6 +326,7 @@ module BABYLON {
                 ...options
             }
             this._scene = scene;
+            this.onErrorObservable = new Observable();
 
             this._setupBackground();
             this._setupImageProcessing();
@@ -557,7 +564,7 @@ module BABYLON {
                 return;
             }
 
-            const diffuseTexture = new Texture(this._options.groundTexture, this._scene);
+            const diffuseTexture = new Texture(this._options.groundTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             diffuseTexture.gammaSpace = false;
             diffuseTexture.hasAlpha = true;
             this._groundMaterial.diffuseTexture = diffuseTexture;
@@ -664,12 +671,16 @@ module BABYLON {
                 return;
             }
 
-            this._skyboxTexture = new CubeTexture(this._options.skyboxTexture, this._scene);
+            this._skyboxTexture = new CubeTexture(this._options.skyboxTexture, this._scene, undefined, undefined, undefined, undefined, this._errorHandler);
             this._skyboxTexture.coordinatesMode = Texture.SKYBOX_MODE;
             this._skyboxTexture.gammaSpace = false;
             this._skyboxMaterial.reflectionTexture = this._skyboxTexture;
         }
 
+        private _errorHandler = (message?: string, exception?: any) => {
+            this.onErrorObservable.notifyObservers({ message: message, exception: exception });
+        }
+
         /**
          * Dispose all the elements created by the Helper.
          */

+ 5 - 4
src/Helpers/babylon.videoDome.ts

@@ -92,15 +92,16 @@ module BABYLON {
         }
 
         /**
-         * Releases all associated resources
+         * Releases resources associated with this node.
+         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
+         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
          */
-        public dispose(): void {
-            super.dispose();
-
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
             this._videoTexture.dispose();
             this._mesh.dispose();
             this._material.dispose();
 
+            super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
     }
 }

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

@@ -467,9 +467,11 @@ module BABYLON {
         }
 
         /**
-         * Disposes the light.  
+         * Releases resources associated with this node.
+         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
+         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
          */
-        public dispose(): void {
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
             if (this._shadowGenerator) {
                 this._shadowGenerator.dispose();
                 this._shadowGenerator = null;
@@ -487,7 +489,7 @@ module BABYLON {
 
             // Remove from scene
             this.getScene().removeLight(this);
-            super.dispose();
+            super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
 
         /**

+ 1 - 2
src/Materials/Background/babylon.backgroundMaterial.ts

@@ -64,8 +64,6 @@
          */
         public NOISE = false;
 
-
-
         /**
          * is the reflection texture in BGR color scheme? 
          * Mainly used to solve a bug in ios10 video tag
@@ -85,6 +83,7 @@
         public SAMPLER3DBGRMAP = false;
         public IMAGEPROCESSINGPOSTPROCESS = false;
         public EXPOSURE = false;
+        public GRAIN = false;
 
         // Reflection.
         public REFLECTION = false;

+ 1 - 0
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -119,6 +119,7 @@
         public SAMPLER3DBGRMAP = false;
         public IMAGEPROCESSINGPOSTPROCESS = false;
         public EXPOSURE = false;
+        public GRAIN = false;
 
         public USEPHYSICALLIGHTFALLOFF = false;
         public TWOSIDEDLIGHTING = false;

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

@@ -93,7 +93,7 @@
         protected _format: Nullable<number>;
         private _delayedOnLoad: Nullable<() => void>;
         private _delayedOnError: Nullable<() => void>;
-        private _onLoadObservable: Nullable<Observable<Texture>>;
+        protected _onLoadObservable: Nullable<Observable<Texture>>;
 
         protected _isBlocking: boolean = true;
         public set isBlocking(value: boolean) {

+ 4 - 0
src/Materials/Textures/babylon.videoTexture.ts

@@ -142,6 +142,10 @@
 
             this._texture.isReady = true;
             this._updateInternalTexture();
+
+            if (this._onLoadObservable && this._onLoadObservable.hasObservers()) {
+                this.onLoadObservable.notifyObservers(this);
+            }
         };
 
         private reset = (): void => {

+ 65 - 1
src/Materials/babylon.imageProcessingConfiguration.ts

@@ -2,6 +2,7 @@ module BABYLON {
     /**
      * Interface to follow in your material defines to integrate easily the
      * Image proccessing functions.
+     * @ignore
      */
     export interface IImageProcessingConfigurationDefines {
         IMAGEPROCESSING: boolean;
@@ -17,6 +18,10 @@ module BABYLON {
         SAMPLER3DGREENDEPTH: boolean;
         SAMPLER3DBGRMAP: boolean;
         IMAGEPROCESSINGPOSTPROCESS: boolean;
+        /** 
+         * If the grain should be performed in the image processing shader.
+         */
+        GRAIN: boolean;
     }
 
     /**
@@ -216,6 +221,57 @@ module BABYLON {
         public vignetteCameraFov = 0.5;
 
         @serialize()
+        private _grainEnabled = false;
+
+        /**
+         * If the grain effect should be enabled.
+         */
+        public get grainEnabled(): boolean {
+            return this._grainEnabled;
+        }
+        public set grainEnabled(value: boolean) {
+            if (this._grainEnabled === value) {
+                return;
+            }
+
+            this._grainEnabled = value;
+            this._updateParameters();
+        }
+
+        @serialize()
+        private _grainIntensity = 30;
+        /**
+         * Amount of grain to be applied by the grain effect.
+         */
+        public get grainIntensity(): number {
+            return this._grainIntensity;
+        }
+        public set grainIntensity(value: number) {
+            if (this._grainIntensity === value) {
+                return;
+            }
+            this._grainIntensity = value;
+        }
+
+        @serialize()
+        private _grainAnimated = false;
+
+        /**
+         * If the grain effect should be animated.
+         */
+        public get grainAnimated(): boolean {
+            return this._grainAnimated;
+        }
+        public set grainAnimated(value: boolean) {
+            if (this._grainAnimated === value) {
+                return;
+            }
+
+            this._grainAnimated = value;
+            this._updateParameters();
+        }
+
+        @serialize()
         private _vignetteBlendMode = ImageProcessingConfiguration.VIGNETTEMODE_MULTIPLY;
         /**
          * Gets the vignette blend mode allowing different kind of effect.
@@ -335,6 +391,10 @@ module BABYLON {
             if (defines.COLORCURVES) {
                 ColorCurves.PrepareUniforms(uniforms);
             }
+            if (defines.GRAIN){
+                uniforms.push("grainVarianceAmount");
+                uniforms.push("grainAnimatedSeed");
+            }
         }
 
         /**
@@ -382,7 +442,8 @@ module BABYLON {
             defines.SAMPLER3DGREENDEPTH = this.colorGradingWithGreenDepth;
             defines.SAMPLER3DBGRMAP = this.colorGradingBGR;
             defines.IMAGEPROCESSINGPOSTPROCESS = this.applyByPostProcess;
-            defines.IMAGEPROCESSING = defines.VIGNETTE || defines.TONEMAPPING || defines.CONTRAST || defines.EXPOSURE || defines.COLORCURVES || defines.COLORGRADING;
+            defines.GRAIN = this.grainEnabled;
+            defines.IMAGEPROCESSING = defines.VIGNETTE || defines.TONEMAPPING || defines.CONTRAST || defines.EXPOSURE || defines.COLORCURVES || defines.COLORGRADING || defines.GRAIN;
         }
 
         /**
@@ -440,6 +501,9 @@ module BABYLON {
                     this.colorGradingTexture.level // weight
                 );
             }
+
+            effect.setFloat("grainVarianceAmount", this.grainIntensity);
+            effect.setFloat("grainAnimatedSeed", this.grainAnimated ? Math.random() + 1 : 1);
         }
 
         /**

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

@@ -88,6 +88,7 @@ module BABYLON {
         public SAMPLER3DBGRMAP = false;
         public IMAGEPROCESSINGPOSTPROCESS = false;
         public EXPOSURE = false;
+        public GRAIN = false;
 
         constructor() {
             super();

+ 6 - 5
src/Mesh/Compression/babylon.dracoCompression.ts

@@ -1,5 +1,3 @@
-/// <reference path="../../../dist/preview release/babylon.d.ts" />
-
 declare var DracoDecoderModule: any;
 
 module BABYLON {
@@ -19,9 +17,14 @@ module BABYLON {
          * @param numWorkers The number of workers for async operations
          */
         constructor(numWorkers = (navigator.hardwareConcurrency || 4)) {
+            let workerBlobUrl = URL && URL.createObjectURL && URL.createObjectURL(new Blob([`(${DracoCompression._Worker.toString()})()`], { type: "application/javascript" }));
+            if (!workerBlobUrl || !Worker) {
+                Tools.Error("Draco Compression disabled. The current context doesn't support worker creation or URL.createObjectURL");
+                return;
+            }
             const workers = new Array<Worker>(numWorkers);
             for (let i = 0; i < workers.length; i++) {
-                const worker = new Worker(DracoCompression._WorkerBlobUrl);
+                const worker = new Worker(workerBlobUrl);
                 worker.postMessage({ id: "initDecoder", url: DracoCompression.DecoderUrl });
                 workers[i] = worker;
             }
@@ -184,8 +187,6 @@ module BABYLON {
             };
         }
 
-        private static _WorkerBlobUrl = URL.createObjectURL(new Blob([`(${DracoCompression._Worker.toString()})()`], { type: "application/javascript" }));
-
         private static _GetDefaultDecoderUrl(): Nullable<string> {
             for (let i = 0; i < document.scripts.length; i++) {
                 if (document.scripts[i].type === "text/x-draco-decoder") {

+ 5 - 5
src/Mesh/babylon.abstractMesh.ts

@@ -1326,11 +1326,11 @@
         }
 
         /**
-         * Disposes the AbstractMesh.  
-         * By default, all the mesh children are also disposed unless the parameter `doNotRecurse` is set to `true`.  
-         * Returns nothing.  
+         * Releases resources associated with this abstract mesh.
+         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
+         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
          */
-        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures: boolean = false): void {
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
             var index: number;
 
             // Action manager
@@ -1448,7 +1448,7 @@
             this.onCollideObservable.clear();
             this.onCollisionPositionChangeObservable.clear();
 
-            super.dispose(doNotRecurse);
+            super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
 
         /**

+ 2 - 2
src/Mesh/babylon.instancedMesh.ts

@@ -282,13 +282,13 @@
          * Disposes the InstancedMesh.  
          * Returns nothing.  
          */
-        public dispose(doNotRecurse?: boolean): void {
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
 
             // Remove from mesh
             var index = this._sourceMesh.instances.indexOf(this);
             this._sourceMesh.instances.splice(index, 1);
 
-            super.dispose(doNotRecurse);
+            super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
     }
 } 

+ 4 - 4
src/Mesh/babylon.mesh.ts

@@ -1714,11 +1714,11 @@
         }
 
         /**
-         * Disposes the Mesh.  
-         * By default, all the mesh children are also disposed unless the parameter `doNotRecurse` is set to `true`.  
-         * Returns nothing.  
+         * Releases resources associated with this mesh.
+         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
+         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
          */
-        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures: boolean = false): void {
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
             this.morphTargetManager = null;
 
             if (this._geometry) {

+ 13 - 20
src/Mesh/babylon.transformNode.ts

@@ -58,6 +58,14 @@ module BABYLON {
         }
 
         /**
+         * Gets a string idenfifying the name of the class
+         * @returns "TransformNode" string
+         */
+        public getClassName(): string {
+            return "TransformNode";
+        }
+
+        /**
           * Rotation property : a Vector3 depicting the rotation value in radians around each local axis X, Y, Z. 
           * If rotation quaternion is set, this Vector3 will (almost always) be the Zero vector!
           * Default : (0.0, 0.0, 0.0)
@@ -965,35 +973,20 @@ module BABYLON {
         }
 
         /**
-         * Disposes the TransformNode.  
-         * By default, all the children are also disposed unless the parameter `doNotRecurse` is set to `true`.  
-         * Returns nothing.  
+         * Releases resources associated with this transform node.
+         * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
+         * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
          */
-        public dispose(doNotRecurse?: boolean): void {
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
             // Animations
             this.getScene().stopAnimation(this);
 
             // Remove from scene
             this.getScene().removeTransformNode(this);
 
-            if (!doNotRecurse) {
-                // Children
-                var objects = this.getDescendants(true);
-                for (var index = 0; index < objects.length; index++) {
-                    objects[index].dispose();
-                }
-            } else {
-                var childMeshes = this.getChildMeshes(true);
-                for (index = 0; index < childMeshes.length; index++) {
-                    var child = childMeshes[index];
-                    child.parent = null;
-                    child.computeWorldMatrix(true);
-                }
-            }
-
             this.onAfterWorldMatrixUpdateObservable.clear();
 
-            super.dispose();
+            super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
 
     }

+ 99 - 40
src/PostProcess/RenderPipeline/Pipelines/babylon.defaultRenderingPipeline.ts

@@ -5,7 +5,7 @@
      */
     export class DefaultRenderingPipeline extends PostProcessRenderPipeline implements IDisposable, IAnimatable {
         private _scene: Scene;
-
+        private _originalCameras:Array<Camera> = [];
         /**
 		 * ID of the sharpen post process,
 		 */
@@ -52,6 +52,7 @@
 		 * Sharpen post process which will apply a sharpen convolution to enhance edges
 		 */
         public sharpen: SharpenPostProcess;
+        private _sharpenEffect: PostProcessRenderEffect;
         /**
 		 * First pass of bloom to capture the original image texture for later use.
 		 */
@@ -92,6 +93,7 @@
 		 * Chromatic aberration post process which will shift rgb colors in the image
 		 */
         public chromaticAberration: ChromaticAberrationPostProcess;
+        private _chromaticAberrationEffect: PostProcessRenderEffect;
 
         /**
          * Animations which can be used to tweak settings over a period of time
@@ -228,6 +230,20 @@
                 return;
             }
             this._depthOfFieldBlurLevel = value;
+            
+            // recreate dof and dispose old as this setting is not dynamic
+            var oldDof = this.depthOfField;
+
+            this.depthOfField = new DepthOfFieldEffect(this._scene, null, this._depthOfFieldBlurLevel, this._defaultPipelineTextureType);
+            this.depthOfField.focalLength = oldDof.focalLength;
+            this.depthOfField.focusDistance = oldDof.focusDistance;
+            this.depthOfField.fStop = oldDof.fStop;
+            this.depthOfField.lensSize = oldDof.lensSize;
+            
+            for (var i = 0; i < this._cameras.length; i++) {
+                oldDof.disposeEffects(this._cameras[i]);
+            }
+
             this._buildPipeline();
         }
 
@@ -310,6 +326,7 @@
         constructor(name: string, hdr: boolean, scene: Scene, cameras?: Camera[], automaticBuild = true) {
             super(scene.getEngine(), name);
             this._cameras = cameras ||  [];
+            this._originalCameras = this._cameras.slice();
 
             this._buildAllowed = automaticBuild;
 
@@ -333,6 +350,15 @@
             // Attach
             scene.postProcessRenderPipelineManager.addPipeline(this);
 
+            var engine = this._scene.getEngine();
+            this.sharpen = new SharpenPostProcess("sharpen", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
+            this._sharpenEffect = new PostProcessRenderEffect(engine, this.SharpenPostProcessId, () => { return this.sharpen; }, true);
+
+            this.depthOfField = new DepthOfFieldEffect(this._scene, null, this._depthOfFieldBlurLevel, this._defaultPipelineTextureType);
+
+            this.chromaticAberration = new ChromaticAberrationPostProcess("ChromaticAberration", engine.getRenderWidth(), engine.getRenderHeight(), 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
+            this._chromaticAberrationEffect = new PostProcessRenderEffect(engine, this.ChromaticAberrationPostProcessId, () => { return this.chromaticAberration; }, true);
+            
             this._buildPipeline();
         }
 
@@ -346,6 +372,30 @@
             this._buildAllowed = previousState;
         }
 
+        private _prevPostProcess:Nullable<PostProcess> = null;
+        private _prevPrevPostProcess:Nullable<PostProcess> = null;
+
+        private _setAutoClearAndTextureSharing(postProcess:PostProcess, skipTextureSharing = false){
+            if(this._prevPostProcess && this._prevPostProcess.autoClear){
+                postProcess.autoClear = false;
+            }else{
+                postProcess.autoClear = true;
+            }
+
+            if(!skipTextureSharing){
+                if(this._prevPrevPostProcess){
+                    postProcess.shareOutputWith(this._prevPrevPostProcess);
+                }else{
+                    postProcess.useOwnOutput();
+                }
+
+                if(this._prevPostProcess){
+                    this._prevPrevPostProcess = this._prevPostProcess;
+                }
+                this._prevPostProcess = postProcess;
+            }
+        }
+
         private _buildPipeline() {
             if (!this._buildAllowed) {
                 return;
@@ -354,23 +404,37 @@
             var engine = this._scene.getEngine();
 
             this._disposePostProcesses();
+            if (this._cameras !== null) {
+                this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._cameras);
+                // get back cameras to be used to reattach pipeline
+                this._cameras = this._originalCameras.slice();
+            }
             this._reset();
+            this._prevPostProcess = null;
+            this._prevPrevPostProcess = null;
+
+            if (this.fxaaEnabled) {
+                this.fxaa = new FxaaPostProcess("fxaa", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
+                this.addEffect(new PostProcessRenderEffect(engine, this.FxaaPostProcessId, () => { return this.fxaa; }, true));
+                this._setAutoClearAndTextureSharing(this.fxaa);
+                
+            }
 
             if (this.sharpenEnabled) {
-                this.sharpen = new SharpenPostProcess("sharpen", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
-                this.addEffect(new PostProcessRenderEffect(engine, this.SharpenPostProcessId, () => { return this.sharpen; }, true));
+                this.addEffect(this._sharpenEffect);
+                this._setAutoClearAndTextureSharing(this.sharpen);
             }
 
-            if(this.depthOfFieldEnabled){
-                // Enable and get current depth map
+            if (this.depthOfFieldEnabled) {
                 var depthTexture = this._scene.enableDepthRenderer(this._cameras[0]).getDepthMap();
-
-                this.depthOfField = new DepthOfFieldEffect(this._scene, depthTexture, this._depthOfFieldBlurLevel, this._defaultPipelineTextureType);
+                this.depthOfField.depthTexture = depthTexture;
                 this.addEffect(this.depthOfField);
+                this._setAutoClearAndTextureSharing(this.depthOfField._depthOfFieldMerge);
             }
 
             if (this.bloomEnabled) {
                 this.pass = new PassPostProcess("sceneRenderTarget", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
+                this._setAutoClearAndTextureSharing(this.pass, true);
                 this.addEffect(new PostProcessRenderEffect(engine, this.PassPostProcessId, () => { return this.pass; }, true));
 
                 if (!this._hdr) { // Need to enhance highlights if not using float rendering
@@ -420,18 +484,14 @@
                 }
             }
 
-            if (this.fxaaEnabled) {
-                this.fxaa = new FxaaPostProcess("fxaa", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
-                this.addEffect(new PostProcessRenderEffect(engine, this.FxaaPostProcessId, () => { return this.fxaa; }, true));
-
-                this.fxaa.autoClear = !this.bloomEnabled && (!this._hdr || !this.imageProcessing);
-            } else if (this._hdr && this.imageProcessing) {
+            if (this._hdr && this.imageProcessing) {
                 this.finalMerge = this.imageProcessing;
             }
             else {
                 this.finalMerge = new PassPostProcess("finalMerge", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
                 this.addEffect(new PostProcessRenderEffect(engine, this.FinalMergePostProcessId, () => { return this.finalMerge; }, true));
-
+                this._setAutoClearAndTextureSharing(this.finalMerge, true);
+                
                 this.finalMerge.autoClear = !this.bloomEnabled && (!this._hdr || !this.imageProcessing);
             }
 
@@ -441,30 +501,23 @@
                     if (this.imageProcessing) {
                         this.imageProcessing.shareOutputWith(this.pass);
                         this.imageProcessing.autoClear = false;
-                    } else if (this.fxaa) {
-                        this.fxaa.shareOutputWith(this.pass);
                     } else {
                         this.finalMerge.shareOutputWith(this.pass);
                     }
                 } else {
-                    if (this.fxaa) {
-                        this.fxaa.shareOutputWith(this.pass);
-                    } else {
-                        this.finalMerge.shareOutputWith(this.pass);
-                    }
+                    this.finalMerge.shareOutputWith(this.pass);
                 }
             }
 
             if (this.chromaticAberrationEnabled) {
-                this.chromaticAberration = new ChromaticAberrationPostProcess("ChromaticAberration", engine.getRenderWidth(), engine.getRenderHeight(), 1.0, null, Texture.BILINEAR_SAMPLINGMODE, engine, false, this._defaultPipelineTextureType);
-                this.addEffect(new PostProcessRenderEffect(engine, this.ChromaticAberrationPostProcessId, () => { return this.chromaticAberration; }, true));
+                this.addEffect(this._chromaticAberrationEffect);
+                this._setAutoClearAndTextureSharing(this.chromaticAberration);
             }
 
-
             if (this._cameras !== null) {
                 this._scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(this._name, this._cameras);
             }
-            
+
             if(this.msaaEnabled){
                 if(!this._enableMSAAOnFirstPostProcess()){
                     BABYLON.Tools.Warn("MSAA failed to enable, MSAA is only supported in browsers that support webGL >= 2.0");
@@ -472,14 +525,10 @@
             }
         }
 
-        private _disposePostProcesses(): void {
+        private _disposePostProcesses(disposeNonRecreated = false): void {
             for (var i = 0; i < this._cameras.length; i++) {
                 var camera = this._cameras[i];
 
-                if (this.sharpen) {
-                    this.sharpen.dispose(camera);
-                }
-
                 if (this.pass) {
                     this.pass.dispose(camera);
                 }
@@ -512,16 +561,22 @@
                     this.finalMerge.dispose(camera);
                 }
 
-                if(this.depthOfField){
-                    this.depthOfField.disposeEffects(camera);
-                }
-
-                if(this.chromaticAberration){
-                    this.chromaticAberration.dispose(camera);
+                // These are created in the constructor and should not be disposed on every pipeline change
+                if(disposeNonRecreated){
+                    if (this.sharpen) {
+                        this.sharpen.dispose(camera);
+                    }
+    
+                    if(this.depthOfField){
+                        this.depthOfField.disposeEffects(camera);
+                    }
+    
+                    if(this.chromaticAberration){
+                        this.chromaticAberration.dispose(camera);
+                    }
                 }
             }
 
-            (<any>this.sharpen) = null;
             (<any>this.pass) = null;
             (<any>this.highlights) = null;
             (<any>this.blurX) = null;
@@ -530,15 +585,19 @@
             (<any>this.imageProcessing) = null;
             (<any>this.fxaa) = null;
             (<any>this.finalMerge) = null;
-            (<any>this.depthOfField) = null;
-            (<any>this.chromaticAberration) = null;
+
+            if(disposeNonRecreated){
+                (<any>this.sharpen) = null;
+                (<any>this.depthOfField) = null;
+                (<any>this.chromaticAberration) = null;
+            } 
         }
 
         /**
          * Dispose of the pipeline and stop all post processes
          */
         public dispose(): void {
-            this._disposePostProcesses();
+            this._disposePostProcesses(true);
 
             this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._cameras);
 

+ 21 - 8
src/PostProcess/babylon.circleOfConfusionPostProcess.ts

@@ -6,24 +6,25 @@ module BABYLON {
         /**
          * Max lens size in scene units/1000 (eg. millimeter). Standard cameras are 50mm. (default: 50) The diamater of the resulting aperture can be computed by lensSize/fStop.
          */
-        lensSize = 50
+        public lensSize = 50
         /**
          * F-Stop of the effect's camera. The diamater of the resulting aperture can be computed by lensSize/fStop. (default: 1.4)
          */
-        fStop = 1.4;
+        public fStop = 1.4;
         /**
          * Distance away from the camera to focus on in scene units/1000 (eg. millimeter). (default: 2000)
          */
-        focusDistance = 2000;
+        public focusDistance = 2000;
         /**
          * Focal length of the effect's camera in scene units/1000 (eg. millimeter). (default: 50)
          */
-        focalLength = 50;
+        public focalLength = 50;
         
+        private _depthTexture:Nullable<RenderTargetTexture> = null;
         /**
          * Creates a new instance of @see CircleOfConfusionPostProcess
          * @param name The name of the effect.
-         * @param depthTexture The depth texture of the scene to compute the circle of confusion.
+         * @param depthTexture The depth texture of the scene to compute the circle of confusion. This must be set in order for this to function but may be set after initialization if needed.
          * @param options The required width/height ratio to downsize to before computing the render pass.
          * @param camera The camera to apply the render pass to.
          * @param samplingMode The sampling mode to be used when computing the pass. (default: 0)
@@ -31,10 +32,15 @@ module BABYLON {
          * @param reusable If the post process can be reused on the same frame. (default: false)
          * @param textureType Type of textures used when performing the post process. (default: 0)
          */
-        constructor(name: string, depthTexture: RenderTargetTexture, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
+        constructor(name: string, depthTexture: Nullable<RenderTargetTexture>, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT) {
             super(name, "circleOfConfusion", ["cameraMinMaxZ", "focusDistance", "cocPrecalculation"], ["depthSampler"], options, camera, samplingMode, engine, reusable, null, textureType);
+            this._depthTexture = depthTexture;
             this.onApplyObservable.add((effect: Effect) => {
-                effect.setTexture("depthSampler", depthTexture);
+                if(!this._depthTexture){
+                    BABYLON.Tools.Warn("No depth texture set on CircleOfConfusionPostProcess")
+                    return;
+                }
+                effect.setTexture("depthSampler", this._depthTexture);
                 
                 // Circle of confusion calculation, See https://developer.nvidia.com/gpugems/GPUGems/gpugems_ch23.html
                 var aperture = this.lensSize/this.fStop;
@@ -42,8 +48,15 @@ module BABYLON {
                 
                 effect.setFloat('focusDistance', this.focusDistance);
                 effect.setFloat('cocPrecalculation', cocPrecalculation);
-                effect.setFloat2('cameraMinMaxZ', depthTexture.activeCamera!.minZ, depthTexture.activeCamera!.maxZ);
+                effect.setFloat2('cameraMinMaxZ', this._depthTexture.activeCamera!.minZ, this._depthTexture.activeCamera!.maxZ);
             })
         }
+
+        /**
+         * Depth texture to be used to compute the circle of confusion. This must be set here or in the constructor in order for the post process to function.
+         */
+        public set depthTexture(value: RenderTargetTexture){
+            this._depthTexture = value;
+        }
     }
 }

+ 64 - 52
src/PostProcess/babylon.depthOfFieldEffect.ts

@@ -24,7 +24,12 @@ module BABYLON {
         private _circleOfConfusion: CircleOfConfusionPostProcess;
         private _depthOfFieldBlurX: Array<DepthOfFieldBlurPostProcess>;
         private _depthOfFieldBlurY: Array<DepthOfFieldBlurPostProcess>;
-        private _depthOfFieldMerge: DepthOfFieldMergePostProcess;
+        /**
+         * Private, last post process of dof
+         */
+        public _depthOfFieldMerge: DepthOfFieldMergePostProcess;
+
+        private _effects: Array<PostProcess> = [];
 
         /**
          * The focal the length of the camera used in the effect
@@ -66,64 +71,71 @@ module BABYLON {
         /**
          * Creates a new instance of @see DepthOfFieldEffect
          * @param scene The scene the effect belongs to.
-         * @param depthTexture The depth texture of the scene to compute the circle of confusion.
+         * @param depthTexture The depth texture of the scene to compute the circle of confusion.This must be set in order for this to function but may be set after initialization if needed.
          * @param pipelineTextureType The type of texture to be used when performing the post processing.
          */
-        constructor(scene: Scene, depthTexture: RenderTargetTexture, blurLevel: DepthOfFieldEffectBlurLevel = DepthOfFieldEffectBlurLevel.Low, pipelineTextureType = 0) {
+        constructor(scene: Scene, depthTexture: Nullable<RenderTargetTexture>, blurLevel: DepthOfFieldEffectBlurLevel = DepthOfFieldEffectBlurLevel.Low, pipelineTextureType = 0) {
             super(scene.getEngine(), "depth of field", ()=>{
-                // Circle of confusion value for each pixel is used to determine how much to blur that pixel
-                this._circleOfConfusion = new BABYLON.CircleOfConfusionPostProcess("circleOfConfusion", depthTexture, 1, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
-                // Capture circle of confusion texture
-                this._depthOfFieldPass = new PassPostProcess("depthOfFieldPass", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
-                this._depthOfFieldPass.autoClear = false;
+                return this._effects;
+            }, true);
+            // Circle of confusion value for each pixel is used to determine how much to blur that pixel
+            this._circleOfConfusion = new BABYLON.CircleOfConfusionPostProcess("circleOfConfusion", depthTexture, 1, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+            // Capture circle of confusion texture
+            this._depthOfFieldPass = new PassPostProcess("depthOfFieldPass", 1.0, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+            this._depthOfFieldPass.autoClear = false;
 
-                // Create a pyramid of blurred images (eg. fullSize 1/4 blur, half size 1/2 blur, quarter size 3/4 blur, eith size 4/4 blur)
-                // Blur the image but do not blur on sharp far to near distance changes to avoid bleeding artifacts 
-                // See section 2.6.2 http://fileadmin.cs.lth.se/cs/education/edan35/lectures/12dof.pdf
-                this._depthOfFieldBlurY = []
-                this._depthOfFieldBlurX = []
-                var blurCount = 1;
-                var kernelSize = 15;
-                switch(blurLevel){
-                    case DepthOfFieldEffectBlurLevel.High: {
-                        blurCount = 3;
-                        kernelSize = 51;
-                        break;
-                    }
-                    case DepthOfFieldEffectBlurLevel.Medium: {
-                        blurCount = 2;
-                        kernelSize = 31;
-                        break;
-                    }
-                    default: {
-                        kernelSize = 15;
-                        blurCount = 1;
-                        break;
-                    }
+            // Create a pyramid of blurred images (eg. fullSize 1/4 blur, half size 1/2 blur, quarter size 3/4 blur, eith size 4/4 blur)
+            // Blur the image but do not blur on sharp far to near distance changes to avoid bleeding artifacts 
+            // See section 2.6.2 http://fileadmin.cs.lth.se/cs/education/edan35/lectures/12dof.pdf
+            this._depthOfFieldBlurY = []
+            this._depthOfFieldBlurX = []
+            var blurCount = 1;
+            var kernelSize = 15;
+            switch(blurLevel){
+                case DepthOfFieldEffectBlurLevel.High: {
+                    blurCount = 3;
+                    kernelSize = 51;
+                    break;
                 }
-                var adjustedKernelSize = kernelSize/Math.pow(2, blurCount-1);
-                for(var i = 0;i<blurCount;i++){
-                    var blurY = new DepthOfFieldBlurPostProcess("verticle blur", scene, new Vector2(0, 1.0), adjustedKernelSize, 1.0/Math.pow(2, i), null, this._depthOfFieldPass, i == 0 ? this._circleOfConfusion : null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
-                    blurY.autoClear = false;
-                    var blurX = new DepthOfFieldBlurPostProcess("horizontal blur", scene, new Vector2(1.0, 0), adjustedKernelSize, 1.0/Math.pow(2, i), null,  this._depthOfFieldPass, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
-                    blurX.autoClear = false;
-                    this._depthOfFieldBlurY.push(blurY);
-                    this._depthOfFieldBlurX.push(blurX);
+                case DepthOfFieldEffectBlurLevel.Medium: {
+                    blurCount = 2;
+                    kernelSize = 31;
+                    break;
                 }
-
-                // Merge blurred images with original image based on circleOfConfusion
-                this._depthOfFieldMerge = new DepthOfFieldMergePostProcess("depthOfFieldMerge", this._circleOfConfusion, this._depthOfFieldPass, this._depthOfFieldBlurY.slice(1), 1, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
-                this._depthOfFieldMerge.autoClear = false;
-                
-                // Set all post processes on the effect.
-                var effects= [this._circleOfConfusion, this._depthOfFieldPass];
-                for(var i=0;i<this._depthOfFieldBlurX.length;i++){
-                    effects.push(this._depthOfFieldBlurY[i]);
-                    effects.push(this._depthOfFieldBlurX[i]);
+                default: {
+                    kernelSize = 15;
+                    blurCount = 1;
+                    break;
                 }
-                effects.push(this._depthOfFieldMerge);
-                return effects;
-            }, true);
+            }
+            var adjustedKernelSize = kernelSize/Math.pow(2, blurCount-1);
+            for(var i = 0;i<blurCount;i++){
+                var blurY = new DepthOfFieldBlurPostProcess("verticle blur", scene, new Vector2(0, 1.0), adjustedKernelSize, 1.0/Math.pow(2, i), null, this._depthOfFieldPass, i == 0 ? this._circleOfConfusion : null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+                blurY.autoClear = false;
+                var blurX = new DepthOfFieldBlurPostProcess("horizontal blur", scene, new Vector2(1.0, 0), adjustedKernelSize, 1.0/Math.pow(2, i), null,  this._depthOfFieldPass, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+                blurX.autoClear = false;
+                this._depthOfFieldBlurY.push(blurY);
+                this._depthOfFieldBlurX.push(blurX);
+            }
+
+            // Merge blurred images with original image based on circleOfConfusion
+            this._depthOfFieldMerge = new DepthOfFieldMergePostProcess("depthOfFieldMerge", this._circleOfConfusion, this._depthOfFieldPass, this._depthOfFieldBlurY.slice(1), 1, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, pipelineTextureType);
+            this._depthOfFieldMerge.autoClear = false;
+            
+            // Set all post processes on the effect.
+            this._effects= [this._circleOfConfusion, this._depthOfFieldPass];
+            for(var i=0;i<this._depthOfFieldBlurX.length;i++){
+                this._effects.push(this._depthOfFieldBlurY[i]);
+                this._effects.push(this._depthOfFieldBlurX[i]);
+            }
+            this._effects.push(this._depthOfFieldMerge);
+        }
+
+        /**
+         * Depth texture to be used to compute the circle of confusion. This must be set here or in the constructor in order for the post process to function.
+         */
+        public set depthTexture(value: RenderTargetTexture){
+            this._circleOfConfusion.depthTexture = value;
         }
 
         /**

+ 40 - 0
src/PostProcess/babylon.imageProcessingPostProcess.ts

@@ -272,6 +272,45 @@
             this.imageProcessingConfiguration.vignetteEnabled = value;
         }
 
+        /**
+         * Gets wether the grain effect is enabled.
+         */
+        public get grainEnabled(): boolean {
+            return this.imageProcessingConfiguration.grainEnabled;
+        }
+        /**
+         * Sets wether the grain effect is enabled.
+         */
+        public set grainEnabled(value: boolean) {
+            this.imageProcessingConfiguration.grainEnabled = value;
+        }
+
+        /**
+         * Gets the grain effect's intensity.
+         */
+        public get grainIntensity(): number {
+            return this.imageProcessingConfiguration.grainIntensity;
+        }
+        /**
+         * Sets the grain effect's intensity.
+         */
+        public set grainIntensity(value: number) {
+            this.imageProcessingConfiguration.grainIntensity = value;
+        }
+
+        /**
+         * Gets wether the grain effect is animated.
+         */
+        public get grainAnimated(): boolean {
+            return this.imageProcessingConfiguration.grainAnimated;
+        }
+        /**
+         * Sets wether the grain effect is animated.
+         */
+        public set grainAnimated(value: boolean) {
+            this.imageProcessingConfiguration.grainAnimated = value;
+        }
+
         @serialize()
         private _fromLinearSpace = true;
         /**
@@ -310,6 +349,7 @@
             SAMPLER3DBGRMAP: false,
             IMAGEPROCESSINGPOSTPROCESS: false,
             EXPOSURE: false,
+            GRAIN: false,
         }
 
         constructor(name: string, options: number | PostProcessOptions, camera: Nullable<Camera> = null, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Engine.TEXTURETYPE_UNSIGNED_INT, imageProcessingConfiguration?: ImageProcessingConfiguration) {

+ 13 - 1
src/PostProcess/babylon.postProcess.ts

@@ -85,7 +85,7 @@
         private _parameters: string[];
         private _scaleRatio = new Vector2(1, 1);
         protected _indexParameters: any;
-        private _shareOutputWithPostProcess: PostProcess;
+        private _shareOutputWithPostProcess: Nullable<PostProcess>;
         private _texelSize = Vector2.Zero();
         private _forcedOutputTexture: InternalTexture;
 
@@ -297,6 +297,18 @@
         }
 
         /**
+         * Reverses the effect of calling shareOutputWith and returns the post process back to its original state. 
+         * This should be called if the post process that shares output with this post process is disabled/disposed.
+         */
+        public useOwnOutput() {
+            if(this._textures.length == 0){
+                this._textures = new SmartArray<InternalTexture>(2);
+            }
+
+            this._shareOutputWithPostProcess = null;
+        }
+
+        /**
          * Updates the effect with the current post process compile time values and recompiles the shader.
          * @param defines Define statements that should be added at the beginning of the shader. (default: null)
          * @param uniforms Set of uniform variables that will be passed to the shader. (default: null)

+ 1 - 1
src/Rendering/babylon.depthRenderer.ts

@@ -133,7 +133,7 @@
             var mesh = subMesh.getMesh();
 
             // Alpha test
-            if (material && material.needAlphaTesting()) {
+            if (material && material.needAlphaTesting() && material.getAlphaTestTexture()) {
                 defines.push("#define ALPHATEST");
                 if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
                     attribs.push(VertexBuffer.UVKind);

+ 4 - 4
src/Shaders/ShadersInclude/helperFunctions.fx

@@ -70,9 +70,9 @@ float getRand(vec2 seed) {
 	return fract(sin(dot(seed.xy ,vec2(12.9898,78.233))) * 43758.5453);
 }
 
-vec3 dither(vec2 seed, vec3 color) {
+float dither(vec2 seed, float varianceAmount) {
 	float rand = getRand(seed);
-	color += mix(-0.5/255.0, 0.5/255.0, rand);
-	color = max(color, 0.0);
-	return color;
+	float dither = mix(-varianceAmount/255.0, varianceAmount/255.0, rand);
+	
+	return dither;
 }

+ 5 - 0
src/Shaders/ShadersInclude/imageProcessingDeclaration.fx

@@ -25,4 +25,9 @@
 		uniform sampler2D txColorTransform;
 	#endif
 	uniform vec4 colorTransformSettings;
+#endif
+
+#ifdef GRAIN
+	uniform float grainVarianceAmount;
+	uniform float grainAnimatedSeed;
 #endif

+ 0 - 0
src/Shaders/background.fragment.fx


Некоторые файлы не были показаны из-за большого количества измененных файлов