Forráskód Böngészése

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

Julian 8 éve
szülő
commit
addc10eb11
43 módosított fájl, 23005 hozzáadás és 21965 törlés
  1. 3 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonStandardMaterial.cs
  2. 4933 4912
      dist/preview release/babylon.d.ts
  3. 44 44
      dist/preview release/babylon.js
  4. 297 176
      dist/preview release/babylon.max.js
  5. 4933 4912
      dist/preview release/babylon.module.d.ts
  6. 46 46
      dist/preview release/babylon.worker.js
  7. 5623 5602
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  8. 35 35
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  9. 318 228
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  10. 5623 5602
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  11. 1 1
      dist/preview release/gui/babylon.gui.min.js
  12. 572 5
      dist/preview release/inspector/babylon.inspector.bundle.js
  13. 3 4
      dist/preview release/inspector/babylon.inspector.min.js
  14. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  15. 4 1
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  16. 62 56
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  17. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  18. 4 1
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  19. 62 56
      dist/preview release/loaders/babylon.glTFFileLoader.js
  20. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  21. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  22. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  23. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  24. 3 2
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  25. 71 61
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  26. 185 165
      src/Loading/Plugins/babylon.babylonFileLoader.ts
  27. 16 1
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  28. 14 2
      src/Materials/PBR/babylon.pbrMaterial.ts
  29. 10 10
      src/Materials/babylon.material.ts
  30. 32 0
      src/Materials/babylon.shaderMaterial.ts
  31. 16 2
      src/Mesh/babylon.geometry.ts
  32. 4 3
      src/Mesh/babylon.subMesh.ts
  33. 4 0
      src/Particles/babylon.particleSystem.ts
  34. 6 1
      src/Rendering/babylon.renderingGroup.ts
  35. 2 2
      src/Shaders/ShadersInclude/helperFunctions.fx
  36. 1 1
      src/Shaders/ShadersInclude/pbrLightFunctions.fx
  37. 3 3
      src/Shaders/ShadersInclude/reflectionFunction.fx
  38. 15 13
      src/Shaders/pbr.fragment.fx
  39. 41 2
      src/Tools/babylon.extendedGamepad.ts
  40. 9 6
      src/babylon.engine.ts
  41. BIN
      tests/validation/ReferenceImages/Flat2009.png
  42. 1 1
      tests/validation/config.json
  43. 1 1
      tests/validation/validation.js

+ 3 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonStandardMaterial.cs

@@ -68,6 +68,9 @@ namespace BabylonExport.Entities
         [DataMember]
         public bool linkEmissiveWithDiffuse { get; set; }
 
+        [DataMember]
+        public bool twoSidedLighting { get; set; }
+
         public BabylonStandardMaterial() : base()
         {
             ambient = new[] {1.0f, 1.0f, 1.0f};

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 4933 - 4912
dist/preview release/babylon.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 44 - 44
dist/preview release/babylon.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 297 - 176
dist/preview release/babylon.max.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 4933 - 4912
dist/preview release/babylon.module.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 46 - 46
dist/preview release/babylon.worker.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 5623 - 5602
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 35 - 35
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 318 - 228
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 5623 - 5602
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 572 - 5
dist/preview release/inspector/babylon.inspector.bundle.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 3 - 4
dist/preview release/inspector/babylon.inspector.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


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

@@ -310,6 +310,9 @@ declare module BABYLON.GLTF2 {
         private _onLoaderComplete();
         private _loadData(data);
         private _addRightHandToLeftHandRootTransform();
+        private _getMeshes();
+        private _getSkeletons();
+        private _getAnimationTargets();
         private _showMeshes();
         private _startAnimations();
         private _loadScene(nodeNames);
@@ -336,7 +339,7 @@ declare module BABYLON.GLTF2 {
         removeLoaderPendingData(data: any): void;
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessProperties(material);
-        loadMaterial(index: number, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
+        loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
         createPbrMaterial(material: IGLTFMaterial): void;
         loadMaterialBaseProperties(material: IGLTFMaterial): void;
         loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void;

+ 62 - 56
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -380,25 +380,7 @@ var BABYLON;
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
                 var _this = this;
                 this._loadAsync(meshesNames, scene, data, rootUrl, function () {
-                    var meshes = [];
-                    if (_this._gltf.nodes) {
-                        for (var i = 0; i < _this._gltf.nodes.length; i++) {
-                            var node = _this._gltf.nodes[i];
-                            if (node.babylonMesh) {
-                                meshes.push(node.babylonMesh);
-                            }
-                        }
-                    }
-                    var skeletons = [];
-                    if (_this._gltf.skins) {
-                        for (var i = 0; i < _this._gltf.skins.length; i++) {
-                            var skin = _this._gltf.skins[i];
-                            if (skin.babylonSkeleton instanceof BABYLON.Skeleton) {
-                                skeletons.push(skin.babylonSkeleton);
-                            }
-                        }
-                    }
-                    onSuccess(meshes, null, skeletons);
+                    onSuccess(_this._getMeshes(), null, _this._getSkeletons());
                 }, onProgress, onError);
             };
             GLTFLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
@@ -482,26 +464,46 @@ var BABYLON;
                     }
                 }
             };
-            GLTFLoader.prototype._showMeshes = function () {
+            GLTFLoader.prototype._getMeshes = function () {
+                var meshes = [];
                 var nodes = this._gltf.nodes;
-                for (var i = 0; i < nodes.length; i++) {
-                    var node = nodes[i];
-                    if (node.babylonMesh) {
-                        node.babylonMesh.isVisible = true;
-                    }
+                if (nodes) {
+                    nodes.forEach(function (node) {
+                        if (node.babylonMesh) {
+                            meshes.push(node.babylonMesh);
+                        }
+                    });
                 }
+                return meshes;
             };
-            GLTFLoader.prototype._startAnimations = function () {
-                var animations = this._gltf.animations;
-                if (!animations) {
-                    return;
+            GLTFLoader.prototype._getSkeletons = function () {
+                var skeletons = [];
+                var skins = this._gltf.skins;
+                if (skins) {
+                    skins.forEach(function (skin) {
+                        if (skin.babylonSkeleton instanceof BABYLON.Skeleton) {
+                            skeletons.push(skin.babylonSkeleton);
+                        }
+                    });
                 }
-                for (var i = 0; i < animations.length; i++) {
-                    var animation = animations[i];
-                    for (var j = 0; j < animation.targets.length; j++) {
-                        this._babylonScene.beginAnimation(animation.targets[j], 0, Number.MAX_VALUE, true);
-                    }
+                return skeletons;
+            };
+            GLTFLoader.prototype._getAnimationTargets = function () {
+                var targets = [];
+                var animations = this._gltf.animations;
+                if (animations) {
+                    animations.forEach(function (animation) {
+                        targets.push.apply(targets, animation.targets);
+                    });
                 }
+                return targets;
+            };
+            GLTFLoader.prototype._showMeshes = function () {
+                this._getMeshes().forEach(function (mesh) { return mesh.isVisible = true; });
+            };
+            GLTFLoader.prototype._startAnimations = function () {
+                var _this = this;
+                this._getAnimationTargets().forEach(function (target) { return _this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true); });
             };
             GLTFLoader.prototype._loadScene = function (nodeNames) {
                 var _this = this;
@@ -512,6 +514,10 @@ var BABYLON;
                     node.parent = parentNode;
                     return true;
                 });
+                var materials = this._gltf.materials;
+                if (materials) {
+                    materials.forEach(function (material, index) { return material.index = index; });
+                }
                 if (nodeNames) {
                     if (!(nodeNames instanceof Array)) {
                         nodeNames = [nodeNames];
@@ -625,29 +631,30 @@ var BABYLON;
                             verticesStart: vertexData.positions.length,
                             verticesCount: subVertexData.positions.length,
                             indicesStart: vertexData.indices.length,
-                            indicesCount: subVertexData.indices.length
-                        });
-                        vertexData.merge(subVertexData);
-                        if (primitive.material === undefined) {
-                            babylonMultiMaterial.subMaterials[i] = _this._getDefaultMaterial();
-                        }
-                        else {
-                            _this.loadMaterial(primitive.material, function (babylonMaterial, isNew) {
-                                if (isNew && _this._parent.onMaterialLoaded) {
-                                    _this._parent.onMaterialLoaded(babylonMaterial);
-                                }
-                                if (_this._renderReady) {
-                                    babylonMaterial.forceCompilation(babylonMesh, function (babylonSubMaterial) {
-                                        babylonMultiMaterial.subMaterials[i] = babylonSubMaterial;
-                                    });
+                            indicesCount: subVertexData.indices.length,
+                            loadMaterial: function () {
+                                if (primitive.material === undefined) {
+                                    babylonMultiMaterial.subMaterials[i] = _this._getDefaultMaterial();
                                 }
                                 else {
-                                    babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                    var material = _this._gltf.materials[primitive.material];
+                                    _this.loadMaterial(material, function (babylonMaterial, isNew) {
+                                        if (isNew && _this._parent.onMaterialLoaded) {
+                                            _this._parent.onMaterialLoaded(babylonMaterial);
+                                        }
+                                        _this.addPendingData(material);
+                                        babylonMaterial.forceCompilation(babylonMesh, function (babylonMaterial) {
+                                            babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                            _this.removePendingData(material);
+                                        });
+                                    });
                                 }
-                            });
-                        }
+                            }
+                        });
+                        vertexData.merge(subVertexData);
                         if (++loadedPrimitives === totalPrimitives) {
                             geometry.setAllVerticesData(vertexData, false);
+                            subMeshInfos.forEach(function (info) { return info.loadMaterial(); });
                             // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                             // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
                             babylonMesh.subMeshes = [];
@@ -1112,9 +1119,7 @@ var BABYLON;
                 }
                 this.loadMaterialAlphaProperties(material, properties.baseColorFactor);
             };
-            GLTFLoader.prototype.loadMaterial = function (index, assign) {
-                var material = this._gltf.materials[index];
-                material.index = index;
+            GLTFLoader.prototype.loadMaterial = function (material, assign) {
                 if (material.babylonMaterial) {
                     assign(material.babylonMaterial, false);
                     return;
@@ -1449,7 +1454,8 @@ var BABYLON;
                 };
                 MSFTLOD.prototype.loadMaterialLOD = function (loader, material, materialLODs, lod, assign) {
                     var _this = this;
-                    loader.loadMaterial(materialLODs[lod], function (babylonMaterial, isNew) {
+                    var materialLOD = loader.gltf.materials[materialLODs[lod]];
+                    loader.loadMaterial(materialLOD, function (babylonMaterial, isNew) {
                         assign(babylonMaterial, isNew);
                         // Loading is complete if this is the highest quality LOD.
                         if (lod === 0) {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


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

@@ -805,6 +805,9 @@ declare module BABYLON.GLTF2 {
         private _onLoaderComplete();
         private _loadData(data);
         private _addRightHandToLeftHandRootTransform();
+        private _getMeshes();
+        private _getSkeletons();
+        private _getAnimationTargets();
         private _showMeshes();
         private _startAnimations();
         private _loadScene(nodeNames);
@@ -831,7 +834,7 @@ declare module BABYLON.GLTF2 {
         removeLoaderPendingData(data: any): void;
         private _getDefaultMaterial();
         private _loadMaterialMetallicRoughnessProperties(material);
-        loadMaterial(index: number, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
+        loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void;
         createPbrMaterial(material: IGLTFMaterial): void;
         loadMaterialBaseProperties(material: IGLTFMaterial): void;
         loadMaterialAlphaProperties(material: IGLTFMaterial, colorFactor?: number[]): void;

+ 62 - 56
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -2538,25 +2538,7 @@ var BABYLON;
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
                 var _this = this;
                 this._loadAsync(meshesNames, scene, data, rootUrl, function () {
-                    var meshes = [];
-                    if (_this._gltf.nodes) {
-                        for (var i = 0; i < _this._gltf.nodes.length; i++) {
-                            var node = _this._gltf.nodes[i];
-                            if (node.babylonMesh) {
-                                meshes.push(node.babylonMesh);
-                            }
-                        }
-                    }
-                    var skeletons = [];
-                    if (_this._gltf.skins) {
-                        for (var i = 0; i < _this._gltf.skins.length; i++) {
-                            var skin = _this._gltf.skins[i];
-                            if (skin.babylonSkeleton instanceof BABYLON.Skeleton) {
-                                skeletons.push(skin.babylonSkeleton);
-                            }
-                        }
-                    }
-                    onSuccess(meshes, null, skeletons);
+                    onSuccess(_this._getMeshes(), null, _this._getSkeletons());
                 }, onProgress, onError);
             };
             GLTFLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
@@ -2640,26 +2622,46 @@ var BABYLON;
                     }
                 }
             };
-            GLTFLoader.prototype._showMeshes = function () {
+            GLTFLoader.prototype._getMeshes = function () {
+                var meshes = [];
                 var nodes = this._gltf.nodes;
-                for (var i = 0; i < nodes.length; i++) {
-                    var node = nodes[i];
-                    if (node.babylonMesh) {
-                        node.babylonMesh.isVisible = true;
-                    }
+                if (nodes) {
+                    nodes.forEach(function (node) {
+                        if (node.babylonMesh) {
+                            meshes.push(node.babylonMesh);
+                        }
+                    });
                 }
+                return meshes;
             };
-            GLTFLoader.prototype._startAnimations = function () {
-                var animations = this._gltf.animations;
-                if (!animations) {
-                    return;
+            GLTFLoader.prototype._getSkeletons = function () {
+                var skeletons = [];
+                var skins = this._gltf.skins;
+                if (skins) {
+                    skins.forEach(function (skin) {
+                        if (skin.babylonSkeleton instanceof BABYLON.Skeleton) {
+                            skeletons.push(skin.babylonSkeleton);
+                        }
+                    });
                 }
-                for (var i = 0; i < animations.length; i++) {
-                    var animation = animations[i];
-                    for (var j = 0; j < animation.targets.length; j++) {
-                        this._babylonScene.beginAnimation(animation.targets[j], 0, Number.MAX_VALUE, true);
-                    }
+                return skeletons;
+            };
+            GLTFLoader.prototype._getAnimationTargets = function () {
+                var targets = [];
+                var animations = this._gltf.animations;
+                if (animations) {
+                    animations.forEach(function (animation) {
+                        targets.push.apply(targets, animation.targets);
+                    });
                 }
+                return targets;
+            };
+            GLTFLoader.prototype._showMeshes = function () {
+                this._getMeshes().forEach(function (mesh) { return mesh.isVisible = true; });
+            };
+            GLTFLoader.prototype._startAnimations = function () {
+                var _this = this;
+                this._getAnimationTargets().forEach(function (target) { return _this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true); });
             };
             GLTFLoader.prototype._loadScene = function (nodeNames) {
                 var _this = this;
@@ -2670,6 +2672,10 @@ var BABYLON;
                     node.parent = parentNode;
                     return true;
                 });
+                var materials = this._gltf.materials;
+                if (materials) {
+                    materials.forEach(function (material, index) { return material.index = index; });
+                }
                 if (nodeNames) {
                     if (!(nodeNames instanceof Array)) {
                         nodeNames = [nodeNames];
@@ -2783,29 +2789,30 @@ var BABYLON;
                             verticesStart: vertexData.positions.length,
                             verticesCount: subVertexData.positions.length,
                             indicesStart: vertexData.indices.length,
-                            indicesCount: subVertexData.indices.length
-                        });
-                        vertexData.merge(subVertexData);
-                        if (primitive.material === undefined) {
-                            babylonMultiMaterial.subMaterials[i] = _this._getDefaultMaterial();
-                        }
-                        else {
-                            _this.loadMaterial(primitive.material, function (babylonMaterial, isNew) {
-                                if (isNew && _this._parent.onMaterialLoaded) {
-                                    _this._parent.onMaterialLoaded(babylonMaterial);
-                                }
-                                if (_this._renderReady) {
-                                    babylonMaterial.forceCompilation(babylonMesh, function (babylonSubMaterial) {
-                                        babylonMultiMaterial.subMaterials[i] = babylonSubMaterial;
-                                    });
+                            indicesCount: subVertexData.indices.length,
+                            loadMaterial: function () {
+                                if (primitive.material === undefined) {
+                                    babylonMultiMaterial.subMaterials[i] = _this._getDefaultMaterial();
                                 }
                                 else {
-                                    babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                    var material = _this._gltf.materials[primitive.material];
+                                    _this.loadMaterial(material, function (babylonMaterial, isNew) {
+                                        if (isNew && _this._parent.onMaterialLoaded) {
+                                            _this._parent.onMaterialLoaded(babylonMaterial);
+                                        }
+                                        _this.addPendingData(material);
+                                        babylonMaterial.forceCompilation(babylonMesh, function (babylonMaterial) {
+                                            babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                            _this.removePendingData(material);
+                                        });
+                                    });
                                 }
-                            });
-                        }
+                            }
+                        });
+                        vertexData.merge(subVertexData);
                         if (++loadedPrimitives === totalPrimitives) {
                             geometry.setAllVerticesData(vertexData, false);
+                            subMeshInfos.forEach(function (info) { return info.loadMaterial(); });
                             // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                             // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
                             babylonMesh.subMeshes = [];
@@ -3270,9 +3277,7 @@ var BABYLON;
                 }
                 this.loadMaterialAlphaProperties(material, properties.baseColorFactor);
             };
-            GLTFLoader.prototype.loadMaterial = function (index, assign) {
-                var material = this._gltf.materials[index];
-                material.index = index;
+            GLTFLoader.prototype.loadMaterial = function (material, assign) {
                 if (material.babylonMaterial) {
                     assign(material.babylonMaterial, false);
                     return;
@@ -3607,7 +3612,8 @@ var BABYLON;
                 };
                 MSFTLOD.prototype.loadMaterialLOD = function (loader, material, materialLODs, lod, assign) {
                     var _this = this;
-                    loader.loadMaterial(materialLODs[lod], function (babylonMaterial, isNew) {
+                    var materialLOD = loader.gltf.materials[materialLODs[lod]];
+                    loader.loadMaterial(materialLOD, function (babylonMaterial, isNew) {
                         assign(babylonMaterial, isNew);
                         // Loading is complete if this is the highest quality LOD.
                         if (lod === 0) {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


+ 3 - 2
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -33,8 +33,9 @@ module BABYLON.GLTF2.Extensions {
             return true;
         }
 
-        private loadMaterialLOD(loader: GLTFLoader, material: IGLTFMaterial, materialLODs: number[], lod: number, assign: (material: Material, isNew: boolean) => void): void {
-            loader.loadMaterial(materialLODs[lod], (babylonMaterial, isNew) => {
+        private loadMaterialLOD(loader: GLTFLoader, material: IGLTFMaterial, materialLODs: number[], lod: number, assign: (babylonMaterial: Material, isNew: boolean) => void): void {
+            var materialLOD = loader.gltf.materials[materialLODs[lod]];
+            loader.loadMaterial(materialLOD, (babylonMaterial, isNew) => {
                 assign(babylonMaterial, isNew);
 
                 // Loading is complete if this is the highest quality LOD.

+ 71 - 61
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -83,27 +83,7 @@ module BABYLON.GLTF2 {
 
         public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void {
             this._loadAsync(meshesNames, scene, data, rootUrl, () => {
-                var meshes = [];
-                if (this._gltf.nodes) {
-                    for (var i = 0; i < this._gltf.nodes.length; i++) {
-                        var node = this._gltf.nodes[i];
-                        if (node.babylonMesh) {
-                            meshes.push(node.babylonMesh);
-                        }
-                    }
-                }
-
-                var skeletons = [];
-                if (this._gltf.skins) {
-                    for (var i = 0; i < this._gltf.skins.length; i++) {
-                        var skin = this._gltf.skins[i];
-                        if (skin.babylonSkeleton instanceof Skeleton) {
-                            skeletons.push(skin.babylonSkeleton);
-                        }
-                    }
-                }
-
-                onSuccess(meshes, null, skeletons);
+                onSuccess(this._getMeshes(), null, this._getSkeletons());
             }, onProgress, onError);
         }
 
@@ -204,28 +184,55 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private _showMeshes(): void {
+        private _getMeshes(): Mesh[] {
+            var meshes = [];
+
             var nodes = this._gltf.nodes;
-            for (var i = 0; i < nodes.length; i++) {
-                var node = nodes[i];
-                if (node.babylonMesh) {
-                    node.babylonMesh.isVisible = true;
-                }
+            if (nodes) {
+                nodes.forEach(node => {
+                    if (node.babylonMesh) {
+                        meshes.push(node.babylonMesh);
+                    }
+                });
             }
+
+            return meshes;
         }
 
-        private _startAnimations(): void {
-            var animations = this._gltf.animations;
-            if (!animations) {
-                return;
+        private _getSkeletons(): Skeleton[] {
+            var skeletons = [];
+
+            var skins = this._gltf.skins;
+            if (skins) {
+                skins.forEach(skin => {
+                    if (skin.babylonSkeleton instanceof Skeleton) {
+                        skeletons.push(skin.babylonSkeleton);
+                    }
+                });
             }
 
-            for (var i = 0; i < animations.length; i++) {
-                var animation = animations[i];
-                for (var j = 0; j < animation.targets.length; j++) {
-                    this._babylonScene.beginAnimation(animation.targets[j], 0, Number.MAX_VALUE, true);
-                }
+            return skeletons;
+        }
+
+        private _getAnimationTargets(): any[] {
+            var targets = [];
+
+            var animations = this._gltf.animations;
+            if (animations) {
+                animations.forEach(animation => {
+                    targets.push(...animation.targets);
+                });
             }
+
+            return targets;
+        }
+
+        private _showMeshes(): void {
+            this._getMeshes().forEach(mesh => mesh.isVisible = true);
+        }
+
+        private _startAnimations(): void {
+            this._getAnimationTargets().forEach(target => this._babylonScene.beginAnimation(target, 0, Number.MAX_VALUE, true));
         }
 
         private _loadScene(nodeNames: any): void {
@@ -238,6 +245,11 @@ module BABYLON.GLTF2 {
                 return true;
             });
 
+            var materials = this._gltf.materials;
+            if (materials) {
+                materials.forEach((material, index) => material.index = index);
+            }
+
             if (nodeNames) {
                 if (!(nodeNames instanceof Array)) {
                     nodeNames = [nodeNames];
@@ -362,7 +374,7 @@ module BABYLON.GLTF2 {
             vertexData.positions = [];
             vertexData.indices = [];
 
-            var subMeshInfos: { materialIndex: number, verticesStart: number, verticesCount: number, indicesStart: number, indicesCount: number }[] = [];
+            var subMeshInfos: { materialIndex: number, verticesStart: number, verticesCount: number, indicesStart: number, indicesCount: number, loadMaterial: () => void }[] = [];
 
             var loadedPrimitives = 0;
             var totalPrimitives = mesh.primitives.length;
@@ -383,34 +395,35 @@ module BABYLON.GLTF2 {
                         verticesStart: vertexData.positions.length,
                         verticesCount: subVertexData.positions.length,
                         indicesStart: vertexData.indices.length,
-                        indicesCount: subVertexData.indices.length
-                    });
-
-                    vertexData.merge(subVertexData);
-
-                    if (primitive.material === undefined) {
-                        babylonMultiMaterial.subMaterials[i] = this._getDefaultMaterial();
-                    }
-                    else {
-                        this.loadMaterial(primitive.material, (babylonMaterial, isNew) => {
-                            if (isNew && this._parent.onMaterialLoaded) {
-                                this._parent.onMaterialLoaded(babylonMaterial);
+                        indicesCount: subVertexData.indices.length,
+                        loadMaterial: () => {
+                            if (primitive.material === undefined) {
+                                babylonMultiMaterial.subMaterials[i] = this._getDefaultMaterial();
                             }
+                            else {
+                                var material = this._gltf.materials[primitive.material];
+                                this.loadMaterial(material, (babylonMaterial, isNew) => {
+                                    if (isNew && this._parent.onMaterialLoaded) {
+                                        this._parent.onMaterialLoaded(babylonMaterial);
+                                    }
 
-                            if (this._renderReady) {
-                                babylonMaterial.forceCompilation(babylonMesh, babylonSubMaterial => {
-                                    babylonMultiMaterial.subMaterials[i] = babylonSubMaterial;
+                                    this.addPendingData(material);
+                                    babylonMaterial.forceCompilation(babylonMesh, babylonMaterial => {
+                                        babylonMultiMaterial.subMaterials[i] = babylonMaterial;
+                                        this.removePendingData(material);
+                                    });
                                 });
                             }
-                            else {
-                                babylonMultiMaterial.subMaterials[i] = babylonMaterial;
-                            }
-                        });
-                    }
+                        }
+                    });
+
+                    vertexData.merge(subVertexData);
 
                     if (++loadedPrimitives === totalPrimitives) {
                         geometry.setAllVerticesData(vertexData, false);
 
+                        subMeshInfos.forEach(info => info.loadMaterial());
+
                         // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
                         // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
                         babylonMesh.subMeshes = [];
@@ -916,10 +929,7 @@ module BABYLON.GLTF2 {
             this.loadMaterialAlphaProperties(material, properties.baseColorFactor);
         }
 
-        public loadMaterial(index: number, assign: (babylonMaterial: Material, isNew: boolean) => void): void {
-            var material = this._gltf.materials[index];
-            material.index = index;
-
+        public loadMaterial(material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): void {
             if (material.babylonMaterial) {
                 assign(material.babylonMaterial, false);
                 return;

+ 185 - 165
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -10,8 +10,7 @@
         return null;
     };
 
-    var isDescendantOf = (mesh, names, hierarchyIds) => {
-        names = (names instanceof Array) ? names : [names];
+    var isDescendantOf = (mesh, names: Array<any>, hierarchyIds) => {
         for (var i in names) {
             if (mesh.name === names[i]) {
                 hierarchyIds.push(mesh.id);
@@ -49,149 +48,156 @@
                 var parsedData = JSON.parse(data);
                 log = "";
                 var fullDetails = SceneLoader.loggingLevel === SceneLoader.DETAILED_LOGGING;
+                if (!meshesNames) {
+                    meshesNames = null;
+                } else if (!Array.isArray(meshesNames)) {
+                    meshesNames = [meshesNames];
+                }
+                
+                if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
+                    var loadedSkeletonsIds = [];
+                    var loadedMaterialsIds = [];
+                    var hierarchyIds = [];
+                    var index: number;
+                    var cache: number;
+                    for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
+                        var parsedMesh = parsedData.meshes[index];
 
-                var loadedSkeletonsIds = [];
-                var loadedMaterialsIds = [];
-                var hierarchyIds = [];
-                var index: number;
-                var cache: number;
-                for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
-                    var parsedMesh = parsedData.meshes[index];
-
-                    if (!meshesNames || isDescendantOf(parsedMesh, meshesNames, hierarchyIds)) {
-                        if (meshesNames instanceof Array) {
-                            // Remove found mesh name from list.
-                            delete meshesNames[meshesNames.indexOf(parsedMesh.name)];
-                        }
-    
-                        //Geometry?
-                        if (parsedMesh.geometryId) {
-                            //does the file contain geometries?
-                            if (parsedData.geometries) {
-                                //find the correct geometry and add it to the scene
-                                var found: boolean = false;
-                                ["boxes", "spheres", "cylinders", "toruses", "grounds", "planes", "torusKnots", "vertexData"].forEach((geometryType: string) => {
-                                    if (found || !parsedData.geometries[geometryType] || !(parsedData.geometries[geometryType] instanceof Array)) {
-                                        return;
-                                    } else {
-                                        parsedData.geometries[geometryType].forEach((parsedGeometryData) => {
-                                            if (parsedGeometryData.id === parsedMesh.geometryId) {
-                                                switch (geometryType) {
-                                                    case "boxes":
-                                                        Geometry.Primitives.Box.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "spheres":
-                                                        Geometry.Primitives.Sphere.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "cylinders":
-                                                        Geometry.Primitives.Cylinder.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "toruses":
-                                                        Geometry.Primitives.Torus.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "grounds":
-                                                        Geometry.Primitives.Ground.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "planes":
-                                                        Geometry.Primitives.Plane.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "torusKnots":
-                                                        Geometry.Primitives.TorusKnot.Parse(parsedGeometryData, scene);
-                                                        break;
-                                                    case "vertexData":
-                                                        Geometry.Parse(parsedGeometryData, scene, rootUrl);
-                                                        break;
+                        if (meshesNames === null || isDescendantOf(parsedMesh, meshesNames, hierarchyIds)) {
+                            if (meshesNames !== null) {
+                                // Remove found mesh name from list.
+                                delete meshesNames[meshesNames.indexOf(parsedMesh.name)];
+                            }
+        
+                            //Geometry?
+                            if (parsedMesh.geometryId !== undefined && parsedMesh.geometryId !== null) {
+                                //does the file contain geometries?
+                                if (parsedData.geometries !== undefined && parsedData.geometries !== null) {
+                                    //find the correct geometry and add it to the scene
+                                    var found: boolean = false;
+                                    ["boxes", "spheres", "cylinders", "toruses", "grounds", "planes", "torusKnots", "vertexData"].forEach((geometryType: string) => {
+                                        if (found === true || !parsedData.geometries[geometryType] || !(Array.isArray(parsedData.geometries[geometryType]))) {
+                                            return;
+                                        } else {
+                                            parsedData.geometries[geometryType].forEach((parsedGeometryData) => {
+                                                if (parsedGeometryData.id === parsedMesh.geometryId) {
+                                                    switch (geometryType) {
+                                                        case "boxes":
+                                                            Geometry.Primitives.Box.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "spheres":
+                                                            Geometry.Primitives.Sphere.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "cylinders":
+                                                            Geometry.Primitives.Cylinder.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "toruses":
+                                                            Geometry.Primitives.Torus.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "grounds":
+                                                            Geometry.Primitives.Ground.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "planes":
+                                                            Geometry.Primitives.Plane.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "torusKnots":
+                                                            Geometry.Primitives.TorusKnot.Parse(parsedGeometryData, scene);
+                                                            break;
+                                                        case "vertexData":
+                                                            Geometry.Parse(parsedGeometryData, scene, rootUrl);
+                                                            break;
+                                                    }
+                                                    found = true;
                                                 }
-                                                found = true;
-                                            }
-                                        });
+                                            });
 
+                                        }
+                                    });
+                                    if (found === false) {
+                                        Tools.Warn("Geometry not found for mesh " + parsedMesh.id);
                                     }
-                                });
-                                if (!found) {
-                                    Tools.Warn("Geometry not found for mesh " + parsedMesh.id);
                                 }
                             }
-                        }
-    
-                        // Material ?
-                        if (parsedMesh.materialId) {
-                            var materialFound = (loadedMaterialsIds.indexOf(parsedMesh.materialId) !== -1);
-                            if (!materialFound && parsedData.multiMaterials) {
-                                for (var multimatIndex = 0, multimatCache = parsedData.multiMaterials.length; multimatIndex < multimatCache; multimatIndex++) {
-                                    var parsedMultiMaterial = parsedData.multiMaterials[multimatIndex];
-                                    if (parsedMultiMaterial.id === parsedMesh.materialId) {
-                                        for (var matIndex = 0, matCache = parsedMultiMaterial.materials.length; matIndex < matCache; matIndex++) {
-                                            var subMatId = parsedMultiMaterial.materials[matIndex];
-                                            loadedMaterialsIds.push(subMatId);
-                                            var mat = parseMaterialById(subMatId, parsedData, scene, rootUrl);
-                                            log += "\n\tMaterial " + mat.toString(fullDetails);
+        
+                            // Material ?
+                            if (parsedMesh.materialId) {
+                                var materialFound = (loadedMaterialsIds.indexOf(parsedMesh.materialId) !== -1);
+                                if (materialFound === false && parsedData.multiMaterials !== undefined && parsedData.multiMaterials !== null) {
+                                    for (var multimatIndex = 0, multimatCache = parsedData.multiMaterials.length; multimatIndex < multimatCache; multimatIndex++) {
+                                        var parsedMultiMaterial = parsedData.multiMaterials[multimatIndex];
+                                        if (parsedMultiMaterial.id === parsedMesh.materialId) {
+                                            for (var matIndex = 0, matCache = parsedMultiMaterial.materials.length; matIndex < matCache; matIndex++) {
+                                                var subMatId = parsedMultiMaterial.materials[matIndex];
+                                                loadedMaterialsIds.push(subMatId);
+                                                var mat = parseMaterialById(subMatId, parsedData, scene, rootUrl);
+                                                log += "\n\tMaterial " + mat.toString(fullDetails);
+                                            }
+                                            loadedMaterialsIds.push(parsedMultiMaterial.id);
+                                            var mmat = Material.ParseMultiMaterial(parsedMultiMaterial, scene);
+                                            materialFound = true;
+                                            log += "\n\tMulti-Material " + mmat.toString(fullDetails);
+                                            break;
                                         }
-                                        loadedMaterialsIds.push(parsedMultiMaterial.id);
-                                        var mmat = Material.ParseMultiMaterial(parsedMultiMaterial, scene);
-                                        materialFound = true;
-                                        log += "\n\tMulti-Material " + mmat.toString(fullDetails);
-                                        break;
                                     }
                                 }
-                            }
 
-                            if (!materialFound) {
-                                loadedMaterialsIds.push(parsedMesh.materialId);
-                                var mat = parseMaterialById(parsedMesh.materialId, parsedData, scene, rootUrl);
-                                if (!mat) {
-                                    Tools.Warn("Material not found for mesh " + parsedMesh.id);
-                                } else {
-                                    log += "\n\tMaterial " + mat.toString(fullDetails);
+                                if (materialFound === false) {
+                                    loadedMaterialsIds.push(parsedMesh.materialId);
+                                    var mat = parseMaterialById(parsedMesh.materialId, parsedData, scene, rootUrl);
+                                    if (!mat) {
+                                        Tools.Warn("Material not found for mesh " + parsedMesh.id);
+                                    } else {
+                                        log += "\n\tMaterial " + mat.toString(fullDetails);
+                                    }
                                 }
                             }
-                        }
-    
-                        // Skeleton ?
-                        if (parsedMesh.skeletonId > -1 && scene.skeletons) {
-                            var skeletonAlreadyLoaded = (loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1);
-                            if (!skeletonAlreadyLoaded) {
-                                for (var skeletonIndex = 0, skeletonCache = parsedData.skeletons.length; skeletonIndex < skeletonCache; skeletonIndex++) {
-                                    var parsedSkeleton = parsedData.skeletons[skeletonIndex];
-                                    if (parsedSkeleton.id === parsedMesh.skeletonId) {
-                                        var skeleton = Skeleton.Parse(parsedSkeleton, scene);
-                                        skeletons.push(skeleton);
-                                        loadedSkeletonsIds.push(parsedSkeleton.id);
-                                        log += "\n\tSkeleton " + skeleton.toString(fullDetails);
+        
+                            // Skeleton ?
+                            if (parsedMesh.skeletonId > -1 && parsedData.skeletons !== undefined && parsedData.skeletons !== null) {
+                                var skeletonAlreadyLoaded = (loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1);
+                                if (skeletonAlreadyLoaded === false) {
+                                    for (var skeletonIndex = 0, skeletonCache = parsedData.skeletons.length; skeletonIndex < skeletonCache; skeletonIndex++) {
+                                        var parsedSkeleton = parsedData.skeletons[skeletonIndex];
+                                        if (parsedSkeleton.id === parsedMesh.skeletonId) {
+                                            var skeleton = Skeleton.Parse(parsedSkeleton, scene);
+                                            skeletons.push(skeleton);
+                                            loadedSkeletonsIds.push(parsedSkeleton.id);
+                                            log += "\n\tSkeleton " + skeleton.toString(fullDetails);
+                                        }
                                     }
                                 }
                             }
-                        }
 
-                        var mesh = Mesh.Parse(parsedMesh, scene, rootUrl);
-                        meshes.push(mesh);
-                        log += "\n\tMesh " + mesh.toString(fullDetails);
+                            var mesh = Mesh.Parse(parsedMesh, scene, rootUrl);
+                            meshes.push(mesh);
+                            log += "\n\tMesh " + mesh.toString(fullDetails);
+                        }
                     }
-                }
-    
-                // Connecting parents
-                var currentMesh: AbstractMesh;
-                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                    currentMesh = scene.meshes[index];
-                    if (currentMesh._waitingParentId) {
-                        currentMesh.parent = scene.getLastEntryByID(currentMesh._waitingParentId);
-                        currentMesh._waitingParentId = undefined;
+        
+                    // Connecting parents
+                    var currentMesh: AbstractMesh;
+                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                        currentMesh = scene.meshes[index];
+                        if (currentMesh._waitingParentId) {
+                            currentMesh.parent = scene.getLastEntryByID(currentMesh._waitingParentId);
+                            currentMesh._waitingParentId = undefined;
+                        }
                     }
-                }
-    
-                // freeze and compute world matrix application
-                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                    currentMesh = scene.meshes[index];
-                    if (currentMesh._waitingFreezeWorldMatrix) {
-                        currentMesh.freezeWorldMatrix();
-                        currentMesh._waitingFreezeWorldMatrix = undefined;
-                    } else {
-                        currentMesh.computeWorldMatrix(true);
+        
+                    // freeze and compute world matrix application
+                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                        currentMesh = scene.meshes[index];
+                        if (currentMesh._waitingFreezeWorldMatrix) {
+                            currentMesh.freezeWorldMatrix();
+                            currentMesh._waitingFreezeWorldMatrix = undefined;
+                        } else {
+                            currentMesh.computeWorldMatrix(true);
+                        }
                     }
                 }
     
                 // Particles
-                if (parsedData.particleSystems) {
+                if (parsedData.particleSystems !== undefined && parsedData.particleSystems !== null) {
                     for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
                         var parsedParticleSystem = parsedData.particleSystems[index];
                         if (hierarchyIds.indexOf(parsedParticleSystem.emitterId) !== -1) {
@@ -225,11 +231,19 @@
                 var fullDetails = SceneLoader.loggingLevel === SceneLoader.DETAILED_LOGGING;
                 
                 // Scene
-                scene.useDelayedTextureLoading = parsedData.useDelayedTextureLoading && !BABYLON.SceneLoader.ForceFullSceneLoadingForIncremental;
-                scene.autoClear = parsedData.autoClear;
-                scene.clearColor = BABYLON.Color4.FromArray(parsedData.clearColor);
-                scene.ambientColor = BABYLON.Color3.FromArray(parsedData.ambientColor);
-                if (parsedData.gravity) {
+                if (parsedData.useDelayedTextureLoading !== undefined && parsedData.useDelayedTextureLoading !== null) {
+                    scene.useDelayedTextureLoading = parsedData.useDelayedTextureLoading && !BABYLON.SceneLoader.ForceFullSceneLoadingForIncremental;
+                }
+                if (parsedData.autoClear !== undefined && parsedData.autoClear !== null) {
+                    scene.autoClear = parsedData.autoClear;
+                }
+                if (parsedData.clearColor !== undefined && parsedData.clearColor !== null) {
+                    scene.clearColor = BABYLON.Color4.FromArray(parsedData.clearColor);
+                }
+                if (parsedData.ambientColor !== undefined && parsedData.ambientColor !== null) {
+                    scene.ambientColor = BABYLON.Color3.FromArray(parsedData.ambientColor);
+                }
+                if (parsedData.gravity !== undefined && parsedData.gravity !== null) {
                     scene.gravity = BABYLON.Vector3.FromArray(parsedData.gravity);
                 }
                 
@@ -264,12 +278,12 @@
                 }
                 
                 // Metadata
-                if (parsedData.metadata !== undefined) {
+                if (parsedData.metadata !== undefined && parsedData.metadata !== null) {
                     scene.metadata = parsedData.metadata;
                 }
                 
                 //collisions, if defined. otherwise, default is true
-                if (parsedData.collisionsEnabled != undefined) {
+                if (parsedData.collisionsEnabled !== undefined && parsedData.collisionsEnabled !== null) {
                     scene.collisionsEnabled = parsedData.collisionsEnabled;
                 }
                 scene.workerCollisions = !!parsedData.workerCollisions;
@@ -277,15 +291,17 @@
                 var index: number;
                 var cache: number;
                 // Lights
-                for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
-                    var parsedLight = parsedData.lights[index];
-                    var light = Light.Parse(parsedLight, scene);
-                    log += (index === 0 ? "\n\tLights:" : "");
-                    log += "\n\t\t" + light.toString(fullDetails);
+                if (parsedData.lights !== undefined && parsedData.lights !== null) {
+                    for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
+                        var parsedLight = parsedData.lights[index];
+                        var light = Light.Parse(parsedLight, scene);
+                        log += (index === 0 ? "\n\tLights:" : "");
+                        log += "\n\t\t" + light.toString(fullDetails);
+                    }
                 }
     
                 // Animations
-                if (parsedData.animations) {
+                if (parsedData.animations !== undefined && parsedData.animations !== null) {
                     for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
                         var parsedAnimation = parsedData.animations[index];
                         var animation = Animation.Parse(parsedAnimation);
@@ -300,7 +316,7 @@
                 }
     
                 // Materials
-                if (parsedData.materials) {
+                if (parsedData.materials !== undefined && parsedData.materials !== null) {
                     for (index = 0, cache = parsedData.materials.length; index < cache; index++) {
                         var parsedMaterial = parsedData.materials[index];
                         var mat = Material.Parse(parsedMaterial, scene, rootUrl);
@@ -309,7 +325,7 @@
                     }
                 }
 
-                if (parsedData.multiMaterials) {
+                if (parsedData.multiMaterials !== undefined && parsedData.multiMaterials !== null) {
                     for (index = 0, cache = parsedData.multiMaterials.length; index < cache; index++) {
                         var parsedMultiMaterial = parsedData.multiMaterials[index];
                         var mmat = Material.ParseMultiMaterial(parsedMultiMaterial, scene);
@@ -319,14 +335,14 @@
                 }
 
                 // Morph targets
-                if (parsedData.morphTargetManagers) {
+                if (parsedData.morphTargetManagers !== undefined && parsedData.morphTargetManagers !== null) {
                     for (var managerData of parsedData.morphTargetManagers) {
                         var parsedManager = MorphTargetManager.Parse(managerData, scene);
                     }
                 }
     
                 // Skeletons
-                if (parsedData.skeletons) {
+                if (parsedData.skeletons !== undefined && parsedData.skeletons !== null) {
                     for (index = 0, cache = parsedData.skeletons.length; index < cache; index++) {
                         var parsedSkeleton = parsedData.skeletons[index];
                         var skeleton = Skeleton.Parse(parsedSkeleton, scene);
@@ -337,10 +353,10 @@
     
                 // Geometries
                 var geometries = parsedData.geometries;
-                if (geometries) {
+                if (geometries !== undefined && geometries !== null) {
                     // Boxes
                     var boxes = geometries.boxes;
-                    if (boxes) {
+                    if (boxes !== undefined && boxes !== null) {
                         for (index = 0, cache = boxes.length; index < cache; index++) {
                             var parsedBox = boxes[index];
                             Geometry.Primitives.Box.Parse(parsedBox, scene);
@@ -349,7 +365,7 @@
     
                     // Spheres
                     var spheres = geometries.spheres;
-                    if (spheres) {
+                    if (spheres !== undefined && spheres !== null) {
                         for (index = 0, cache = spheres.length; index < cache; index++) {
                             var parsedSphere = spheres[index];
                             Geometry.Primitives.Sphere.Parse(parsedSphere, scene);
@@ -358,7 +374,7 @@
     
                     // Cylinders
                     var cylinders = geometries.cylinders;
-                    if (cylinders) {
+                    if (cylinders !== undefined && cylinders !== null) {
                         for (index = 0, cache = cylinders.length; index < cache; index++) {
                             var parsedCylinder = cylinders[index];
                             Geometry.Primitives.Cylinder.Parse(parsedCylinder, scene);
@@ -367,7 +383,7 @@
     
                     // Toruses
                     var toruses = geometries.toruses;
-                    if (toruses) {
+                    if (toruses !== undefined && toruses !== null) {
                         for (index = 0, cache = toruses.length; index < cache; index++) {
                             var parsedTorus = toruses[index];
                             Geometry.Primitives.Torus.Parse(parsedTorus, scene);
@@ -376,7 +392,7 @@
     
                     // Grounds
                     var grounds = geometries.grounds;
-                    if (grounds) {
+                    if (grounds !== undefined && grounds !== null) {
                         for (index = 0, cache = grounds.length; index < cache; index++) {
                             var parsedGround = grounds[index];
                             Geometry.Primitives.Ground.Parse(parsedGround, scene);
@@ -385,7 +401,7 @@
     
                     // Planes
                     var planes = geometries.planes;
-                    if (planes) {
+                    if (planes !== undefined && planes !== null) {
                         for (index = 0, cache = planes.length; index < cache; index++) {
                             var parsedPlane = planes[index];
                             Geometry.Primitives.Plane.Parse(parsedPlane, scene);
@@ -394,7 +410,7 @@
     
                     // TorusKnots
                     var torusKnots = geometries.torusKnots;
-                    if (torusKnots) {
+                    if (torusKnots !== undefined && torusKnots !== null) {
                         for (index = 0, cache = torusKnots.length; index < cache; index++) {
                             var parsedTorusKnot = torusKnots[index];
                             Geometry.Primitives.TorusKnot.Parse(parsedTorusKnot, scene);
@@ -403,7 +419,7 @@
     
                     // VertexData
                     var vertexData = geometries.vertexData;
-                    if (vertexData) {
+                    if (vertexData !== undefined && vertexData !== null) {
                         for (index = 0, cache = vertexData.length; index < cache; index++) {
                             var parsedVertexData = vertexData[index];
                             Geometry.Parse(parsedVertexData, scene, rootUrl);
@@ -412,21 +428,25 @@
                 }
     
                 // Meshes
-                for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
-                    var parsedMesh = parsedData.meshes[index];
-                    var mesh = <AbstractMesh>Mesh.Parse(parsedMesh, scene, rootUrl);
-                    log += (index === 0 ? "\n\tMeshes:" : "");
-                    log += "\n\t\t" + mesh.toString(fullDetails);
+                if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
+                    for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
+                        var parsedMesh = parsedData.meshes[index];
+                        var mesh = <AbstractMesh>Mesh.Parse(parsedMesh, scene, rootUrl);
+                        log += (index === 0 ? "\n\tMeshes:" : "");
+                        log += "\n\t\t" + mesh.toString(fullDetails);
+                    }
                 }
     
                 // Cameras
-                for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
-                    var parsedCamera = parsedData.cameras[index];
-                    var camera = Camera.Parse(parsedCamera, scene);
-                    log += (index === 0 ? "\n\tCameras:" : "");
-                    log += "\n\t\t" + camera.toString(fullDetails);
+                if (parsedData.cameras !== undefined && parsedData.cameras !== null) {
+                    for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
+                        var parsedCamera = parsedData.cameras[index];
+                        var camera = Camera.Parse(parsedCamera, scene);
+                        log += (index === 0 ? "\n\tCameras:" : "");
+                        log += "\n\t\t" + camera.toString(fullDetails);
+                    }
                 }
-                if (parsedData.activeCameraID) {
+                if (parsedData.activeCameraID !== undefined && parsedData.activeCameraID !== null) {
                     scene.setActiveCameraByID(parsedData.activeCameraID);
                 }
     
@@ -450,7 +470,7 @@
                 // Sounds
                 var loadedSounds: Sound[] = [];
                 var loadedSound: Sound;
-                if (AudioEngine && parsedData.sounds) {
+                if (AudioEngine && parsedData.sounds !== undefined && parsedData.sounds !== null) {
                     for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
                         var parsedSound = parsedData.sounds[index];
                         if (Engine.audioEngine.canUseWebAudio) {
@@ -495,7 +515,7 @@
                 }
     
                 // Particles Systems
-                if (parsedData.particleSystems) {
+                if (parsedData.particleSystems !== undefined && parsedData.particleSystems !== null) {
                     for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
                         var parsedParticleSystem = parsedData.particleSystems[index];
                         ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl);
@@ -503,7 +523,7 @@
                 }
     
                 // Lens flares
-                if (parsedData.lensFlareSystems) {
+                if (parsedData.lensFlareSystems !== undefined && parsedData.lensFlareSystems !== null) {
                     for (index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
                         var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
                         LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
@@ -511,7 +531,7 @@
                 }
     
                 // Shadows
-                if (parsedData.shadowGenerators) {
+                if (parsedData.shadowGenerators !== undefined && parsedData.shadowGenerators !== null) {
                     for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
                         var parsedShadowGenerator = parsedData.shadowGenerators[index];
                         ShadowGenerator.Parse(parsedShadowGenerator, scene);
@@ -549,7 +569,7 @@
                 }
     
                 // Actions (scene)
-                if (parsedData.actions) {
+                if (parsedData.actions !== undefined && parsedData.actions !== null) {
                     ActionManager.Parse(parsedData.actions, null, scene);
                 }
     

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

@@ -117,6 +117,8 @@
         public FOG = false;
         public LOGARITHMICDEPTH = false;
 
+        public FORCENORMALFORWARD = false;
+
         constructor() {
             super();
             this.rebuild();
@@ -398,6 +400,12 @@
         protected _forceIrradianceInFragment = false;
 
         /**
+         * Force normal to face away from face.
+         * (Temporary internal fix to remove before 3.1)
+         */
+        protected _forceNormalForward = false;
+
+        /**
          * Default configuration related to image processing available in the PBR Material.
          */
         @serializeAsImageProcessingConfiguration()
@@ -801,6 +809,8 @@
                 this._imageProcessingConfiguration.prepareDefines(defines);
             }
 
+            defines.FORCENORMALFORWARD = this._forceNormalForward;
+
             // Misc.
             MaterialHelper.PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, defines);
 
@@ -1251,7 +1261,12 @@
                 // Colors
                 scene.ambientColor.multiplyToRef(this._ambientColor, this._globalAmbientColor);
 
-                effect.setVector3("vEyePosition", scene._mirroredCameraPosition ? scene._mirroredCameraPosition : scene.activeCamera.position);
+                var eyePosition = scene._mirroredCameraPosition ? scene._mirroredCameraPosition : scene.activeCamera.position;
+                effect.setFloat4("vEyePosition", 
+                    eyePosition.x,
+                    eyePosition.y,
+                    eyePosition.z,
+                    scene._mirroredCameraPosition ? -1 : 1);
                 effect.setColor3("vAmbientColor", this._globalAmbientColor);
             }
 

+ 14 - 2
src/Materials/PBR/babylon.pbrMaterial.ts

@@ -405,6 +405,14 @@
         public environmentBRDFTexture: BaseTexture = null;
 
         /**
+         * Force normal to face away from face.
+         * (Temporary internal fix to remove before 3.1)
+         */
+        @serialize()
+        @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+        public forceNormalForward = false;
+
+        /**
          * Gets the image processing configuration used either in this material.
          */
         public get imageProcessingConfiguration(): ImageProcessingConfiguration {
@@ -564,8 +572,8 @@
                 activeTextures.push(this._emissiveTexture);
             }
 
-            if (this._reflectionTexture) {
-                activeTextures.push(this._reflectionTexture);
+            if (this._reflectivityTexture) {
+                activeTextures.push(this._reflectivityTexture);
             }
 
             if (this._metallicTexture) {
@@ -612,6 +620,10 @@
                 return true;
             }     
 
+            if (this._reflectivityTexture === texture) {
+                return true;
+            }                
+
             if (this._metallicTexture === texture) {
                 return true;
             }      

+ 10 - 10
src/Materials/babylon.material.ts

@@ -349,7 +349,7 @@
 
         constructor(name: string, scene: Scene, doNotAdd?: boolean) {
             this.name = name;
-            this.id = name;
+            this.id = name || Tools.RandomId();
 
             this._scene = scene || Engine.LastCreatedScene;
 
@@ -519,29 +519,29 @@
         // Force shader compilation including textures ready check
         public forceCompilation(mesh: AbstractMesh, onCompiled: (material: Material) => void, options?: { alphaTest: boolean }): void {
             var subMesh = new BaseSubMesh();
-            var scene = this.getScene();
-            var engine = scene.getEngine();
+            var engine = this.getScene().getEngine();
 
-            var beforeRenderCallback = () => {
+            var checkReady = () => {
                 if (subMesh._materialDefines) {
                     subMesh._materialDefines._renderId = -1;
                 }
-                
+
                 var alphaTestState = engine.getAlphaTesting();
                 engine.setAlphaTesting(options ? options.alphaTest : this.needAlphaTesting());
-                
-                if (this.isReadyForSubMesh(mesh, subMesh)) {
-                    scene.unregisterBeforeRender(beforeRenderCallback);
 
+                if (this.isReadyForSubMesh(mesh, subMesh)) {
                     if (onCompiled) {
                         onCompiled(this);
                     }
                 }
+                else {
+                    setTimeout(checkReady, 16);
+                }
 
                 engine.setAlphaTesting(alphaTestState);
             };
 
-            scene.registerBeforeRender(beforeRenderCallback);
+            checkReady();
         }
        
         public markAsDirty(flag: number): void {
@@ -579,7 +579,7 @@
                     }
 
                     if (!subMesh._materialDefines) {
-                        return;
+                        continue;
                     }
 
                     func(subMesh._materialDefines);

+ 32 - 0
src/Materials/babylon.shaderMaterial.ts

@@ -7,6 +7,7 @@
         private _floats: { [name: string]: number } = {};
         private _floatsArrays: { [name: string]: number[] } = {};
         private _colors3: { [name: string]: Color3 } = {};
+        private _colors3Arrays: { [name: string]: number[] } = {};
         private _colors4: { [name: string]: Color4 } = {};
         private _vectors2: { [name: string]: Vector2 } = {};
         private _vectors3: { [name: string]: Vector3 } = {};
@@ -92,6 +93,14 @@
 
             return this;
         }
+        public setColor3Array(name: string, value: Color3[]): ShaderMaterial {
+            this._checkUniform(name);
+            this._colors3Arrays[name] = value.reduce((arr, color) => {
+                color.toArray(arr, arr.length);
+                return arr;
+            }, [])
+            return this;
+        }
 
         public setColor4(name: string, value: Color4): ShaderMaterial {
             this._checkUniform(name);
@@ -316,6 +325,10 @@
                     this._effect.setColor3(name, this._colors3[name]);
                 }
 
+                for (name in this._colors3Arrays) {
+                    this._effect.setArray3(name, this._colors3Arrays[name]);
+                }
+
                 // Color4      
                 for (name in this._colors4) {
                     var color = this._colors4[name];
@@ -471,6 +484,12 @@
                 serializationObject.colors3[name] = this._colors3[name].asArray();
             }
 
+            // Color3 array
+            serializationObject.colors3Arrays = {};
+            for (name in this._colors3Arrays) {
+                serializationObject.colors3Arrays[name] = this._colors3Arrays[name];
+            }
+
             // Color4  
             serializationObject.colors4 = {};
             for (name in this._colors4) {
@@ -558,6 +577,19 @@
                 material.setColor3(name, Color3.FromArray(source.colors3[name]));
             }
 
+            // Color3 arrays
+            for (name in source.colors3Arrays) {
+                const colors: Color3[] = source.colors3Arrays[name].reduce((arr, num, i) => {
+                    if (i % 3 === 0) {
+                        arr.push([num]);
+                    } else {
+                        arr[arr.length - 1].push(num);
+                    }
+                    return arr;
+                }, []).map(color => Color3.FromArray(color));
+                material.setColor3Array(name, colors);
+            }
+
             // Color4      
             for (name in source.colors4) {
                 material.setColor4(name, Color4.FromArray(source.colors4[name]));

+ 16 - 2
src/Mesh/babylon.geometry.ts

@@ -842,7 +842,7 @@
                 }
 
                 if (binaryInfo.matricesWeightsAttrDesc && binaryInfo.matricesWeightsAttrDesc.count > 0) {
-                    var matricesWeightsData = new Float32Array(parsedGeometry, binaryInfo.matricesWeightsAttrDesc.offset, binaryInfo.matricesWeightsAttrDesc.count);
+                    var matricesWeightsData = new Float32Array(parsedGeometry, binaryInfo.matricesWeightsAttrDesc.offset, binaryInfo.matricesWeightsAttrDesc.count);                    
                     mesh.setVerticesData(VertexBuffer.MatricesWeightsKind, matricesWeightsData, false);
                 }
 
@@ -939,10 +939,12 @@
                 }
 
                 if (parsedGeometry.matricesWeights) {
+                    Geometry._CleanMatricesWeights(parsedGeometry.matricesWeights, parsedGeometry.numBoneInfluencers);
                     mesh.setVerticesData(VertexBuffer.MatricesWeightsKind, parsedGeometry.matricesWeights, parsedGeometry.matricesWeights._updatable);
                 }
 
-                if (parsedGeometry.matricesWeightsExtra) {
+                if (parsedGeometry.matricesWeightsExtra) {                    
+                    Geometry._CleanMatricesWeights(parsedGeometry.matricesWeightsExtra, parsedGeometry.numBoneInfluencers);
                     mesh.setVerticesData(VertexBuffer.MatricesWeightsExtraKind, parsedGeometry.matricesWeightsExtra, parsedGeometry.matricesWeights._updatable);
                 }
 
@@ -974,6 +976,18 @@
             }
         }
 
+        private static _CleanMatricesWeights(matricesWeights: number[], influencers: number): void {
+            let size = matricesWeights.length;
+            for (var i = 0; i < size; i += influencers) {
+                let weight = 0;
+                for (var j = 0; j < influencers - 1; j++) {
+                    weight += matricesWeights[i + j];
+                }
+
+                matricesWeights[i + (influencers - 1)] = Math.max(0, 1.0 - weight);
+            }
+        }
+
         public static Parse(parsedVertexData: any, scene: Scene, rootUrl: string): Geometry {
             if (scene.getGeometryByID(parsedVertexData.id)) {
                 return null; // null since geometry could be something else than a box...

+ 4 - 3
src/Mesh/babylon.subMesh.ts

@@ -9,6 +9,9 @@
 
         public setEffect(effect: Effect, defines?: MaterialDefines) {
             if (this._materialEffect === effect) {
+                if (!effect) {
+                    this._materialDefines = undefined;
+                }
                 return;
             }
             this._materialDefines = defines;
@@ -100,9 +103,7 @@
 
                 if (this._currentMaterial !== effectiveMaterial) {
                     this._currentMaterial = effectiveMaterial;
-                    if (this._materialDefines) {
-                        this._materialDefines.markAllAsDirty();
-                    }
+                    this._materialDefines = undefined;
                 }
 
                 return effectiveMaterial;

+ 4 - 0
src/Particles/babylon.particleSystem.ts

@@ -73,6 +73,7 @@
         }
 
         public updateFunction: (particles: Particle[]) => void;
+        public onAnimationEnd: () => void = null;
 
         public blendMode = ParticleSystem.BLENDMODE_ONEONE;
 
@@ -372,6 +373,9 @@
             if (this._stopped) {
                 if (!this._alive) {
                     this._started = false;
+                    if (this.onAnimationEnd) {
+                        this.onAnimationEnd();
+                    }
                     if (this.disposeOnStop) {
                         this._scene._toBeDisposed.push(this);
                     }

+ 6 - 1
src/Rendering/babylon.renderingGroup.ts

@@ -126,12 +126,17 @@
                 this._renderTransparent(this._transparentSubMeshes);
                 engine.setAlphaMode(Engine.ALPHA_DISABLE);
             }
-            engine.setStencilBuffer(stencilState);
+
+            // Set back stencil to false in case it changes before the edge renderer.
+            engine.setStencilBuffer(false);
 
             // Edges
             for (var edgesRendererIndex = 0; edgesRendererIndex < this._edgesRenderers.length; edgesRendererIndex++) {
                 this._edgesRenderers.data[edgesRendererIndex].render();
             }
+
+            // Restore Stencil state.
+            engine.setStencilBuffer(stencilState);
         }
 
         /**

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

@@ -20,8 +20,8 @@ mat3 transposeMat3(mat3 inMatrix) {
 
 float computeFallOff(float value, vec2 clipSpace, float frustumEdgeFalloff)
 {
-	float mask = smoothstep(1.0, 1.0 - frustumEdgeFalloff, dot(clipSpace, clipSpace));
-	return mix(1.0, value, mask);
+	float mask = smoothstep(1.0 - frustumEdgeFalloff, 1.0, clamp(dot(clipSpace, clipSpace), 0., 1.));
+	return mix(value, 1.0, mask);
 }
 
 vec3 applyEaseInOut(vec3 x){

+ 1 - 1
src/Shaders/ShadersInclude/pbrLightFunctions.fx

@@ -38,7 +38,7 @@ float computeDirectionalLightFalloff(vec3 lightDirection, vec3 directionToLightC
         falloff = exp2(dot(vec4(directionToLightCenterW, 1.0), lightDirectionSpreadSG));
     #else
         float cosAngle = max(0.000000000000001, dot(-lightDirection, directionToLightCenterW));
-        if (cosAngle >= lightAngle)
+        if (cosAngle >= cosHalfAngle)
         {
             falloff = max(0., pow(cosAngle, exponent));
         }

+ 3 - 3
src/Shaders/ShadersInclude/reflectionFunction.fx

@@ -15,7 +15,7 @@
 
 #ifdef REFLECTIONMAP_EQUIRECTANGULAR
 
-	vec3 cameraToVertex = normalize(worldPos.xyz - vEyePosition);
+	vec3 cameraToVertex = normalize(worldPos.xyz - vEyePosition.xyz);
 	vec3 r = reflect(cameraToVertex, worldNormal);
 	float t = clamp(r.y * -0.5 + 0.5, 0., 1.0);
 	float s = atan(r.z, r.x) * RECIPROCAL_PI2 + 0.5;
@@ -36,14 +36,14 @@
 #endif
 
 #ifdef REFLECTIONMAP_PLANAR
-	vec3 viewDir = worldPos.xyz - vEyePosition;
+	vec3 viewDir = worldPos.xyz - vEyePosition.xyz;
 	vec3 coords = normalize(reflect(viewDir, worldNormal));
 
 	return vec3(reflectionMatrix * vec4(coords, 1));
 #endif
 
 #ifdef REFLECTIONMAP_CUBIC
-	vec3 viewDir = worldPos.xyz - vEyePosition;
+	vec3 viewDir = worldPos.xyz - vEyePosition.xyz;
 	vec3 coords = reflect(viewDir, worldNormal);
 #ifdef INVERTCUBICMAP
 	coords.y = 1.0 - coords.y;

+ 15 - 13
src/Shaders/pbr.fragment.fx

@@ -1,4 +1,4 @@
-#if defined(BUMP)|| !defined(NORMAL)
+#if defined(BUMP) || !defined(NORMAL) || defined(FORCENORMALFORWARD)
 #extension GL_OES_standard_derivatives : enable
 #endif
 
@@ -14,7 +14,7 @@ precision highp float;
 
 #include<__decl__pbrFragment>
 
-uniform vec3 vEyePosition;
+uniform vec4 vEyePosition;
 uniform vec3 vAmbientColor;
 uniform vec4 vCameraInfos;
 
@@ -218,26 +218,28 @@ void main(void) {
 
 // _______________________________________________________________________________
 // _____________________________ Geometry Information ____________________________
-	vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
+	vec3 viewDirectionW = normalize(vEyePosition.xyz - vPositionW);
 
 #ifdef NORMAL
 	vec3 normalW = normalize(vNormalW);
 #else
-	vec3 normalW = normalize(cross(dFdx(vPositionW), dFdy(vPositionW)));
-#endif
-
-#ifdef BUMP
-	vec3 originalNormalW = normalW;
+	vec3 normalW = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;
 #endif
 
 #include<bumpFragment>
 
-#if defined(TWOSIDEDLIGHTING) && defined(NORMAL) 
-	normalW = gl_FrontFacing ? normalW : -normalW;
-
-	#ifdef BUMP
-		originalNormalW = gl_FrontFacing ? originalNormalW : -originalNormalW;;
+#if defined(FORCENORMALFORWARD) && defined(NORMAL)
+	vec3 faceNormal = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;
+	#if defined(TWOSIDEDLIGHTING)
+		faceNormal = gl_FrontFacing ? faceNormal : -faceNormal;
 	#endif
+
+	float comp = sign(dot(normalW, faceNormal));
+    normalW *= -comp;
+#endif
+
+#if defined(TWOSIDEDLIGHTING) && defined(NORMAL)
+	normalW = gl_FrontFacing ? normalW : -normalW;
 #endif
 
 // _____________________________ Albedo Information ______________________________

+ 41 - 2
src/Tools/babylon.extendedGamepad.ts

@@ -3,6 +3,7 @@ module BABYLON {
     export enum PoseEnabledControllerType {
         VIVE,
         OCULUS,
+        WINDOWS,
         GENERIC
     }
 
@@ -14,12 +15,22 @@ module BABYLON {
 
     export class PoseEnabledControllerHelper {
         public static InitiateController(vrGamepad: any) {
-            // for now, only Oculus and Vive are supported
+            // Oculus Touch
             if (vrGamepad.id.indexOf('Oculus Touch') !== -1) {
                 return new OculusTouchController(vrGamepad);
-            } else {
+            }
+            // Windows Mixed Reality controllers 
+            // else if (vrGamepad.id.indexOf('Spatial Control') === 0) {
+            //     //return new WindowsMixedRealityController(vrGamepad);
+            // }
+            // HTC Vive
+            else if (vrGamepad.id.toLowerCase().indexOf('openvr') !== -1) {
                 return new ViveController(vrGamepad);
             }
+            // Generic 
+            else {
+                return new GenericController(vrGamepad);
+            }
         }
     }
 
@@ -197,6 +208,13 @@ module BABYLON {
         public abstract initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void)
 
         private _setButtonValue(newState: ExtendedGamepadButton, currentState: ExtendedGamepadButton, buttonIndex: number) {
+            if (!newState) {
+                newState = {
+                    pressed: false,
+                    touched: false,
+                    value: 0
+                };
+            }  
             if (!currentState) {
                 this._buttons[buttonIndex] = {
                     pressed: newState.pressed,
@@ -440,7 +458,28 @@ module BABYLON {
         }
     }
 
+    export class GenericController extends WebVRController {
+        private _defaultModel: BABYLON.AbstractMesh;
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+        }
+
+        public initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void) {
+            SceneLoader.ImportMesh("", "http://yoda.blob.core.windows.net/models/", "genericvrcontroller.babylon", scene, (newMeshes) => {
+                this._defaultModel = newMeshes[1];
+                if (meshLoaded) {
+                    meshLoaded(this._defaultModel);
+                }
+                this.attachToMesh(this._defaultModel);
+            });
+        }
 
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            console.log("Button id: " + buttonIdx + "state: ");
+            console.dir(state);
+        }
+    }
 }
 
 interface ExtendedGamepadButton extends GamepadButton {

+ 9 - 6
src/babylon.engine.ts

@@ -543,7 +543,7 @@
         public renderEvenInBackground = true;
         public preventCacheWipeBetweenFrames = false;
         // To enable/disable IDB support and avoid XHR on .manifest
-        public enableOfflineSupport = BABYLON.Database;
+        public enableOfflineSupport = false;
         public scenes = new Array<Scene>();
 
         // Observables
@@ -967,18 +967,20 @@
                 this._currentBufferPointers[i] = new BufferPointer();
             }
 
-            //Load WebVR Devices
+            // Load WebVR Devices
             if (options.autoEnableWebVR) {
                 this.initWebVR();
             }
 
-            //Detect if we are running on a faulty buggy OS.
+            // Detect if we are running on a faulty buggy OS.
             this._badOS = /iPad/i.test(navigator.userAgent) || /iPhone/i.test(navigator.userAgent);
 
-            //Detect if we are running on a faulty buggy desktop OS.
+            // Detect if we are running on a faulty buggy desktop OS.
             this._badDesktopOS = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
 
             Tools.Log("Babylon.js engine (v" + Engine.Version + ") launched");
+
+            this.enableOfflineSupport = (BABYLON.Database !== undefined);
         }
 
         public get webGLVersion(): number {
@@ -2445,11 +2447,12 @@
             }
             this.resetTextureCache();
             this._currentEffect = null;
-            this._currentProgram = null;
 
             // 6/8/2017: deltakosh: Should not be required anymore. 
             // This message is then mostly for the future myself which will scream out loud when seeing that actually it was required :)
             if (bruteForce) {
+                this._currentProgram = null;
+
                 this._stencilState.reset();
                 this._depthCullingState.reset();
                 this.setDepthFunctionToLessOrEqual();
@@ -3269,7 +3272,7 @@
                     let maxLODIndex = Scalar.Log2(width) * scale + offset; // roughness = 1
 
                     let lodIndex = minLODIndex + (maxLODIndex - minLODIndex) * roughness;
-                    let mipmapIndex = Math.min(Math.max(Math.round(lodIndex), 0), maxLODIndex);
+                    let mipmapIndex = Math.round(Math.min(Math.max(lodIndex, 0), maxLODIndex));
 
                     var glTextureFromLod = gl.createTexture();
                     glTextureFromLod.isCube = true;

BIN
tests/validation/ReferenceImages/Flat2009.png


+ 1 - 1
tests/validation/config.json

@@ -209,7 +209,7 @@
     {
       "title": "MultiSample render targets",
       "renderCount": 10,
-      "playgroundId": "#12MKMN",
+      "playgroundId": "#12MKMN#0",
       "referenceImage": "MultiSample render targets.png"
     },            
     {

+ 1 - 1
tests/validation/validation.js

@@ -196,7 +196,7 @@ runTest(index) {
             }
         }
 
-        xmlHttp.open("GET", snippetUrl + "/" + test.playgroundId.replace("#", "/"));
+        xmlHttp.open("GET", snippetUrl + test.playgroundId.replace(/#/g, "/"));
         xmlHttp.send();
     } else {
         // Fix references